1 # Copyright 2016 Cisco Systems, Inc. All rights reserved.
3 # Licensed under the Apache License, Version 2.0 (the "License"); you may
4 # not use this file except in compliance with the License. You may obtain
5 # a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 # License for the specific language governing permissions and limitations
14 """Module to interface with nova and glance."""
19 from glanceclient import exc as glance_exception
21 from glanceclient.openstack.common.apiclient.exceptions import NotFound as GlanceImageNotFound
23 from glanceclient.v1.apiclient.exceptions import NotFound as GlanceImageNotFound
26 from novaclient.exceptions import NotFound
32 class Compute(object):
33 """Class to interface with nova and glance."""
35 def __init__(self, nova_client, glance_client, config):
36 """Create a new compute instance to interact with nova and glance."""
37 self.novaclient = nova_client
38 self.glance_client = glance_client
41 def find_image(self, image_name):
42 """Find an image by name."""
44 return next(self.glance_client.images.list(filters={'name': image_name}), None)
45 except (novaclient.exceptions.NotFound, keystoneauth1.exceptions.http.NotFound,
50 def upload_image_via_url(self, final_image_name, image_file, retry_count=60):
51 """Directly upload image to Nova via URL if image is not present."""
54 # check image is file/url based.
55 with open(image_file, 'rb') as f_image:
56 img = self.glance_client.images.create(name=str(final_image_name),
58 container_format="bare",
60 self.glance_client.images.upload(img.id, image_data=f_image)
61 # Check for the image in glance
62 while img.status in ['queued', 'saving'] and retry < retry_count:
63 img = self.glance_client.images.get(img.id)
65 LOG.debug("Image not yet active, retrying %s of %s...", retry, retry_count)
66 time.sleep(self.config.generic_poll_sec)
67 if img.status != 'active':
68 LOG.error("Image uploaded but too long to get to active state")
69 raise Exception("Image update active state timeout")
70 except glance_exception.HTTPForbidden:
71 LOG.error("Cannot upload image without admin access. Please make "
72 "sure the image is uploaded and is either public or owned by you.")
75 # catch the exception for file based errors.
76 LOG.error("Failed while uploading the image. Please make sure the "
77 "image at the specified location %s is correct.", image_file)
79 except keystoneauth1.exceptions.http.NotFound as exc:
80 LOG.error("Authentication error while uploading the image: %s", str(exc))
83 LOG.error(traceback.format_exc())
84 LOG.error("Failed to upload image %s.", image_file)
88 def delete_image(self, img_name):
89 """Delete an image by name."""
91 LOG.log("Deleting image %s...", img_name)
92 img = self.find_image(image_name=img_name)
93 self.glance_client.images.delete(img.id)
95 LOG.error("Failed to delete the image %s.", img_name)
100 def image_multiqueue_enabled(self, img):
101 """Check if multiqueue property is enabled on given image."""
103 return img['hw_vif_multiqueue_enabled'] == 'true'
107 def image_set_multiqueue(self, img, enabled):
108 """Set multiqueue property as enabled or disabled on given image."""
109 cur_mqe = self.image_multiqueue_enabled(img)
110 LOG.info('Image %s hw_vif_multiqueue_enabled property is "%s"',
111 img.name, str(cur_mqe).lower())
112 if cur_mqe != enabled:
113 mqe = str(enabled).lower()
114 self.glance_client.images.update(img.id, hw_vif_multiqueue_enabled=mqe)
115 img['hw_vif_multiqueue_enabled'] = mqe
116 LOG.info('Image %s hw_vif_multiqueue_enabled property changed to "%s"', img.name, mqe)
118 # Create a server instance with name vmname
119 # and check that it gets into the ACTIVE state
120 def create_server(self, vmname, image, flavor, key_name,
121 nic, sec_group, avail_zone=None, user_data=None,
122 config_drive=None, files=None):
123 """Create a new server."""
125 security_groups = [sec_group['id']]
127 security_groups = None
129 # Also attach the created security group for the test
130 LOG.info('Creating instance %s with AZ: "%s"', vmname, avail_zone)
131 instance = self.novaclient.servers.create(name=vmname,
136 availability_zone=avail_zone,
138 config_drive=config_drive,
140 security_groups=security_groups)
143 def poll_server(self, instance):
144 """Poll a server from its reference."""
145 return self.novaclient.servers.get(instance.id)
147 def get_server_list(self):
148 """Get the list of all servers."""
149 servers_list = self.novaclient.servers.list()
152 def instance_exists(self, server):
154 self.novaclient.servers.get(server)
159 def delete_server(self, server):
160 """Delete a server from its object reference."""
161 utils.delete_server(self.novaclient, server)
162 utils.waiting_servers_deletion(self.novaclient, [server])
164 def find_flavor(self, flavor_type):
165 """Find a flavor by name."""
167 flavor = self.novaclient.flavors.find(name=flavor_type)
172 def create_flavor(self, name, ram, vcpus, disk, ephemeral=0):
173 """Create a flavor."""
174 return self.novaclient.flavors.create(name=name, ram=ram, vcpus=vcpus, disk=disk,
177 def get_hypervisor(self, hyper_name):
178 """Get the hypervisor from its name.
180 Can raise novaclient.exceptions.NotFound
182 # first get the id from name
183 hyper = self.novaclient.hypervisors.search(hyper_name)[0]
184 # get full hypervisor object
185 return self.novaclient.hypervisors.get(hyper.id)