Improved unit tests
Fixed docstring formatting Merged with trunk
This commit is contained in:
		
							
								
								
									
										1
									
								
								Authors
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								Authors
									
									
									
									
									
								
							| @@ -32,6 +32,7 @@ Jesse Andrews <anotherjesse@gmail.com> | ||||
| Joe Heck <heckj@mac.com> | ||||
| Joel Moore <joelbm24@gmail.com> | ||||
| John Dewey <john@dewey.ws> | ||||
| John Tran <jtran@attinteractive.com> | ||||
| Jonathan Bryce <jbryce@jbryce.com> | ||||
| Jordan Rinke <jordan@openstack.org> | ||||
| Josh Durgin <joshd@hq.newdream.net> | ||||
|   | ||||
| @@ -108,17 +108,17 @@ class AjaxConsoleProxy(object): | ||||
|             return "Server Error" | ||||
|  | ||||
|     def register_listeners(self): | ||||
|         class Callback: | ||||
|             def __call__(self, data, message): | ||||
|                 if data['method'] == 'authorize_ajax_console': | ||||
|                     AjaxConsoleProxy.tokens[data['args']['token']] =  \ | ||||
|                         {'args': data['args'], 'last_activity': time.time()} | ||||
|         class TopicProxy(): | ||||
|             @staticmethod | ||||
|             def authorize_ajax_console(context, **kwargs): | ||||
|                 AjaxConsoleProxy.tokens[kwargs['token']] =  \ | ||||
|                     {'args': kwargs, 'last_activity': time.time()} | ||||
|  | ||||
|         conn = rpc.Connection.instance(new=True) | ||||
|         consumer = rpc.TopicAdapterConsumer( | ||||
|                        connection=conn, | ||||
|                        proxy=TopicProxy, | ||||
|                        topic=FLAGS.ajax_console_proxy_topic) | ||||
|         consumer.register_callback(Callback()) | ||||
|  | ||||
|         def delete_expired_tokens(): | ||||
|             now = time.time() | ||||
| @@ -130,8 +130,7 @@ class AjaxConsoleProxy(object): | ||||
|             for k in to_delete: | ||||
|                 del AjaxConsoleProxy.tokens[k] | ||||
|  | ||||
|         utils.LoopingCall(consumer.fetch, auto_ack=True, | ||||
|                           enable_callbacks=True).start(0.1) | ||||
|         utils.LoopingCall(consumer.fetch, enable_callbacks=True).start(0.1) | ||||
|         utils.LoopingCall(delete_expired_tokens).start(1) | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|   | ||||
| @@ -902,7 +902,7 @@ class ImageCommands(object): | ||||
|                 'disk_format': disk_format, | ||||
|                 'container_format': container_format, | ||||
|                 'properties': {'image_state': 'available', | ||||
|                                'owner': owner, | ||||
|                                'owner_id': owner, | ||||
|                                'type': image_type, | ||||
|                                'architecture': architecture, | ||||
|                                'image_location': 'local', | ||||
| @@ -980,7 +980,7 @@ class ImageCommands(object): | ||||
|                'is_public': True, | ||||
|                'name': old['imageId'], | ||||
|                'properties': {'image_state': old['imageState'], | ||||
|                               'owner': old['imageOwnerId'], | ||||
|                               'owner_id': old['imageOwnerId'], | ||||
|                               'architecture': old['architecture'], | ||||
|                               'type': old['type'], | ||||
|                               'image_location': old['imageLocation'], | ||||
|   | ||||
| @@ -1,473 +0,0 @@ | ||||
| # vim: tabstop=4 shiftwidth=4 softtabstop=4 | ||||
|  | ||||
| # Copyright 2010 United States Government as represented by the | ||||
| # Administrator of the National Aeronautics and Space Administration. | ||||
| # All Rights Reserved. | ||||
| # | ||||
| #    Licensed under the Apache License, Version 2.0 (the "License"); you may | ||||
| #    not use this file except in compliance with the License. You may obtain | ||||
| #    a copy of the License at | ||||
| # | ||||
| #         http://www.apache.org/licenses/LICENSE-2.0 | ||||
| # | ||||
| #    Unless required by applicable law or agreed to in writing, software | ||||
| #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
| #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
| #    License for the specific language governing permissions and limitations | ||||
| #    under the License. | ||||
| """ | ||||
| Nova User API client library. | ||||
| """ | ||||
|  | ||||
| import base64 | ||||
| import boto | ||||
| import boto.exception | ||||
| import httplib | ||||
| import re | ||||
| import string | ||||
|  | ||||
| from boto.ec2.regioninfo import RegionInfo | ||||
|  | ||||
|  | ||||
| DEFAULT_CLC_URL = 'http://127.0.0.1:8773' | ||||
| DEFAULT_REGION = 'nova' | ||||
|  | ||||
|  | ||||
| class UserInfo(object): | ||||
|     """ | ||||
|     Information about a Nova user, as parsed through SAX. | ||||
|  | ||||
|     **Fields Include** | ||||
|  | ||||
|     * username | ||||
|     * accesskey | ||||
|     * secretkey | ||||
|     * file (optional) containing zip of X509 cert & rc file | ||||
|  | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, connection=None, username=None, endpoint=None): | ||||
|         self.connection = connection | ||||
|         self.username = username | ||||
|         self.endpoint = endpoint | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return 'UserInfo:%s' % self.username | ||||
|  | ||||
|     def startElement(self, name, attrs, connection): | ||||
|         return None | ||||
|  | ||||
|     def endElement(self, name, value, connection): | ||||
|         if name == 'username': | ||||
|             self.username = str(value) | ||||
|         elif name == 'file': | ||||
|             self.file = base64.b64decode(str(value)) | ||||
|         elif name == 'accesskey': | ||||
|             self.accesskey = str(value) | ||||
|         elif name == 'secretkey': | ||||
|             self.secretkey = str(value) | ||||
|  | ||||
|  | ||||
| class UserRole(object): | ||||
|     """ | ||||
|     Information about a Nova user's role, as parsed through SAX. | ||||
|  | ||||
|     **Fields include** | ||||
|  | ||||
|     * role | ||||
|  | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, connection=None): | ||||
|         self.connection = connection | ||||
|         self.role = None | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return 'UserRole:%s' % self.role | ||||
|  | ||||
|     def startElement(self, name, attrs, connection): | ||||
|         return None | ||||
|  | ||||
|     def endElement(self, name, value, connection): | ||||
|         if name == 'role': | ||||
|             self.role = value | ||||
|         else: | ||||
|             setattr(self, name, str(value)) | ||||
|  | ||||
|  | ||||
| class ProjectInfo(object): | ||||
|     """ | ||||
|     Information about a Nova project, as parsed through SAX. | ||||
|  | ||||
|     **Fields include** | ||||
|  | ||||
|     * projectname | ||||
|     * description | ||||
|     * projectManagerId | ||||
|     * memberIds | ||||
|  | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, connection=None): | ||||
|         self.connection = connection | ||||
|         self.projectname = None | ||||
|         self.description = None | ||||
|         self.projectManagerId = None | ||||
|         self.memberIds = [] | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return 'ProjectInfo:%s' % self.projectname | ||||
|  | ||||
|     def startElement(self, name, attrs, connection): | ||||
|         return None | ||||
|  | ||||
|     def endElement(self, name, value, connection): | ||||
|         if name == 'projectname': | ||||
|             self.projectname = value | ||||
|         elif name == 'description': | ||||
|             self.description = value | ||||
|         elif name == 'projectManagerId': | ||||
|             self.projectManagerId = value | ||||
|         elif name == 'memberId': | ||||
|             self.memberIds.append(value) | ||||
|         else: | ||||
|             setattr(self, name, str(value)) | ||||
|  | ||||
|  | ||||
| class ProjectMember(object): | ||||
|     """ | ||||
|     Information about a Nova project member, as parsed through SAX. | ||||
|  | ||||
|     **Fields include** | ||||
|  | ||||
|     * memberId | ||||
|  | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, connection=None): | ||||
|         self.connection = connection | ||||
|         self.memberId = None | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return 'ProjectMember:%s' % self.memberId | ||||
|  | ||||
|     def startElement(self, name, attrs, connection): | ||||
|         return None | ||||
|  | ||||
|     def endElement(self, name, value, connection): | ||||
|         if name == 'member': | ||||
|             self.memberId = value | ||||
|         else: | ||||
|             setattr(self, name, str(value)) | ||||
|  | ||||
|  | ||||
| class HostInfo(object): | ||||
|     """ | ||||
|     Information about a Nova Host, as parsed through SAX. | ||||
|  | ||||
|     **Fields Include** | ||||
|  | ||||
|     * Hostname | ||||
|     * Compute service status | ||||
|     * Volume service status | ||||
|     * Instance count | ||||
|     * Volume count | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, connection=None): | ||||
|         self.connection = connection | ||||
|         self.hostname = None | ||||
|         self.compute = None | ||||
|         self.volume = None | ||||
|         self.instance_count = 0 | ||||
|         self.volume_count = 0 | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return 'Host:%s' % self.hostname | ||||
|  | ||||
|     # this is needed by the sax parser, so ignore the ugly name | ||||
|     def startElement(self, name, attrs, connection): | ||||
|         return None | ||||
|  | ||||
|     # this is needed by the sax parser, so ignore the ugly name | ||||
|     def endElement(self, name, value, connection): | ||||
|         fixed_name = string.lower(re.sub(r'([A-Z])', r'_\1', name)) | ||||
|         setattr(self, fixed_name, value) | ||||
|  | ||||
|  | ||||
| class Vpn(object): | ||||
|     """ | ||||
|     Information about a Vpn, as parsed through SAX | ||||
|  | ||||
|     **Fields Include** | ||||
|  | ||||
|     * instance_id | ||||
|     * project_id | ||||
|     * public_ip | ||||
|     * public_port | ||||
|     * created_at | ||||
|     * internal_ip | ||||
|     * state | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, connection=None): | ||||
|         self.connection = connection | ||||
|         self.instance_id = None | ||||
|         self.project_id = None | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return 'Vpn:%s:%s' % (self.project_id, self.instance_id) | ||||
|  | ||||
|     def startElement(self, name, attrs, connection): | ||||
|         return None | ||||
|  | ||||
|     def endElement(self, name, value, connection): | ||||
|         fixed_name = string.lower(re.sub(r'([A-Z])', r'_\1', name)) | ||||
|         setattr(self, fixed_name, value) | ||||
|  | ||||
|  | ||||
| class InstanceType(object): | ||||
|     """ | ||||
|     Information about a Nova instance type, as parsed through SAX. | ||||
|  | ||||
|     **Fields include** | ||||
|  | ||||
|     * name | ||||
|     * vcpus | ||||
|     * disk_gb | ||||
|     * memory_mb | ||||
|     * flavor_id | ||||
|  | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, connection=None): | ||||
|         self.connection = connection | ||||
|         self.name = None | ||||
|         self.vcpus = None | ||||
|         self.disk_gb = None | ||||
|         self.memory_mb = None | ||||
|         self.flavor_id = None | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return 'InstanceType:%s' % self.name | ||||
|  | ||||
|     def startElement(self, name, attrs, connection): | ||||
|         return None | ||||
|  | ||||
|     def endElement(self, name, value, connection): | ||||
|         if name == "memoryMb": | ||||
|             self.memory_mb = str(value) | ||||
|         elif name == "flavorId": | ||||
|             self.flavor_id = str(value) | ||||
|         elif name == "diskGb": | ||||
|             self.disk_gb = str(value) | ||||
|         else: | ||||
|             setattr(self, name, str(value)) | ||||
|  | ||||
|  | ||||
| class NovaAdminClient(object): | ||||
|  | ||||
|     def __init__( | ||||
|             self, | ||||
|             clc_url=DEFAULT_CLC_URL, | ||||
|             region=DEFAULT_REGION, | ||||
|             access_key=None, | ||||
|             secret_key=None, | ||||
|             **kwargs): | ||||
|         parts = self.split_clc_url(clc_url) | ||||
|  | ||||
|         self.clc_url = clc_url | ||||
|         self.region = region | ||||
|         self.access = access_key | ||||
|         self.secret = secret_key | ||||
|         self.apiconn = boto.connect_ec2(aws_access_key_id=access_key, | ||||
|                                         aws_secret_access_key=secret_key, | ||||
|                                         is_secure=parts['is_secure'], | ||||
|                                         region=RegionInfo(None, | ||||
|                                                           region, | ||||
|                                                           parts['ip']), | ||||
|                                         port=parts['port'], | ||||
|                                         path='/services/Admin', | ||||
|                                         **kwargs) | ||||
|         self.apiconn.APIVersion = 'nova' | ||||
|  | ||||
|     def connection_for(self, username, project, clc_url=None, region=None, | ||||
|                        **kwargs): | ||||
|         """Returns a boto ec2 connection for the given username.""" | ||||
|         if not clc_url: | ||||
|             clc_url = self.clc_url | ||||
|         if not region: | ||||
|             region = self.region | ||||
|         parts = self.split_clc_url(clc_url) | ||||
|         user = self.get_user(username) | ||||
|         access_key = '%s:%s' % (user.accesskey, project) | ||||
|         return boto.connect_ec2(aws_access_key_id=access_key, | ||||
|                                 aws_secret_access_key=user.secretkey, | ||||
|                                 is_secure=parts['is_secure'], | ||||
|                                 region=RegionInfo(None, | ||||
|                                                   self.region, | ||||
|                                                   parts['ip']), | ||||
|                                 port=parts['port'], | ||||
|                                 path='/services/Cloud', | ||||
|                                 **kwargs) | ||||
|  | ||||
|     def split_clc_url(self, clc_url): | ||||
|         """Splits a cloud controller endpoint url.""" | ||||
|         parts = httplib.urlsplit(clc_url) | ||||
|         is_secure = parts.scheme == 'https' | ||||
|         ip, port = parts.netloc.split(':') | ||||
|         return {'ip': ip, 'port': int(port), 'is_secure': is_secure} | ||||
|  | ||||
|     def get_users(self): | ||||
|         """Grabs the list of all users.""" | ||||
|         return self.apiconn.get_list('DescribeUsers', {}, [('item', UserInfo)]) | ||||
|  | ||||
|     def get_user(self, name): | ||||
|         """Grab a single user by name.""" | ||||
|         user = self.apiconn.get_object('DescribeUser', | ||||
|                                        {'Name': name}, | ||||
|                                        UserInfo) | ||||
|         if user.username != None: | ||||
|             return user | ||||
|  | ||||
|     def has_user(self, username): | ||||
|         """Determine if user exists.""" | ||||
|         return self.get_user(username) != None | ||||
|  | ||||
|     def create_user(self, username): | ||||
|         """Creates a new user, returning the userinfo object with | ||||
|         access/secret.""" | ||||
|         return self.apiconn.get_object('RegisterUser', {'Name': username}, | ||||
|                                        UserInfo) | ||||
|  | ||||
|     def delete_user(self, username): | ||||
|         """Deletes a user.""" | ||||
|         return self.apiconn.get_object('DeregisterUser', {'Name': username}, | ||||
|                                        UserInfo) | ||||
|  | ||||
|     def get_roles(self, project_roles=True): | ||||
|         """Returns a list of available roles.""" | ||||
|         return self.apiconn.get_list('DescribeRoles', | ||||
|                                      {'ProjectRoles': project_roles}, | ||||
|                                      [('item', UserRole)]) | ||||
|  | ||||
|     def get_user_roles(self, user, project=None): | ||||
|         """Returns a list of roles for the given user. | ||||
|  | ||||
|         Omitting project will return any global roles that the user has. | ||||
|         Specifying project will return only project specific roles. | ||||
|  | ||||
|         """ | ||||
|         params = {'User': user} | ||||
|         if project: | ||||
|             params['Project'] = project | ||||
|         return self.apiconn.get_list('DescribeUserRoles', | ||||
|                                      params, | ||||
|                                      [('item', UserRole)]) | ||||
|  | ||||
|     def add_user_role(self, user, role, project=None): | ||||
|         """Add a role to a user either globally or for a specific project.""" | ||||
|         return self.modify_user_role(user, role, project=project, | ||||
|                                      operation='add') | ||||
|  | ||||
|     def remove_user_role(self, user, role, project=None): | ||||
|         """Remove a role from a user either globally or for a specific | ||||
|         project.""" | ||||
|         return self.modify_user_role(user, role, project=project, | ||||
|                                      operation='remove') | ||||
|  | ||||
|     def modify_user_role(self, user, role, project=None, operation='add', | ||||
|                          **kwargs): | ||||
|         """Add or remove a role for a user and project.""" | ||||
|         params = {'User': user, | ||||
|                   'Role': role, | ||||
|                   'Project': project, | ||||
|                   'Operation': operation} | ||||
|         return self.apiconn.get_status('ModifyUserRole', params) | ||||
|  | ||||
|     def get_projects(self, user=None): | ||||
|         """Returns a list of all projects.""" | ||||
|         if user: | ||||
|             params = {'User': user} | ||||
|         else: | ||||
|             params = {} | ||||
|         return self.apiconn.get_list('DescribeProjects', | ||||
|                                      params, | ||||
|                                      [('item', ProjectInfo)]) | ||||
|  | ||||
|     def get_project(self, name): | ||||
|         """Returns a single project with the specified name.""" | ||||
|         project = self.apiconn.get_object('DescribeProject', | ||||
|                                           {'Name': name}, | ||||
|                                           ProjectInfo) | ||||
|  | ||||
|         if project.projectname != None: | ||||
|             return project | ||||
|  | ||||
|     def create_project(self, projectname, manager_user, description=None, | ||||
|                        member_users=None): | ||||
|         """Creates a new project.""" | ||||
|         params = {'Name': projectname, | ||||
|                   'ManagerUser': manager_user, | ||||
|                   'Description': description, | ||||
|                   'MemberUsers': member_users} | ||||
|         return self.apiconn.get_object('RegisterProject', params, ProjectInfo) | ||||
|  | ||||
|     def modify_project(self, projectname, manager_user=None, description=None): | ||||
|         """Modifies an existing project.""" | ||||
|         params = {'Name': projectname, | ||||
|                   'ManagerUser': manager_user, | ||||
|                   'Description': description} | ||||
|         return self.apiconn.get_status('ModifyProject', params) | ||||
|  | ||||
|     def delete_project(self, projectname): | ||||
|         """Permanently deletes the specified project.""" | ||||
|         return self.apiconn.get_object('DeregisterProject', | ||||
|                                        {'Name': projectname}, | ||||
|                                        ProjectInfo) | ||||
|  | ||||
|     def get_project_members(self, name): | ||||
|         """Returns a list of members of a project.""" | ||||
|         return self.apiconn.get_list('DescribeProjectMembers', | ||||
|                                      {'Name': name}, | ||||
|                                      [('item', ProjectMember)]) | ||||
|  | ||||
|     def add_project_member(self, user, project): | ||||
|         """Adds a user to a project.""" | ||||
|         return self.modify_project_member(user, project, operation='add') | ||||
|  | ||||
|     def remove_project_member(self, user, project): | ||||
|         """Removes a user from a project.""" | ||||
|         return self.modify_project_member(user, project, operation='remove') | ||||
|  | ||||
|     def modify_project_member(self, user, project, operation='add'): | ||||
|         """Adds or removes a user from a project.""" | ||||
|         params = {'User': user, | ||||
|                   'Project': project, | ||||
|                   'Operation': operation} | ||||
|         return self.apiconn.get_status('ModifyProjectMember', params) | ||||
|  | ||||
|     def get_zip(self, user, project): | ||||
|         """Returns the content of a zip file containing novarc and access | ||||
|         credentials.""" | ||||
|         params = {'Name': user, 'Project': project} | ||||
|         zip = self.apiconn.get_object('GenerateX509ForUser', params, UserInfo) | ||||
|         return zip.file | ||||
|  | ||||
|     def start_vpn(self, project): | ||||
|         """ | ||||
|         Starts the vpn for a user | ||||
|         """ | ||||
|         return self.apiconn.get_object('StartVpn', {'Project': project}, Vpn) | ||||
|  | ||||
|     def get_vpns(self): | ||||
|         """Return a list of vpn with project name""" | ||||
|         return self.apiconn.get_list('DescribeVpns', {}, [('item', Vpn)]) | ||||
|  | ||||
|     def get_hosts(self): | ||||
|         return self.apiconn.get_list('DescribeHosts', {}, [('item', HostInfo)]) | ||||
|  | ||||
|     def get_instance_types(self): | ||||
|         """Grabs the list of all users.""" | ||||
|         return self.apiconn.get_list('DescribeInstanceTypes', {}, | ||||
|                                      [('item', InstanceType)]) | ||||
| @@ -74,7 +74,12 @@ class Connection(carrot_connection.BrokerConnection): | ||||
|         """Recreates the connection instance | ||||
|  | ||||
|         This is necessary to recover from some network errors/disconnects""" | ||||
|         try: | ||||
|             del cls._instance | ||||
|         except AttributeError, e: | ||||
|             # The _instance stuff is for testing purposes. Usually we don't use | ||||
|             # it. So don't freak out if it doesn't exist. | ||||
|             pass | ||||
|         return cls.instance() | ||||
|  | ||||
|  | ||||
| @@ -125,9 +130,9 @@ class Consumer(messaging.Consumer): | ||||
|         # NOTE(vish): This is catching all errors because we really don't | ||||
|         #             want exceptions to be logged 10 times a second if some | ||||
|         #             persistent failure occurs. | ||||
|         except Exception:  # pylint: disable=W0703 | ||||
|         except Exception, e:  # pylint: disable=W0703 | ||||
|             if not self.failed_connection: | ||||
|                 LOG.exception(_("Failed to fetch message from queue")) | ||||
|                 LOG.exception(_("Failed to fetch message from queue: %s" % e)) | ||||
|                 self.failed_connection = True | ||||
|  | ||||
|     def attach_to_eventlet(self): | ||||
|   | ||||
| @@ -72,7 +72,9 @@ class SimpleScheduler(chance.ChanceScheduler): | ||||
|                                    {'host': service['host'], | ||||
|                                     'scheduled_at': now}) | ||||
|                 return service['host'] | ||||
|         raise driver.NoValidHost(_("No hosts found")) | ||||
|         raise driver.NoValidHost(_("Scheduler was unable to locate a host" | ||||
|                                    " for this request. Is the appropriate" | ||||
|                                    " service running?")) | ||||
|  | ||||
|     def schedule_create_volume(self, context, volume_id, *_args, **_kwargs): | ||||
|         """Picks a host that is up and has the fewest volumes.""" | ||||
| @@ -107,7 +109,9 @@ class SimpleScheduler(chance.ChanceScheduler): | ||||
|                                  {'host': service['host'], | ||||
|                                   'scheduled_at': now}) | ||||
|                 return service['host'] | ||||
|         raise driver.NoValidHost(_("No hosts found")) | ||||
|         raise driver.NoValidHost(_("Scheduler was unable to locate a host" | ||||
|                                    " for this request. Is the appropriate" | ||||
|                                    " service running?")) | ||||
|  | ||||
|     def schedule_set_network_host(self, context, *_args, **_kwargs): | ||||
|         """Picks a host that is up and has the fewest networks.""" | ||||
| @@ -119,4 +123,6 @@ class SimpleScheduler(chance.ChanceScheduler): | ||||
|                 raise driver.NoValidHost(_("All hosts have too many networks")) | ||||
|             if self.service_is_up(service): | ||||
|                 return service['host'] | ||||
|         raise driver.NoValidHost(_("No hosts found")) | ||||
|         raise driver.NoValidHost(_("Scheduler was unable to locate a host" | ||||
|                                    " for this request. Is the appropriate" | ||||
|                                    " service running?")) | ||||
|   | ||||
| @@ -52,5 +52,8 @@ class ZoneScheduler(driver.Scheduler): | ||||
|         zone = _kwargs.get('availability_zone') | ||||
|         hosts = self.hosts_up_with_zone(context, topic, zone) | ||||
|         if not hosts: | ||||
|             raise driver.NoValidHost(_("No hosts found")) | ||||
|             raise driver.NoValidHost(_("Scheduler was unable to locate a host" | ||||
|                                        " for this request. Is the appropriate" | ||||
|                                        " service running?")) | ||||
|  | ||||
|         return hosts[int(random.random() * len(hosts))] | ||||
|   | ||||
| @@ -41,6 +41,7 @@ from nova.compute import power_state | ||||
| from nova.api.ec2 import cloud | ||||
| from nova.api.ec2 import ec2utils | ||||
| from nova.image import local | ||||
| from nova.exception import NotFound | ||||
|  | ||||
|  | ||||
| FLAGS = flags.FLAGS | ||||
| @@ -71,7 +72,8 @@ class CloudTestCase(test.TestCase): | ||||
|         host = self.network.get_network_host(self.context.elevated()) | ||||
|  | ||||
|         def fake_show(meh, context, id): | ||||
|             return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1}} | ||||
|             return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1, | ||||
|                     'type': 'machine'}} | ||||
|  | ||||
|         self.stubs.Set(local.LocalImageService, 'show', fake_show) | ||||
|         self.stubs.Set(local.LocalImageService, 'show_by_name', fake_show) | ||||
| @@ -216,6 +218,35 @@ class CloudTestCase(test.TestCase): | ||||
|         db.service_destroy(self.context, comp1['id']) | ||||
|         db.service_destroy(self.context, comp2['id']) | ||||
|  | ||||
|     def test_describe_images(self): | ||||
|         describe_images = self.cloud.describe_images | ||||
|  | ||||
|         def fake_detail(meh, context): | ||||
|             return [{'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1, | ||||
|                     'type': 'machine'}}] | ||||
|  | ||||
|         def fake_show_none(meh, context, id): | ||||
|             raise NotFound | ||||
|  | ||||
|         self.stubs.Set(local.LocalImageService, 'detail', fake_detail) | ||||
|         # list all | ||||
|         result1 = describe_images(self.context) | ||||
|         result1 = result1['imagesSet'][0] | ||||
|         self.assertEqual(result1['imageId'], 'ami-00000001') | ||||
|         # provided a valid image_id | ||||
|         result2 = describe_images(self.context, ['ami-00000001']) | ||||
|         self.assertEqual(1, len(result2['imagesSet'])) | ||||
|         # provide more than 1 valid image_id | ||||
|         result3 = describe_images(self.context, ['ami-00000001', | ||||
|                                                  'ami-00000002']) | ||||
|         self.assertEqual(2, len(result3['imagesSet'])) | ||||
|         # provide an non-existing image_id | ||||
|         self.stubs.UnsetAll() | ||||
|         self.stubs.Set(local.LocalImageService, 'show', fake_show_none) | ||||
|         self.stubs.Set(local.LocalImageService, 'show_by_name', fake_show_none) | ||||
|         self.assertRaises(NotFound, describe_images, | ||||
|                           self.context, ['ami-fake']) | ||||
|  | ||||
|     def test_console_output(self): | ||||
|         instance_type = FLAGS.default_instance_type | ||||
|         max_count = 1 | ||||
|   | ||||
| @@ -188,7 +188,7 @@ class XenAPIVMTestCase(test.TestCase): | ||||
|         stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) | ||||
|         stubs.stubout_get_this_vm_uuid(self.stubs) | ||||
|         stubs.stubout_stream_disk(self.stubs) | ||||
|         stubs.stubout_is_vdi_pv(self.stubs) | ||||
|         stubs.stubout_determine_is_pv_objectstore(self.stubs) | ||||
|         self.stubs.Set(VMOps, 'reset_network', reset_network) | ||||
|         stubs.stub_out_vm_methods(self.stubs) | ||||
|         glance_stubs.stubout_glance_client(self.stubs, | ||||
| @@ -327,18 +327,17 @@ class XenAPIVMTestCase(test.TestCase): | ||||
|         self.assertEquals(self.vm['HVM_boot_params'], {}) | ||||
|         self.assertEquals(self.vm['HVM_boot_policy'], '') | ||||
|  | ||||
|     def _check_no_unbound_vdi(self): | ||||
|     def _list_vdis(self): | ||||
|         url = FLAGS.xenapi_connection_url | ||||
|         username = FLAGS.xenapi_connection_username | ||||
|         password = FLAGS.xenapi_connection_password | ||||
|         session = xenapi_conn.XenAPISession(url, username, password) | ||||
|         vdi_refs = session.call_xenapi('VDI.get_all') | ||||
|         for vdi_ref in vdi_refs: | ||||
|             vdi_rec = session.call_xenapi('VDI.get_record', vdi_ref) | ||||
|             if 'VBDs' in vdi_rec: | ||||
|                 self.assertEquals(vdi_rec['VBDs'], {}) | ||||
|             else: | ||||
|                 self.fail('Found unexpected unbound VDI:%s' % vdi_rec['uuid']) | ||||
|         return session.call_xenapi('VDI.get_all') | ||||
|  | ||||
|     def _check_vdis(self, start_list, end_list): | ||||
|         for vdi_ref in end_list: | ||||
|             if not vdi_ref in start_list: | ||||
|                 self.fail('Found unexpected VDI:%s' % vdi_ref) | ||||
|  | ||||
|     def _test_spawn(self, image_id, kernel_id, ramdisk_id, | ||||
|                     instance_type="m1.large", os_type="linux", | ||||
| @@ -365,28 +364,32 @@ class XenAPIVMTestCase(test.TestCase): | ||||
|                           1, 2, 3, "m1.xlarge") | ||||
|  | ||||
|     def test_spawn_fail_cleanup_1(self): | ||||
|         """Simulates an error while downloading an image. | ||||
|         Verifies that VDIs created are properly cleaned up. | ||||
|  | ||||
|         """ | ||||
|         Simulates an error while downloading image | ||||
|         Verifies VDI create are properly cleaned up. | ||||
|         """ | ||||
|         vdi_recs_start = self._list_vdis() | ||||
|         FLAGS.xenapi_image_service = 'glance' | ||||
|         stubs.stubout_fetch_image_glance_disk(self.stubs) | ||||
|         self.assertRaises(xenapi_fake.Failure, | ||||
|                           self._test_spawn, 1, 2, 3) | ||||
|         # ensure there is no VDI without a VBD | ||||
|         self._check_no_unbound_vdi() | ||||
|         # no new VDI should be found | ||||
|         vdi_recs_end = self._list_vdis() | ||||
|         self._check_vdis(vdi_recs_start, vdi_recs_end) | ||||
|  | ||||
|     def test_spawn_fail_cleanup_2(self): | ||||
|         """Simulates an error while creating VM record. It | ||||
|         verifies that VDIs created are properly cleaned up. | ||||
|  | ||||
|         """ | ||||
|         Simulates an error while creating VM record. It | ||||
|         verifies that VDI created are properly cleaned up. | ||||
|         """ | ||||
|         vdi_recs_start = self._list_vdis() | ||||
|         FLAGS.xenapi_image_service = 'glance' | ||||
|         stubs.stubout_create_vm(self.stubs) | ||||
|         self.assertRaises(xenapi_fake.Failure, | ||||
|                           self._test_spawn, 1, 2, 3) | ||||
|         # ensure there is no VDI without a VBD | ||||
|         self._check_no_unbound_vdi() | ||||
|         # no new VDI should be found | ||||
|         vdi_recs_end = self._list_vdis() | ||||
|         self._check_vdis(vdi_recs_start, vdi_recs_end) | ||||
|  | ||||
|     def test_spawn_raw_objectstore(self): | ||||
|         FLAGS.xenapi_image_service = 'objectstore' | ||||
|   | ||||
| @@ -132,10 +132,13 @@ def stubout_stream_disk(stubs): | ||||
|     stubs.Set(vm_utils, '_stream_disk', f) | ||||
|  | ||||
|  | ||||
| def stubout_is_vdi_pv(stubs): | ||||
|     def f(_1): | ||||
| def stubout_determine_is_pv_objectstore(stubs): | ||||
|     """Assumes VMs never have PV kernels""" | ||||
|  | ||||
|     @classmethod | ||||
|     def f(cls, *args): | ||||
|         return False | ||||
|     stubs.Set(vm_utils, '_is_vdi_pv', f) | ||||
|     stubs.Set(vm_utils.VMHelper, '_determine_is_pv_objectstore', f) | ||||
|  | ||||
|  | ||||
| def stubout_lookup_image(stubs): | ||||
| @@ -176,7 +179,10 @@ class FakeSessionForVMTests(fake.SessionBase): | ||||
|     def __init__(self, uri): | ||||
|         super(FakeSessionForVMTests, self).__init__(uri) | ||||
|  | ||||
|     def host_call_plugin(self, _1, _2, _3, _4, _5): | ||||
|     def host_call_plugin(self, _1, _2, plugin, fn, args): | ||||
|         # copy_kernel_vdi returns nothing | ||||
|         if fn == 'copy_kernel_vdi': | ||||
|             return | ||||
|         sr_ref = fake.get_all('SR')[0] | ||||
|         vdi_ref = fake.create_vdi('', False, sr_ref, False) | ||||
|         vdi_rec = fake.get_record('VDI', vdi_ref) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Salvatore Orlando
					Salvatore Orlando