Bug 1577638

Summary: Regression: allow filtering stacks based on regex / non-exact match
Product: Red Hat OpenStack Reporter: Andreas Karis <akaris>
Component: python-django-horizonAssignee: Radomir Dopieralski <rdopiera>
Status: CLOSED EOL QA Contact: Ido Ovadia <iovadia>
Severity: medium Docs Contact:
Priority: medium    
Version: 12.0 (Pike)CC: athomas, jrist, mrunge, rdopiera, srevivo
Target Milestone: Upstream M2Keywords: Triaged, ZStream
Target Release: 12.0 (Pike)   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: Doc Type: If docs needed, set a value
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2019-01-11 16:26:38 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Attachments:
Description Flags
osp11 baseline
none
osp 11 filter 1
none
OSP 11 filter 2
none
OSP 10 - regex filtering works none

Description Andreas Karis 2018-05-13 16:57:01 UTC
Description of problem:
OSP 10 and before allowed non-exact match searches in dashboard/project/stacks/

This was possible because the web interface had a JavaScript filter that was applied on the client side. As of OSP 11, this filter was exchanged for a server side filter. While this step seems reasonable to me, we currently can only do exact match filtering which significantly reduces the utility of the filter tool.

Additional info:


I suppose that a change to FilterAction or another method is needed, but did not dig any further than this:

+++++++++++++++++++++++++++++++++++++++++++++

This gets translated into a POST request:
~~~
<form action="/dashboard/project/stacks/" method="POST" class="ng-pristine ng-valid"><input name="csrfmiddlewaretoken" value="BQGe1h5vk2wvo6DSSpog8VR0d1JpaihJ" type="hidden">
  

   <table id="stacks" class="table table-striped datatable ">
    
      <caption>
        
        
<div class="table_actions clearfix">

  
    <div class="table_search">
      
        

<div class="themable-select dropdown " xmlns="http://www.w3.org/1999/html"><button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" title="name" aria-expanded="false"><span class="dropdown-title"> Stack Name</span><span class="fa fa-caret-down"></span></button><ul class="dropdown-menu"><li data-original-index="0" data-toggle="tooltip" data-placement="top" title="Case-sensitive"><a data-select-value="name" true="">Stack Name</a></li><li data-original-index="1" data-toggle="tooltip" data-placement="top"><a data-select-value="id" true="">Stack ID =</a></li><li data-original-index="2" data-toggle="tooltip" data-placement="top"><a data-select-value="status" true="">Status =</a></li></ul><select name="stacks__filter__q_field"><option value="name" selected="selected" true="">Stack Name</option><option value="id" true="">Stack ID =</option><option value="status" true="">Status =</option></select></div>

      
      <input class="form-control" value="test" name="stacks__filter__q" type="text">
      <button type="submit" class="btn btn-default " id="stacks__action_filter">Filter</button>
    </div>
  




  
  
    
<a id="stacks__action_launch" class="btn data-table-action btn-default ajax-modal" href="/dashboard/project/stacks/select_template" title="Launch Stack"><span class="fa fa-plus"></span> Launch Stack</a>

  
    
<a id="stacks__action_preview" class="btn data-table-action btn-default ajax-modal" href="/dashboard/project/stacks/preview_template" title="Preview Stack"><span class="fa fa-eye"></span> Preview Stack</a>

  

  
  


</div>

      </caption>
    
   <thead>
  
      
  
  
  
      
      <tr class="table_column_header">
        
          <th class="multi_select_column">
            
            
          </th>
        
          <th class="sortable anchor normal_column">
            Stack Name
            
          </th>
        
          <th class="sortable normal_column">
            Created
            
          </th>
        
          <th class="sortable normal_column">
            Updated
            
          </th>
        
          <th class="sortable hide normal_column">
            Status
            
          </th>
        
          <th class="sortable normal_column">
            Status
            
          </th>
        
          <th class="actions_column">
            Actions
            
          </th>
        
      </tr>
      
  
    </thead>
  
    <tbody>
    
    <tr class="odd empty">
      
        <td colspan="7">No items to display.</td>
      
    </tr>
    
    </tbody>
  
  
    
  
  </table>

  
  </form>
~~~

~~~
POST /dashboard/project/stacks/ HTTP/1.1
Host: 10.0.0.10
User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:59.0) Gecko/20100101 Firefox/59.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://10.0.0.10/dashboard/project/stacks/
Content-Type: application/x-www-form-urlencoded
Content-Length: 104
Cookie: SERVERID=overcloud-controller-0; csrftoken=BQGe1h5vk2wvo6DSSpog8VR0d1JpaihJ; login_region="http://172.16.2.14:5000/v2.0"; login_domain=; sessionid=jn8wbxxzjqm9esjnubek3pp58fn7hhq8
Connection: keep-alive
Upgrade-Insecure-Requests: 1

csrfmiddlewaretoken=BQGe1h5vk2wvo6DSSpog8VR0d1JpaihJ
stacks__filter__q_field=name
stacks__filter__q=test
~~~

MySQL query:
~~~
                118596 Query    SELECT stack.created_at AS stack_created_at, stack.deleted_at AS stack_deleted_at, stack.action 
AS stack_action, stack.status AS stack_status, stack.status_reason AS stack_status_reason, stack.id AS stack_id, stack.name AS s
tack_name, stack.raw_template_id AS stack_raw_template_id, stack.prev_raw_template_id AS stack_prev_raw_template_id, stack.usern
ame AS stack_username, stack.tenant AS stack_tenant, stack.user_creds_id AS stack_user_creds_id, stack.owner_id AS stack_owner_i
d, stack.parent_resource_name AS stack_parent_resource_name, stack.timeout AS stack_timeout, stack.disable_rollback AS stack_dis
able_rollback, stack.stack_user_project_id AS stack_stack_user_project_id, stack.backup AS stack_backup, stack.nested_depth AS s
tack_nested_depth, stack.convergence AS stack_convergence, stack.current_traversal AS stack_current_traversal, stack.current_dep
s AS stack_current_deps, stack.updated_at AS stack_updated_at 
FROM stack 
WHERE stack.deleted_at IS NULL AND stack.owner_id IS NULL AND NOT (EXISTS (SELECT 1 
FROM stack_tag 
WHERE stack.id = stack_tag.stack_id AND stack_tag.tag IN ('data-processing-cluster'))) AND stack.name = 'anotherstack' ORDER BY 
stack.created_at DESC, stack.id DESC 
 LIMIT 21
~~~

~~~
                118531 Query    SELECT 1
                118531 Query    SELECT stack_tag.created_at AS stack_tag_created_at, stack_tag.updated_at AS stack_tag_updated_a
t, stack_tag.id AS stack_tag_id, stack_tag.tag AS stack_tag_tag, stack_tag.stack_id AS stack_tag_stack_id, anon_1.stack_id AS an
on_1_stack_id 
FROM (SELECT stack.id AS stack_id 
FROM stack 
WHERE stack.deleted_at IS NULL AND stack.owner_id IS NULL AND NOT (EXISTS (SELECT 1 
FROM stack_tag 
WHERE stack.id = stack_tag.stack_id AND stack_tag.tag IN ('data-processing-cluster'))) AND stack.name = 'anotherstack' ORDER BY 
stack.created_at DESC, stack.id DESC 
 LIMIT 21) AS anon_1 INNER JOIN stack_tag ON anon_1.stack_id = stack_tag.stack_id ORDER BY anon_1.stack_id
                118531 Query    ROLLBACK
                118596 Query    SELECT 1
                118596 Query    SELECT stack_tag.created_at AS stack_tag_created_at, stack_tag.updated_at AS stack_tag_updated_a
t, stack_tag.id AS stack_tag_id, stack_tag.tag AS stack_tag_tag, stack_tag.stack_id AS stack_tag_stack_id 
~~~

The code that defines/registers the filter is here - StacksFilterAction inherits from FilterAction:
~~~
[root@overcloud-controller-0 stacks]# pwd
/usr/share/openstack-dashboard/openstack_dashboard/dashboards/project/stacks
[root@overcloud-controller-0 stacks]# cat tables.py  | grep 'Stack ID =' -C5


class StacksFilterAction(tables.FilterAction):
    filter_type = 'server'
    filter_choices = (('name', _('Stack Name'), True, _('Case-sensitive')),
                      ('id', _('Stack ID ='), True),
                      ('status', _('Status ='), True))


class StacksTable(tables.DataTable):
    STATUS_CHOICES = (
[root@overcloud-controller-0 stacks]# 
~~~

Imported from modules horizon:
~~~
from horizon import tables
(...)
~~~

The code for the base class is here:
~~~
[root@overcloud-controller-0 horizon]# pwd
/usr/lib/python2.7/site-packages/horizon
[root@overcloud-controller-0 horizon]# grep 'class FilterAction' * -R
tables/actions.py:class FilterAction(BaseAction):
[root@overcloud-controller-0 horizon]# 
~~~

Comment 1 Andreas Karis 2018-05-13 16:57:42 UTC
At least OSP 11 and OSP 12 are affected

Comment 2 Andreas Karis 2018-05-13 16:59:03 UTC
Created attachment 1435773 [details]
osp11 baseline

Comment 3 Andreas Karis 2018-05-13 16:59:26 UTC
Created attachment 1435774 [details]
osp 11 filter 1

Comment 4 Andreas Karis 2018-05-13 16:59:53 UTC
Created attachment 1435775 [details]
OSP 11 filter 2

Comment 5 Andreas Karis 2018-05-13 17:01:21 UTC
Created attachment 1435777 [details]
OSP 10 - regex filtering works