<p dir="auto"><a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/mpdehaan/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/mpdehaan">@mpdehaan</a> this is for you buddy</p>
<ul dir="auto">
<li>Bump specfile</li>
<li>Review man pages (any new paramaters added?)</li>
<li>For good measure:</li>
</ul>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="$ touch VERSION
$ make docs"><pre class="notranslate"><code class="notranslate">$ touch VERSION
$ make docs
</code></pre></div>
<ul dir="auto">
<li>Tag 0.7</li>
<li>ping me to start working on the corresponding Fedora update</li>
</ul>
<h5 dir="auto">ISSUE TYPE</h5>

<ul dir="auto">
<li>Bug Report</li>
</ul>
<h5 dir="auto">COMPONENT NAME</h5>

<p dir="auto">ansible/modules/cloud/openstack/os_server.py<br>
ansible/modules/cloud/openstack/os_volume.py<br>
ansible/modules/cloud/openstack/os_server_volume.py</p>
<h5 dir="auto">ANSIBLE VERSION</h5>

<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="2.3.1.0"><pre class="notranslate"><code class="notranslate">2.3.1.0
</code></pre></div>
<h5 dir="auto">CONFIGURATION</h5>

<p dir="auto">default ansible.cfg</p>
<h5 dir="auto">OS / ENVIRONMENT</h5>

<ol dir="auto">
<li>ansible installed Ubuntu 14.04</li>
<li>Test public cloud based on Openstack</li>
</ol>
<h5 dir="auto">SUMMARY</h5>

<h5 dir="auto">STEPS TO REPRODUCE</h5>

<p dir="auto">ansible-playbook test.yml</p>

<div class="highlight highlight-source-yaml notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="test.yml
---
- name: Create an instance
  hosts: localhost
  gather_facts: false

  vars:
    ipv4_address: 10.32.15.150

  tasks:
    - name: Create System Volume
      os_volume:
        state:                      present
        availability_zone:          &quot;AZ1&quot;
        size:                       &quot;8&quot;
        wait:                       yes                                 
        image:                      &quot;Standard_CentOS_7.3_latest&quot;
        volume_type:                &quot;SATA&quot;
        display_name:               &quot;ops-dummy-0001-volume-system&quot;  
     
    - name: Create Data Volume
      os_volume:
        state:                      present
        availability_zone:          &quot;AZ1&quot;
        size:                       &quot;50&quot;
        wait:                       yes                              
        volume_type:                &quot;SSD&quot;
        display_name:               &quot;ops-dummy-0001-volume-data&quot; 

    - name: Create static IP NIC
      os_port:
        state:                      present
        name:                       &quot;ops-dummy-0001-port1&quot;
        admin_state_up:             true
        network:                    &quot;76cc4d0c-18b8-433d-9dc8-a26b5c3f6508&quot;  
        security_groups:            &quot;ops-sg-tier1&quot;
        fixed_ips:
          - ip_address: &quot;10.32.15.150&quot;
      when: ipv4_address is defined
      
    - name: Create DHCP NIC
      os_port:
        state: present
        name:                       &quot;ops-dummy-0001-port1&quot;
        admin_state_up:             true
        network:                    &quot;76cc4d0c-18b8-433d-9dc8-a26b5c3f6508&quot;
        security_groups:            &quot;ops-sg-tier1&quot;
      when: ipv4_address is not defined

    - name: Create an instance
      os_server:
        state:                      present
        name:                       &quot;ops-dummy-0001&quot;                    
        key_name:                   &quot;ops&quot;
        boot_volume:                &quot;ops-dummy-0001-volume-system&quot;      
        timeout:                    200                                     
        flavor:                     &quot;c1.medium&quot;                             
        availability_zone:          &quot;AZ1&quot;
        auto_ip:                    false                                   
        reuse_ips:                  false # for now
        nics:
         - port-name:               &quot;ops-dummy-0001-port1&quot;              
      register: osvm                                                       
      
    - name: Attach volumes
      os_server_volume:
        state:                      present
        server:                     &quot;ops-dummy-0001&quot;
        volume:                     &quot;ops-dummy-0001-volume-data&quot;
        device:                     &quot;/dev/xvdb&quot;

    - name: Return configured local ipv4
      set_fact:
        instance_ipv4: &quot;{{ osvm.openstack.private_v4}}&quot;

    - name: Wait for 90 seconds so the instance is ready for the next task
      pause:
        seconds: 90"><pre class="notranslate"><span class="pl-s">test.yml</span>
---
- <span class="pl-ent">name</span>: <span class="pl-s">Create an instance</span>
  <span class="pl-ent">hosts</span>: <span class="pl-s">localhost</span>
  <span class="pl-ent">gather_facts</span>: <span class="pl-c1">false</span>

  <span class="pl-ent">vars</span>:
    <span class="pl-ent">ipv4_address</span>: <span class="pl-s">10.32.15.150</span>

  <span class="pl-ent">tasks</span>:
    - <span class="pl-ent">name</span>: <span class="pl-s">Create System Volume</span>
      <span class="pl-ent">os_volume</span>:
        <span class="pl-ent">state</span>:                      <span class="pl-s">present</span>
        <span class="pl-ent">availability_zone</span>:          <span class="pl-s"><span class="pl-pds">"</span>AZ1<span class="pl-pds">"</span></span>
        <span class="pl-ent">size</span>:                       <span class="pl-s"><span class="pl-pds">"</span>8<span class="pl-pds">"</span></span>
        <span class="pl-ent">wait</span>:                       <span class="pl-s">yes                                 </span>
        <span class="pl-ent">image</span>:                      <span class="pl-s"><span class="pl-pds">"</span>Standard_CentOS_7.3_latest<span class="pl-pds">"</span></span>
        <span class="pl-ent">volume_type</span>:                <span class="pl-s"><span class="pl-pds">"</span>SATA<span class="pl-pds">"</span></span>
        <span class="pl-ent">display_name</span>:               <span class="pl-s"><span class="pl-pds">"</span>ops-dummy-0001-volume-system<span class="pl-pds">"</span></span>  
     
    - <span class="pl-ent">name</span>: <span class="pl-s">Create Data Volume</span>
      <span class="pl-ent">os_volume</span>:
        <span class="pl-ent">state</span>:                      <span class="pl-s">present</span>
        <span class="pl-ent">availability_zone</span>:          <span class="pl-s"><span class="pl-pds">"</span>AZ1<span class="pl-pds">"</span></span>
        <span class="pl-ent">size</span>:                       <span class="pl-s"><span class="pl-pds">"</span>50<span class="pl-pds">"</span></span>
        <span class="pl-ent">wait</span>:                       <span class="pl-s">yes                              </span>
        <span class="pl-ent">volume_type</span>:                <span class="pl-s"><span class="pl-pds">"</span>SSD<span class="pl-pds">"</span></span>
        <span class="pl-ent">display_name</span>:               <span class="pl-s"><span class="pl-pds">"</span>ops-dummy-0001-volume-data<span class="pl-pds">"</span></span> 

    - <span class="pl-ent">name</span>: <span class="pl-s">Create static IP NIC</span>
      <span class="pl-ent">os_port</span>:
        <span class="pl-ent">state</span>:                      <span class="pl-s">present</span>
        <span class="pl-ent">name</span>:                       <span class="pl-s"><span class="pl-pds">"</span>ops-dummy-0001-port1<span class="pl-pds">"</span></span>
        <span class="pl-ent">admin_state_up</span>:             <span class="pl-c1">true</span>
        <span class="pl-ent">network</span>:                    <span class="pl-s"><span class="pl-pds">"</span>76cc4d0c-18b8-433d-9dc8-a26b5c3f6508<span class="pl-pds">"</span></span>  
        <span class="pl-ent">security_groups</span>:            <span class="pl-s"><span class="pl-pds">"</span>ops-sg-tier1<span class="pl-pds">"</span></span>
        <span class="pl-ent">fixed_ips</span>:
          - <span class="pl-ent">ip_address</span>: <span class="pl-s"><span class="pl-pds">"</span>10.32.15.150<span class="pl-pds">"</span></span>
      <span class="pl-ent">when</span>: <span class="pl-s">ipv4_address is defined</span>
      
    - <span class="pl-ent">name</span>: <span class="pl-s">Create DHCP NIC</span>
      <span class="pl-ent">os_port</span>:
        <span class="pl-ent">state</span>: <span class="pl-s">present</span>
        <span class="pl-ent">name</span>:                       <span class="pl-s"><span class="pl-pds">"</span>ops-dummy-0001-port1<span class="pl-pds">"</span></span>
        <span class="pl-ent">admin_state_up</span>:             <span class="pl-c1">true</span>
        <span class="pl-ent">network</span>:                    <span class="pl-s"><span class="pl-pds">"</span>76cc4d0c-18b8-433d-9dc8-a26b5c3f6508<span class="pl-pds">"</span></span>
        <span class="pl-ent">security_groups</span>:            <span class="pl-s"><span class="pl-pds">"</span>ops-sg-tier1<span class="pl-pds">"</span></span>
      <span class="pl-ent">when</span>: <span class="pl-s">ipv4_address is not defined</span>

    - <span class="pl-ent">name</span>: <span class="pl-s">Create an instance</span>
      <span class="pl-ent">os_server</span>:
        <span class="pl-ent">state</span>:                      <span class="pl-s">present</span>
        <span class="pl-ent">name</span>:                       <span class="pl-s"><span class="pl-pds">"</span>ops-dummy-0001<span class="pl-pds">"</span></span>                    
        <span class="pl-ent">key_name</span>:                   <span class="pl-s"><span class="pl-pds">"</span>ops<span class="pl-pds">"</span></span>
        <span class="pl-ent">boot_volume</span>:                <span class="pl-s"><span class="pl-pds">"</span>ops-dummy-0001-volume-system<span class="pl-pds">"</span></span>      
        <span class="pl-ent">timeout</span>:                    <span class="pl-c1">200</span>                                     
        <span class="pl-ent">flavor</span>:                     <span class="pl-s"><span class="pl-pds">"</span>c1.medium<span class="pl-pds">"</span></span>                             
        <span class="pl-ent">availability_zone</span>:          <span class="pl-s"><span class="pl-pds">"</span>AZ1<span class="pl-pds">"</span></span>
        <span class="pl-ent">auto_ip</span>:                    <span class="pl-c1">false</span>                                   
        <span class="pl-ent">reuse_ips</span>:                  <span class="pl-s">false </span><span class="pl-c"><span class="pl-c">#</span> for now</span>
        <span class="pl-ent">nics</span>:
         - <span class="pl-ent">port-name</span>:               <span class="pl-s"><span class="pl-pds">"</span>ops-dummy-0001-port1<span class="pl-pds">"</span></span>              
      <span class="pl-ent">register</span>: <span class="pl-s">osvm                                                       </span>
      
    - <span class="pl-ent">name</span>: <span class="pl-s">Attach volumes</span>
      <span class="pl-ent">os_server_volume</span>:
        <span class="pl-ent">state</span>:                      <span class="pl-s">present</span>
        <span class="pl-ent">server</span>:                     <span class="pl-s"><span class="pl-pds">"</span>ops-dummy-0001<span class="pl-pds">"</span></span>
        <span class="pl-ent">volume</span>:                     <span class="pl-s"><span class="pl-pds">"</span>ops-dummy-0001-volume-data<span class="pl-pds">"</span></span>
        <span class="pl-ent">device</span>:                     <span class="pl-s"><span class="pl-pds">"</span>/dev/xvdb<span class="pl-pds">"</span></span>

    - <span class="pl-ent">name</span>: <span class="pl-s">Return configured local ipv4</span>
      <span class="pl-ent">set_fact</span>:
        <span class="pl-ent">instance_ipv4</span>: <span class="pl-s"><span class="pl-pds">"</span>{{ osvm.openstack.private_v4}}<span class="pl-pds">"</span></span>

    - <span class="pl-ent">name</span>: <span class="pl-s">Wait for 90 seconds so the instance is ready for the next task</span>
      <span class="pl-ent">pause</span>:
        <span class="pl-ent">seconds</span>: <span class="pl-c1">90</span></pre></div>

<h5 dir="auto">EXPECTED RESULTS</h5>

<p dir="auto">1.The whole job completes within a very short time.<br>
2. No performance difference between openstack plugin and aws plugin</p>
<h5 dir="auto">ACTUAL RESULTS</h5>

<p dir="auto">===============================================================================<br>
Create an instance ---------------------------------------------------- 218.52s<br>
Attach volumes -------------------------------------------------------- 173.52s<br>
Create System Volume -------------------------------------------------- 116.32s<br>
Wait for 90 seconds so the instance is ready for the next task --------- 90.01s<br>
Create static IP NIC --------------------------------------------------- 16.40s<br>
Create Data Volume ----------------------------------------------------- 13.59s<br>
Return configured local ipv4 -------------------------------------------- 0.02s<br>
Create DHCP NIC --------------------------------------------------------- 0.01s<br>
real	10m29.258s<br>
user	0m44.327s<br>
sys	0m6.167s</p>
<p dir="auto"><strong>Analyse:</strong></p>
<p dir="auto">From the API log, we find some unreasonable parts , which means unreasonable api calls.</p>
<p dir="auto">1.When we create system volume, the playbook specify volume name and image id, the plugin should use the list API with filter for volume and get image by id, rather than List all Volumes with detail and List all images with detail.</p>
<p dir="auto">2.The API like this list all images and all volumes cost much time that the API with some filter parameters.</p>
<ol start="3" dir="auto">
<li>
<p dir="auto">Since the playbook specify flavor name already, the OpenStack plugin shouldn’t get all flavors with detail and loop to query every flavor detail</p>
</li>
<li>
<p dir="auto">When we create data volume, the playbook specify volume name, the plugin should use the list API with filter for volume rather than List all Volumes with detail, the same to the port, floatingip, network, subnet, security-group, image.</p>
</li>
</ol>
<p dir="auto">5.After the VM create successful, the plugin should call get detail by instance id rather that loop to get all of the resources, such as port, floatingip, network, security-group, image, subnet etc.</p>
<p dir="auto">6.The playbook specify image id, the plugin should use get image by id API, rather than List all images with detail.</p>
<p dir="auto">7.For the step attaching volume for instance, there is no reason to list all of the resource about the tenant, from the process, we find that this step call lots of list all resource for port, servers, floatingips, networks, subnets, security-groups, volumes etc.</p>
<p dir="auto">In all,</p>
<p dir="auto">Every get_${resource} function in OpenStack plugin has the below logic:<br>
a.	List all of the resources for the specify tenant.<br>
b.	Loop all resource to find the one which the user specify.<br>
The resource maybe image, flavor, volume, server, port, subnet, floating-ip, security-group, network etc.</p>
<p dir="auto">These functions are called very frequently during the task, for example, after create instance request sent, the plugin will call get_server(server_id) function to check if the VM status is ACTIVE.</p>
<p dir="auto">Every query function to check the resource status in Amazon plugin is not similar with the implementation in OpenStack plugin, and we can find the amazon plugin have better performance and specialized optimization in the Plugin.</p>
<p dir="auto"><strong>Above all,</strong><br>
<strong>a.	The result of callback plugin isn’t very proper, it includes lots of unreasonable API calls lead to more time cost of the testing, and we can confirm that all the API calls  during the testing is normal. For example, the time of creating instances cost 23s, but the ansible count 218s totally.<br>
For create instance task, it include the following API calls:</strong><br>
List all flavors<br>
Get every flavors detail<br>
List all flavors with detail<br>
List all volumes with detail<br>
Create Instances<br>
List all servers<br>
List all networks<br>
List all subnets<br>
List all servers to check if servers ACTIVE<br>
List all ports<br>
List all floatingips<br>
List all ports<br>
List all floatingips<br>
List all flavors with detail<br>
Get Server security-groups<br>
List all images with detail<br>
List all volumes with detail<br>
<strong>b.	The logic of OpenStack plugin of Ansible has poor performance, if the plugin would get a resource by id, it will get all the resources of the tenant, the loop to find the specify resource.</strong></p>

<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="We use a temporary method to improve a little the  performance, but it is really not the best way. We hope ansible can fix this issue. Thanks~

Our steps are as follows:

1. Modify these three modules

ansible/modules/cloud/openstack/os_server.py
ansible/modules/cloud/openstack/os_volume.py
ansible/modules/cloud/openstack/os_server_volume.py

 a. Edit the os_server.py and append the below code before the line with content “cloud = shade.openstack_cloud(**cloud_params)”

from shade import os_monkey_patch
os_monkey_patch.monkey_patch_os_server()

b.Edit the os_volume.py and append the below code before the line with content “cloud = shade.openstack_cloud(**module.params)”

from shade import os_monkey_patch
os_monkey_patch.monkey_patch_os_volume()

c.Edit the os_server_volume.py and append the below code before the line with content “cloud = shade.openstack_cloud(**module.params)”

from shade import os_monkey_patch
os_monkey_patch.monkey_patch_os_server_volume()


2. Touch a new file called **os_monkey_patch.py**
python2.7/dist-packages/shade/os_monkey_patch.py

The file is as follows:
import shade
import uuid
import keystoneauth1.exceptions
import warnings
from urllib import urlencode

from shade import _utils
from shade import meta
from shade.openstackcloud import OpenStackCloud


# Patch the os_server module in ansible.modules.cloud.openstack
def patch_list_flavors(self, get_extra=None, filters=None):
    if get_extra is None:
        get_extra = self._extra_config['get_flavor_extra_specs']
    flavors = self._normalize_flavors(
        self._compute_client.get(
            '/flavors/detail', params=dict(is_public='None'),
            error_message=&quot;Error fetching flavor list&quot;))

    filters_values = filters.values() if filters else []
    for flavor in flavors:
        if flavor.id not in filters_values and flavor.name not in filters_values:
            continue
        if not flavor.extra_specs and get_extra:
            endpoint = &quot;/flavors/{id}/os-extra_specs&quot;.format(
                id=flavor.id)
            try:
                flavor.extra_specs = self._compute_client.get(
                    endpoint,
                    error_message=&quot;Error fetching flavor extra specs&quot;)
            except shade.exc.OpenStackCloudHTTPError as e:
                flavor.extra_specs = {}
                self.log.debug(
                    'Fetching extra specs for flavor failed:'
                    ' %(msg)s', {'msg': str(e)})
    return flavors


def patch_search_flavors(self, name_or_id=None, filters=None, get_extra=True):
    flavors = self.list_flavors(get_extra=get_extra, filters={'name_or_id': name_or_id})
    return _utils._filter_list(flavors, name_or_id, filters)


def _no_pending_images(images):
    &quot;&quot;&quot;If there are any images not in a steady state, don't cache&quot;&quot;&quot;
    for image in images:
        if image.status not in ('active', 'deleted', 'killed'):
            return False
    return True


@_utils.cache_on_arguments(should_cache_fn=_no_pending_images)
def patch_list_images(self, filter_deleted=True, filters=None):
    images = []
    image_list = []
    try:
        if self.cloud_config.get_api_version('image') == '2':
            endpoint = '/images'
        else:
            endpoint = '/images/detail'
        params = {}
        try:
            uuid.UUID(filters['name_or_id'])
            params['id'] = filters['name_or_id']
        except ValueError:
            params['name'] = filters['name_or_id']
        if params:
            response = self._image_client.get(endpoint + &quot;?&quot; + urlencode(params))
            if len(response['images']) == 0 and 'id' in params:
                params['name'] = params.pop('id')
                response = self._image_client.get(endpoint + &quot;?&quot; + urlencode(params))
        else:
            response = self._image_client.get(endpoint)

    except keystoneauth1.exceptions.catalog.EndpointNotFound:
        response = self._compute_client.get('/images/detail')
    while 'next' in response:
        image_list.extend(meta.obj_list_to_munch(response['images']))
        endpoint = response['next']
        response = self._raw_image_client.get(endpoint)
    if 'images' in response:
        image_list.extend(meta.obj_list_to_munch(response['images']))
    else:
        image_list.extend(response)

    for image in image_list:
        if not filter_deleted:
            images.append(image)
        elif image.status.lower() != 'deleted':
            images.append(image)
    return self._normalize_images(images)


def patch_search_images(self, name_or_id=None, filters=None):
    images = self.list_images(filters={'name_or_id': name_or_id})
    return _utils._filter_list(images, name_or_id, filters)


def _no_pending_volumes(volumes):
    for volume in volumes:
        if volume['status'] not in ('available', 'error', 'in-use'):
            return False
    return True


def patch_search_volumes(self, name_or_id=None, filters=None):
    volumes = self.list_volumes(filters={'name_or_id': name_or_id})
    return _utils._filter_list(
        volumes, name_or_id, filters)


@_utils.cache_on_arguments(should_cache_fn=_no_pending_volumes)
def patch_list_volumes(self, cache=True, filters=None):
    def _list(data):
        volumes.extend(data.get('volumes', []))
        endpoint = None
        for l in data.get('volumes_links', []):
            if 'rel' in l and 'next' == l['rel']:
                endpoint = l['href']
                break
        if endpoint:
            try:
                _list(self._volume_client.get(endpoint))
            except shade.exc.OpenStackCloudURINotFound:
                self.log.debug(
                    &quot;While listing volumes, could not find next link&quot;
                    &quot; {link}.&quot;.format(link=data))
                raise

    if not cache:
        warnings.warn('cache argument to list_volumes is deprecated. Use '
                      'invalidate instead.')
    attempts = 5
    for _ in range(attempts):
        volumes = []
        params = {}
        if not filters:
            data = self._volume_client.get('/volumes/detail')
        else:
            try:
                uuid.UUID(filters['name_or_id'])
                params['id'] = filters['name_or_id']
            except ValueError:
                params['name'] = filters['name_or_id']
            if params:
                data = self._volume_client.get('/volumes/detail', params=params)
                if len(data.get('volumes', [])) == 0 and 'id' in params:
                    params['name'] = params.pop('id')
                    data = self._volume_client.get('/volumes/detail', params=params)
            else:
                data = self._volume_client.get('/volumes/detail')

        if 'volumes_links' not in data:
            volumes.extend(data.get('volumes', []))
            break
        try:
            _list(data)
            break
        except shade.exc.OpenStackCloudURINotFound:
            pass
    else:
        self.log.debug(
            &quot;List volumes failed to retrieve all volumes after&quot;
            &quot; {attempts} attempts. Returning what we found.&quot;.format(
                attempts=attempts))
    return self._normalize_volumes(
        meta.get_and_munchify(key=None, data=volumes))


def monkey_patch_os_server():
    setattr(OpenStackCloud, 'list_flavors', patch_list_flavors)
    setattr(OpenStackCloud, 'search_flavors', patch_search_flavors)
    setattr(OpenStackCloud, 'list_images', patch_list_images)
    setattr(OpenStackCloud, 'search_images', patch_search_images)
    setattr(OpenStackCloud, 'list_volumes', patch_list_volumes)
    setattr(OpenStackCloud, 'search_volumes', patch_search_volumes)


def monkey_patch_os_volume():
    setattr(OpenStackCloud, 'list_images', patch_list_images)
    setattr(OpenStackCloud, 'search_images', patch_search_images)
    setattr(OpenStackCloud, 'list_volumes', patch_list_volumes)
    setattr(OpenStackCloud, 'search_volumes', patch_search_volumes)


def monkey_patch_os_server_volume():
    setattr(OpenStackCloud, 'list_volumes', patch_list_volumes)
    setattr(OpenStackCloud, 'search_volumes', patch_search_volumes)


"><pre class="notranslate"><code class="notranslate">We use a temporary method to improve a little the  performance, but it is really not the best way. We hope ansible can fix this issue. Thanks~

Our steps are as follows:

1. Modify these three modules

ansible/modules/cloud/openstack/os_server.py
ansible/modules/cloud/openstack/os_volume.py
ansible/modules/cloud/openstack/os_server_volume.py

 a. Edit the os_server.py and append the below code before the line with content “cloud = shade.openstack_cloud(**cloud_params)”

from shade import os_monkey_patch
os_monkey_patch.monkey_patch_os_server()

b.Edit the os_volume.py and append the below code before the line with content “cloud = shade.openstack_cloud(**module.params)”

from shade import os_monkey_patch
os_monkey_patch.monkey_patch_os_volume()

c.Edit the os_server_volume.py and append the below code before the line with content “cloud = shade.openstack_cloud(**module.params)”

from shade import os_monkey_patch
os_monkey_patch.monkey_patch_os_server_volume()


2. Touch a new file called **os_monkey_patch.py**
python2.7/dist-packages/shade/os_monkey_patch.py

The file is as follows:
import shade
import uuid
import keystoneauth1.exceptions
import warnings
from urllib import urlencode

from shade import _utils
from shade import meta
from shade.openstackcloud import OpenStackCloud


# Patch the os_server module in ansible.modules.cloud.openstack
def patch_list_flavors(self, get_extra=None, filters=None):
    if get_extra is None:
        get_extra = self._extra_config['get_flavor_extra_specs']
    flavors = self._normalize_flavors(
        self._compute_client.get(
            '/flavors/detail', params=dict(is_public='None'),
            error_message="Error fetching flavor list"))

    filters_values = filters.values() if filters else []
    for flavor in flavors:
        if flavor.id not in filters_values and flavor.name not in filters_values:
            continue
        if not flavor.extra_specs and get_extra:
            endpoint = "/flavors/{id}/os-extra_specs".format(
                id=flavor.id)
            try:
                flavor.extra_specs = self._compute_client.get(
                    endpoint,
                    error_message="Error fetching flavor extra specs")
            except shade.exc.OpenStackCloudHTTPError as e:
                flavor.extra_specs = {}
                self.log.debug(
                    'Fetching extra specs for flavor failed:'
                    ' %(msg)s', {'msg': str(e)})
    return flavors


def patch_search_flavors(self, name_or_id=None, filters=None, get_extra=True):
    flavors = self.list_flavors(get_extra=get_extra, filters={'name_or_id': name_or_id})
    return _utils._filter_list(flavors, name_or_id, filters)


def _no_pending_images(images):
    """If there are any images not in a steady state, don't cache"""
    for image in images:
        if image.status not in ('active', 'deleted', 'killed'):
            return False
    return True


@_utils.cache_on_arguments(should_cache_fn=_no_pending_images)
def patch_list_images(self, filter_deleted=True, filters=None):
    images = []
    image_list = []
    try:
        if self.cloud_config.get_api_version('image') == '2':
            endpoint = '/images'
        else:
            endpoint = '/images/detail'
        params = {}
        try:
            uuid.UUID(filters['name_or_id'])
            params['id'] = filters['name_or_id']
        except ValueError:
            params['name'] = filters['name_or_id']
        if params:
            response = self._image_client.get(endpoint + "?" + urlencode(params))
            if len(response['images']) == 0 and 'id' in params:
                params['name'] = params.pop('id')
                response = self._image_client.get(endpoint + "?" + urlencode(params))
        else:
            response = self._image_client.get(endpoint)

    except keystoneauth1.exceptions.catalog.EndpointNotFound:
        response = self._compute_client.get('/images/detail')
    while 'next' in response:
        image_list.extend(meta.obj_list_to_munch(response['images']))
        endpoint = response['next']
        response = self._raw_image_client.get(endpoint)
    if 'images' in response:
        image_list.extend(meta.obj_list_to_munch(response['images']))
    else:
        image_list.extend(response)

    for image in image_list:
        if not filter_deleted:
            images.append(image)
        elif image.status.lower() != 'deleted':
            images.append(image)
    return self._normalize_images(images)


def patch_search_images(self, name_or_id=None, filters=None):
    images = self.list_images(filters={'name_or_id': name_or_id})
    return _utils._filter_list(images, name_or_id, filters)


def _no_pending_volumes(volumes):
    for volume in volumes:
        if volume['status'] not in ('available', 'error', 'in-use'):
            return False
    return True


def patch_search_volumes(self, name_or_id=None, filters=None):
    volumes = self.list_volumes(filters={'name_or_id': name_or_id})
    return _utils._filter_list(
        volumes, name_or_id, filters)


@_utils.cache_on_arguments(should_cache_fn=_no_pending_volumes)
def patch_list_volumes(self, cache=True, filters=None):
    def _list(data):
        volumes.extend(data.get('volumes', []))
        endpoint = None
        for l in data.get('volumes_links', []):
            if 'rel' in l and 'next' == l['rel']:
                endpoint = l['href']
                break
        if endpoint:
            try:
                _list(self._volume_client.get(endpoint))
            except shade.exc.OpenStackCloudURINotFound:
                self.log.debug(
                    "While listing volumes, could not find next link"
                    " {link}.".format(link=data))
                raise

    if not cache:
        warnings.warn('cache argument to list_volumes is deprecated. Use '
                      'invalidate instead.')
    attempts = 5
    for _ in range(attempts):
        volumes = []
        params = {}
        if not filters:
            data = self._volume_client.get('/volumes/detail')
        else:
            try:
                uuid.UUID(filters['name_or_id'])
                params['id'] = filters['name_or_id']
            except ValueError:
                params['name'] = filters['name_or_id']
            if params:
                data = self._volume_client.get('/volumes/detail', params=params)
                if len(data.get('volumes', [])) == 0 and 'id' in params:
                    params['name'] = params.pop('id')
                    data = self._volume_client.get('/volumes/detail', params=params)
            else:
                data = self._volume_client.get('/volumes/detail')

        if 'volumes_links' not in data:
            volumes.extend(data.get('volumes', []))
            break
        try:
            _list(data)
            break
        except shade.exc.OpenStackCloudURINotFound:
            pass
    else:
        self.log.debug(
            "List volumes failed to retrieve all volumes after"
            " {attempts} attempts. Returning what we found.".format(
                attempts=attempts))
    return self._normalize_volumes(
        meta.get_and_munchify(key=None, data=volumes))


def monkey_patch_os_server():
    setattr(OpenStackCloud, 'list_flavors', patch_list_flavors)
    setattr(OpenStackCloud, 'search_flavors', patch_search_flavors)
    setattr(OpenStackCloud, 'list_images', patch_list_images)
    setattr(OpenStackCloud, 'search_images', patch_search_images)
    setattr(OpenStackCloud, 'list_volumes', patch_list_volumes)
    setattr(OpenStackCloud, 'search_volumes', patch_search_volumes)


def monkey_patch_os_volume():
    setattr(OpenStackCloud, 'list_images', patch_list_images)
    setattr(OpenStackCloud, 'search_images', patch_search_images)
    setattr(OpenStackCloud, 'list_volumes', patch_list_volumes)
    setattr(OpenStackCloud, 'search_volumes', patch_search_volumes)


def monkey_patch_os_server_volume():
    setattr(OpenStackCloud, 'list_volumes', patch_list_volumes)
    setattr(OpenStackCloud, 'search_volumes', patch_search_volumes)



</code></pre></div>