From 2a6f97940f71c056b4bfb0cd9a86f5d676abc4e1 Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Mon, 11 Jul 2011 13:34:39 -0700 Subject: add optional parameter networks to the Create server OS API --- bin/nova-manage | 6 +- nova/api/openstack/create_instance_helper.py | 55 +++++++++++- nova/compute/api.py | 34 ++++++-- nova/compute/manager.py | 4 +- nova/db/api.py | 32 +++++++ nova/db/sqlalchemy/api.py | 125 ++++++++++++++++++++++++++- nova/exception.py | 26 ++++++ nova/network/api.py | 9 ++ nova/network/manager.py | 125 ++++++++++++++++++++++----- nova/utils.py | 16 ++++ 10 files changed, 394 insertions(+), 38 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 7dfe91698..53e3d5c93 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -615,12 +615,14 @@ class NetworkCommands(object): def list(self): """List all created networks""" - print "%-18s\t%-15s\t%-15s\t%-15s" % (_('network'), + print "%-5s\t%-18s\t%-15s\t%-15s\t%-15s" % (_('id'), + _('network'), _('netmask'), _('start address'), 'DNS') for network in db.network_get_all(context.get_admin_context()): - print "%-18s\t%-15s\t%-15s\t%-15s" % (network.cidr, + print "%-5s\t%-18s\t%-15s\t%-15s\t%-15s" % (network.id, + network.cidr, network.netmask, network.dhcp_start, network.dns) diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 1066713a3..839aa9fb9 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -32,7 +32,6 @@ from nova.api.openstack import faults from nova.api.openstack import wsgi from nova.auth import manager as auth_manager - LOG = logging.getLogger('nova.api.openstack.create_instance_helper') FLAGS = flags.FLAGS @@ -102,6 +101,15 @@ class CreateInstanceHelper(object): if personality: injected_files = self._get_injected_files(personality) + requested_networks = body['server'].get('networks') + + if requested_networks is not None: + if len(requested_networks) == 0: + msg = _("No networks found") + raise faults.Fault(exc.HTTPBadRequest(explanation=msg)) + requested_networks = self._get_requested_networks( + requested_networks) + flavor_id = self.controller._flavor_id_from_req_data(body) if not 'name' in body['server']: @@ -148,12 +156,17 @@ class CreateInstanceHelper(object): zone_blob=zone_blob, reservation_id=reservation_id, min_count=min_count, - max_count=max_count)) + max_count=max_count, + requested_networks=requested_networks)) except quota.QuotaError as error: self._handle_quota_error(error) except exception.ImageNotFound as error: msg = _("Can not find requested image") raise faults.Fault(exc.HTTPBadRequest(explanation=msg)) + except exception.NovaException as ex: + LOG.error(ex) + msg = _("Failed to create server: %s") % ex + raise faults.Fault(exc.HTTPBadRequest(explanation=msg)) # Let the caller deal with unhandled exceptions. @@ -276,6 +289,44 @@ class CreateInstanceHelper(object): raise exc.HTTPBadRequest(explanation=msg) return password + def _get_requested_networks(self, requested_networks): + """ + Create a list of requested networks from the networks attribute + """ + networks = [] + for network in requested_networks: + try: + network_id = network['id'] + network_id = int(network_id) + #fixed IP address is optional + #if the fixed IP address is not provided then + #it will used one of the available IP address from the network + fixed_ip = network.get('fixed_ip', None) + + # check if the network id is already present in the list, + # we don't want duplicate networks to be passed + # at the boot time + for id, ip in networks: + if id == network_id: + expl = _("Duplicate networks (%s) are not allowed")\ + % network_id + raise faults.Fault(exc.HTTPBadRequest( + explanation=expl)) + + networks.append((network_id, fixed_ip)) + except KeyError as key: + expl = _('Bad network format: missing %s') % key + raise faults.Fault(exc.HTTPBadRequest(explanation=expl)) + except ValueError: + expl = _("Bad networks format: network id should " + "be integer (%s)") % network_id + raise faults.Fault(exc.HTTPBadRequest(explanation=expl)) + except TypeError: + expl = _('Bad networks format') + raise faults.Fault(exc.HTTPBadRequest(explanation=expl)) + + return networks + class ServerXMLDeserializer(wsgi.XMLDeserializer): """ diff --git a/nova/compute/api.py b/nova/compute/api.py index 28459dc75..273ff285d 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -95,6 +95,7 @@ class API(base.Base): if not network_api: network_api = network.API() self.network_api = network_api + self.network_manager = utils.import_object(FLAGS.network_manager) if not volume_api: volume_api = volume.API() self.volume_api = volume_api @@ -142,6 +143,16 @@ class API(base.Base): LOG.warn(msg) raise quota.QuotaError(msg, "MetadataLimitExceeded") + def _check_requested_networks(self, context, requested_networks): + """ Check if the networks requested belongs to the project + and the fixed IP address for each network provided is within + same the network block + """ + if requested_networks is None: + return + + self.network_api.validate_networks(context, requested_networks) + def _check_create_parameters(self, context, instance_type, image_href, kernel_id=None, ramdisk_id=None, min_count=None, max_count=None, @@ -149,7 +160,7 @@ class API(base.Base): key_name=None, key_data=None, security_group='default', availability_zone=None, user_data=None, metadata={}, injected_files=None, admin_password=None, zone_blob=None, - reservation_id=None): + reservation_id=None, requested_networks=None): """Verify all the input parameters regardless of the provisioning strategy being performed.""" @@ -176,6 +187,7 @@ class API(base.Base): self._check_metadata_properties_quota(context, metadata) self._check_injected_file_quota(context, injected_files) + self._check_requested_networks(context, requested_networks) (image_service, image_id) = nova.image.get_image_service(image_href) image = image_service.show(context, image_id) @@ -315,7 +327,8 @@ class API(base.Base): instance_type, zone_blob, availability_zone, injected_files, admin_password, - instance_id=None, num_instances=1): + instance_id=None, num_instances=1, + requested_networks=None): """Send the run_instance request to the schedulers for processing.""" pid = context.project_id uid = context.user_id @@ -343,7 +356,8 @@ class API(base.Base): "request_spec": request_spec, "availability_zone": availability_zone, "admin_password": admin_password, - "injected_files": injected_files}}) + "injected_files": injected_files, + "requested_networks": requested_networks}}) def create_all_at_once(self, context, instance_type, image_href, kernel_id=None, ramdisk_id=None, @@ -381,7 +395,8 @@ class API(base.Base): key_name=None, key_data=None, security_group='default', availability_zone=None, user_data=None, metadata={}, injected_files=None, admin_password=None, zone_blob=None, - reservation_id=None, block_device_mapping=None): + reservation_id=None, block_device_mapping=None, + requested_networks=None): """ Provision the instances by sending off a series of single instance requests to the Schedulers. This is fine for trival @@ -402,7 +417,7 @@ class API(base.Base): key_name, key_data, security_group, availability_zone, user_data, metadata, injected_files, admin_password, zone_blob, - reservation_id) + reservation_id, requested_networks) instances = [] LOG.debug(_("Going to run %s instances..."), num_instances) @@ -414,10 +429,11 @@ class API(base.Base): instance_id = instance['id'] self._ask_scheduler_to_create_instance(context, base_options, - instance_type, zone_blob, - availability_zone, injected_files, - admin_password, - instance_id=instance_id) + instance_type, zone_blob, + availability_zone, injected_files, + admin_password, + instance_id=instance_id, + requested_networks=requested_networks) return [dict(x.iteritems()) for x in instances] diff --git a/nova/compute/manager.py b/nova/compute/manager.py index bbbddde0a..f77728034 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -269,6 +269,7 @@ class ComputeManager(manager.SchedulerDependentManager): context = context.elevated() instance = self.db.instance_get(context, instance_id) instance.injected_files = kwargs.get('injected_files', []) + requested_networks = kwargs.get('requested_networks', None) instance.admin_pass = kwargs.get('admin_password', None) if instance['name'] in self.driver.list_instances(): raise exception.Error(_("Instance has already been created")) @@ -291,7 +292,8 @@ class ComputeManager(manager.SchedulerDependentManager): # will eventually also need to save the address here. if not FLAGS.stub_network: network_info = self.network_api.allocate_for_instance(context, - instance, vpn=is_vpn) + instance, vpn=is_vpn, + requested_networks=requested_networks) LOG.debug(_("instance network_info: |%s|"), network_info) self.network_manager.setup_compute_network(context, instance_id) diff --git a/nova/db/api.py b/nova/db/api.py index b7c5700e5..ca904738d 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -341,6 +341,14 @@ def fixed_ip_associate_pool(context, network_id, instance_id): return IMPL.fixed_ip_associate_pool(context, network_id, instance_id) +def fixed_ip_associate_by_address(context, network_id, instance_id, address): + """check if the address is free and is in the network + and it is not associated to any instance. + """ + return IMPL.fixed_ip_associate_by_address(context, network_id, + instance_id, address) + + def fixed_ip_create(context, values): """Create a fixed ip from the values dictionary.""" return IMPL.fixed_ip_create(context, values) @@ -400,6 +408,13 @@ def fixed_ip_update(context, address, values): return IMPL.fixed_ip_update(context, address, values) +def fixed_ip_validate_by_network_address(context, network_id, + address): + """validates if the address belongs to the network""" + return IMPL.fixed_ip_validate_by_network_address(context, network_id, + address) + + #################### @@ -689,7 +704,14 @@ def network_get_all(context): return IMPL.network_get_all(context) +def network_get_requested_networks(context, requested_networks): + """Return all defined networks.""" + return IMPL.network_get_requested_networks(context, requested_networks) + + # pylint: disable=C0103 + + def network_get_associated_fixed_ips(context, network_id): """Get all network's ips that have been associated.""" return IMPL.network_get_associated_fixed_ips(context, network_id) @@ -1228,6 +1250,16 @@ def project_get_networks(context, project_id, associate=True): return IMPL.project_get_networks(context, project_id, associate) +def project_get_requested_networks(context, requested_networks): + """Return the network associated with the project. + + If associate is true, it will attempt to associate a new + network if one is not found, otherwise it returns None. + + """ + return IMPL.project_get_requested_networks(context, requested_networks) + + def project_get_networks_v6(context, project_id): return IMPL.project_get_networks_v6(context, project_id) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index ffd009513..d5cfc6099 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -251,7 +251,7 @@ def service_get_all_network_sorted(context): session = get_session() with session.begin(): topic = 'network' - label = 'network_count' + label = 'network_' subq = session.query(models.Network.host, func.count(models.Network.id).label(label)).\ filter_by(deleted=False).\ @@ -684,6 +684,40 @@ def fixed_ip_associate_pool(context, network_id, instance_id): return fixed_ip_ref['address'] +@require_admin_context +def fixed_ip_associate_by_address(context, network_id, instance_id, + address): + if address is None: + return fixed_ip_associate_pool(context, network_id, instance_id) + + session = get_session() + with session.begin(): + fixed_ip_ref = session.query(models.FixedIp).\ + filter_by(reserved=False).\ + filter_by(deleted=False).\ + filter_by(network_id=network_id).\ + filter_by(address=address).\ + with_lockmode('update').\ + first() + # NOTE(vish): if with_lockmode isn't supported, as in sqlite, + # then this has concurrency issues + if fixed_ip_ref is None: + raise exception.FixedIpNotFoundForNetwork(address=address, + network_id=network_id) + if fixed_ip_ref.instance is not None: + raise exception.FixedIpAlreadyInUse(address=address) + + if not fixed_ip_ref.network: + fixed_ip_ref.network = network_get(context, + network_id, + session=session) + fixed_ip_ref.instance = instance_get(context, + instance_id, + session=session) + session.add(fixed_ip_ref) + return fixed_ip_ref['address'] + + @require_context def fixed_ip_create(_context, values): fixed_ip_ref = models.FixedIp() @@ -771,6 +805,26 @@ def fixed_ip_get_by_address(context, address, session=None): return result +@require_context +def fixed_ip_validate_by_network_address(context, network_id, + address): + session = get_session() + fixed_ip_ref = session.query(models.FixedIp).\ + filter_by(address=address).\ + filter_by(reserved=False).\ + filter_by(network_id=network_id).\ + filter_by(deleted=can_read_deleted(context)).\ + first() + + if fixed_ip_ref is None: + raise exception.FixedIpNotFoundForNetwork(address=address, + network_id=network_id) + if fixed_ip_ref.instance is not None: + raise exception.FixedIpAlreadyInUse(address=address) + + return fixed_ip_ref + + @require_context def fixed_ip_get_by_instance(context, instance_id): session = get_session() @@ -1613,6 +1667,39 @@ def network_get_all(context): return result +@require_admin_context +def network_get_requested_networks(context, requested_networks): + session = get_session() + + network_ids = [] + for id, fixed_ip in requested_networks: + network_ids.append(id) + + result = session.query(models.Network).\ + filter(models.Network.id.in_(network_ids)).\ + filter_by(deleted=False).all() + if not result: + raise exception.NoNetworksFound() + + #check if host is set to all of the networks + # returned in the result + for network in result: + if network['host'] is None: + raise exception.NetworkHostNotSet(network_id=network['id']) + + #check if the result contains all the networks + #we are looking for + for network_id in network_ids: + found = False + for network in result: + if network['id'] == network_id: + found = True + break + if not found: + raise exception.NetworkNotFound(network_id=network_id) + + return result + # NOTE(vish): pylint complains because of the long method name, but # it fits with the names of the rest of the methods # pylint: disable=C0103 @@ -2727,6 +2814,42 @@ def project_get_networks(context, project_id, associate=True): return result +@require_context +def project_get_requested_networks(context, requested_networks): + session = get_session() + + network_ids = [] + for id, fixed_ip in requested_networks: + network_ids.append(id) + + result = session.query(models.Network).\ + filter(models.Network.id.in_(network_ids)).\ + filter_by(deleted=False).\ + filter_by(project_id=context.project_id).all() + + if not result: + raise exception.NoNetworksFound() + + #check if host is set to all of the networks + # returned in the result + for network in result: + if network['host'] is None: + raise exception.NetworkHostNotSet(network_id=network['id']) + + #check if the result contains all the networks + #we are looking for + for network_id in network_ids: + found = False + for network in result: + if network['id'] == network_id: + found = True + break + if not found: + raise exception.NetworkNotFoundForProject(network_id=network_id, + project_id=context.project_id) + return result + + @require_context def project_get_networks_v6(context, project_id): return project_get_networks(context, project_id) diff --git a/nova/exception.py b/nova/exception.py index a6776b64f..a1803d4f6 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -142,6 +142,10 @@ class Invalid(NovaException): message = _("Unacceptable parameters.") +class AlreadyInUse(NovaException): + message = _("Already is in use.") + + class InvalidSignature(Invalid): message = _("Invalid signature %(signature)s for user %(user)s.") @@ -361,6 +365,15 @@ class NoNetworksFound(NotFound): message = _("No networks defined.") +class NetworkNotFoundForProject(NotFound): + message = _("Either Network %(network_id)s is not present or " + "is not assigned to the project %(project_id)s.") + + +class NetworkHostNotSet(NovaException): + message = _("Host is not set to the network (%(network_id)s).") + + class DatastoreNotFound(NotFound): message = _("Could not find the datastore reference(s) which the VM uses.") @@ -385,6 +398,19 @@ class FixedIpNotFoundForHost(FixedIpNotFound): message = _("Host %(host)s has zero fixed ips.") +class FixedIpNotFoundForNetwork(FixedIpNotFound): + message = _("Fixed IP address (%(address)s) does not exist in " + "network (%(network_id)s).") + + +class FixedIpAlreadyInUse(AlreadyInUse): + message = _("Fixed IP address %(address)s is already in use.") + + +class FixedIpInvalid(Invalid): + message = _("Fixed IP address %(address)s is invalid.") + + class NoMoreFixedIps(Error): message = _("Zero fixed ips available.") diff --git a/nova/network/api.py b/nova/network/api.py index b2b96082b..0993038a7 100644 --- a/nova/network/api.py +++ b/nova/network/api.py @@ -169,3 +169,12 @@ class API(base.Base): return rpc.call(context, FLAGS.network_topic, {'method': 'get_instance_nw_info', 'args': args}) + + def validate_networks(self, context, requested_networks): + """validate the networks passed at the time of creating + the server + """ + args = {'networks': requested_networks} + return rpc.call(context, FLAGS.network_topic, + {'method': 'validate_networks', + 'args': args}) diff --git a/nova/network/manager.py b/nova/network/manager.py index d42bc8c4e..746b2ecc2 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -124,11 +124,18 @@ class RPCAllocateFixedIP(object): used since they share code to RPC.call allocate_fixed_ip on the correct network host to configure dnsmasq """ - def _allocate_fixed_ips(self, context, instance_id, networks): + def _allocate_fixed_ips(self, context, instance_id, networks, + requested_networks=None): """Calls allocate_fixed_ip once for each network.""" green_pool = greenpool.GreenPool() - for network in networks: + address = None + if requested_networks is not None: + for id, fixed_ip in requested_networks: + if (network['id'] == id): + address = fixed_ip + break + if network['host'] != self.host: # need to call allocate_fixed_ip to correct network host topic = self.db.queue_get_for(context, FLAGS.network_topic, @@ -136,23 +143,25 @@ class RPCAllocateFixedIP(object): args = {} args['instance_id'] = instance_id args['network_id'] = network['id'] - + args['address'] = address green_pool.spawn_n(rpc.call, context, topic, {'method': '_rpc_allocate_fixed_ip', 'args': args}) else: # i am the correct host, run here - self.allocate_fixed_ip(context, instance_id, network) + self.allocate_fixed_ip(context, instance_id, network, + address=address) # wait for all of the allocates (if any) to finish green_pool.waitall() - def _rpc_allocate_fixed_ip(self, context, instance_id, network_id): + def _rpc_allocate_fixed_ip(self, context, instance_id, network_id, + address=None): """Sits in between _allocate_fixed_ips and allocate_fixed_ip to perform network lookup on the far side of rpc. """ network = self.db.network_get(context, network_id) - self.allocate_fixed_ip(context, instance_id, network) + self.allocate_fixed_ip(context, instance_id, network, address=address) class FloatingIP(object): @@ -185,6 +194,7 @@ class FloatingIP(object): """ instance_id = kwargs.get('instance_id') project_id = kwargs.get('project_id') + requested_networks = kwargs.get('requested_networks') LOG.debug(_("floating IP allocation for instance |%s|"), instance_id, context=context) # call the next inherited class's allocate_for_instance() @@ -339,16 +349,21 @@ class NetworkManager(manager.SchedulerDependentManager): networks = self.db.network_get_all(context) for network in networks: host = network['host'] - if not host: + if host: # return so worker will only grab 1 (to help scale flatter) return self.set_network_host(context, network['id']) - def _get_networks_for_instance(self, context, instance_id, project_id): + def _get_networks_for_instance(self, context, instance_id, project_id, + requested_networks=None): """Determine & return which networks an instance should connect to.""" # TODO(tr3buchet) maybe this needs to be updated in the future if # there is a better way to determine which networks # a non-vlan instance should connect to - networks = self.db.network_get_all(context) + if requested_networks: + networks = self.db.network_get_requested_networks(context, + requested_networks) + else: + networks = self.db.network_get_all(context) # return only networks which are not vlan networks and have host set return [network for network in networks if @@ -362,13 +377,19 @@ class NetworkManager(manager.SchedulerDependentManager): instance_id = kwargs.pop('instance_id') project_id = kwargs.pop('project_id') type_id = kwargs.pop('instance_type_id') + requested_networks = kwargs.pop('requested_networks') + LOG.debug(requested_networks) admin_context = context.elevated() LOG.debug(_("network allocations for instance %s"), instance_id, context=context) - networks = self._get_networks_for_instance(admin_context, instance_id, - project_id) + networks = self._get_networks_for_instance(admin_context, + instance_id, + project_id, + requested_networks) self._allocate_mac_addresses(context, instance_id, networks) - self._allocate_fixed_ips(admin_context, instance_id, networks) + self._allocate_fixed_ips(admin_context, instance_id, + networks, + requested_networks=requested_networks) return self.get_instance_nw_info(context, instance_id, type_id) def deallocate_for_instance(self, context, **kwargs): @@ -486,9 +507,11 @@ class NetworkManager(manager.SchedulerDependentManager): # with a network, or a cluster of computes with a network # and use that network here with a method like # network_get_by_compute_host - address = self.db.fixed_ip_associate_pool(context.elevated(), + address = kwargs.get('address', None) + address = self.db.fixed_ip_associate_by_address(context.elevated(), network['id'], - instance_id) + instance_id, + address) vif = self.db.virtual_interface_get_by_instance_and_network(context, instance_id, network['id']) @@ -637,7 +660,8 @@ class NetworkManager(manager.SchedulerDependentManager): 'address': address, 'reserved': reserved}) - def _allocate_fixed_ips(self, context, instance_id, networks): + def _allocate_fixed_ips(self, context, instance_id, networks, + requested_networks=None): """Calls allocate_fixed_ip once for each network.""" raise NotImplementedError() @@ -653,6 +677,25 @@ class NetworkManager(manager.SchedulerDependentManager): """ raise NotImplementedError() + def validate_networks(self, context, networks): + """check if the networks exists and host + is set to each network. + """ + if networks is None: + return + + result = self.db.network_get_requested_networks(context, networks) + for network_id, fixed_ip in networks: + # check if the fixed IP address is valid and + # it actually belongs to the network + if fixed_ip is not None: + if not utils.is_valid_ipv4(fixed_ip): + raise exception.FixedIpInvalid(address=fixed_ip) + + self.db.fixed_ip_validate_by_network_address(context, + network_id, + fixed_ip) + class FlatManager(NetworkManager): """Basic network where no vlans are used. @@ -684,10 +727,18 @@ class FlatManager(NetworkManager): timeout_fixed_ips = False - def _allocate_fixed_ips(self, context, instance_id, networks): + def _allocate_fixed_ips(self, context, instance_id, networks, + requested_networks=None): """Calls allocate_fixed_ip once for each network.""" for network in networks: - self.allocate_fixed_ip(context, instance_id, network) + address = None + if requested_networks is not None: + for id, fixed_ip in requested_networks: + if (network['id'] == id): + address = fixed_ip + break + self.allocate_fixed_ip(context, instance_id, + network, address=address) def deallocate_fixed_ip(self, context, address, **kwargs): """Returns a fixed ip to the pool.""" @@ -741,11 +792,13 @@ class FlatDHCPManager(FloatingIP, RPCAllocateFixedIP, NetworkManager): self.driver.ensure_bridge(network['bridge'], network['bridge_interface']) - def allocate_fixed_ip(self, context, instance_id, network): + def allocate_fixed_ip(self, context, instance_id, network, + address=None): """Allocate flat_network fixed_ip, then setup dhcp for this network.""" address = super(FlatDHCPManager, self).allocate_fixed_ip(context, instance_id, - network) + network, + address) if not FLAGS.fake_network: self.driver.update_dhcp(context, network['id']) @@ -794,15 +847,17 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): def allocate_fixed_ip(self, context, instance_id, network, **kwargs): """Gets a fixed ip from the pool.""" + address = kwargs.get('address', None) if kwargs.get('vpn', None): address = network['vpn_private_address'] self.db.fixed_ip_associate(context, address, instance_id) else: - address = self.db.fixed_ip_associate_pool(context, + address = self.db.fixed_ip_associate_by_address(context, network['id'], - instance_id) + instance_id, + address) vif = self.db.virtual_interface_get_by_instance_and_network(context, instance_id, network['id']) @@ -826,10 +881,15 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): network['bridge'], network['bridge_interface']) - def _get_networks_for_instance(self, context, instance_id, project_id): + def _get_networks_for_instance(self, context, instance_id, project_id, + requested_networks=None): """Determine which networks an instance should connect to.""" # get networks associated with project - networks = self.db.project_get_networks(context, project_id) + if requested_networks is not None: + networks = self.db.project_get_requested_networks(context, + requested_networks) + else: + networks = self.db.project_get_networks(context, project_id) # return only networks which have host set return [network for network in networks if network['host']] @@ -878,6 +938,25 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): if(FLAGS.use_ipv6): self.driver.update_ra(context, network_id) + def validate_networks(self, context, networks): + """check if the networks exists and host + is set to each network. + """ + if networks is None: + return + + result = self.db.project_get_requested_networks(context, networks) + for network_id, fixed_ip in networks: + # check if the fixed IP address is valid and + # it actually belongs to the network + if fixed_ip is not None: + if not utils.is_valid_ipv4(fixed_ip): + raise exception.FixedIpInvalid(address=fixed_ip) + + self.db.fixed_ip_validate_by_network_address(context, + network_id, + fixed_ip) + @property def _bottom_reserved_ips(self): """Number of reserved ips at the bottom of the range.""" diff --git a/nova/utils.py b/nova/utils.py index 8784a227d..22b3a29bf 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -775,6 +775,22 @@ def bool_from_str(val): return val.lower() == 'true' +def is_valid_ipv4(address): + """valid the address strictly as per format xxx.xxx.xxx.xxx. + where xxx is a value between 0 and 255. + """ + parts = address.split(".") + if len(parts) != 4: + return False + for item in parts: + try: + if not 0 <= int(item) <= 255: + return False + except ValueError: + return False + return True + + class Bootstrapper(object): """Provides environment bootstrapping capabilities for entry points.""" -- cgit From e30a9e1516e44d839ebd5b41586a32e99c47b8c9 Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Mon, 11 Jul 2011 14:25:51 -0700 Subject: unknowingly made these changes, reverting to original --- nova/network/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index 746b2ecc2..dd847ac28 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -349,7 +349,7 @@ class NetworkManager(manager.SchedulerDependentManager): networks = self.db.network_get_all(context) for network in networks: host = network['host'] - if host: + if not host: # return so worker will only grab 1 (to help scale flatter) return self.set_network_host(context, network['id']) -- cgit From bf30f9c1d65053aba29ffff8d8d9a3810455a082 Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Mon, 11 Jul 2011 16:02:46 -0700 Subject: fixed all failed unit test cases --- nova/compute/api.py | 8 +++++--- nova/tests/test_network.py | 5 +++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index ddcb23db5..fd7df4560 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -366,7 +366,8 @@ class API(base.Base): key_name=None, key_data=None, security_group='default', availability_zone=None, user_data=None, metadata={}, injected_files=None, admin_password=None, zone_blob=None, - reservation_id=None, block_device_mapping=None): + reservation_id=None, block_device_mapping=None, + requested_networks=None): """Provision the instances by passing the whole request to the Scheduler for execution. Returns a Reservation ID related to the creation of all of these instances.""" @@ -378,13 +379,14 @@ class API(base.Base): key_name, key_data, security_group, availability_zone, user_data, metadata, injected_files, admin_password, zone_blob, - reservation_id) + reservation_id, requested_networks) self._ask_scheduler_to_create_instance(context, base_options, instance_type, zone_blob, availability_zone, injected_files, admin_password, - num_instances=num_instances) + num_instances=num_instances, + requested_networks=requested_networks) return base_options['reservation_id'] diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index 6d5166019..30e1c0512 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -211,12 +211,13 @@ class VlanNetworkTestCase(test.TestCase): self.network.allocate_fixed_ip(None, 0, network, vpn=True) def test_allocate_fixed_ip(self): - self.mox.StubOutWithMock(db, 'fixed_ip_associate_pool') + self.mox.StubOutWithMock(db, 'fixed_ip_associate_by_address') self.mox.StubOutWithMock(db, 'fixed_ip_update') self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance_and_network') - db.fixed_ip_associate_pool(mox.IgnoreArg(), + db.fixed_ip_associate_by_address(mox.IgnoreArg(), + mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg()).AndReturn('192.168.0.1') db.fixed_ip_update(mox.IgnoreArg(), -- cgit From 51834c2141bdbc283b9d165372be08eb6b9409ca Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Tue, 12 Jul 2011 10:36:55 -0700 Subject: Allowed empty networks, handled RemoteError properly, implemented xml format for networks and fixed broken unit test cases --- nova/api/openstack/create_instance_helper.py | 29 ++++++++++++++++++++++------ nova/network/manager.py | 4 ++-- nova/tests/api/openstack/test_servers.py | 14 ++++++++++++++ 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index ef2a8393f..5e1c8d8d9 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -25,6 +25,7 @@ from nova import flags from nova import log as logging import nova.image from nova import quota +from nova import rpc from nova import utils from nova.compute import instance_types @@ -104,9 +105,6 @@ class CreateInstanceHelper(object): requested_networks = body['server'].get('networks') if requested_networks is not None: - if len(requested_networks) == 0: - msg = _("No networks found") - raise faults.Fault(exc.HTTPBadRequest(explanation=msg)) requested_networks = self._get_requested_networks( requested_networks) @@ -163,9 +161,9 @@ class CreateInstanceHelper(object): except exception.ImageNotFound as error: msg = _("Can not find requested image") raise faults.Fault(exc.HTTPBadRequest(explanation=msg)) - except exception.NovaException as ex: - LOG.error(ex) - msg = _("Failed to create server: %s") % ex + except rpc.RemoteError as err: + LOG.error(err) + msg = _("%s:%s") % (err.exc_type, err.value) raise faults.Fault(exc.HTTPBadRequest(explanation=msg)) # Let the caller deal with unhandled exceptions. @@ -355,6 +353,9 @@ class ServerXMLDeserializer(wsgi.XMLDeserializer): personality = self._extract_personality(server_node) if personality is not None: server["personality"] = personality + networks = self._extract_networks(server_node) + if networks is not None: + server["networks"] = networks return server def _extract_metadata(self, server_node): @@ -383,6 +384,22 @@ class ServerXMLDeserializer(wsgi.XMLDeserializer): personality.append(item) return personality + def _extract_networks(self, server_node): + """Marshal the networks attribute of a parsed request""" + networks_node = \ + self._find_first_child_named(server_node, "networks") + if networks_node is None: + return None + networks = [] + for network_node in self._find_children_named(networks_node, "network"): + item = {} + if network_node.hasAttribute("id"): + item["id"] = network_node.getAttribute("id") + if network_node.hasAttribute("fixed_ip"): + item["fixed_ip"] = network_node.getAttribute("fixed_ip") + networks.append(item) + return networks + def _find_first_child_named(self, parent, name): """Search a nodes children for the first child with a given name""" for node in parent.childNodes: diff --git a/nova/network/manager.py b/nova/network/manager.py index dd847ac28..90ff81374 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -359,7 +359,7 @@ class NetworkManager(manager.SchedulerDependentManager): # TODO(tr3buchet) maybe this needs to be updated in the future if # there is a better way to determine which networks # a non-vlan instance should connect to - if requested_networks: + if requested_networks is not None and len(requested_networks) != 0: networks = self.db.network_get_requested_networks(context, requested_networks) else: @@ -885,7 +885,7 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): requested_networks=None): """Determine which networks an instance should connect to.""" # get networks associated with project - if requested_networks is not None: + if requested_networks is not None and len(requested_networks) != 0: networks = self.db.project_get_requested_networks(context, requested_networks) else: diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 8e82a7edc..3bac184d5 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -1902,6 +1902,20 @@ b25zLiINCg0KLVJpY2hhcmQgQmFjaA==""", self.assertEquals(request['body']["server"]["imageRef"], "http://localhost:8774/v1.1/images/1") + def test_request_with_empty_networks(self): + serial_request = """ + + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageId": "1", + "flavorId": "1", + "networks": [], + }} + self.assertEquals(request['body'], expected) class TestServerInstanceCreation(test.TestCase): -- cgit From 2be9a4e19449f9cf37f62f3f6e380de3e7ca0d38 Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Tue, 12 Jul 2011 11:28:06 -0700 Subject: added xml deserialization unit test cases and fixe some pep errors --- nova/api/openstack/create_instance_helper.py | 3 +- nova/tests/api/openstack/test_servers.py | 144 +++++++++++++++++++++++++++ 2 files changed, 146 insertions(+), 1 deletion(-) diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 5e1c8d8d9..86fa8becc 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -391,7 +391,8 @@ class ServerXMLDeserializer(wsgi.XMLDeserializer): if networks_node is None: return None networks = [] - for network_node in self._find_children_named(networks_node, "network"): + for network_node in self._find_children_named(networks_node, + "network"): item = {} if network_node.hasAttribute("id"): item["id"] = network_node.getAttribute("id") diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 3bac184d5..cde6bd310 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -1917,6 +1917,150 @@ b25zLiINCg0KLVJpY2hhcmQgQmFjaA==""", }} self.assertEquals(request['body'], expected) + def test_request_with_one_network(self): + serial_request = """ + + + + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageId": "1", + "flavorId": "1", + "networks": [{"id": "1", "fixed_ip": "10.0.1.12"}], + }} + self.assertEquals(request['body'], expected) + + def test_request_with_two_networks(self): + serial_request = """ + + + + + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageId": "1", + "flavorId": "1", + "networks": [{"id": "1", "fixed_ip": "10.0.1.12"}, + {"id": "2", "fixed_ip": "10.0.2.12"}], + }} + self.assertEquals(request['body'], expected) + + def test_request_with_second_network_node_ignored(self): + serial_request = """ + + + + + + + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageId": "1", + "flavorId": "1", + "networks": [{"id": "1", "fixed_ip": "10.0.1.12"}], + }} + self.assertEquals(request['body'], expected) + + def test_request_with_one_network_missing_id(self): + serial_request = """ + + + + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageId": "1", + "flavorId": "1", + "networks": [{"fixed_ip": "10.0.1.12"}], + }} + self.assertEquals(request['body'], expected) + + def test_request_with_one_network_missing_fixed_ip(self): + serial_request = """ + + + + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageId": "1", + "flavorId": "1", + "networks": [{"id": "1"}], + }} + self.assertEquals(request['body'], expected) + + def test_request_with_one_network_empty_id(self): + serial_request = """ + + + + + """ + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageId": "1", + "flavorId": "1", + "networks": [{"id": "", "fixed_ip": "10.0.1.12"}], + }} + self.assertEquals(request['body'], expected) + + def test_request_with_one_network_empty_fixed_ip(self): + serial_request = """ + + + + + """ + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageId": "1", + "flavorId": "1", + "networks": [{"id": "1", "fixed_ip": ""}], + }} + self.assertEquals(request['body'], expected) + + def test_request_with_networks_duplicate_ids(self): + serial_request = """ + + + + + + """ + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageId": "1", + "flavorId": "1", + "networks": [{"id": "1", "fixed_ip": "10.0.1.12"}, + {"id": "1", "fixed_ip": "10.0.2.12"}], + }} + self.assertEquals(request['body'], expected) + + class TestServerInstanceCreation(test.TestCase): def setUp(self): -- cgit From 6e75e608cc7260317f014e57ba070b152f83d0e7 Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Tue, 12 Jul 2011 17:46:03 -0700 Subject: added unit test cases and minor changes (localization fix and added fixed_ip validation) --- nova/api/openstack/create_instance_helper.py | 18 ++- nova/tests/api/openstack/test_servers.py | 200 ++++++++++++++++++++++++++- 2 files changed, 212 insertions(+), 6 deletions(-) diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 86fa8becc..5eef0ae00 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -162,8 +162,7 @@ class CreateInstanceHelper(object): msg = _("Can not find requested image") raise faults.Fault(exc.HTTPBadRequest(explanation=msg)) except rpc.RemoteError as err: - LOG.error(err) - msg = _("%s:%s") % (err.exc_type, err.value) + msg = _("%(err.exc_type)s:%(err.value)s") raise faults.Fault(exc.HTTPBadRequest(explanation=msg)) # Let the caller deal with unhandled exceptions. @@ -205,6 +204,15 @@ class CreateInstanceHelper(object): msg = _("Server name is an empty string") raise exc.HTTPBadRequest(explanation=msg) + def _validate_fixed_ip(self, value): + if not isinstance(value, basestring): + msg = _("Fixed IP is not a string or unicode") + raise exc.HTTPBadRequest(explanation=msg) + + if value.strip() == '': + msg = _("Fixed IP is an empty string") + raise exc.HTTPBadRequest(explanation=msg) + def _get_kernel_ramdisk_from_image(self, req, image_id): """Fetch an image from the ImageService, then if present, return the associated kernel and ramdisk image IDs. @@ -298,9 +306,10 @@ class CreateInstanceHelper(object): network_id = int(network_id) #fixed IP address is optional #if the fixed IP address is not provided then - #it will used one of the available IP address from the network + #it will use one of the available IP address from the network fixed_ip = network.get('fixed_ip', None) - + if fixed_ip is not None: + self._validate_fixed_ip(fixed_ip) # check if the network id is already present in the list, # we don't want duplicate networks to be passed # at the boot time @@ -404,6 +413,7 @@ class ServerXMLDeserializer(wsgi.XMLDeserializer): def _find_first_child_named(self, parent, name): """Search a nodes children for the first child with a given name""" for node in parent.childNodes: + LOG.debug(node.nodeName) if node.nodeName == name: return node return None diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index cde6bd310..f70ad41cc 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -588,6 +588,11 @@ class ServersTest(test.TestCase): def project_get_networks(context, user_id): return dict(id='1', host='localhost') + def project_get_requested_networks(context, requested_networks): + return [{'id': 1, 'host': 'localhost1'}, + {'id': 2, 'host': 'localhost2'}, + ] + def queue_get_for(context, *args): return 'network_topic' @@ -599,6 +604,8 @@ class ServersTest(test.TestCase): self.stubs.Set(nova.db.api, 'project_get_networks', project_get_networks) + self.stubs.Set(nova.db.api, 'project_get_requested_networks', + project_get_requested_networks) self.stubs.Set(nova.db.api, 'instance_create', instance_create) self.stubs.Set(nova.rpc, 'cast', fake_method) self.stubs.Set(nova.rpc, 'call', fake_method) @@ -901,6 +908,29 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 400) + def test_create_instance_whitespace_name(self): + self._setup_for_create_instance() + + body = { + 'server': { + 'name': ' ', + 'imageId': 3, + 'flavorId': 1, + 'metadata': { + 'hello': 'world', + 'open': 'stack', + }, + 'personality': {}, + }, + } + + req = webob.Request.blank('/v1.0/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 400) + def test_update_no_body(self): req = webob.Request.blank('/v1.0/servers/1') req.method = 'PUT' @@ -2078,18 +2108,24 @@ class TestServerInstanceCreation(test.TestCase): FLAGS.allow_admin_api = self.allow_admin super(TestServerInstanceCreation, self).tearDown() - def _setup_mock_compute_api_for_personality(self): + def _setup_mock_compute_api(self): class MockComputeAPI(nova.compute.API): def __init__(self): self.injected_files = None + self.networks = None def create(self, *args, **kwargs): if 'injected_files' in kwargs: self.injected_files = kwargs['injected_files'] else: self.injected_files = None + + if 'requested_networks' in kwargs: + self.networks = kwargs['requested_networks'] + else: + self.networks = None return [{'id': '1234', 'display_name': 'fakeinstance', 'uuid': FAKE_UUID}] @@ -2120,6 +2156,18 @@ class TestServerInstanceCreation(test.TestCase): server['personality'] = personalities return {'server': server} + def _create_networks_request_dict(self, networks): + server = {} + server['name'] = 'new-server-test' + server['imageId'] = 1 + server['flavorId'] = 1 + if networks is not None: + network_list = [] + for id, fixed_ip in networks: + network_list.append({'id': id, 'fixed_ip': fixed_ip}) + server['networks'] = network_list + return {'server': server} + def _get_create_request_json(self, body_dict): req = webob.Request.blank('/v1.0/servers') req.headers['Content-Type'] = 'application/json' @@ -2128,7 +2176,7 @@ class TestServerInstanceCreation(test.TestCase): return req def _run_create_instance_with_mock_compute_api(self, request): - compute_api = self._setup_mock_compute_api_for_personality() + compute_api = self._setup_mock_compute_api() response = request.get_response(fakes.wsgi_app()) return compute_api, response @@ -2153,6 +2201,14 @@ class TestServerInstanceCreation(test.TestCase): item = (file['path'], file['contents']) body_parts.append('%s' % item) body_parts.append('') + if 'networks' in server: + networks = server['networks'] + body_parts.append('') + for network in networks: + item = (network['id'], network['fixed_ip']) + body_parts.append('' + % item) + body_parts.append('') body_parts.append('') return ''.join(body_parts) @@ -2178,6 +2234,20 @@ class TestServerInstanceCreation(test.TestCase): self._run_create_instance_with_mock_compute_api(request) return request, response, compute_api.injected_files + def _create_instance_with_networks_json(self, networks): + body_dict = self._create_networks_request_dict(networks) + request = self._get_create_request_json(body_dict) + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + return request, response, compute_api.networks + + def _create_instance_with_networks_xml(self, networks): + body_dict = self._create_networks_request_dict(networks) + request = self._get_create_request_xml(body_dict) + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + return request, response, compute_api.networks + def test_create_instance_with_no_personality(self): request, response, injected_files = \ self._create_instance_with_personality_json(personality=None) @@ -2312,6 +2382,132 @@ class TestServerInstanceCreation(test.TestCase): self.assertEquals(server.nodeName, 'server') self.assertEqual(16, len(server.getAttribute('adminPass'))) + def test_create_instance_with_no_networks(self): + request, response, networks = \ + self._create_instance_with_networks_json(networks=None) + self.assertEquals(response.status_int, 200) + self.assertEquals(networks, None) + + def test_create_instance_with_no_networks_xml(self): + request, response, networks = \ + self._create_instance_with_networks_xml(networks=None) + self.assertEquals(response.status_int, 200) + self.assertEquals(networks, None) + + def test_create_instance_with_one_network(self): + id = 1 + fixed_ip = '10.0.1.12' + networks = [(id, fixed_ip)] + request, response, networks = \ + self._create_instance_with_networks_json(networks) + self.assertEquals(response.status_int, 200) + self.assertEquals(networks, [(id, fixed_ip)]) + + def test_create_instance_with_one_network_xml(self): + id = 1 + fixed_ip = '10.0.1.12' + networks = [(id, fixed_ip)] + request, response, networks = \ + self._create_instance_with_networks_xml(networks) + self.assertEquals(response.status_int, 200) + self.assertEquals(networks, [(id, fixed_ip)]) + + def test_create_instance_with_two_networks(self): + networks = [(1, '10.0.1.12'), (2, '10.0.2.12')] + request, response, networks = \ + self._create_instance_with_networks_json(networks) + self.assertEquals(response.status_int, 200) + self.assertEquals(networks, [(1, '10.0.1.12'), (2, '10.0.2.12')]) + + def test_create_instance_with_two_networks_xml(self): + networks = [(1, '10.0.1.12'), (2, '10.0.2.12')] + request, response, networks = \ + self._create_instance_with_networks_xml(networks) + self.assertEquals(response.status_int, 200) + self.assertEquals(networks, [(1, '10.0.1.12'), (2, '10.0.2.12')]) + + def test_create_instance_with_duplicate_networks(self): + networks = [(1, '10.0.1.12'), (1, '10.0.2.12')] + request, response, networks = \ + self._create_instance_with_networks_json(networks) + self.assertEquals(response.status_int, 400) + self.assertEquals(networks, None) + + def test_create_instance_with_duplicate_networks_xml(self): + networks = [(1, '10.0.1.12'), (1, '10.0.2.12')] + request, response, networks = \ + self._create_instance_with_networks_xml(networks) + self.assertEquals(response.status_int, 400) + self.assertEquals(networks, None) + + def test_create_instance_with_network_no_id(self): + networks = [(1, '10.0.1.12')] + body_dict = self._create_networks_request_dict(networks) + del body_dict['server']['networks'][0]['id'] + request = self._get_create_request_json(body_dict) + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + self.assertEquals(response.status_int, 400) + self.assertEquals(compute_api.networks, None) + + def test_create_instance_with_network_no_id_xml(self): + networks = [(1, '10.0.1.12')] + body_dict = self._create_networks_request_dict(networks) + request = self._get_create_request_xml(body_dict) + request.body = request.body.replace(' id="1"', '') + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + self.assertEquals(response.status_int, 400) + self.assertEquals(compute_api.networks, None) + + def test_create_instance_with_network_invalid_id(self): + networks = [('asd123', '10.0.1.12')] + request, response, networks = \ + self._create_instance_with_networks_json(networks) + self.assertEquals(response.status_int, 400) + self.assertEquals(networks, None) + + def test_create_instance_with_network_invalid_id_xml(self): + networks = [('asd123', '10.0.1.12')] + request, response, networks = \ + self._create_instance_with_networks_xml(networks) + self.assertEquals(response.status_int, 400) + self.assertEquals(networks, None) + + def test_create_instance_with_network_empty_fixed_ip(self): + networks = [('1', '')] + request, response, networks = \ + self._create_instance_with_networks_json(networks) + self.assertEquals(response.status_int, 400) + self.assertEquals(networks, None) + + def test_create_instance_with_network_empty_fixed_ip_xml(self): + networks = [('1', '')] + request, response, networks = \ + self._create_instance_with_networks_xml(networks) + self.assertEquals(response.status_int, 400) + self.assertEquals(networks, None) + + def test_create_instance_with_network_no_fixed_ip(self): + networks = [(1, '10.0.1.12')] + body_dict = self._create_networks_request_dict(networks) + del body_dict['server']['networks'][0]['fixed_ip'] + request = self._get_create_request_json(body_dict) + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + self.assertEquals(response.status_int, 200) + self.assertEquals(compute_api.networks, [(1, None)]) + + def test_create_instance_with_network_no_fixed_ip_xml(self): + networks = [(1, '10.0.1.12')] + body_dict = self._create_networks_request_dict(networks) + request = self._get_create_request_xml(body_dict) + request.body = request.body.replace(' fixed_ip="10.0.1.12"', '') + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + self.assertEquals(response.status_int, 200) + self.assertEquals(compute_api.networks, [(1, None)]) + class TestGetKernelRamdiskFromImage(test.TestCase): """ -- cgit From 0b905cc45b940579c6fc7363ecf5657d10a7aeed Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Wed, 13 Jul 2011 12:07:05 -0700 Subject: added unit testcases for validating the requested networks --- nova/tests/test_network.py | 120 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index 30e1c0512..d6c363481 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -16,6 +16,7 @@ # under the License. from nova import db +from nova import exception from nova import flags from nova import log as logging from nova import test @@ -183,6 +184,65 @@ class FlatNetworkTestCase(test.TestCase): 'netmask': '255.255.255.0'}] self.assertDictListMatch(nw[1]['ips'], check) + def test_validate_networks(self): + self.mox.StubOutWithMock(db, 'network_get_requested_networks') + self.mox.StubOutWithMock(db, "fixed_ip_validate_by_network_address") + + requested_networks = [(1, "192.168.0.100")] + db.network_get_requested_networks(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(networks) + db.fixed_ip_validate_by_network_address(mox.IgnoreArg(), + mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(fixed_ips[0]) + + self.mox.ReplayAll() + self.network.validate_networks(None, requested_networks) + + def test_validate_networks_none_requested_networks(self): + self.network.validate_networks(None, None) + + def test_validate_networks_empty_requested_networks(self): + requested_networks = [] + self.mox.StubOutWithMock(db, 'network_get_requested_networks') + db.network_get_requested_networks(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(networks) + self.mox.ReplayAll() + + self.network.validate_networks(None, requested_networks) + + def test_validate_networks_invalid_fixed_ip(self): + self.mox.StubOutWithMock(db, 'network_get_requested_networks') + requested_networks = [(1, "192.168.0.100.1")] + db.network_get_requested_networks(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(networks) + self.mox.ReplayAll() + + self.assertRaises(exception.FixedIpInvalid, + self.network.validate_networks, None, + requested_networks) + + def test_validate_networks_empty_fixed_ip(self): + self.mox.StubOutWithMock(db, 'network_get_requested_networks') + + requested_networks = [(1, "")] + db.network_get_requested_networks(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(networks) + self.mox.ReplayAll() + + self.assertRaises(exception.FixedIpInvalid, + self.network.validate_networks, + None, requested_networks) + + def test_validate_networks_none_fixed_ip(self): + self.mox.StubOutWithMock(db, 'network_get_requested_networks') + + requested_networks = [(1, None)] + db.network_get_requested_networks(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(networks) + self.mox.ReplayAll() + + self.network.validate_networks(None, requested_networks) + class VlanNetworkTestCase(test.TestCase): def setUp(self): @@ -239,3 +299,63 @@ class VlanNetworkTestCase(test.TestCase): self.assertRaises(ValueError, self.network.create_networks, None, num_networks=100, vlan_start=1, cidr='192.168.0.1/24', network_size=100) + + def test_validate_networks(self): + self.mox.StubOutWithMock(db, 'project_get_requested_networks') + self.mox.StubOutWithMock(db, "fixed_ip_validate_by_network_address") + + requested_networks = [(1, "192.168.0.100")] + db.project_get_requested_networks(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(networks) + db.fixed_ip_validate_by_network_address(mox.IgnoreArg(), + mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(fixed_ips[0]) + self.mox.ReplayAll() + + self.network.validate_networks(None, requested_networks) + + def test_validate_networks_none_requested_networks(self): + self.network.validate_networks(None, None) + + def test_validate_networks_empty_requested_networks(self): + requested_networks = [] + self.mox.StubOutWithMock(db, 'project_get_requested_networks') + db.project_get_requested_networks(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(networks) + self.mox.ReplayAll() + + self.network.validate_networks(None, requested_networks) + + def test_validate_networks_invalid_fixed_ip(self): + self.mox.StubOutWithMock(db, 'project_get_requested_networks') + + requested_networks = [(1, "192.168.0.100.1")] + db.project_get_requested_networks(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(networks) + self.mox.ReplayAll() + + self.assertRaises(exception.FixedIpInvalid, + self.network.validate_networks, + None, requested_networks) + + def test_validate_networks_empty_fixed_ip(self): + self.mox.StubOutWithMock(db, 'project_get_requested_networks') + + requested_networks = [(1, "")] + db.project_get_requested_networks(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(networks) + self.mox.ReplayAll() + + self.assertRaises(exception.FixedIpInvalid, + self.network.validate_networks, + None, requested_networks) + + def test_validate_networks_none_fixed_ip(self): + self.mox.StubOutWithMock(db, 'project_get_requested_networks') + + requested_networks = [(1, None)] + db.project_get_requested_networks(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(networks) + self.mox.ReplayAll() + + self.network.validate_networks(None, requested_networks) -- cgit From 2ecbdd46d48bafbeb451875ba6e7f67276d83602 Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Wed, 13 Jul 2011 13:57:50 -0700 Subject: Minor fixes --- nova/api/openstack/create_instance_helper.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 5eef0ae00..0337dd5bb 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -162,7 +162,8 @@ class CreateInstanceHelper(object): msg = _("Can not find requested image") raise faults.Fault(exc.HTTPBadRequest(explanation=msg)) except rpc.RemoteError as err: - msg = _("%(err.exc_type)s:%(err.value)s") + msg = "%(err_type)s: %(err_msg)s" % \ + {'err_type': err.exc_type, 'err_msg': err.value} raise faults.Fault(exc.HTTPBadRequest(explanation=msg)) # Let the caller deal with unhandled exceptions. -- cgit From 0655f97b2cce1e28485ddb4c37a854a65cbbc276 Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Thu, 14 Jul 2011 15:53:16 -0700 Subject: added integrated unit testcases and minor fixes --- nova/api/openstack/create_instance_helper.py | 3 - nova/network/manager.py | 6 +- nova/tests/__init__.py | 3 +- nova/tests/api/openstack/test_servers.py | 7 + nova/tests/integrated/test_servers.py | 218 +++++++++++++++++++++++++++ nova/tests/test_network.py | 8 - 6 files changed, 231 insertions(+), 14 deletions(-) diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 0337dd5bb..32b1b2f7c 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -329,9 +329,6 @@ class CreateInstanceHelper(object): expl = _("Bad networks format: network id should " "be integer (%s)") % network_id raise faults.Fault(exc.HTTPBadRequest(explanation=expl)) - except TypeError: - expl = _('Bad networks format') - raise faults.Fault(exc.HTTPBadRequest(explanation=expl)) return networks diff --git a/nova/network/manager.py b/nova/network/manager.py index e665bb1fc..b2b9335a9 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -614,6 +614,7 @@ class NetworkManager(manager.SchedulerDependentManager): net['gateway'] = str(project_net[1]) net['broadcast'] = str(project_net.broadcast) net['dhcp_start'] = str(project_net[2]) + net['project_id'] = kwargs['project_id'] if num_networks > 1: net['label'] = '%s_%d' % (label, index) else: @@ -705,7 +706,7 @@ class NetworkManager(manager.SchedulerDependentManager): """check if the networks exists and host is set to each network. """ - if networks is None: + if networks is None or len(networks) == 0: return result = self.db.network_get_requested_networks(context, networks) @@ -965,10 +966,11 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): """check if the networks exists and host is set to each network. """ - if networks is None: + if networks is None or len(networks) == 0: return result = self.db.project_get_requested_networks(context, networks) + for network_id, fixed_ip in networks: # check if the fixed IP address is valid and # it actually belongs to the network diff --git a/nova/tests/__init__.py b/nova/tests/__init__.py index e4ed75d37..44ce2e635 100644 --- a/nova/tests/__init__.py +++ b/nova/tests/__init__.py @@ -66,7 +66,8 @@ def setup(): bridge=FLAGS.flat_network_bridge, bridge_interface=bridge_interface, vpn_start=FLAGS.vpn_start, - vlan_start=FLAGS.vlan_start) + vlan_start=FLAGS.vlan_start, + project_id="openstack") for net in db.network_get_all(ctxt): network.set_network_host(ctxt, net['id']) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 52af4c17a..702dcb7a8 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -2516,6 +2516,13 @@ class TestServerInstanceCreation(test.TestCase): self.assertEquals(response.status_int, 400) self.assertEquals(networks, None) + def test_create_instance_with_network_non_string_fixed_ip(self): + networks = [('1', 12345)] + request, response, networks = \ + self._create_instance_with_networks_json(networks) + self.assertEquals(response.status_int, 400) + self.assertEquals(networks, None) + def test_create_instance_with_network_empty_fixed_ip_xml(self): networks = [('1', '')] request, response, networks = \ diff --git a/nova/tests/integrated/test_servers.py b/nova/tests/integrated/test_servers.py index fcb517cf5..08522f720 100644 --- a/nova/tests/integrated/test_servers.py +++ b/nova/tests/integrated/test_servers.py @@ -179,6 +179,224 @@ class ServersTest(integrated_helpers._IntegratedTestBase): # Cleanup self._delete_server(created_server_id) + def test_create_server_with_one_networks(self): + """Creates a server with networks.""" + + # Build the server data gradually, checking errors along the way + server = self._build_minimal_create_server_request() + + network_list = [{'id': 1, 'fixed_ip': '10.0.0.3'}] + server['networks'] = network_list + + post = {'server': server} + created_server = self.api.post_server(post) + LOG.debug("created_server: %s" % created_server) + self.assertTrue(created_server['id']) + created_server_id = created_server['id'] + + # Check it's there + found_server = self.api.get_server(created_server_id) + self.assertEqual(created_server_id, found_server['id']) + + # Cleanup + self._delete_server(created_server_id) + + def test_create_server_with_two_networks(self): + """Creates a server with networks.""" + + # Build the server data gradually, checking errors along the way + server = self._build_minimal_create_server_request() + + network_list = [{'id': 1, 'fixed_ip': '10.0.0.3'}, + {'id': 2, 'fixed_ip': '10.0.0.12'}] + + server['networks'] = network_list + + post = {'server': server} + created_server = self.api.post_server(post) + LOG.debug("created_server: %s" % created_server) + self.assertTrue(created_server['id']) + created_server_id = created_server['id'] + + # Check it's there + found_server = self.api.get_server(created_server_id) + self.assertEqual(created_server_id, found_server['id']) + + # Cleanup + self._delete_server(created_server_id) + + def test_create_server_with_empty_networks(self): + """Creates a server with networks.""" + + # Build the server data gradually, checking errors along the way + server = self._build_minimal_create_server_request() + + network_list = [] + server['networks'] = network_list + + post = {'server': server} + created_server = self.api.post_server(post) + LOG.debug("created_server: %s" % created_server) + self.assertTrue(created_server['id']) + created_server_id = created_server['id'] + + # Check it's there + found_server = self.api.get_server(created_server_id) + self.assertEqual(created_server_id, found_server['id']) + + # Cleanup + self._delete_server(created_server_id) + + def test_create_server_with_networks_invalid_id(self): + """Creates a server with networks.""" + + # Build the server data gradually, checking errors along the way + server = self._build_minimal_create_server_request() + + network_list = [{'id': "invalid", 'fixed_ip': '10.0.0.3'}] + server['networks'] = network_list + + post = {'server': server} + self.assertRaises(client.OpenStackApiException, + self.api.post_server, post) + + def test_create_server_with_networks_no_id(self): + """Creates a server with networks.""" + + # Build the server data gradually, checking errors along the way + server = self._build_minimal_create_server_request() + + network_list = [{'fixed_ip': '10.0.0.3'}] + server['networks'] = network_list + + post = {'server': server} + self.assertRaises(client.OpenStackApiException, + self.api.post_server, post) + + def test_create_server_with_networks_non_exists_id(self): + """Creates a server with networks.""" + + # Build the server data gradually, checking errors along the way + server = self._build_minimal_create_server_request() + + network_list = [{'id': 3, 'fixed_ip': '10.0.0.3'}] + server['networks'] = network_list + + post = {'server': server} + self.assertRaises(client.OpenStackApiException, + self.api.post_server, post) + + def test_create_server_with_networks_not_found_for_project(self): + """Creates a server with networks.""" + + # Build the server data gradually, checking errors along the way + server = self._build_minimal_create_server_request() + + network_list = [{'id': 1, 'fixed_ip': '10.0.0.3'}, + {'id': 3, 'fixed_ip': '10.0.0.12'}] + server['networks'] = network_list + + post = {'server': server} + self.assertRaises(client.OpenStackApiException, + self.api.post_server, post) + + def test_create_server_with_networks_invalid_fixed_ip(self): + """Creates a server with networks.""" + + # Build the server data gradually, checking errors along the way + server = self._build_minimal_create_server_request() + + network_list = [{'id': 1, 'fixed_ip': '10.0.0.3.0'}] + server['networks'] = network_list + + post = {'server': server} + self.assertRaises(client.OpenStackApiException, + self.api.post_server, post) + + def test_create_server_with_networks_non_existing_fixed_ip(self): + """Creates a server with metadata.""" + + # Build the server data gradually, checking errors along the way + server = self._build_minimal_create_server_request() + + network_list = [{'id': 1, 'fixed_ip': '10.100.0.3'}] + server['networks'] = network_list + + post = {'server': server} + self.assertRaises(client.OpenStackApiException, + self.api.post_server, post) + + + def test_create_server_with_networks_empty_fixed_ip(self): + """Creates a server with networks.""" + + # Build the server data gradually, checking errors along the way + server = self._build_minimal_create_server_request() + + network_list = [{'id': 1, 'fixed_ip': ''}] + server['networks'] = network_list + + post = {'server': server} + self.assertRaises(client.OpenStackApiException, + self.api.post_server, post) + + def test_create_server_with_networks_non_string_fixed_ip(self): + """Creates a server with networks.""" + + # Build the server data gradually, checking errors along the way + server = self._build_minimal_create_server_request() + + network_list = [{'id': 1, 'fixed_ip': 12}] + server['networks'] = network_list + + post = {'server': server} + self.assertRaises(client.OpenStackApiException, + self.api.post_server, post) + + def test_create_server_with_networks_none_fixed_ip(self): + """Creates a server with networks.""" + + # Build the server data gradually, checking errors along the way + server = self._build_minimal_create_server_request() + + network_list = [{'id': 1, 'fixed_ip': None}] + server['networks'] = network_list + + post = {'server': server} + created_server = self.api.post_server(post) + LOG.debug("created_server: %s" % created_server) + self.assertTrue(created_server['id']) + created_server_id = created_server['id'] + + # Check it's there + found_server = self.api.get_server(created_server_id) + self.assertEqual(created_server_id, found_server['id']) + + # Cleanup + self._delete_server(created_server_id) + + def test_create_server_with_networks_no_fixed_ip(self): + """Creates a server with networks.""" + + # Build the server data gradually, checking errors along the way + server = self._build_minimal_create_server_request() + + network_list = [{'id': 1}] + server['networks'] = network_list + + post = {'server': server} + created_server = self.api.post_server(post) + LOG.debug("created_server: %s" % created_server) + self.assertTrue(created_server['id']) + created_server_id = created_server['id'] + + # Check it's there + found_server = self.api.get_server(created_server_id) + self.assertEqual(created_server_id, found_server['id']) + + # Cleanup + self._delete_server(created_server_id) + def test_create_and_rebuild_server(self): """Rebuild a server.""" diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index 63317b05a..fd4408831 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -203,9 +203,6 @@ class FlatNetworkTestCase(test.TestCase): def test_validate_networks_empty_requested_networks(self): requested_networks = [] - self.mox.StubOutWithMock(db, 'network_get_requested_networks') - db.network_get_requested_networks(mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn(networks) self.mox.ReplayAll() self.network.validate_networks(None, requested_networks) @@ -319,11 +316,6 @@ class VlanNetworkTestCase(test.TestCase): def test_validate_networks_empty_requested_networks(self): requested_networks = [] - self.mox.StubOutWithMock(db, 'project_get_requested_networks') - db.project_get_requested_networks(mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn(networks) - self.mox.ReplayAll() - self.network.validate_networks(None, requested_networks) def test_validate_networks_invalid_fixed_ip(self): -- cgit From b4fba58f2785936f68a712d1cdd1d5c34f6a7c22 Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Thu, 14 Jul 2011 16:40:05 -0700 Subject: mistakenly commited this code into my branch, reverting it to original from trunk --- nova/rpc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/rpc.py b/nova/rpc.py index 055f8220e..e2771ca88 100644 --- a/nova/rpc.py +++ b/nova/rpc.py @@ -279,7 +279,7 @@ class FanoutAdapterConsumer(AdapterConsumer): # them when done, so they're not left around on restart. # Also, we're the only one that should be consuming. exclusive # implies auto_delete, so we'll just set that.. - self.exclusive = False + self.exclusive = True LOG.info(_('Created "%(exchange)s" fanout exchange ' 'with "%(key)s" routing key'), dict(exchange=self.exchange, key=self.routing_key)) -- cgit From 6cbd1d860d6a3fe96417391c21fb79b1750ecdcf Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Tue, 19 Jul 2011 15:35:54 -0700 Subject: Added a new extension instead of directly making changes to OS V1.1. API --- nova/api/openstack/contrib/createserverext.py | 234 ++++++++++++++++++++++++++ nova/api/openstack/create_instance_helper.py | 51 +----- nova/api/openstack/extensions.py | 9 +- nova/api/openstack/servers.py | 35 ++-- 4 files changed, 262 insertions(+), 67 deletions(-) create mode 100644 nova/api/openstack/contrib/createserverext.py diff --git a/nova/api/openstack/contrib/createserverext.py b/nova/api/openstack/contrib/createserverext.py new file mode 100644 index 000000000..e8fe9afad --- /dev/null +++ b/nova/api/openstack/contrib/createserverext.py @@ -0,0 +1,234 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# +# 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 +from webob import exc + +from nova import exception +import nova.image +from nova import network +from nova import quota +from nova import rpc + +from nova.api.openstack import create_instance_helper as helper +from nova.api.openstack import extensions +from nova.api.openstack import faults +from nova.compute import instance_types +from nova.api.openstack import servers +from nova.api.openstack import wsgi +from nova.auth import manager as auth_manager + + +class CreateInstanceHelperEx(helper.CreateInstanceHelper): + def __init__(self, controller): + super(CreateInstanceHelperEx, self).__init__(controller) + + def create_instance(self, req, body, create_method): + """Creates a new server for the given user as per + the network information if it is provided + """ + if not body: + raise faults.Fault(exc.HTTPUnprocessableEntity()) + + context = req.environ['nova.context'] + + password = self.controller._get_server_admin_password(body['server']) + + key_name = None + key_data = None + key_pairs = auth_manager.AuthManager.get_key_pairs(context) + if key_pairs: + key_pair = key_pairs[0] + key_name = key_pair['name'] + key_data = key_pair['public_key'] + + image_href = self.controller._image_ref_from_req_data(body) + try: + image_service, image_id = nova.image.get_image_service(image_href) + kernel_id, ramdisk_id = self._get_kernel_ramdisk_from_image( + req, image_id) + images = set([str(x['id']) for x in image_service.index(context)]) + assert str(image_id) in images + except Exception, e: + msg = _("Cannot find requested image %(image_href)s: %(e)s" % + locals()) + raise faults.Fault(exc.HTTPBadRequest(explanation=msg)) + + personality = body['server'].get('personality') + + injected_files = [] + if personality: + injected_files = self._get_injected_files(personality) + + requested_networks = body['server'].get('networks') + + if requested_networks is not None: + requested_networks = self._get_requested_networks( + requested_networks) + + flavor_id = self.controller._flavor_id_from_req_data(body) + + if not 'name' in body['server']: + msg = _("Server name is not defined") + raise exc.HTTPBadRequest(explanation=msg) + + zone_blob = body['server'].get('blob') + name = body['server']['name'] + self._validate_server_name(name) + name = name.strip() + + reservation_id = body['server'].get('reservation_id') + min_count = body['server'].get('min_count') + max_count = body['server'].get('max_count') + # min_count and max_count are optional. If they exist, they come + # in as strings. We want to default 'min_count' to 1, and default + # 'max_count' to be 'min_count'. + min_count = int(min_count) if min_count else 1 + max_count = int(max_count) if max_count else min_count + if min_count > max_count: + min_count = max_count + + try: + inst_type = \ + instance_types.get_instance_type_by_flavor_id(flavor_id) + extra_values = { + 'instance_type': inst_type, + 'image_ref': image_href, + 'password': password} + + return (extra_values, + create_method(context, + inst_type, + image_id, + kernel_id=kernel_id, + ramdisk_id=ramdisk_id, + display_name=name, + display_description=name, + key_name=key_name, + key_data=key_data, + metadata=body['server'].get('metadata', {}), + injected_files=injected_files, + admin_password=password, + zone_blob=zone_blob, + reservation_id=reservation_id, + min_count=min_count, + max_count=max_count, + requested_networks=requested_networks)) + except quota.QuotaError as error: + self._handle_quota_error(error) + except exception.ImageNotFound as error: + msg = _("Can not find requested image") + raise faults.Fault(exc.HTTPBadRequest(explanation=msg)) + except rpc.RemoteError as err: + msg = "%(err_type)s: %(err_msg)s" % \ + {'err_type': err.exc_type, 'err_msg': err.value} + raise faults.Fault(exc.HTTPBadRequest(explanation=msg)) + + # Let the caller deal with unhandled exceptions. + + def _get_requested_networks(self, requested_networks): + """ + Create a list of requested networks from the networks attribute + """ + networks = [] + for network in requested_networks: + try: + network_id = network['id'] + network_id = int(network_id) + #fixed IP address is optional + #if the fixed IP address is not provided then + #it will use one of the available IP address from the network + fixed_ip = network.get('fixed_ip', None) + if fixed_ip is not None: + self._validate_fixed_ip(fixed_ip) + # check if the network id is already present in the list, + # we don't want duplicate networks to be passed + # at the boot time + for id, ip in networks: + if id == network_id: + expl = _("Duplicate networks (%s) are not allowed")\ + % network_id + raise faults.Fault(exc.HTTPBadRequest( + explanation=expl)) + + networks.append((network_id, fixed_ip)) + except KeyError as key: + expl = _('Bad network format: missing %s') % key + raise faults.Fault(exc.HTTPBadRequest(explanation=expl)) + except ValueError: + expl = _("Bad networks format: network id should " + "be integer (%s)") % network_id + raise faults.Fault(exc.HTTPBadRequest(explanation=expl)) + except TypeError: + expl = _('Bad networks format') + raise exc.HTTPBadRequest(explanation=expl) + + return networks + + +class CreateServerExtController(servers.ControllerV11): + """This is the controller for the extended version + of the create server OS V1.1 + """ + def __init__(self): + super(CreateServerExtController, self).__init__() + self.helper = CreateInstanceHelperEx(self) + + +class Createserverext(extensions.ExtensionDescriptor): + """The servers create ext + + Exposes addFixedIp and removeFixedIp actions on servers. + + """ + def get_name(self): + return "Createserverext" + + def get_alias(self): + return "os-servers-create-ext" + + def get_description(self): + return "Extended support to the Create Server v1.1 API" + + def get_namespace(self): + return "http://docs.openstack.org/ext/serverscreateext/api/v1.1" + + def get_updated(self): + return "2011-07-19T00:00:00+00:00" + + def get_resources(self): + resources = [] + + headers_serializer = servers.HeadersSerializer() + metadata = servers._get_metadata() + body_serializers = { + 'application/xml': wsgi.XMLDictSerializer(metadata=metadata, + xmlns=wsgi.XMLNS_V11), + } + + body_deserializers = { + 'application/xml': helper.ServerXMLDeserializer(), + } + + serializer = wsgi.ResponseSerializer(body_serializers, + headers_serializer) + deserializer = wsgi.RequestDeserializer(body_deserializers) + + res = extensions.ResourceExtension('os-servers-create-ext', + controller=CreateServerExtController(), + deserializer=deserializer, + serializer=serializer) + resources.append(res) + + return resources diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 32b1b2f7c..8579c45df 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -25,7 +25,6 @@ from nova import flags from nova import log as logging import nova.image from nova import quota -from nova import rpc from nova import utils from nova.compute import instance_types @@ -102,12 +101,6 @@ class CreateInstanceHelper(object): if personality: injected_files = self._get_injected_files(personality) - requested_networks = body['server'].get('networks') - - if requested_networks is not None: - requested_networks = self._get_requested_networks( - requested_networks) - flavor_id = self.controller._flavor_id_from_req_data(body) if not 'name' in body['server']: @@ -154,17 +147,12 @@ class CreateInstanceHelper(object): zone_blob=zone_blob, reservation_id=reservation_id, min_count=min_count, - max_count=max_count, - requested_networks=requested_networks)) + max_count=max_count)) except quota.QuotaError as error: self._handle_quota_error(error) except exception.ImageNotFound as error: msg = _("Can not find requested image") raise faults.Fault(exc.HTTPBadRequest(explanation=msg)) - except rpc.RemoteError as err: - msg = "%(err_type)s: %(err_msg)s" % \ - {'err_type': err.exc_type, 'err_msg': err.value} - raise faults.Fault(exc.HTTPBadRequest(explanation=msg)) # Let the caller deal with unhandled exceptions. @@ -296,42 +284,6 @@ class CreateInstanceHelper(object): raise exc.HTTPBadRequest(explanation=msg) return password - def _get_requested_networks(self, requested_networks): - """ - Create a list of requested networks from the networks attribute - """ - networks = [] - for network in requested_networks: - try: - network_id = network['id'] - network_id = int(network_id) - #fixed IP address is optional - #if the fixed IP address is not provided then - #it will use one of the available IP address from the network - fixed_ip = network.get('fixed_ip', None) - if fixed_ip is not None: - self._validate_fixed_ip(fixed_ip) - # check if the network id is already present in the list, - # we don't want duplicate networks to be passed - # at the boot time - for id, ip in networks: - if id == network_id: - expl = _("Duplicate networks (%s) are not allowed")\ - % network_id - raise faults.Fault(exc.HTTPBadRequest( - explanation=expl)) - - networks.append((network_id, fixed_ip)) - except KeyError as key: - expl = _('Bad network format: missing %s') % key - raise faults.Fault(exc.HTTPBadRequest(explanation=expl)) - except ValueError: - expl = _("Bad networks format: network id should " - "be integer (%s)") % network_id - raise faults.Fault(exc.HTTPBadRequest(explanation=expl)) - - return networks - class ServerXMLDeserializer(wsgi.XMLDeserializer): """ @@ -411,7 +363,6 @@ class ServerXMLDeserializer(wsgi.XMLDeserializer): def _find_first_child_named(self, parent, name): """Search a nodes children for the first child with a given name""" for node in parent.childNodes: - LOG.debug(node.nodeName) if node.nodeName == name: return node return None diff --git a/nova/api/openstack/extensions.py b/nova/api/openstack/extensions.py index da06ecd15..dec66d8a4 100644 --- a/nova/api/openstack/extensions.py +++ b/nova/api/openstack/extensions.py @@ -263,7 +263,9 @@ class ExtensionMiddleware(base_wsgi.Middleware): LOG.debug(_('Extended resource: %s'), resource.collection) mapper.resource(resource.collection, resource.collection, - controller=wsgi.Resource(resource.controller), + controller=wsgi.Resource(resource.controller, + resource.deserializer, + resource.serializer), collection=resource.collection_actions, member=resource.member_actions, parent_resource=resource.parent) @@ -456,9 +458,12 @@ class ResourceExtension(object): """Add top level resources to the OpenStack API in nova.""" def __init__(self, collection, controller, parent=None, - collection_actions={}, member_actions={}): + collection_actions={}, member_actions={}, + deserializer=None, serializer=None): self.collection = collection self.controller = controller self.parent = parent self.collection_actions = collection_actions self.member_actions = member_actions + self.deserializer = deserializer + self.serializer = serializer diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 93f8e832c..6a28f7bf1 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -618,21 +618,7 @@ def create_resource(version='1.0'): '1.1': ControllerV11, }[version]() - metadata = { - "attributes": { - "server": ["id", "imageId", "name", "flavorId", "hostId", - "status", "progress", "adminPass", "flavorRef", - "imageRef"], - "link": ["rel", "type", "href"], - }, - "dict_collections": { - "metadata": {"item_name": "meta", "item_key": "key"}, - }, - "list_collections": { - "public": {"item_name": "ip", "item_key": "addr"}, - "private": {"item_name": "ip", "item_key": "addr"}, - }, - } + metadata = _get_metadata() xmlns = { '1.0': wsgi.XMLNS_V10, @@ -654,3 +640,22 @@ def create_resource(version='1.0'): deserializer = wsgi.RequestDeserializer(body_deserializers) return wsgi.Resource(controller, deserializer, serializer) + + +def _get_metadata(): + metadata = { + "attributes": { + "server": ["id", "imageId", "name", "flavorId", "hostId", + "status", "progress", "adminPass", "flavorRef", + "imageRef"], + "link": ["rel", "type", "href"], + }, + "dict_collections": { + "metadata": {"item_name": "meta", "item_key": "key"}, + }, + "list_collections": { + "public": {"item_name": "ip", "item_key": "addr"}, + "private": {"item_name": "ip", "item_key": "addr"}, + }, + } + return metadata -- cgit From 6b47f87c9e22fa09cedc3e48b7c8dcf52b5d016a Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Tue, 19 Jul 2011 17:35:44 -0700 Subject: Fixed broken unit testcases after adding extension and minor code refactoring --- nova/api/openstack/contrib/createserverext.py | 11 +- nova/api/openstack/create_instance_helper.py | 9 - .../api/openstack/contrib/test_createserverext.py | 637 +++++++++++++++++++++ nova/tests/api/openstack/test_servers.py | 151 ----- nova/tests/integrated/test_servers.py | 216 ------- 5 files changed, 647 insertions(+), 377 deletions(-) create mode 100644 nova/tests/api/openstack/contrib/test_createserverext.py diff --git a/nova/api/openstack/contrib/createserverext.py b/nova/api/openstack/contrib/createserverext.py index e8fe9afad..78dba425a 100644 --- a/nova/api/openstack/contrib/createserverext.py +++ b/nova/api/openstack/contrib/createserverext.py @@ -137,6 +137,15 @@ class CreateInstanceHelperEx(helper.CreateInstanceHelper): # Let the caller deal with unhandled exceptions. + def _validate_fixed_ip(self, value): + if not isinstance(value, basestring): + msg = _("Fixed IP is not a string or unicode") + raise exc.HTTPBadRequest(explanation=msg) + + if value.strip() == '': + msg = _("Fixed IP is an empty string") + raise exc.HTTPBadRequest(explanation=msg) + def _get_requested_networks(self, requested_networks): """ Create a list of requested networks from the networks attribute @@ -202,7 +211,7 @@ class Createserverext(extensions.ExtensionDescriptor): return "Extended support to the Create Server v1.1 API" def get_namespace(self): - return "http://docs.openstack.org/ext/serverscreateext/api/v1.1" + return "http://docs.openstack.org/ext/createserverext/api/v1.1" def get_updated(self): return "2011-07-19T00:00:00+00:00" diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 8579c45df..fba0cb8ba 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -193,15 +193,6 @@ class CreateInstanceHelper(object): msg = _("Server name is an empty string") raise exc.HTTPBadRequest(explanation=msg) - def _validate_fixed_ip(self, value): - if not isinstance(value, basestring): - msg = _("Fixed IP is not a string or unicode") - raise exc.HTTPBadRequest(explanation=msg) - - if value.strip() == '': - msg = _("Fixed IP is an empty string") - raise exc.HTTPBadRequest(explanation=msg) - def _get_kernel_ramdisk_from_image(self, req, image_id): """Fetch an image from the ImageService, then if present, return the associated kernel and ramdisk image IDs. diff --git a/nova/tests/api/openstack/contrib/test_createserverext.py b/nova/tests/api/openstack/contrib/test_createserverext.py new file mode 100644 index 000000000..f2aa136e2 --- /dev/null +++ b/nova/tests/api/openstack/contrib/test_createserverext.py @@ -0,0 +1,637 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010-2011 OpenStack LLC. +# 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. + +import base64 +import json +import unittest +from xml.dom import minidom + +import stubout +import webob + +from nova import exception +from nova import flags +from nova import test +from nova import utils +import nova.api.openstack +from nova.api.openstack import servers +from nova.api.openstack.contrib import createserverext +import nova.compute.api + +import nova.scheduler.api +import nova.image.fake +import nova.rpc +from nova.tests.api.openstack import fakes + +FLAGS = flags.FLAGS +FLAGS.verbose = True + +FAKE_UUID = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' + + +def fake_gen_uuid(): + return FAKE_UUID + + +def return_server_by_id(context, id): + return stub_instance(id) + + +def return_server_by_uuid(context, uuid): + id = 1 + return stub_instance(id, uuid=uuid) + + +def return_virtual_interface_by_instance(interfaces): + def _return_virtual_interface_by_instance(context, instance_id): + return interfaces + return _return_virtual_interface_by_instance + + +def return_virtual_interface_instance_nonexistant(interfaces): + def _return_virtual_interface_by_instance(context, instance_id): + raise exception.InstanceNotFound(instance_id=instance_id) + return _return_virtual_interface_by_instance + + +def return_server_with_addresses(private, public): + def _return_server(context, id): + return stub_instance(id, private_address=private, + public_addresses=public) + return _return_server + + +def return_server_with_interfaces(interfaces): + def _return_server(context, id): + return stub_instance(id, interfaces=interfaces) + return _return_server + + +def return_server_with_power_state(power_state): + def _return_server(context, id): + return stub_instance(id, power_state=power_state) + return _return_server + + +def return_servers(context, user_id=1): + return [stub_instance(i, user_id) for i in xrange(5)] + + +def return_servers_by_reservation(context, reservation_id=""): + return [stub_instance(i, reservation_id) for i in xrange(5)] + + +def return_servers_by_reservation_empty(context, reservation_id=""): + return [] + + +def return_servers_from_child_zones_empty(*args, **kwargs): + return [] + + +def return_servers_from_child_zones(*args, **kwargs): + class Server(object): + pass + + zones = [] + for zone in xrange(3): + servers = [] + for server_id in xrange(5): + server = Server() + server._info = stub_instance(server_id, reservation_id="child") + servers.append(server) + + zones.append(("Zone%d" % zone, servers)) + return zones + + +def return_security_group(context, instance_id, security_group_id): + pass + + +def instance_update(context, instance_id, kwargs): + return stub_instance(instance_id) + + +def instance_addresses(context, instance_id): + return None + + +def stub_instance(id, user_id=1, private_address=None, public_addresses=None, + host=None, power_state=0, reservation_id="", + uuid=FAKE_UUID, interfaces=None): + metadata = [] + metadata.append(InstanceMetadata(key='seq', value=id)) + + if interfaces is None: + interfaces = [] + + inst_type = instance_types.get_instance_type_by_flavor_id(1) + + if public_addresses is None: + public_addresses = list() + + if host is not None: + host = str(host) + + # ReservationID isn't sent back, hack it in there. + server_name = "server%s" % id + if reservation_id != "": + server_name = "reservation_%s" % (reservation_id, ) + + instance = { + "id": int(id), + "admin_pass": "", + "user_id": user_id, + "project_id": "", + "image_ref": "10", + "kernel_id": "", + "ramdisk_id": "", + "launch_index": 0, + "key_name": "", + "key_data": "", + "state": power_state, + "state_description": "", + "memory_mb": 0, + "vcpus": 0, + "local_gb": 0, + "hostname": "", + "host": host, + "instance_type": dict(inst_type), + "user_data": "", + "reservation_id": reservation_id, + "mac_address": "", + "scheduled_at": utils.utcnow(), + "launched_at": utils.utcnow(), + "terminated_at": utils.utcnow(), + "availability_zone": "", + "display_name": server_name, + "display_description": "", + "locked": False, + "metadata": metadata, + "uuid": uuid, + "virtual_interfaces": interfaces} + + instance["fixed_ips"] = { + "address": private_address, + "floating_ips": [{"address":ip} for ip in public_addresses]} + + return instance + + +def fake_compute_api(cls, req, id): + return True + + +def find_host(self, context, instance_id): + return "nova" + + +class MockSetAdminPassword(object): + def __init__(self): + self.instance_id = None + self.password = None + + def __call__(self, context, instance_id, password): + self.instance_id = instance_id + self.password = password + + +class CreateserverextTest(test.TestCase): + + def setUp(self): + super(CreateserverextTest, self).setUp() + self.controller = createserverext.CreateServerExtController() + self.stubs = stubout.StubOutForTesting() + fakes.FakeAuthManager.auth_data = {} + fakes.FakeAuthDatabase.data = {} + fakes.stub_out_auth(self.stubs) + fakes.stub_out_image_service(self.stubs) + fakes.stub_out_key_pair_funcs(self.stubs) + self.allow_admin = FLAGS.allow_admin_api + + def tearDown(self): + self.stubs.UnsetAll() + FLAGS.allow_admin_api = self.allow_admin + super(CreateserverextTest, self).tearDown() + + def _setup_mock_compute_api(self): + + class MockComputeAPI(nova.compute.API): + + def __init__(self): + self.injected_files = None + self.networks = None + + def create(self, *args, **kwargs): + if 'injected_files' in kwargs: + self.injected_files = kwargs['injected_files'] + else: + self.injected_files = None + + if 'requested_networks' in kwargs: + self.networks = kwargs['requested_networks'] + else: + self.networks = None + return [{'id': '1234', 'display_name': 'fakeinstance', + 'uuid': FAKE_UUID}] + + def set_admin_password(self, *args, **kwargs): + pass + + def make_stub_method(canned_return): + def stub_method(*args, **kwargs): + return canned_return + return stub_method + + compute_api = MockComputeAPI() + self.stubs.Set(nova.compute, 'API', make_stub_method(compute_api)) + self.stubs.Set( + createserverext.CreateInstanceHelperEx, + '_get_kernel_ramdisk_from_image', make_stub_method((1, 1))) + return compute_api + + def _create_personality_request_dict(self, personality_files): + server = {} + server['name'] = 'new-server-test' + server['imageRef'] = 1 + server['flavorRef'] = 1 + if personality_files is not None: + personalities = [] + for path, contents in personality_files: + personalities.append({'path': path, 'contents': contents}) + server['personality'] = personalities + return {'server': server} + + def _create_networks_request_dict(self, networks): + server = {} + server['name'] = 'new-server-test' + server['imageRef'] = 1 + server['flavorRef'] = 1 + if networks is not None: + network_list = [] + for id, fixed_ip in networks: + network_list.append({'id': id, 'fixed_ip': fixed_ip}) + server['networks'] = network_list + return {'server': server} + + def _get_create_request_json(self, body_dict): + req = webob.Request.blank('/v1.1/os-servers-create-ext') + req.headers['Content-Type'] = 'application/json' + req.method = 'POST' + req.body = json.dumps(body_dict) + return req + + def _run_create_instance_with_mock_compute_api(self, request): + compute_api = self._setup_mock_compute_api() + response = request.get_response(fakes.wsgi_app()) + return compute_api, response + + def _format_xml_request_body(self, body_dict): + server = body_dict['server'] + body_parts = [] + body_parts.extend([ + '', + '' % ( + server['name'], server['imageRef'], server['flavorRef'])]) + if 'metadata' in server: + metadata = server['metadata'] + body_parts.append('') + for item in metadata.iteritems(): + body_parts.append('%s' % item) + body_parts.append('') + if 'personality' in server: + personalities = server['personality'] + body_parts.append('') + for file in personalities: + item = (file['path'], file['contents']) + body_parts.append('%s' % item) + body_parts.append('') + if 'networks' in server: + networks = server['networks'] + body_parts.append('') + for network in networks: + item = (network['id'], network['fixed_ip']) + body_parts.append('' + % item) + body_parts.append('') + body_parts.append('') + return ''.join(body_parts) + + def _get_create_request_xml(self, body_dict): + req = webob.Request.blank('/v1.1/os-servers-create-ext') + req.content_type = 'application/xml' + req.accept = 'application/xml' + req.method = 'POST' + req.body = self._format_xml_request_body(body_dict) + return req + + def _create_instance_with_personality_json(self, personality): + body_dict = self._create_personality_request_dict(personality) + request = self._get_create_request_json(body_dict) + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + return request, response, compute_api.injected_files + + def _create_instance_with_personality_xml(self, personality): + body_dict = self._create_personality_request_dict(personality) + request = self._get_create_request_xml(body_dict) + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + return request, response, compute_api.injected_files + + def _create_instance_with_networks_json(self, networks): + body_dict = self._create_networks_request_dict(networks) + request = self._get_create_request_json(body_dict) + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + return request, response, compute_api.networks + + def _create_instance_with_networks_xml(self, networks): + body_dict = self._create_networks_request_dict(networks) + request = self._get_create_request_xml(body_dict) + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + return request, response, compute_api.networks + + def test_create_instance_with_no_personality(self): + request, response, injected_files = \ + self._create_instance_with_personality_json(personality=None) + self.assertEquals(response.status_int, 200) + self.assertEquals(injected_files, []) + + def test_create_instance_with_no_personality_xml(self): + request, response, injected_files = \ + self._create_instance_with_personality_xml(personality=None) + self.assertEquals(response.status_int, 200) + self.assertEquals(injected_files, []) + + def test_create_instance_with_personality(self): + path = '/my/file/path' + contents = '#!/bin/bash\necho "Hello, World!"\n' + b64contents = base64.b64encode(contents) + personality = [(path, b64contents)] + request, response, injected_files = \ + self._create_instance_with_personality_json(personality) + self.assertEquals(response.status_int, 200) + self.assertEquals(injected_files, [(path, contents)]) + + def test_create_instance_with_personality_xml(self): + path = '/my/file/path' + contents = '#!/bin/bash\necho "Hello, World!"\n' + b64contents = base64.b64encode(contents) + personality = [(path, b64contents)] + request, response, injected_files = \ + self._create_instance_with_personality_xml(personality) + self.assertEquals(response.status_int, 200) + self.assertEquals(injected_files, [(path, contents)]) + + def test_create_instance_with_personality_no_path(self): + personality = [('/remove/this/path', + base64.b64encode('my\n\file\ncontents'))] + body_dict = self._create_personality_request_dict(personality) + del body_dict['server']['personality'][0]['path'] + request = self._get_create_request_json(body_dict) + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + self.assertEquals(response.status_int, 400) + self.assertEquals(compute_api.injected_files, None) + + def _test_create_instance_with_personality_no_path_xml(self): + personality = [('/remove/this/path', + base64.b64encode('my\n\file\ncontents'))] + body_dict = self._create_personality_request_dict(personality) + request = self._get_create_request_xml(body_dict) + request.body = request.body.replace(' path="/remove/this/path"', '') + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + self.assertEquals(response.status_int, 400) + self.assertEquals(compute_api.injected_files, None) + + def test_create_instance_with_personality_no_contents(self): + personality = [('/test/path', + base64.b64encode('remove\nthese\ncontents'))] + body_dict = self._create_personality_request_dict(personality) + del body_dict['server']['personality'][0]['contents'] + request = self._get_create_request_json(body_dict) + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + self.assertEquals(response.status_int, 400) + self.assertEquals(compute_api.injected_files, None) + + def test_create_instance_with_personality_not_a_list(self): + personality = [('/test/path', base64.b64encode('test\ncontents\n'))] + body_dict = self._create_personality_request_dict(personality) + body_dict['server']['personality'] = \ + body_dict['server']['personality'][0] + request = self._get_create_request_json(body_dict) + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + self.assertEquals(response.status_int, 400) + self.assertEquals(compute_api.injected_files, None) + + def test_create_instance_with_personality_with_non_b64_content(self): + path = '/my/file/path' + contents = '#!/bin/bash\necho "Oh no!"\n' + personality = [(path, contents)] + request, response, injected_files = \ + self._create_instance_with_personality_json(personality) + self.assertEquals(response.status_int, 400) + self.assertEquals(injected_files, None) + + def test_create_instance_with_null_personality(self): + personality = None + body_dict = self._create_personality_request_dict(personality) + body_dict['server']['personality'] = None + request = self._get_create_request_json(body_dict) + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + self.assertEquals(response.status_int, 200) + + def test_create_instance_with_three_personalities(self): + files = [ + ('/etc/sudoers', 'ALL ALL=NOPASSWD: ALL\n'), + ('/etc/motd', 'Enjoy your root access!\n'), + ('/etc/dovecot.conf', 'dovecot\nconfig\nstuff\n'), + ] + personality = [] + for path, content in files: + personality.append((path, base64.b64encode(content))) + request, response, injected_files = \ + self._create_instance_with_personality_json(personality) + self.assertEquals(response.status_int, 200) + self.assertEquals(injected_files, files) + + def test_create_instance_personality_empty_content(self): + path = '/my/file/path' + contents = '' + personality = [(path, contents)] + request, response, injected_files = \ + self._create_instance_with_personality_json(personality) + self.assertEquals(response.status_int, 200) + self.assertEquals(injected_files, [(path, contents)]) + + def test_create_instance_admin_pass_json(self): + request, response, dummy = \ + self._create_instance_with_personality_json(None) + self.assertEquals(response.status_int, 200) + response = json.loads(response.body) + self.assertTrue('adminPass' in response['server']) + self.assertEqual(16, len(response['server']['adminPass'])) + + def test_create_instance_admin_pass_xml(self): + request, response, dummy = \ + self._create_instance_with_personality_xml(None) + self.assertEquals(response.status_int, 200) + dom = minidom.parseString(response.body) + server = dom.childNodes[0] + self.assertEquals(server.nodeName, 'server') + self.assertEqual(16, len(server.getAttribute('adminPass'))) + + def test_create_instance_with_no_networks(self): + request, response, networks = \ + self._create_instance_with_networks_json(networks=None) + self.assertEquals(response.status_int, 200) + self.assertEquals(networks, None) + + def test_create_instance_with_no_networks_xml(self): + request, response, networks = \ + self._create_instance_with_networks_xml(networks=None) + self.assertEquals(response.status_int, 200) + self.assertEquals(networks, None) + + def test_create_instance_with_one_network(self): + id = 1 + fixed_ip = '10.0.1.12' + networks = [(id, fixed_ip)] + request, response, networks = \ + self._create_instance_with_networks_json(networks) + self.assertEquals(response.status_int, 200) + self.assertEquals(networks, [(id, fixed_ip)]) + + def test_create_instance_with_one_network_xml(self): + id = 1 + fixed_ip = '10.0.1.12' + networks = [(id, fixed_ip)] + request, response, networks = \ + self._create_instance_with_networks_xml(networks) + self.assertEquals(response.status_int, 200) + self.assertEquals(networks, [(id, fixed_ip)]) + + def test_create_instance_with_two_networks(self): + networks = [(1, '10.0.1.12'), (2, '10.0.2.12')] + request, response, networks = \ + self._create_instance_with_networks_json(networks) + self.assertEquals(response.status_int, 200) + self.assertEquals(networks, [(1, '10.0.1.12'), (2, '10.0.2.12')]) + + def test_create_instance_with_two_networks_xml(self): + networks = [(1, '10.0.1.12'), (2, '10.0.2.12')] + request, response, networks = \ + self._create_instance_with_networks_xml(networks) + self.assertEquals(response.status_int, 200) + self.assertEquals(networks, [(1, '10.0.1.12'), (2, '10.0.2.12')]) + + def test_create_instance_with_duplicate_networks(self): + networks = [(1, '10.0.1.12'), (1, '10.0.2.12')] + request, response, networks = \ + self._create_instance_with_networks_json(networks) + self.assertEquals(response.status_int, 400) + self.assertEquals(networks, None) + + def test_create_instance_with_duplicate_networks_xml(self): + networks = [(1, '10.0.1.12'), (1, '10.0.2.12')] + request, response, networks = \ + self._create_instance_with_networks_xml(networks) + self.assertEquals(response.status_int, 400) + self.assertEquals(networks, None) + + def test_create_instance_with_network_no_id(self): + networks = [(1, '10.0.1.12')] + body_dict = self._create_networks_request_dict(networks) + del body_dict['server']['networks'][0]['id'] + request = self._get_create_request_json(body_dict) + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + self.assertEquals(response.status_int, 400) + self.assertEquals(compute_api.networks, None) + + def test_create_instance_with_network_no_id_xml(self): + networks = [(1, '10.0.1.12')] + body_dict = self._create_networks_request_dict(networks) + request = self._get_create_request_xml(body_dict) + request.body = request.body.replace(' id="1"', '') + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + self.assertEquals(response.status_int, 400) + self.assertEquals(compute_api.networks, None) + + def test_create_instance_with_network_invalid_id(self): + networks = [('asd123', '10.0.1.12')] + request, response, networks = \ + self._create_instance_with_networks_json(networks) + self.assertEquals(response.status_int, 400) + self.assertEquals(networks, None) + + def test_create_instance_with_network_invalid_id_xml(self): + networks = [('asd123', '10.0.1.12')] + request, response, networks = \ + self._create_instance_with_networks_xml(networks) + self.assertEquals(response.status_int, 400) + self.assertEquals(networks, None) + + def test_create_instance_with_network_empty_fixed_ip(self): + networks = [('1', '')] + request, response, networks = \ + self._create_instance_with_networks_json(networks) + self.assertEquals(response.status_int, 400) + self.assertEquals(networks, None) + + def test_create_instance_with_network_non_string_fixed_ip(self): + networks = [('1', 12345)] + request, response, networks = \ + self._create_instance_with_networks_json(networks) + self.assertEquals(response.status_int, 400) + self.assertEquals(networks, None) + + def test_create_instance_with_network_empty_fixed_ip_xml(self): + networks = [('1', '')] + request, response, networks = \ + self._create_instance_with_networks_xml(networks) + self.assertEquals(response.status_int, 400) + self.assertEquals(networks, None) + + def test_create_instance_with_network_no_fixed_ip(self): + networks = [(1, '10.0.1.12')] + body_dict = self._create_networks_request_dict(networks) + del body_dict['server']['networks'][0]['fixed_ip'] + request = self._get_create_request_json(body_dict) + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + self.assertEquals(response.status_int, 200) + self.assertEquals(compute_api.networks, [(1, None)]) + + def test_create_instance_with_network_no_fixed_ip_xml(self): + networks = [(1, '10.0.1.12')] + body_dict = self._create_networks_request_dict(networks) + request = self._get_create_request_xml(body_dict) + request.body = request.body.replace(' fixed_ip="10.0.1.12"', '') + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + self.assertEquals(response.status_int, 200) + self.assertEquals(compute_api.networks, [(1, None)]) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index b2f1a8ef0..ec7dedafc 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -2324,10 +2324,6 @@ class TestServerInstanceCreation(test.TestCase): else: self.injected_files = None - if 'requested_networks' in kwargs: - self.networks = kwargs['requested_networks'] - else: - self.networks = None return [{'id': '1234', 'display_name': 'fakeinstance', 'uuid': FAKE_UUID}] @@ -2436,20 +2432,6 @@ class TestServerInstanceCreation(test.TestCase): self._run_create_instance_with_mock_compute_api(request) return request, response, compute_api.injected_files - def _create_instance_with_networks_json(self, networks): - body_dict = self._create_networks_request_dict(networks) - request = self._get_create_request_json(body_dict) - compute_api, response = \ - self._run_create_instance_with_mock_compute_api(request) - return request, response, compute_api.networks - - def _create_instance_with_networks_xml(self, networks): - body_dict = self._create_networks_request_dict(networks) - request = self._get_create_request_xml(body_dict) - compute_api, response = \ - self._run_create_instance_with_mock_compute_api(request) - return request, response, compute_api.networks - def test_create_instance_with_no_personality(self): request, response, injected_files = \ self._create_instance_with_personality_json(personality=None) @@ -2584,139 +2566,6 @@ class TestServerInstanceCreation(test.TestCase): self.assertEquals(server.nodeName, 'server') self.assertEqual(16, len(server.getAttribute('adminPass'))) - def test_create_instance_with_no_networks(self): - request, response, networks = \ - self._create_instance_with_networks_json(networks=None) - self.assertEquals(response.status_int, 200) - self.assertEquals(networks, None) - - def test_create_instance_with_no_networks_xml(self): - request, response, networks = \ - self._create_instance_with_networks_xml(networks=None) - self.assertEquals(response.status_int, 200) - self.assertEquals(networks, None) - - def test_create_instance_with_one_network(self): - id = 1 - fixed_ip = '10.0.1.12' - networks = [(id, fixed_ip)] - request, response, networks = \ - self._create_instance_with_networks_json(networks) - self.assertEquals(response.status_int, 200) - self.assertEquals(networks, [(id, fixed_ip)]) - - def test_create_instance_with_one_network_xml(self): - id = 1 - fixed_ip = '10.0.1.12' - networks = [(id, fixed_ip)] - request, response, networks = \ - self._create_instance_with_networks_xml(networks) - self.assertEquals(response.status_int, 200) - self.assertEquals(networks, [(id, fixed_ip)]) - - def test_create_instance_with_two_networks(self): - networks = [(1, '10.0.1.12'), (2, '10.0.2.12')] - request, response, networks = \ - self._create_instance_with_networks_json(networks) - self.assertEquals(response.status_int, 200) - self.assertEquals(networks, [(1, '10.0.1.12'), (2, '10.0.2.12')]) - - def test_create_instance_with_two_networks_xml(self): - networks = [(1, '10.0.1.12'), (2, '10.0.2.12')] - request, response, networks = \ - self._create_instance_with_networks_xml(networks) - self.assertEquals(response.status_int, 200) - self.assertEquals(networks, [(1, '10.0.1.12'), (2, '10.0.2.12')]) - - def test_create_instance_with_duplicate_networks(self): - networks = [(1, '10.0.1.12'), (1, '10.0.2.12')] - request, response, networks = \ - self._create_instance_with_networks_json(networks) - self.assertEquals(response.status_int, 400) - self.assertEquals(networks, None) - - def test_create_instance_with_duplicate_networks_xml(self): - networks = [(1, '10.0.1.12'), (1, '10.0.2.12')] - request, response, networks = \ - self._create_instance_with_networks_xml(networks) - self.assertEquals(response.status_int, 400) - self.assertEquals(networks, None) - - def test_create_instance_with_network_no_id(self): - networks = [(1, '10.0.1.12')] - body_dict = self._create_networks_request_dict(networks) - del body_dict['server']['networks'][0]['id'] - request = self._get_create_request_json(body_dict) - compute_api, response = \ - self._run_create_instance_with_mock_compute_api(request) - self.assertEquals(response.status_int, 400) - self.assertEquals(compute_api.networks, None) - - def test_create_instance_with_network_no_id_xml(self): - networks = [(1, '10.0.1.12')] - body_dict = self._create_networks_request_dict(networks) - request = self._get_create_request_xml(body_dict) - request.body = request.body.replace(' id="1"', '') - compute_api, response = \ - self._run_create_instance_with_mock_compute_api(request) - self.assertEquals(response.status_int, 400) - self.assertEquals(compute_api.networks, None) - - def test_create_instance_with_network_invalid_id(self): - networks = [('asd123', '10.0.1.12')] - request, response, networks = \ - self._create_instance_with_networks_json(networks) - self.assertEquals(response.status_int, 400) - self.assertEquals(networks, None) - - def test_create_instance_with_network_invalid_id_xml(self): - networks = [('asd123', '10.0.1.12')] - request, response, networks = \ - self._create_instance_with_networks_xml(networks) - self.assertEquals(response.status_int, 400) - self.assertEquals(networks, None) - - def test_create_instance_with_network_empty_fixed_ip(self): - networks = [('1', '')] - request, response, networks = \ - self._create_instance_with_networks_json(networks) - self.assertEquals(response.status_int, 400) - self.assertEquals(networks, None) - - def test_create_instance_with_network_non_string_fixed_ip(self): - networks = [('1', 12345)] - request, response, networks = \ - self._create_instance_with_networks_json(networks) - self.assertEquals(response.status_int, 400) - self.assertEquals(networks, None) - - def test_create_instance_with_network_empty_fixed_ip_xml(self): - networks = [('1', '')] - request, response, networks = \ - self._create_instance_with_networks_xml(networks) - self.assertEquals(response.status_int, 400) - self.assertEquals(networks, None) - - def test_create_instance_with_network_no_fixed_ip(self): - networks = [(1, '10.0.1.12')] - body_dict = self._create_networks_request_dict(networks) - del body_dict['server']['networks'][0]['fixed_ip'] - request = self._get_create_request_json(body_dict) - compute_api, response = \ - self._run_create_instance_with_mock_compute_api(request) - self.assertEquals(response.status_int, 200) - self.assertEquals(compute_api.networks, [(1, None)]) - - def test_create_instance_with_network_no_fixed_ip_xml(self): - networks = [(1, '10.0.1.12')] - body_dict = self._create_networks_request_dict(networks) - request = self._get_create_request_xml(body_dict) - request.body = request.body.replace(' fixed_ip="10.0.1.12"', '') - compute_api, response = \ - self._run_create_instance_with_mock_compute_api(request) - self.assertEquals(response.status_int, 200) - self.assertEquals(compute_api.networks, [(1, None)]) - class TestGetKernelRamdiskFromImage(test.TestCase): """ diff --git a/nova/tests/integrated/test_servers.py b/nova/tests/integrated/test_servers.py index 33ffa7cf1..4e8e85c7b 100644 --- a/nova/tests/integrated/test_servers.py +++ b/nova/tests/integrated/test_servers.py @@ -179,222 +179,6 @@ class ServersTest(integrated_helpers._IntegratedTestBase): # Cleanup self._delete_server(created_server_id) - def test_create_server_with_one_networks(self): - """Creates a server with networks.""" - - # Build the server data gradually, checking errors along the way - server = self._build_minimal_create_server_request() - - network_list = [{'id': 1, 'fixed_ip': '10.0.0.3'}] - server['networks'] = network_list - - post = {'server': server} - created_server = self.api.post_server(post) - LOG.debug("created_server: %s" % created_server) - self.assertTrue(created_server['id']) - created_server_id = created_server['id'] - - # Check it's there - found_server = self.api.get_server(created_server_id) - self.assertEqual(created_server_id, found_server['id']) - - # Cleanup - self._delete_server(created_server_id) - - def test_create_server_with_two_networks(self): - """Creates a server with networks.""" - - # Build the server data gradually, checking errors along the way - server = self._build_minimal_create_server_request() - - network_list = [{'id': 1, 'fixed_ip': '10.0.0.3'}, - {'id': 2, 'fixed_ip': '10.0.0.12'}] - server['networks'] = network_list - - post = {'server': server} - created_server = self.api.post_server(post) - LOG.debug("created_server: %s" % created_server) - self.assertTrue(created_server['id']) - created_server_id = created_server['id'] - - # Check it's there - found_server = self.api.get_server(created_server_id) - self.assertEqual(created_server_id, found_server['id']) - - # Cleanup - self._delete_server(created_server_id) - - def test_create_server_with_empty_networks(self): - """Creates a server with networks.""" - - # Build the server data gradually, checking errors along the way - server = self._build_minimal_create_server_request() - - network_list = [] - server['networks'] = network_list - - post = {'server': server} - created_server = self.api.post_server(post) - LOG.debug("created_server: %s" % created_server) - self.assertTrue(created_server['id']) - created_server_id = created_server['id'] - - # Check it's there - found_server = self.api.get_server(created_server_id) - self.assertEqual(created_server_id, found_server['id']) - - # Cleanup - self._delete_server(created_server_id) - - def test_create_server_with_networks_invalid_id(self): - """Creates a server with networks.""" - - # Build the server data gradually, checking errors along the way - server = self._build_minimal_create_server_request() - - network_list = [{'id': "invalid", 'fixed_ip': '10.0.0.3'}] - server['networks'] = network_list - - post = {'server': server} - self.assertRaises(client.OpenStackApiException, - self.api.post_server, post) - - def test_create_server_with_networks_no_id(self): - """Creates a server with networks.""" - - # Build the server data gradually, checking errors along the way - server = self._build_minimal_create_server_request() - - network_list = [{'fixed_ip': '10.0.0.3'}] - server['networks'] = network_list - - post = {'server': server} - self.assertRaises(client.OpenStackApiException, - self.api.post_server, post) - - def test_create_server_with_networks_non_exists_id(self): - """Creates a server with networks.""" - - # Build the server data gradually, checking errors along the way - server = self._build_minimal_create_server_request() - - network_list = [{'id': 3, 'fixed_ip': '10.0.0.3'}] - server['networks'] = network_list - - post = {'server': server} - self.assertRaises(client.OpenStackApiException, - self.api.post_server, post) - - def test_create_server_with_networks_not_found_for_project(self): - """Creates a server with networks.""" - - # Build the server data gradually, checking errors along the way - server = self._build_minimal_create_server_request() - - network_list = [{'id': 1, 'fixed_ip': '10.0.0.3'}, - {'id': 3, 'fixed_ip': '10.0.0.12'}] - server['networks'] = network_list - - post = {'server': server} - self.assertRaises(client.OpenStackApiException, - self.api.post_server, post) - - def test_create_server_with_networks_invalid_fixed_ip(self): - """Creates a server with networks.""" - - # Build the server data gradually, checking errors along the way - server = self._build_minimal_create_server_request() - - network_list = [{'id': 1, 'fixed_ip': '10.0.0.3.0'}] - server['networks'] = network_list - - post = {'server': server} - self.assertRaises(client.OpenStackApiException, - self.api.post_server, post) - - def test_create_server_with_networks_non_existing_fixed_ip(self): - """Creates a server with metadata.""" - - # Build the server data gradually, checking errors along the way - server = self._build_minimal_create_server_request() - - network_list = [{'id': 1, 'fixed_ip': '10.100.0.3'}] - server['networks'] = network_list - - post = {'server': server} - self.assertRaises(client.OpenStackApiException, - self.api.post_server, post) - - def test_create_server_with_networks_empty_fixed_ip(self): - """Creates a server with networks.""" - - # Build the server data gradually, checking errors along the way - server = self._build_minimal_create_server_request() - - network_list = [{'id': 1, 'fixed_ip': ''}] - server['networks'] = network_list - - post = {'server': server} - self.assertRaises(client.OpenStackApiException, - self.api.post_server, post) - - def test_create_server_with_networks_non_string_fixed_ip(self): - """Creates a server with networks.""" - - # Build the server data gradually, checking errors along the way - server = self._build_minimal_create_server_request() - - network_list = [{'id': 1, 'fixed_ip': 12}] - server['networks'] = network_list - - post = {'server': server} - self.assertRaises(client.OpenStackApiException, - self.api.post_server, post) - - def test_create_server_with_networks_none_fixed_ip(self): - """Creates a server with networks.""" - - # Build the server data gradually, checking errors along the way - server = self._build_minimal_create_server_request() - - network_list = [{'id': 1, 'fixed_ip': None}] - server['networks'] = network_list - - post = {'server': server} - created_server = self.api.post_server(post) - LOG.debug("created_server: %s" % created_server) - self.assertTrue(created_server['id']) - created_server_id = created_server['id'] - - # Check it's there - found_server = self.api.get_server(created_server_id) - self.assertEqual(created_server_id, found_server['id']) - - # Cleanup - self._delete_server(created_server_id) - - def test_create_server_with_networks_no_fixed_ip(self): - """Creates a server with networks.""" - - # Build the server data gradually, checking errors along the way - server = self._build_minimal_create_server_request() - - network_list = [{'id': 1}] - server['networks'] = network_list - - post = {'server': server} - created_server = self.api.post_server(post) - LOG.debug("created_server: %s" % created_server) - self.assertTrue(created_server['id']) - created_server_id = created_server['id'] - - # Check it's there - found_server = self.api.get_server(created_server_id) - self.assertEqual(created_server_id, found_server['id']) - - # Cleanup - self._delete_server(created_server_id) - def test_create_and_rebuild_server(self): """Rebuild a server.""" -- cgit From c8de39d52d48fc14b851cdd5de8b0d356f3291dc Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Tue, 19 Jul 2011 17:38:51 -0700 Subject: Reverted to original code, after network binding to project code is in integration code for testing new extension will be added --- nova/tests/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nova/tests/__init__.py b/nova/tests/__init__.py index 44ce2e635..e4ed75d37 100644 --- a/nova/tests/__init__.py +++ b/nova/tests/__init__.py @@ -66,8 +66,7 @@ def setup(): bridge=FLAGS.flat_network_bridge, bridge_interface=bridge_interface, vpn_start=FLAGS.vpn_start, - vlan_start=FLAGS.vlan_start, - project_id="openstack") + vlan_start=FLAGS.vlan_start) for net in db.network_get_all(ctxt): network.set_network_host(ctxt, net['id']) -- cgit From 038565bdc735ff7a227a39d2ee21df0e8194929b Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Tue, 19 Jul 2011 18:24:44 -0700 Subject: Modified alias ^Cd minor fixes --- bin/nova-manage | 2 +- nova/api/openstack/contrib/createserverext.py | 4 ++-- nova/network/manager.py | 1 - nova/tests/api/openstack/contrib/test_createserverext.py | 4 ++-- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 47bf81008..7c2dd4d6d 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -621,7 +621,7 @@ class NetworkCommands(object): def list(self): """List all created networks""" - print "%-5s\%-18s\t%-15s\t%-15s\t%-15s\t%-15s\t%-15s" % (_('id'), + print "%-5s\t%-18s\t%-15s\t%-15s\t%-15s\t%-15s\t%-15s" % (_('id'), _('network'), _('netmask'), _('start address'), diff --git a/nova/api/openstack/contrib/createserverext.py b/nova/api/openstack/contrib/createserverext.py index 78dba425a..2f8b4389f 100644 --- a/nova/api/openstack/contrib/createserverext.py +++ b/nova/api/openstack/contrib/createserverext.py @@ -205,7 +205,7 @@ class Createserverext(extensions.ExtensionDescriptor): return "Createserverext" def get_alias(self): - return "os-servers-create-ext" + return "os-create-server-ext" def get_description(self): return "Extended support to the Create Server v1.1 API" @@ -234,7 +234,7 @@ class Createserverext(extensions.ExtensionDescriptor): headers_serializer) deserializer = wsgi.RequestDeserializer(body_deserializers) - res = extensions.ResourceExtension('os-servers-create-ext', + res = extensions.ResourceExtension('os-create-server-ext', controller=CreateServerExtController(), deserializer=deserializer, serializer=serializer) diff --git a/nova/network/manager.py b/nova/network/manager.py index 349faa84d..27ea669b4 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -614,7 +614,6 @@ class NetworkManager(manager.SchedulerDependentManager): net['gateway'] = str(project_net[1]) net['broadcast'] = str(project_net.broadcast) net['dhcp_start'] = str(project_net[2]) - net['project_id'] = kwargs['project_id'] if num_networks > 1: net['label'] = '%s_%d' % (label, index) else: diff --git a/nova/tests/api/openstack/contrib/test_createserverext.py b/nova/tests/api/openstack/contrib/test_createserverext.py index f2aa136e2..5d1687ee9 100644 --- a/nova/tests/api/openstack/contrib/test_createserverext.py +++ b/nova/tests/api/openstack/contrib/test_createserverext.py @@ -290,7 +290,7 @@ class CreateserverextTest(test.TestCase): return {'server': server} def _get_create_request_json(self, body_dict): - req = webob.Request.blank('/v1.1/os-servers-create-ext') + req = webob.Request.blank('/v1.1/os-create-server-ext') req.headers['Content-Type'] = 'application/json' req.method = 'POST' req.body = json.dumps(body_dict) @@ -334,7 +334,7 @@ class CreateserverextTest(test.TestCase): return ''.join(body_parts) def _get_create_request_xml(self, body_dict): - req = webob.Request.blank('/v1.1/os-servers-create-ext') + req = webob.Request.blank('/v1.1/os-create-server-ext') req.content_type = 'application/xml' req.accept = 'application/xml' req.method = 'POST' -- cgit From ae1fd7e73890d361d0bd6ad764118c1e5601cdca Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Thu, 21 Jul 2011 11:25:02 -0700 Subject: pep8 --- nova/network/manager.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index c9a68af68..bafa1c8e8 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -409,9 +409,8 @@ class NetworkManager(manager.SchedulerDependentManager): LOG.debug(_("network allocations for instance %s"), instance_id, context=context) networks = self._get_networks_for_instance(admin_context, - instance_id, - project_id, - requested_networks=requested_networks) + instance_id, project_id, + requested_networks=requested_networks) self._allocate_mac_addresses(context, instance_id, networks) self._allocate_fixed_ips(admin_context, instance_id, host, networks, vpn=vpn, @@ -928,7 +927,7 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): requested_networks) else: networks = self.db.project_get_networks(context, project_id) - return networks; + return networks def create_networks(self, context, **kwargs): """Create networks based on parameters.""" -- cgit From d963e25906b75a48c75b6e589deb2a53f75d6ee3 Mon Sep 17 00:00:00 2001 From: Christopher MacGown Date: Fri, 22 Jul 2011 20:29:37 -0700 Subject: Config-Drive happiness, minus smoketest --- Authors | 1 + nova/api/openstack/create_instance_helper.py | 7 +- nova/api/openstack/views/servers.py | 4 + nova/compute/api.py | 21 +++- .../versions/035_add_config_drive_to_instances.py | 43 +++++++ nova/db/sqlalchemy/models.py | 1 + nova/scheduler/simple.py | 1 + nova/tests/api/openstack/test_servers.py | 135 ++++++++++++++++++++- nova/tests/test_compute.py | 14 +++ nova/virt/libvirt.xml.template | 7 ++ nova/virt/libvirt/connection.py | 51 ++++++-- run_tests.sh | 2 +- 12 files changed, 265 insertions(+), 22 deletions(-) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/035_add_config_drive_to_instances.py diff --git a/Authors b/Authors index f3129c26e..51f511996 100644 --- a/Authors +++ b/Authors @@ -16,6 +16,7 @@ Chiradeep Vittal Chmouel Boudjnah Chris Behrens Christian Berendt +Christopher MacGown Chuck Short Cory Wright Dan Prince diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 2654e3c40..fe92bae2e 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -96,6 +96,7 @@ class CreateInstanceHelper(object): locals()) raise faults.Fault(exc.HTTPBadRequest(explanation=msg)) + config_drive = body['server'].get('config_drive') personality = body['server'].get('personality') injected_files = [] @@ -130,6 +131,7 @@ class CreateInstanceHelper(object): extra_values = { 'instance_type': inst_type, 'image_ref': image_href, + 'config_drive': config_drive, 'password': password} return (extra_values, @@ -148,7 +150,8 @@ class CreateInstanceHelper(object): zone_blob=zone_blob, reservation_id=reservation_id, min_count=min_count, - max_count=max_count)) + max_count=max_count, + config_drive=config_drive,)) except quota.QuotaError as error: self._handle_quota_error(error) except exception.ImageNotFound as error: @@ -160,6 +163,8 @@ class CreateInstanceHelper(object): def _handle_quota_error(self, error): """ Reraise quota errors as api-specific http exceptions + + """ if error.code == "OnsetFileLimitExceeded": expl = _("Personality file limit exceeded") diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py index ab7e8da61..961932a4e 100644 --- a/nova/api/openstack/views/servers.py +++ b/nova/api/openstack/views/servers.py @@ -164,6 +164,7 @@ class ViewBuilderV11(ViewBuilder): def _build_extra(self, response, inst): self._build_links(response, inst) + self._build_config_drive(response, inst) def _build_links(self, response, inst): href = self.generate_href(inst["id"]) @@ -182,6 +183,9 @@ class ViewBuilderV11(ViewBuilder): response["server"]["links"] = links + def _build_config_drive(self, response, inst): + response['server']['config_drive'] = inst.get('config_drive') + def generate_href(self, server_id): """Create an url that refers to a specific server id.""" return os.path.join(self.base_url, "servers", str(server_id)) diff --git a/nova/compute/api.py b/nova/compute/api.py index 67aa3c20f..8adcbbef6 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -150,7 +150,7 @@ class API(base.Base): key_name=None, key_data=None, security_group='default', availability_zone=None, user_data=None, metadata={}, injected_files=None, admin_password=None, zone_blob=None, - reservation_id=None): + reservation_id=None, config_drive=None,): """Verify all the input parameters regardless of the provisioning strategy being performed.""" @@ -181,6 +181,11 @@ class API(base.Base): (image_service, image_id) = nova.image.get_image_service(image_href) image = image_service.show(context, image_id) + config_drive_id = None + if config_drive and config_drive is not True: + # config_drive is volume id + config_drive, config_drive_id = None, config_drive + os_type = None if 'properties' in image and 'os_type' in image['properties']: os_type = image['properties']['os_type'] @@ -208,6 +213,8 @@ class API(base.Base): image_service.show(context, kernel_id) if ramdisk_id: image_service.show(context, ramdisk_id) + if config_drive_id: + image_service.show(context, config_drive_id) self.ensure_default_security_group(context) @@ -226,6 +233,8 @@ class API(base.Base): 'image_ref': image_href, 'kernel_id': kernel_id or '', 'ramdisk_id': ramdisk_id or '', + 'config_drive_id': config_drive_id or '', + 'config_drive': config_drive or '', 'state': 0, 'state_description': 'scheduling', 'user_id': context.user_id, @@ -397,7 +406,8 @@ class API(base.Base): key_name=None, key_data=None, security_group='default', availability_zone=None, user_data=None, metadata={}, injected_files=None, admin_password=None, zone_blob=None, - reservation_id=None, block_device_mapping=None): + reservation_id=None, block_device_mapping=None, + config_drive=None): """Provision the instances by passing the whole request to the Scheduler for execution. Returns a Reservation ID related to the creation of all of these instances.""" @@ -409,7 +419,7 @@ class API(base.Base): key_name, key_data, security_group, availability_zone, user_data, metadata, injected_files, admin_password, zone_blob, - reservation_id) + reservation_id, config_drive) self._ask_scheduler_to_create_instance(context, base_options, instance_type, zone_blob, @@ -426,7 +436,8 @@ class API(base.Base): key_name=None, key_data=None, security_group='default', availability_zone=None, user_data=None, metadata={}, injected_files=None, admin_password=None, zone_blob=None, - reservation_id=None, block_device_mapping=None): + reservation_id=None, block_device_mapping=None, + config_drive=None,): """ Provision the instances by sending off a series of single instance requests to the Schedulers. This is fine for trival @@ -447,7 +458,7 @@ class API(base.Base): key_name, key_data, security_group, availability_zone, user_data, metadata, injected_files, admin_password, zone_blob, - reservation_id) + reservation_id, config_drive) block_device_mapping = block_device_mapping or [] instances = [] diff --git a/nova/db/sqlalchemy/migrate_repo/versions/035_add_config_drive_to_instances.py b/nova/db/sqlalchemy/migrate_repo/versions/035_add_config_drive_to_instances.py new file mode 100644 index 000000000..65ea012dd --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/035_add_config_drive_to_instances.py @@ -0,0 +1,43 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# +# 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. + +from sqlalchemy import Column, Integer, MetaData, String, Table + +from nova import utils + + +meta = MetaData() + +instances = Table("instances", meta, + Column("id", Integer(), primary_key=True, nullable=False)) +config_drive_column = Column("config_drive", String(255)) # matches image_ref + + +def upgrade(migrate_engine): + meta.bind = migrate_engine + instances.create_column(config_drive_column) + + rows = migrate_engine.execute(instances.select()) + for row in rows: + instance_config_drive = None # pre-existing instances don't have one. + migrate_engine.execute(instances.update()\ + .where(instances.c.id == row[0])\ + .values(config_drive=instance_config_drive)) + + +def downgrade(migrate_engine): + meta.bind = migrate_engine + instances.drop_column(config_drive_column) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index c1150f7ca..73ad1f011 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -238,6 +238,7 @@ class Instance(BASE, NovaBase): uuid = Column(String(36)) root_device_name = Column(String(255)) + config_drive = Column(String(255)) # TODO(vish): see Ewan's email about state improvements, probably # should be in a driver base class or some such diff --git a/nova/scheduler/simple.py b/nova/scheduler/simple.py index fc1b3142a..61c76b35d 100644 --- a/nova/scheduler/simple.py +++ b/nova/scheduler/simple.py @@ -41,6 +41,7 @@ class SimpleScheduler(chance.ChanceScheduler): def _schedule_instance(self, context, instance_id, *_args, **_kwargs): """Picks a host that is up and has the fewest running instances.""" + instance_ref = db.instance_get(context, instance_id) if (instance_ref['availability_zone'] and ':' in instance_ref['availability_zone'] diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 3fc38b73c..6a1eb8c87 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -775,11 +775,14 @@ class ServersTest(test.TestCase): self.assertEqual(res.status_int, 400) self.assertTrue(res.body.find('marker param') > -1) - def _setup_for_create_instance(self): + def _setup_for_create_instance(self, instance_db_stub=None): """Shared implementation for tests below that create instance""" def instance_create(context, inst): - return {'id': 1, 'display_name': 'server_test', - 'uuid': FAKE_UUID} + if instance_db_stub: + return instance_db_stub + else: + return {'id': 1, 'display_name': 'server_test', + 'uuid': FAKE_UUID,} def server_update(context, id, params): return instance_create(context, id) @@ -997,6 +1000,132 @@ class ServersTest(test.TestCase): self.assertEqual(flavor_ref, server['flavorRef']) self.assertEqual(image_href, server['imageRef']) + def test_create_instance_with_config_drive_v1_1(self): + db_stub = {'id': 100, 'display_name': 'config_drive_test', + 'uuid': FAKE_UUID, 'config_drive': True} + self._setup_for_create_instance(instance_db_stub=db_stub) + + image_href = 'http://localhost/v1.1/images/2' + flavor_ref = 'http://localhost/v1.1/flavors/3' + body = { + 'server': { + 'name': 'config_drive_test', + 'imageRef': image_href, + 'flavorRef': flavor_ref, + 'metadata': { + 'hello': 'world', + 'open': 'stack', + }, + 'personality': {}, + 'config_drive': True, + }, + } + + req = webob.Request.blank('/v1.1/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + server = json.loads(res.body)['server'] + self.assertEqual(100, server['id']) + self.assertTrue(server['config_drive']) + + def test_create_instance_with_config_drive_as_id_v1_1(self): + db_stub = {'id': 100, 'display_name': 'config_drive_test', + 'uuid': FAKE_UUID, 'config_drive': 2} + self._setup_for_create_instance(instance_db_stub=db_stub) + + image_href = 'http://localhost/v1.1/images/2' + flavor_ref = 'http://localhost/v1.1/flavors/3' + body = { + 'server': { + 'name': 'config_drive_test', + 'imageRef': image_href, + 'flavorRef': flavor_ref, + 'metadata': { + 'hello': 'world', + 'open': 'stack', + }, + 'personality': {}, + 'config_drive': 2, + }, + } + + req = webob.Request.blank('/v1.1/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + server = json.loads(res.body)['server'] + self.assertEqual(100, server['id']) + self.assertTrue(server['config_drive']) + self.assertEqual(2, server['config_drive']) + + def test_create_instance_with_bad_config_drive_v1_1(self): + db_stub = {'id': 100, 'display_name': 'config_drive_test', + 'uuid': FAKE_UUID, 'config_drive': 'asdf'} + self._setup_for_create_instance(instance_db_stub=db_stub) + + image_href = 'http://localhost/v1.1/images/2' + flavor_ref = 'http://localhost/v1.1/flavors/3' + body = { + 'server': { + 'name': 'config_drive_test', + 'imageRef': image_href, + 'flavorRef': flavor_ref, + 'metadata': { + 'hello': 'world', + 'open': 'stack', + }, + 'personality': {}, + 'config_drive': 'asdf', + }, + } + + req = webob.Request.blank('/v1.1/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 400) + + def test_create_instance_without_config_drive_v1_1(self): + db_stub = {'id': 100, 'display_name': 'config_drive_test', + 'uuid': FAKE_UUID, 'config_drive': None} + self._setup_for_create_instance(instance_db_stub=db_stub) + + image_href = 'http://localhost/v1.1/images/2' + flavor_ref = 'http://localhost/v1.1/flavors/3' + body = { + 'server': { + 'name': 'config_drive_test', + 'imageRef': image_href, + 'flavorRef': flavor_ref, + 'metadata': { + 'hello': 'world', + 'open': 'stack', + }, + 'personality': {}, + 'config_drive': True, + }, + } + + req = webob.Request.blank('/v1.1/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + server = json.loads(res.body)['server'] + self.assertEqual(100, server['id']) + self.assertFalse(server['config_drive']) + def test_create_instance_v1_1_bad_href(self): self._setup_for_create_instance() diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 5d59b628a..bc681706d 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -162,6 +162,20 @@ class ComputeTestCase(test.TestCase): db.security_group_destroy(self.context, group['id']) db.instance_destroy(self.context, ref[0]['id']) + def test_create_instance_associates_config_drive(self): + """Make sure create associates a config drive.""" + + instance_id = self._create_instance(params={'config_drive': True,}) + + try: + self.compute.run_instance(self.context, instance_id) + instances = db.instance_get_all(context.get_admin_context()) + instance = instances[0] + + self.assertTrue(instance.config_drive) + finally: + db.instance_destroy(self.context, instance_id) + def test_default_hostname_generator(self): cases = [(None, 'server_1'), ('Hello, Server!', 'hello_server'), ('<}\x1fh\x10e\x08l\x02l\x05o\x12!{>', 'hello')] diff --git a/nova/virt/libvirt.xml.template b/nova/virt/libvirt.xml.template index e1a683da8..4422f349f 100644 --- a/nova/virt/libvirt.xml.template +++ b/nova/virt/libvirt.xml.template @@ -55,6 +55,13 @@ #else + #if $getVar('config', False) + + + + + + #end if #if $getVar('rescue', False) diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 342dea98f..ad97dc796 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -123,6 +123,8 @@ flags.DEFINE_string('qemu_img', 'qemu-img', 'binary to use for qemu-img commands') flags.DEFINE_bool('start_guests_on_host_boot', False, 'Whether to restart guests when the host reboots') +flags.DEFINE_string('default_local_format', None, + 'Default filesystem format for local drives') def get_connection(read_only): @@ -586,6 +588,8 @@ class LibvirtConnection(driver.ComputeDriver): block_device_mapping = block_device_mapping or [] self.firewall_driver.setup_basic_filtering(instance, network_info) self.firewall_driver.prepare_instance_filter(instance, network_info) + + # This is where things actually get built. self._create_image(instance, xml, network_info=network_info, block_device_mapping=block_device_mapping) domain = self._create_new_domain(xml) @@ -763,10 +767,15 @@ class LibvirtConnection(driver.ComputeDriver): if size: disk.extend(target, size) - def _create_local(self, target, local_gb): + def _create_local(self, target, local_size, prefix='G', fs_format=None): """Create a blank image of specified size""" - utils.execute('truncate', target, '-s', "%dG" % local_gb) - # TODO(vish): should we format disk by default? + + if not fs_format: + fs_format = FLAGS.default_local_format + + utils.execute('truncate', target, '-s', "%d%c" % (local_size, prefix)) + if fs_format: + utils.execute('mkfs', '-t', fs_format, target) def _create_image(self, inst, libvirt_xml, suffix='', disk_images=None, network_info=None, block_device_mapping=None): @@ -859,6 +868,18 @@ class LibvirtConnection(driver.ComputeDriver): if FLAGS.libvirt_type == 'lxc': target_partition = None + else: + if inst['config_drive_id']: + fname = '%08x' % int(inst['config_drive_id']) + self._cache_image(fn=self._fetch_image, + target=basepath('config'), + fname=fname, + image_id=inst['config_drive_id'], + user=user, + project=project) + elif inst['config_drive']: + self._create_local(basepath('config'), 64, prefix="M", + fs_format='msdos') # 64MB if inst['key_data']: key = str(inst['key_data']) @@ -903,17 +924,23 @@ class LibvirtConnection(driver.ComputeDriver): searchList=[{'interfaces': nets, 'use_ipv6': FLAGS.use_ipv6}])) - if key or net: + if any(key, net, inst['metadata']): inst_name = inst['name'] - img_id = inst.image_ref - if key: - LOG.info(_('instance %(inst_name)s: injecting key into' - ' image %(img_id)s') % locals()) - if net: - LOG.info(_('instance %(inst_name)s: injecting net into' - ' image %(img_id)s') % locals()) + + if inst['config_drive']: # Should be True or None by now. + injection_path = basepath('config') + img_id = 'config-drive' + else: + injection_path = basepath('disk') + img_id = inst.image_ref + + for injection in ('metadata', 'key', 'net'): + if locals()[injection]: + LOG.info(_('instance %(inst_name)s: injecting ' + '%(injection)s into image %(img_id)s' + % locals())) try: - disk.inject_data(basepath('disk'), key, net, + disk.inject_data(injection_path, key, net, inst['metadata'], partition=target_partition, nbd=FLAGS.use_cow_images) diff --git a/run_tests.sh b/run_tests.sh index b8078e150..5dba410b0 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -59,7 +59,7 @@ function run_tests { ERRSIZE=`wc -l run_tests.log | awk '{print \$1}'` if [ "$ERRSIZE" -lt "40" ]; then - cat run_tests.log + echo cat run_tests.log fi fi return $RESULT -- cgit From af9eab72fb89350d23a5dcd5f64f0ddc0a2a6154 Mon Sep 17 00:00:00 2001 From: Launchpad Translations on behalf of nova-core <> Date: Tue, 26 Jul 2011 04:53:45 +0000 Subject: Launchpad automatic translations update. --- po/pt_BR.po | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/po/pt_BR.po b/po/pt_BR.po index f067a69e0..a166c862b 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -8,13 +8,13 @@ msgstr "" "Project-Id-Version: nova\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2011-02-21 10:03-0500\n" -"PO-Revision-Date: 2011-03-24 14:51+0000\n" +"PO-Revision-Date: 2011-07-25 17:40+0000\n" "Last-Translator: msinhore \n" "Language-Team: Brazilian Portuguese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-07-23 05:12+0000\n" +"X-Launchpad-Export-Date: 2011-07-26 04:53+0000\n" "X-Generator: Launchpad (build 13405)\n" #: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55 @@ -36,6 +36,11 @@ msgid "" "Stdout: %(stdout)r\n" "Stderr: %(stderr)r" msgstr "" +"%(description)s\n" +"Comando: %(cmd)s\n" +"Código de saída: %(exit_code)s\n" +"Saída padrão: %(stdout)r\n" +"Erro: %(stderr)r" #: ../nova/exception.py:107 msgid "DB exception wrapped" @@ -420,7 +425,7 @@ msgstr "instância %s: suspendendo" #: ../nova/compute/manager.py:472 #, python-format msgid "instance %s: resuming" -msgstr "" +msgstr "instância %s: resumindo" #: ../nova/compute/manager.py:491 #, python-format @@ -435,12 +440,12 @@ msgstr "instância %s: desbloqueando" #: ../nova/compute/manager.py:513 #, python-format msgid "instance %s: getting locked state" -msgstr "" +msgstr "instância %s: obtendo estado de bloqueio" #: ../nova/compute/manager.py:526 #, python-format msgid "instance %s: reset network" -msgstr "" +msgstr "instância %s: reset da rede" #: ../nova/compute/manager.py:535 ../nova/api/ec2/cloud.py:515 #, python-format @@ -457,6 +462,7 @@ msgstr "instância %s: obtendo console ajax" msgid "" "instance %(instance_id)s: attaching volume %(volume_id)s to %(mountpoint)s" msgstr "" +"instância %(instance_id)s: atachando volume %(volume_id)s para %(mountpoint)s" #. pylint: disable=W0702 #. NOTE(vish): The inline callback eats the exception info so we @@ -466,6 +472,8 @@ msgstr "" #, python-format msgid "instance %(instance_id)s: attach failed %(mountpoint)s, removing" msgstr "" +"instância %(instance_id)s: falha ao atachar ponto de montagem " +"%(mountpoint)s, removendo" #: ../nova/compute/manager.py:585 #, python-format @@ -486,7 +494,7 @@ msgstr "Host %s não está ativo" #: ../nova/scheduler/simple.py:65 msgid "All hosts have too many cores" -msgstr "" +msgstr "Todos os hosts tem muitos núcleos de CPU" #: ../nova/scheduler/simple.py:87 #, python-format @@ -811,7 +819,7 @@ msgstr "Tamanho da imagem %(image)s:%(virtual_size)d" #: ../nova/virt/xenapi/vm_utils.py:332 #, python-format msgid "Glance image %s" -msgstr "" +msgstr "Visão geral da imagem %s" #. we need to invoke a plugin for copying VDI's #. content into proper path @@ -843,7 +851,7 @@ msgstr "Kernel PV no VDI: %s" #: ../nova/virt/xenapi/vm_utils.py:405 #, python-format msgid "Running pygrub against %s" -msgstr "" +msgstr "Rodando pygrub novamente %s" #: ../nova/virt/xenapi/vm_utils.py:411 #, python-format @@ -877,12 +885,12 @@ msgstr "(VM_UTILS) xenapi power_state -> |%s|" #: ../nova/virt/xenapi/vm_utils.py:525 #, python-format msgid "VHD %(vdi_uuid)s has parent %(parent_ref)s" -msgstr "" +msgstr "O VHD %(vdi_uuid)s tem pai %(parent_ref)s" #: ../nova/virt/xenapi/vm_utils.py:542 #, python-format msgid "Re-scanning SR %s" -msgstr "" +msgstr "Re-escaneando SR %s" #: ../nova/virt/xenapi/vm_utils.py:567 #, python-format -- cgit From 6c25483c08965204e8a4199ec600098fe09ad87c Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Wed, 27 Jul 2011 15:02:00 -0700 Subject: added unit testcase to increase code coverage --- nova/tests/api/openstack/contrib/test_createserverext.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/nova/tests/api/openstack/contrib/test_createserverext.py b/nova/tests/api/openstack/contrib/test_createserverext.py index 4866f29fe..4db15d2f1 100644 --- a/nova/tests/api/openstack/contrib/test_createserverext.py +++ b/nova/tests/api/openstack/contrib/test_createserverext.py @@ -222,6 +222,16 @@ class CreateserverextTest(test.TestCase): self._run_create_instance_with_mock_compute_api(request) self.assertEquals(response.status_int, 422) + def test_create_instance_with_no_server_name(self): + server = {} + server['imageRef'] = 1 + server['flavorRef'] = 1 + body_dict = {'server': server} + request = self._get_create_request_json(body_dict) + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + self.assertEquals(response.status_int, 400) + def test_create_instance_with_no_personality(self): request, response, injected_files = \ self._create_instance_with_personality_json(personality=None) -- cgit From b0ffe14da11addd2e1fcf14325b9bcf4c8cd3512 Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Wed, 27 Jul 2011 15:14:13 -0700 Subject: Reverting to original code --- nova/rpc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/rpc.py b/nova/rpc.py index 055f8220e..e2771ca88 100644 --- a/nova/rpc.py +++ b/nova/rpc.py @@ -279,7 +279,7 @@ class FanoutAdapterConsumer(AdapterConsumer): # them when done, so they're not left around on restart. # Also, we're the only one that should be consuming. exclusive # implies auto_delete, so we'll just set that.. - self.exclusive = False + self.exclusive = True LOG.info(_('Created "%(exchange)s" fanout exchange ' 'with "%(key)s" routing key'), dict(exchange=self.exchange, key=self.routing_key)) -- cgit From 3335a91c3c53513cc35e3f39a59975b33524950b Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Fri, 29 Jul 2011 18:45:51 -0700 Subject: Fixed review comments: Put parsing logic of network information in create_instance_helper module and refactored unit testcases as per the changed code. --- nova/api/openstack/contrib/createserverext.py | 213 +----------- nova/api/openstack/create_instance_helper.py | 83 ++++- nova/compute/api.py | 1 - nova/network/manager.py | 2 +- .../api/openstack/contrib/test_createserverext.py | 359 +-------------------- nova/tests/api/openstack/test_extensions.py | 6 - nova/tests/api/openstack/test_servers.py | 229 ++++++++++--- 7 files changed, 265 insertions(+), 628 deletions(-) diff --git a/nova/api/openstack/contrib/createserverext.py b/nova/api/openstack/contrib/createserverext.py index b5c06920c..79c017b42 100644 --- a/nova/api/openstack/contrib/createserverext.py +++ b/nova/api/openstack/contrib/createserverext.py @@ -13,220 +13,11 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License -from webob import exc - -from nova import exception -import nova.image -from nova import network -from nova import quota -from nova import rpc from nova.api.openstack import create_instance_helper as helper from nova.api.openstack import extensions -from nova.compute import instance_types from nova.api.openstack import servers from nova.api.openstack import wsgi -from nova.auth import manager as auth_manager - - -class CreateInstanceHelperEx(helper.CreateInstanceHelper): - def __init__(self, controller): - super(CreateInstanceHelperEx, self).__init__(controller) - - def create_instance(self, req, body, create_method): - """Creates a new server for the given user as per - the network information if it is provided - """ - if not body: - raise exc.HTTPUnprocessableEntity() - - if not 'server' in body: - raise exc.HTTPUnprocessableEntity() - - context = req.environ['nova.context'] - - password = self.controller._get_server_admin_password(body['server']) - - key_name = None - key_data = None - key_pairs = auth_manager.AuthManager.get_key_pairs(context) - if key_pairs: - key_pair = key_pairs[0] - key_name = key_pair['name'] - key_data = key_pair['public_key'] - - image_href = self.controller._image_ref_from_req_data(body) - try: - image_service, image_id = nova.image.get_image_service(image_href) - kernel_id, ramdisk_id = self._get_kernel_ramdisk_from_image( - req, image_id) - images = set([str(x['id']) for x in image_service.index(context)]) - assert str(image_id) in images - except Exception, e: - msg = _("Cannot find requested image %(image_href)s: %(e)s" % - locals()) - raise exc.HTTPBadRequest(explanation=msg) - - personality = body['server'].get('personality') - - injected_files = [] - if personality: - injected_files = self._get_injected_files(personality) - - requested_networks = body['server'].get('networks') - - if requested_networks is not None: - requested_networks = self._get_requested_networks( - requested_networks) - - flavor_id = self.controller._flavor_id_from_req_data(body) - - if not 'name' in body['server']: - msg = _("Server name is not defined") - raise exc.HTTPBadRequest(explanation=msg) - - zone_blob = body['server'].get('blob') - name = body['server']['name'] - self._validate_server_name(name) - name = name.strip() - - reservation_id = body['server'].get('reservation_id') - min_count = body['server'].get('min_count') - max_count = body['server'].get('max_count') - # min_count and max_count are optional. If they exist, they come - # in as strings. We want to default 'min_count' to 1, and default - # 'max_count' to be 'min_count'. - min_count = int(min_count) if min_count else 1 - max_count = int(max_count) if max_count else min_count - if min_count > max_count: - min_count = max_count - - try: - inst_type = \ - instance_types.get_instance_type_by_flavor_id(flavor_id) - extra_values = { - 'instance_type': inst_type, - 'image_ref': image_href, - 'password': password} - - return (extra_values, - create_method(context, - inst_type, - image_id, - kernel_id=kernel_id, - ramdisk_id=ramdisk_id, - display_name=name, - display_description=name, - key_name=key_name, - key_data=key_data, - metadata=body['server'].get('metadata', {}), - injected_files=injected_files, - admin_password=password, - zone_blob=zone_blob, - reservation_id=reservation_id, - min_count=min_count, - max_count=max_count, - requested_networks=requested_networks)) - except quota.QuotaError as error: - self._handle_quota_error(error) - except exception.ImageNotFound as error: - msg = _("Can not find requested image") - raise exc.HTTPBadRequest(explanation=msg) - except rpc.RemoteError as err: - msg = "%(err_type)s: %(err_msg)s" % \ - {'err_type': err.exc_type, 'err_msg': err.value} - raise exc.HTTPBadRequest(explanation=msg) - - # Let the caller deal with unhandled exceptions. - - def _validate_fixed_ip(self, value): - if not isinstance(value, basestring): - msg = _("Fixed IP is not a string or unicode") - raise exc.HTTPBadRequest(explanation=msg) - - if value.strip() == '': - msg = _("Fixed IP is an empty string") - raise exc.HTTPBadRequest(explanation=msg) - - def _get_requested_networks(self, requested_networks): - """ - Create a list of requested networks from the networks attribute - """ - networks = [] - for network in requested_networks: - try: - network_id = network['id'] - network_id = int(network_id) - #fixed IP address is optional - #if the fixed IP address is not provided then - #it will use one of the available IP address from the network - fixed_ip = network.get('fixed_ip', None) - if fixed_ip is not None: - self._validate_fixed_ip(fixed_ip) - # check if the network id is already present in the list, - # we don't want duplicate networks to be passed - # at the boot time - for id, ip in networks: - if id == network_id: - expl = _("Duplicate networks (%s) are not allowed")\ - % network_id - raise exc.HTTPBadRequest(explanation=expl) - - networks.append((network_id, fixed_ip)) - except KeyError as key: - expl = _('Bad network format: missing %s') % key - raise exc.HTTPBadRequest(explanation=expl) - except ValueError: - expl = _("Bad networks format: network id should " - "be integer (%s)") % network_id - raise exc.HTTPBadRequest(explanation=expl) - except TypeError: - expl = _('Bad networks format') - raise exc.HTTPBadRequest(explanation=expl) - - return networks - - -class CreateServerExtController(servers.ControllerV11): - """This is the controller for the extended version - of the create server OS V1.1 - """ - def __init__(self): - super(CreateServerExtController, self).__init__() - self.helper = CreateInstanceHelperEx(self) - - -class ServerXMLDeserializer(helper.ServerXMLDeserializer): - """ - Deserializer to handle xml-formatted server create requests. - - Handles networks element - """ - def _extract_server(self, node): - """Marshal the server attribute of a parsed request""" - server = super(ServerXMLDeserializer, self)._extract_server(node) - server_node = self.find_first_child_named(node, 'server') - networks = self._extract_networks(server_node) - if networks is not None: - server["networks"] = networks - return server - - def _extract_networks(self, server_node): - """Marshal the networks attribute of a parsed request""" - networks_node = \ - self.find_first_child_named(server_node, "networks") - if networks_node is None: - return None - networks = [] - for network_node in self.find_children_named(networks_node, - "network"): - item = {} - if network_node.hasAttribute("id"): - item["id"] = network_node.getAttribute("id") - if network_node.hasAttribute("fixed_ip"): - item["fixed_ip"] = network_node.getAttribute("fixed_ip") - networks.append(item) - return networks class Createserverext(extensions.ExtensionDescriptor): @@ -261,7 +52,7 @@ class Createserverext(extensions.ExtensionDescriptor): } body_deserializers = { - 'application/xml': ServerXMLDeserializer(), + 'application/xml': helper.ServerXMLDeserializer(), } serializer = wsgi.ResponseSerializer(body_serializers, @@ -269,7 +60,7 @@ class Createserverext(extensions.ExtensionDescriptor): deserializer = wsgi.RequestDeserializer(body_deserializers) res = extensions.ResourceExtension('os-create-server-ext', - controller=CreateServerExtController(), + controller=servers.ControllerV11(), deserializer=deserializer, serializer=serializer) resources.append(res) diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 04fbeda73..c7cc88313 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -30,6 +30,7 @@ from nova import utils from nova.compute import instance_types from nova.api.openstack import wsgi +from nova.rpc.common import RemoteError LOG = logging.getLogger('nova.api.openstack.create_instance_helper') FLAGS = flags.FLAGS @@ -106,6 +107,12 @@ class CreateInstanceHelper(object): if personality: injected_files = self._get_injected_files(personality) + requested_networks = server_dict.get('networks') + + if requested_networks is not None: + requested_networks = self._get_requested_networks( + requested_networks) + try: flavor_id = self.controller._flavor_id_from_req_data(body) except ValueError as error: @@ -156,7 +163,8 @@ class CreateInstanceHelper(object): zone_blob=zone_blob, reservation_id=reservation_id, min_count=min_count, - max_count=max_count)) + max_count=max_count, + requested_networks=requested_networks)) except quota.QuotaError as error: self._handle_quota_error(error) except exception.ImageNotFound as error: @@ -165,6 +173,10 @@ class CreateInstanceHelper(object): except exception.FlavorNotFound as error: msg = _("Invalid flavorRef provided.") raise exc.HTTPBadRequest(explanation=msg) + except RemoteError as err: + msg = "%(err_type)s: %(err_msg)s" % \ + {'err_type': err.exc_type, 'err_msg': err.value} + raise exc.HTTPBadRequest(explanation=msg) # Let the caller deal with unhandled exceptions. def _handle_quota_error(self, error): @@ -286,6 +298,53 @@ class CreateInstanceHelper(object): raise exc.HTTPBadRequest(explanation=msg) return password + def _validate_fixed_ip(self, value): + if not isinstance(value, basestring): + msg = _("Fixed IP is not a string or unicode") + raise exc.HTTPBadRequest(explanation=msg) + + if value.strip() == '': + msg = _("Fixed IP is an empty string") + raise exc.HTTPBadRequest(explanation=msg) + + def _get_requested_networks(self, requested_networks): + """ + Create a list of requested networks from the networks attribute + """ + networks = [] + for network in requested_networks: + try: + network_id = network['id'] + network_id = int(network_id) + #fixed IP address is optional + #if the fixed IP address is not provided then + #it will use one of the available IP address from the network + fixed_ip = network.get('fixed_ip', None) + if fixed_ip is not None: + self._validate_fixed_ip(fixed_ip) + # check if the network id is already present in the list, + # we don't want duplicate networks to be passed + # at the boot time + for id, ip in networks: + if id == network_id: + expl = _("Duplicate networks (%s) are not allowed")\ + % network_id + raise exc.HTTPBadRequest(explanation=expl) + + networks.append((network_id, fixed_ip)) + except KeyError as key: + expl = _('Bad network format: missing %s') % key + raise exc.HTTPBadRequest(explanation=expl) + except ValueError: + expl = _("Bad networks format: network id should " + "be integer (%s)") % network_id + raise exc.HTTPBadRequest(explanation=expl) + except TypeError: + expl = _('Bad networks format') + raise exc.HTTPBadRequest(explanation=expl) + + return networks + class ServerXMLDeserializer(wsgi.MetadataXMLDeserializer): """ @@ -317,6 +376,10 @@ class ServerXMLDeserializer(wsgi.MetadataXMLDeserializer): server["personality"] = self._extract_personality(server_node) + networks = self._extract_networks(server_node) + if networks: + server["networks"] = networks + return server def _extract_personality(self, server_node): @@ -331,3 +394,21 @@ class ServerXMLDeserializer(wsgi.MetadataXMLDeserializer): item["contents"] = self.extract_text(file_node) personality.append(item) return personality + + def _extract_networks(self, server_node): + """Marshal the networks attribute of a parsed request""" + networks_node = \ + self.find_first_child_named(server_node, "networks") + if networks_node is None: + return None + networks = [] + if networks_node is not None: + for network_node in self.find_children_named(networks_node, + "network"): + item = {} + if network_node.hasAttribute("id"): + item["id"] = network_node.getAttribute("id") + if network_node.hasAttribute("fixed_ip"): + item["fixed_ip"] = network_node.getAttribute("fixed_ip") + networks.append(item) + return networks diff --git a/nova/compute/api.py b/nova/compute/api.py index 818527809..ea7ba0697 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -96,7 +96,6 @@ class API(base.Base): if not network_api: network_api = network.API() self.network_api = network_api - self.network_manager = utils.import_object(FLAGS.network_manager) if not volume_api: volume_api = volume.API() self.volume_api = volume_api diff --git a/nova/network/manager.py b/nova/network/manager.py index 62a6732d5..655d5800a 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -407,7 +407,7 @@ class NetworkManager(manager.SchedulerDependentManager): host = kwargs.pop('host') project_id = kwargs.pop('project_id') type_id = kwargs.pop('instance_type_id') - requested_networks = kwargs.pop('requested_networks') + requested_networks = kwargs.get('requested_networks') vpn = kwargs.pop('vpn') admin_context = context.elevated() LOG.debug(_("network allocations for instance %s"), instance_id, diff --git a/nova/tests/api/openstack/contrib/test_createserverext.py b/nova/tests/api/openstack/contrib/test_createserverext.py index 4db15d2f1..5a033c761 100644 --- a/nova/tests/api/openstack/contrib/test_createserverext.py +++ b/nova/tests/api/openstack/contrib/test_createserverext.py @@ -48,7 +48,6 @@ class CreateserverextTest(test.TestCase): def setUp(self): super(CreateserverextTest, self).setUp() - self.controller = createserverext.CreateServerExtController() self.stubs = stubout.StubOutForTesting() fakes.FakeAuthManager.auth_data = {} fakes.FakeAuthDatabase.data = {} @@ -82,8 +81,8 @@ class CreateserverextTest(test.TestCase): self.networks = None return [{'id': '1234', 'display_name': 'fakeinstance', 'uuid': FAKE_UUID, - 'created_at': "2010-10-10T12:00:00Z", - 'updated_at': "2010-11-11T11:00:00Z"}] + 'created_at': "", + 'updated_at': ""}] def set_admin_password(self, *args, **kwargs): pass @@ -96,22 +95,10 @@ class CreateserverextTest(test.TestCase): compute_api = MockComputeAPI() self.stubs.Set(nova.compute, 'API', make_stub_method(compute_api)) self.stubs.Set( - createserverext.CreateInstanceHelperEx, + nova.api.openstack.create_instance_helper.CreateInstanceHelper, '_get_kernel_ramdisk_from_image', make_stub_method((1, 1))) return compute_api - def _create_personality_request_dict(self, personality_files): - server = {} - server['name'] = 'new-server-test' - server['imageRef'] = 1 - server['flavorRef'] = 1 - if personality_files is not None: - personalities = [] - for path, contents in personality_files: - personalities.append({'path': path, 'contents': contents}) - server['personality'] = personalities - return {'server': server} - def _create_networks_request_dict(self, networks): server = {} server['name'] = 'new-server-test' @@ -176,20 +163,6 @@ class CreateserverextTest(test.TestCase): req.body = self._format_xml_request_body(body_dict) return req - def _create_instance_with_personality_json(self, personality): - body_dict = self._create_personality_request_dict(personality) - request = self._get_create_request_json(body_dict) - compute_api, response = \ - self._run_create_instance_with_mock_compute_api(request) - return request, response, compute_api.injected_files - - def _create_instance_with_personality_xml(self, personality): - body_dict = self._create_personality_request_dict(personality) - request = self._get_create_request_xml(body_dict) - compute_api, response = \ - self._run_create_instance_with_mock_compute_api(request) - return request, response, compute_api.injected_files - def _create_instance_with_networks_json(self, networks): body_dict = self._create_networks_request_dict(networks) request = self._get_create_request_json(body_dict) @@ -204,168 +177,6 @@ class CreateserverextTest(test.TestCase): self._run_create_instance_with_mock_compute_api(request) return request, response, compute_api.networks - def test_create_instance_with_no_server_element(self): - server = {} - server['name'] = 'new-server-test' - server['imageRef'] = 1 - server['flavorRef'] = 1 - body_dict = {'no-server': server} - request = self._get_create_request_json(body_dict) - compute_api, response = \ - self._run_create_instance_with_mock_compute_api(request) - self.assertEquals(response.status_int, 422) - - def test_create_instance_with_no_request_body(self): - body_dict = None - request = self._get_create_request_json(body_dict) - compute_api, response = \ - self._run_create_instance_with_mock_compute_api(request) - self.assertEquals(response.status_int, 422) - - def test_create_instance_with_no_server_name(self): - server = {} - server['imageRef'] = 1 - server['flavorRef'] = 1 - body_dict = {'server': server} - request = self._get_create_request_json(body_dict) - compute_api, response = \ - self._run_create_instance_with_mock_compute_api(request) - self.assertEquals(response.status_int, 400) - - def test_create_instance_with_no_personality(self): - request, response, injected_files = \ - self._create_instance_with_personality_json(personality=None) - self.assertEquals(response.status_int, 200) - self.assertEquals(injected_files, []) - - def test_create_instance_with_no_personality_xml(self): - request, response, injected_files = \ - self._create_instance_with_personality_xml(personality=None) - self.assertEquals(response.status_int, 200) - self.assertEquals(injected_files, []) - - def test_create_instance_with_personality(self): - path = '/my/file/path' - contents = '#!/bin/bash\necho "Hello, World!"\n' - b64contents = base64.b64encode(contents) - personality = [(path, b64contents)] - request, response, injected_files = \ - self._create_instance_with_personality_json(personality) - self.assertEquals(response.status_int, 200) - self.assertEquals(injected_files, [(path, contents)]) - - def test_create_instance_with_personality_xml(self): - path = '/my/file/path' - contents = '#!/bin/bash\necho "Hello, World!"\n' - b64contents = base64.b64encode(contents) - personality = [(path, b64contents)] - request, response, injected_files = \ - self._create_instance_with_personality_xml(personality) - self.assertEquals(response.status_int, 200) - self.assertEquals(injected_files, [(path, contents)]) - - def test_create_instance_with_personality_no_path(self): - personality = [('/remove/this/path', - base64.b64encode('my\n\file\ncontents'))] - body_dict = self._create_personality_request_dict(personality) - del body_dict['server']['personality'][0]['path'] - request = self._get_create_request_json(body_dict) - compute_api, response = \ - self._run_create_instance_with_mock_compute_api(request) - self.assertEquals(response.status_int, 400) - self.assertEquals(compute_api.injected_files, None) - - def _test_create_instance_with_personality_no_path_xml(self): - personality = [('/remove/this/path', - base64.b64encode('my\n\file\ncontents'))] - body_dict = self._create_personality_request_dict(personality) - request = self._get_create_request_xml(body_dict) - request.body = request.body.replace(' path="/remove/this/path"', '') - compute_api, response = \ - self._run_create_instance_with_mock_compute_api(request) - self.assertEquals(response.status_int, 400) - self.assertEquals(compute_api.injected_files, None) - - def test_create_instance_with_personality_no_contents(self): - personality = [('/test/path', - base64.b64encode('remove\nthese\ncontents'))] - body_dict = self._create_personality_request_dict(personality) - del body_dict['server']['personality'][0]['contents'] - request = self._get_create_request_json(body_dict) - compute_api, response = \ - self._run_create_instance_with_mock_compute_api(request) - self.assertEquals(response.status_int, 400) - self.assertEquals(compute_api.injected_files, None) - - def test_create_instance_with_personality_not_a_list(self): - personality = [('/test/path', base64.b64encode('test\ncontents\n'))] - body_dict = self._create_personality_request_dict(personality) - body_dict['server']['personality'] = \ - body_dict['server']['personality'][0] - request = self._get_create_request_json(body_dict) - compute_api, response = \ - self._run_create_instance_with_mock_compute_api(request) - self.assertEquals(response.status_int, 400) - self.assertEquals(compute_api.injected_files, None) - - def test_create_instance_with_personality_with_non_b64_content(self): - path = '/my/file/path' - contents = '#!/bin/bash\necho "Oh no!"\n' - personality = [(path, contents)] - request, response, injected_files = \ - self._create_instance_with_personality_json(personality) - self.assertEquals(response.status_int, 400) - self.assertEquals(injected_files, None) - - def test_create_instance_with_null_personality(self): - personality = None - body_dict = self._create_personality_request_dict(personality) - body_dict['server']['personality'] = None - request = self._get_create_request_json(body_dict) - compute_api, response = \ - self._run_create_instance_with_mock_compute_api(request) - self.assertEquals(response.status_int, 200) - - def test_create_instance_with_three_personalities(self): - files = [ - ('/etc/sudoers', 'ALL ALL=NOPASSWD: ALL\n'), - ('/etc/motd', 'Enjoy your root access!\n'), - ('/etc/dovecot.conf', 'dovecot\nconfig\nstuff\n'), - ] - personality = [] - for path, content in files: - personality.append((path, base64.b64encode(content))) - request, response, injected_files = \ - self._create_instance_with_personality_json(personality) - self.assertEquals(response.status_int, 200) - self.assertEquals(injected_files, files) - - def test_create_instance_personality_empty_content(self): - path = '/my/file/path' - contents = '' - personality = [(path, contents)] - request, response, injected_files = \ - self._create_instance_with_personality_json(personality) - self.assertEquals(response.status_int, 200) - self.assertEquals(injected_files, [(path, contents)]) - - def test_create_instance_admin_pass_json(self): - request, response, dummy = \ - self._create_instance_with_personality_json(None) - self.assertEquals(response.status_int, 200) - response = json.loads(response.body) - self.assertTrue('adminPass' in response['server']) - self.assertEqual(16, len(response['server']['adminPass'])) - - def test_create_instance_admin_pass_xml(self): - request, response, dummy = \ - self._create_instance_with_personality_xml(None) - self.assertEquals(response.status_int, 200) - dom = minidom.parseString(response.body) - server = dom.childNodes[0] - self.assertEquals(server.nodeName, 'server') - self.assertEqual(16, len(server.getAttribute('adminPass'))) - def test_create_instance_with_no_networks(self): request, response, networks = \ self._create_instance_with_networks_json(networks=None) @@ -498,167 +309,3 @@ class CreateserverextTest(test.TestCase): self._run_create_instance_with_mock_compute_api(request) self.assertEquals(response.status_int, 200) self.assertEquals(compute_api.networks, [(1, None)]) - - -class TestServerCreateRequestXMLDeserializer(unittest.TestCase): - - def setUp(self): - self.deserializer = createserverext.ServerXMLDeserializer() - - def test_request_with_empty_networks(self): - serial_request = """ - - -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = {"server": { - "name": "new-server-test", - "imageId": "1", - "flavorId": "1", - "networks": [], - }} - self.assertEquals(request['body'], expected) - - def test_request_with_one_network(self): - serial_request = """ - - - - -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = {"server": { - "name": "new-server-test", - "imageId": "1", - "flavorId": "1", - "networks": [{"id": "1", "fixed_ip": "10.0.1.12"}], - }} - self.assertEquals(request['body'], expected) - - def test_request_with_two_networks(self): - serial_request = """ - - - - - -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = {"server": { - "name": "new-server-test", - "imageId": "1", - "flavorId": "1", - "networks": [{"id": "1", "fixed_ip": "10.0.1.12"}, - {"id": "2", "fixed_ip": "10.0.2.12"}], - }} - self.assertEquals(request['body'], expected) - - def test_request_with_second_network_node_ignored(self): - serial_request = """ - - - - - - - -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = {"server": { - "name": "new-server-test", - "imageId": "1", - "flavorId": "1", - "networks": [{"id": "1", "fixed_ip": "10.0.1.12"}], - }} - self.assertEquals(request['body'], expected) - - def test_request_with_one_network_missing_id(self): - serial_request = """ - - - - -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = {"server": { - "name": "new-server-test", - "imageId": "1", - "flavorId": "1", - "networks": [{"fixed_ip": "10.0.1.12"}], - }} - self.assertEquals(request['body'], expected) - - def test_request_with_one_network_missing_fixed_ip(self): - serial_request = """ - - - - -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = {"server": { - "name": "new-server-test", - "imageId": "1", - "flavorId": "1", - "networks": [{"id": "1"}], - }} - self.assertEquals(request['body'], expected) - - def test_request_with_one_network_empty_id(self): - serial_request = """ - - - - - """ - request = self.deserializer.deserialize(serial_request, 'create') - expected = {"server": { - "name": "new-server-test", - "imageId": "1", - "flavorId": "1", - "networks": [{"id": "", "fixed_ip": "10.0.1.12"}], - }} - self.assertEquals(request['body'], expected) - - def test_request_with_one_network_empty_fixed_ip(self): - serial_request = """ - - - - - """ - request = self.deserializer.deserialize(serial_request, 'create') - expected = {"server": { - "name": "new-server-test", - "imageId": "1", - "flavorId": "1", - "networks": [{"id": "1", "fixed_ip": ""}], - }} - self.assertEquals(request['body'], expected) - - def test_request_with_networks_duplicate_ids(self): - serial_request = """ - - - - - - """ - request = self.deserializer.deserialize(serial_request, 'create') - expected = {"server": { - "name": "new-server-test", - "imageId": "1", - "flavorId": "1", - "networks": [{"id": "1", "fixed_ip": "10.0.1.12"}, - {"id": "1", "fixed_ip": "10.0.2.12"}], - }} - self.assertEquals(request['body'], expected) diff --git a/nova/tests/api/openstack/test_extensions.py b/nova/tests/api/openstack/test_extensions.py index e71a5f109..50f4d0c09 100644 --- a/nova/tests/api/openstack/test_extensions.py +++ b/nova/tests/api/openstack/test_extensions.py @@ -30,10 +30,6 @@ from nova.api.openstack import wsgi from nova.tests.api.openstack import fakes -from nova import log as logging - -LOG = logging.getLogger('nova.tests.api.openstack.test_extensions') - FLAGS = flags.FLAGS NS = "{http://docs.openstack.org/compute/api/v1.1}" ATOMNS = "{http://www.w3.org/2005/Atom}" @@ -102,8 +98,6 @@ class ExtensionControllerTest(test.TestCase): data = json.loads(response.body) names = [x['name'] for x in data['extensions']] names.sort() - LOG.info("%%%%%%%%%%%%%%%%%%%%%%%%%") - LOG.info(names) self.assertEqual(names, ["Createserverext", "FlavorExtraSpecs", "Floating_ips", "Fox In Socks", "Hosts", "Multinic", "Volumes"]) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 418931989..759a3f9cb 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -1125,11 +1125,6 @@ class ServersTest(test.TestCase): def project_get_networks(context, user_id): return dict(id='1', host='localhost') - def project_get_requested_networks(context, requested_networks): - return [{'id': 1, 'host': 'localhost1'}, - {'id': 2, 'host': 'localhost2'}, - ] - def queue_get_for(context, *args): return 'network_topic' @@ -1141,8 +1136,6 @@ class ServersTest(test.TestCase): self.stubs.Set(nova.db.api, 'project_get_networks', project_get_networks) - self.stubs.Set(nova.db.api, 'project_get_requested_networks', - project_get_requested_networks) self.stubs.Set(nova.db.api, 'instance_create', instance_create) self.stubs.Set(nova.rpc, 'cast', fake_method) self.stubs.Set(nova.rpc, 'call', fake_method) @@ -1295,29 +1288,6 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 422) - def test_create_instance_whitespace_name(self): - self._setup_for_create_instance() - - body = { - 'server': { - 'name': ' ', - 'imageId': 3, - 'flavorId': 1, - 'metadata': { - 'hello': 'world', - 'open': 'stack', - }, - 'personality': {}, - }, - } - - req = webob.Request.blank('/v1.0/servers') - req.method = 'POST' - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 400) - def test_create_instance_v1_1(self): self._setup_for_create_instance() @@ -2844,6 +2814,181 @@ class TestServerCreateRequestXMLDeserializerV11(unittest.TestCase): } self.assertEquals(request['body'], expected) + def test_request_with_empty_networks(self): + serial_request = """ + + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageId": "1", + "flavorId": "1", + "metadata": {}, + "personality": [], + }} + self.assertEquals(request['body'], expected) + + def test_request_with_one_network(self): + serial_request = """ + + + + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageId": "1", + "flavorId": "1", + "personality": [], + "metadata": {}, + "networks": [{"id": "1", "fixed_ip": "10.0.1.12"}], + }} + self.assertEquals(request['body'], expected) + + def test_request_with_two_networks(self): + serial_request = """ + + + + + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageId": "1", + "flavorId": "1", + "metadata": {}, + "personality": [], + "networks": [{"id": "1", "fixed_ip": "10.0.1.12"}, + {"id": "2", "fixed_ip": "10.0.2.12"}], + }} + self.assertEquals(request['body'], expected) + + def test_request_with_second_network_node_ignored(self): + serial_request = """ + + + + + + + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageId": "1", + "flavorId": "1", + "metadata": {}, + "personality": [], + "networks": [{"id": "1", "fixed_ip": "10.0.1.12"}], + }} + self.assertEquals(request['body'], expected) + + def test_request_with_one_network_missing_id(self): + serial_request = """ + + + + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageId": "1", + "flavorId": "1", + "metadata": {}, + "personality": [], + "networks": [{"fixed_ip": "10.0.1.12"}], + }} + self.assertEquals(request['body'], expected) + + def test_request_with_one_network_missing_fixed_ip(self): + serial_request = """ + + + + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageId": "1", + "flavorId": "1", + "metadata": {}, + "personality": [], + "networks": [{"id": "1"}], + }} + self.assertEquals(request['body'], expected) + + def test_request_with_one_network_empty_id(self): + serial_request = """ + + + + + """ + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageId": "1", + "flavorId": "1", + "metadata": {}, + "personality": [], + "networks": [{"id": "", "fixed_ip": "10.0.1.12"}], + }} + self.assertEquals(request['body'], expected) + + def test_request_with_one_network_empty_fixed_ip(self): + serial_request = """ + + + + + """ + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageId": "1", + "flavorId": "1", + "metadata": {}, + "personality": [], + "networks": [{"id": "1", "fixed_ip": ""}], + }} + self.assertEquals(request['body'], expected) + + def test_request_with_networks_duplicate_ids(self): + serial_request = """ + + + + + + """ + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageId": "1", + "flavorId": "1", + "metadata": {}, + "personality": [], + "networks": [{"id": "1", "fixed_ip": "10.0.1.12"}, + {"id": "1", "fixed_ip": "10.0.2.12"}], + }} + self.assertEquals(request['body'], expected) + class TextAddressesXMLSerialization(test.TestCase): @@ -2908,7 +3053,7 @@ class TestServerInstanceCreation(test.TestCase): fakes.stub_out_image_service(self.stubs) fakes.stub_out_key_pair_funcs(self.stubs) - def _setup_mock_compute_api(self): + def _setup_mock_compute_api_for_personality(self): class MockComputeAPI(nova.compute.API): @@ -2952,18 +3097,6 @@ class TestServerInstanceCreation(test.TestCase): server['personality'] = personalities return {'server': server} - def _create_networks_request_dict(self, networks): - server = {} - server['name'] = 'new-server-test' - server['imageId'] = 1 - server['flavorId'] = 1 - if networks is not None: - network_list = [] - for id, fixed_ip in networks: - network_list.append({'id': id, 'fixed_ip': fixed_ip}) - server['networks'] = network_list - return {'server': server} - def _get_create_request_json(self, body_dict): req = webob.Request.blank('/v1.0/servers') req.headers['Content-Type'] = 'application/json' @@ -2972,7 +3105,7 @@ class TestServerInstanceCreation(test.TestCase): return req def _run_create_instance_with_mock_compute_api(self, request): - compute_api = self._setup_mock_compute_api() + compute_api = self._setup_mock_compute_api_for_personality() response = request.get_response(fakes.wsgi_app()) return compute_api, response @@ -2997,14 +3130,6 @@ class TestServerInstanceCreation(test.TestCase): item = (file['path'], file['contents']) body_parts.append('%s' % item) body_parts.append('') - if 'networks' in server: - networks = server['networks'] - body_parts.append('') - for network in networks: - item = (network['id'], network['fixed_ip']) - body_parts.append('' - % item) - body_parts.append('') body_parts.append('') return ''.join(body_parts) -- cgit From 00f47c6637b740ffa890250000e6d02dc557832f Mon Sep 17 00:00:00 2001 From: Launchpad Translations on behalf of nova-core <> Date: Sun, 31 Jul 2011 04:51:55 +0000 Subject: Launchpad automatic translations update. --- po/es.po | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/po/es.po b/po/es.po index eeaf209a9..182ce121c 100644 --- a/po/es.po +++ b/po/es.po @@ -8,20 +8,20 @@ msgstr "" "Project-Id-Version: nova\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2011-02-21 10:03-0500\n" -"PO-Revision-Date: 2011-06-30 16:42+0000\n" -"Last-Translator: David Caro \n" +"PO-Revision-Date: 2011-07-30 19:06+0000\n" +"Last-Translator: Juan Alfredo Salas Santillana \n" "Language-Team: Spanish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-07-23 05:12+0000\n" +"X-Launchpad-Export-Date: 2011-07-31 04:51+0000\n" "X-Generator: Launchpad (build 13405)\n" #: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55 #: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110 #: ../nova/scheduler/simple.py:122 msgid "No hosts found" -msgstr "No se han encontrado hosts" +msgstr "No se encontro anfitriones." #: ../nova/exception.py:33 msgid "Unexpected error while running command." @@ -2769,7 +2769,7 @@ msgstr "" #: ../nova/auth/ldapdriver.py:478 #, python-format msgid "Group can't be created because user %s doesn't exist" -msgstr "" +msgstr "El grupo no se puede crear porque el usuario %s no existe" #: ../nova/auth/ldapdriver.py:495 #, python-format @@ -2789,18 +2789,20 @@ msgstr "" #: ../nova/auth/ldapdriver.py:513 #, python-format msgid "User %(uid)s is already a member of the group %(group_dn)s" -msgstr "" +msgstr "El usuario %(uid)s es actualmente miembro del grupo %(group_dn)s" #: ../nova/auth/ldapdriver.py:524 #, python-format msgid "" "User %s can't be removed from the group because the user doesn't exist" msgstr "" +"El usuario %s no se pudo borrar de el grupo a causa de que el usuario no " +"existe" #: ../nova/auth/ldapdriver.py:528 #, python-format msgid "User %s is not a member of the group" -msgstr "" +msgstr "El usuario %s no es miembro de el grupo" #: ../nova/auth/ldapdriver.py:542 #, python-format -- cgit From 9fcd2e3719e1f3cd33f10a070e44c015a59817dd Mon Sep 17 00:00:00 2001 From: Launchpad Translations on behalf of nova-core <> Date: Mon, 1 Aug 2011 04:58:49 +0000 Subject: Launchpad automatic translations update. --- po/es.po | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/po/es.po b/po/es.po index 182ce121c..f64fe217f 100644 --- a/po/es.po +++ b/po/es.po @@ -8,13 +8,13 @@ msgstr "" "Project-Id-Version: nova\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2011-02-21 10:03-0500\n" -"PO-Revision-Date: 2011-07-30 19:06+0000\n" +"PO-Revision-Date: 2011-08-01 03:23+0000\n" "Last-Translator: Juan Alfredo Salas Santillana \n" "Language-Team: Spanish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-07-31 04:51+0000\n" +"X-Launchpad-Export-Date: 2011-08-01 04:58+0000\n" "X-Generator: Launchpad (build 13405)\n" #: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55 @@ -2625,7 +2625,7 @@ msgstr "" #: ../nova/auth/manager.py:289 #, python-format msgid "User %(uid)s is not a member of project %(pjid)s" -msgstr "" +msgstr "El usuario %(uid)s no es miembro del proyecto %(pjid)s" #: ../nova/auth/manager.py:298 ../nova/auth/manager.py:309 #, python-format @@ -2643,7 +2643,7 @@ msgstr "Debes especificar un proyecto" #: ../nova/auth/manager.py:414 #, python-format msgid "The %s role can not be found" -msgstr "El rol %s no se ha podido encontrar" +msgstr "" #: ../nova/auth/manager.py:416 #, python-format @@ -2673,27 +2673,27 @@ msgstr "" #: ../nova/auth/manager.py:515 #, python-format msgid "Created project %(name)s with manager %(manager_user)s" -msgstr "" +msgstr "Creado el proyecto %(name)s con administrador %(manager_user)s" #: ../nova/auth/manager.py:533 #, python-format msgid "modifying project %s" -msgstr "modificando proyecto %s" +msgstr "Modificando proyecto %s" #: ../nova/auth/manager.py:545 #, python-format msgid "Adding user %(uid)s to project %(pid)s" -msgstr "" +msgstr "Agregando usuario %(uid)s para el proyecto %(pid)s" #: ../nova/auth/manager.py:566 #, python-format msgid "Remove user %(uid)s from project %(pid)s" -msgstr "" +msgstr "Borrar usuario %(uid)s del proyecto %(pid)s" #: ../nova/auth/manager.py:592 #, python-format msgid "Deleting project %s" -msgstr "Eliminando proyecto %s" +msgstr "Borrando proyecto %s" #: ../nova/auth/manager.py:650 #, python-format @@ -2703,7 +2703,7 @@ msgstr "" #: ../nova/auth/manager.py:659 #, python-format msgid "Deleting user %s" -msgstr "Eliminando usuario %s" +msgstr "Borrando usuario %s" #: ../nova/auth/manager.py:669 #, python-format -- cgit From 92ab29ef58dd6dd42bb44ca721ba68cb615ae917 Mon Sep 17 00:00:00 2001 From: Launchpad Translations on behalf of nova-core <> Date: Tue, 2 Aug 2011 04:52:00 +0000 Subject: Launchpad automatic translations update. --- po/es.po | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/po/es.po b/po/es.po index f64fe217f..05edc00c4 100644 --- a/po/es.po +++ b/po/es.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-08-01 04:58+0000\n" -"X-Generator: Launchpad (build 13405)\n" +"X-Launchpad-Export-Date: 2011-08-02 04:52+0000\n" +"X-Generator: Launchpad (build 13552)\n" #: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55 #: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110 -- cgit From 26a14fe56708594362f7f17a4872c8797242fb69 Mon Sep 17 00:00:00 2001 From: Launchpad Translations on behalf of nova-core <> Date: Wed, 3 Aug 2011 04:44:31 +0000 Subject: Launchpad automatic translations update. --- po/ast.po | 63 ++------------------------------- po/cs.po | 63 ++------------------------------- po/da.po | 63 ++------------------------------- po/de.po | 81 +++++++++++-------------------------------- po/en_AU.po | 63 ++------------------------------- po/en_GB.po | 84 +++++++++++++------------------------------- po/es.po | 98 ++++++++++++++++++++-------------------------------- po/fr.po | 113 +++++++++++++++++++++++++++--------------------------------- po/it.po | 86 +++++++++++++-------------------------------- po/ja.po | 109 ++++++++++++++++++++++++++------------------------------- po/pt_BR.po | 86 +++++++++++++-------------------------------- po/ru.po | 81 +++++++++++-------------------------------- po/tl.po | 63 ++------------------------------- po/uk.po | 71 ++++++-------------------------------- po/zh_CN.po | 80 +++++++++++------------------------------- po/zh_TW.po | 74 +++++++-------------------------------- 16 files changed, 300 insertions(+), 978 deletions(-) diff --git a/po/ast.po b/po/ast.po index ac6190424..48682ec90 100644 --- a/po/ast.po +++ b/po/ast.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-07-23 05:11+0000\n" -"X-Generator: Launchpad (build 13405)\n" +"X-Launchpad-Export-Date: 2011-08-03 04:43+0000\n" +"X-Generator: Launchpad (build 13573)\n" #: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55 #: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110 @@ -125,33 +125,6 @@ msgstr "" msgid "compute.api::resume %s" msgstr "" -#: ../nova/twistd.py:157 -msgid "Wrong number of arguments." -msgstr "" - -#: ../nova/twistd.py:209 -#, python-format -msgid "pidfile %s does not exist. Daemon not running?\n" -msgstr "" - -#: ../nova/twistd.py:221 -msgid "No such process" -msgstr "" - -#: ../nova/twistd.py:230 ../nova/service.py:224 -#, python-format -msgid "Serving %s" -msgstr "" - -#: ../nova/twistd.py:262 ../nova/service.py:225 -msgid "Full set of FLAGS:" -msgstr "" - -#: ../nova/twistd.py:266 -#, python-format -msgid "Starting %s" -msgstr "" - #: ../nova/virt/xenapi/volumeops.py:48 ../nova/virt/xenapi/volumeops.py:101 #: ../nova/db/sqlalchemy/api.py:731 ../nova/virt/libvirt_conn.py:741 #: ../nova/api/ec2/__init__.py:317 @@ -1778,34 +1751,6 @@ msgstr "" msgid "Got exception: %s" msgstr "" -#: ../nova/compute/monitor.py:259 -#, python-format -msgid "updating %s..." -msgstr "" - -#: ../nova/compute/monitor.py:289 -msgid "unexpected error during update" -msgstr "" - -#: ../nova/compute/monitor.py:356 -#, python-format -msgid "Cannot get blockstats for \"%(disk)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:379 -#, python-format -msgid "Cannot get ifstats for \"%(interface)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:414 -msgid "unexpected exception getting connection" -msgstr "" - -#: ../nova/compute/monitor.py:429 -#, python-format -msgid "Found instance: %s" -msgstr "" - #: ../nova/volume/san.py:67 #, python-format msgid "Could not find iSCSI export for volume %s" @@ -2263,10 +2208,6 @@ msgstr "" msgid "You must implement __call__" msgstr "" -#: ../bin/nova-instancemonitor.py:55 -msgid "Starting instance monitor" -msgstr "" - #: ../bin/nova-dhcpbridge.py:58 msgid "leasing ip" msgstr "" diff --git a/po/cs.po b/po/cs.po index 33f7808a5..07bdf1928 100644 --- a/po/cs.po +++ b/po/cs.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-07-23 05:11+0000\n" -"X-Generator: Launchpad (build 13405)\n" +"X-Launchpad-Export-Date: 2011-08-03 04:43+0000\n" +"X-Generator: Launchpad (build 13573)\n" #: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55 #: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110 @@ -125,33 +125,6 @@ msgstr "" msgid "compute.api::resume %s" msgstr "" -#: ../nova/twistd.py:157 -msgid "Wrong number of arguments." -msgstr "" - -#: ../nova/twistd.py:209 -#, python-format -msgid "pidfile %s does not exist. Daemon not running?\n" -msgstr "" - -#: ../nova/twistd.py:221 -msgid "No such process" -msgstr "" - -#: ../nova/twistd.py:230 ../nova/service.py:224 -#, python-format -msgid "Serving %s" -msgstr "" - -#: ../nova/twistd.py:262 ../nova/service.py:225 -msgid "Full set of FLAGS:" -msgstr "" - -#: ../nova/twistd.py:266 -#, python-format -msgid "Starting %s" -msgstr "" - #: ../nova/virt/xenapi/volumeops.py:48 ../nova/virt/xenapi/volumeops.py:101 #: ../nova/db/sqlalchemy/api.py:731 ../nova/virt/libvirt_conn.py:741 #: ../nova/api/ec2/__init__.py:317 @@ -1778,34 +1751,6 @@ msgstr "" msgid "Got exception: %s" msgstr "" -#: ../nova/compute/monitor.py:259 -#, python-format -msgid "updating %s..." -msgstr "" - -#: ../nova/compute/monitor.py:289 -msgid "unexpected error during update" -msgstr "" - -#: ../nova/compute/monitor.py:356 -#, python-format -msgid "Cannot get blockstats for \"%(disk)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:379 -#, python-format -msgid "Cannot get ifstats for \"%(interface)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:414 -msgid "unexpected exception getting connection" -msgstr "" - -#: ../nova/compute/monitor.py:429 -#, python-format -msgid "Found instance: %s" -msgstr "" - #: ../nova/volume/san.py:67 #, python-format msgid "Could not find iSCSI export for volume %s" @@ -2265,10 +2210,6 @@ msgstr "" msgid "You must implement __call__" msgstr "" -#: ../bin/nova-instancemonitor.py:55 -msgid "Starting instance monitor" -msgstr "" - #: ../bin/nova-dhcpbridge.py:58 msgid "leasing ip" msgstr "" diff --git a/po/da.po b/po/da.po index 5aead53c3..0b379c9d7 100644 --- a/po/da.po +++ b/po/da.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-07-23 05:12+0000\n" -"X-Generator: Launchpad (build 13405)\n" +"X-Launchpad-Export-Date: 2011-08-03 04:43+0000\n" +"X-Generator: Launchpad (build 13573)\n" #: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55 #: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110 @@ -125,33 +125,6 @@ msgstr "" msgid "compute.api::resume %s" msgstr "" -#: ../nova/twistd.py:157 -msgid "Wrong number of arguments." -msgstr "" - -#: ../nova/twistd.py:209 -#, python-format -msgid "pidfile %s does not exist. Daemon not running?\n" -msgstr "" - -#: ../nova/twistd.py:221 -msgid "No such process" -msgstr "" - -#: ../nova/twistd.py:230 ../nova/service.py:224 -#, python-format -msgid "Serving %s" -msgstr "" - -#: ../nova/twistd.py:262 ../nova/service.py:225 -msgid "Full set of FLAGS:" -msgstr "" - -#: ../nova/twistd.py:266 -#, python-format -msgid "Starting %s" -msgstr "" - #: ../nova/virt/xenapi/volumeops.py:48 ../nova/virt/xenapi/volumeops.py:101 #: ../nova/db/sqlalchemy/api.py:731 ../nova/virt/libvirt_conn.py:741 #: ../nova/api/ec2/__init__.py:317 @@ -1778,34 +1751,6 @@ msgstr "" msgid "Got exception: %s" msgstr "" -#: ../nova/compute/monitor.py:259 -#, python-format -msgid "updating %s..." -msgstr "" - -#: ../nova/compute/monitor.py:289 -msgid "unexpected error during update" -msgstr "" - -#: ../nova/compute/monitor.py:356 -#, python-format -msgid "Cannot get blockstats for \"%(disk)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:379 -#, python-format -msgid "Cannot get ifstats for \"%(interface)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:414 -msgid "unexpected exception getting connection" -msgstr "" - -#: ../nova/compute/monitor.py:429 -#, python-format -msgid "Found instance: %s" -msgstr "" - #: ../nova/volume/san.py:67 #, python-format msgid "Could not find iSCSI export for volume %s" @@ -2263,10 +2208,6 @@ msgstr "" msgid "You must implement __call__" msgstr "" -#: ../bin/nova-instancemonitor.py:55 -msgid "Starting instance monitor" -msgstr "" - #: ../bin/nova-dhcpbridge.py:58 msgid "leasing ip" msgstr "" diff --git a/po/de.po b/po/de.po index cf40a0f5e..1f652c373 100644 --- a/po/de.po +++ b/po/de.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-07-23 05:12+0000\n" -"X-Generator: Launchpad (build 13405)\n" +"X-Launchpad-Export-Date: 2011-08-03 04:44+0000\n" +"X-Generator: Launchpad (build 13573)\n" #: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55 #: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110 @@ -131,33 +131,6 @@ msgstr "" msgid "compute.api::resume %s" msgstr "" -#: ../nova/twistd.py:157 -msgid "Wrong number of arguments." -msgstr "" - -#: ../nova/twistd.py:209 -#, python-format -msgid "pidfile %s does not exist. Daemon not running?\n" -msgstr "PID-Datei %s existiert nicht. Läuft der Daemon nicht?\n" - -#: ../nova/twistd.py:221 -msgid "No such process" -msgstr "Kein passender Prozess gefunden" - -#: ../nova/twistd.py:230 ../nova/service.py:224 -#, python-format -msgid "Serving %s" -msgstr "Bedient %s" - -#: ../nova/twistd.py:262 ../nova/service.py:225 -msgid "Full set of FLAGS:" -msgstr "Alle vorhandenen FLAGS:" - -#: ../nova/twistd.py:266 -#, python-format -msgid "Starting %s" -msgstr "%s wird gestartet" - #: ../nova/virt/xenapi/volumeops.py:48 ../nova/virt/xenapi/volumeops.py:101 #: ../nova/db/sqlalchemy/api.py:731 ../nova/virt/libvirt_conn.py:741 #: ../nova/api/ec2/__init__.py:317 @@ -1785,34 +1758,6 @@ msgstr "" msgid "Got exception: %s" msgstr "" -#: ../nova/compute/monitor.py:259 -#, python-format -msgid "updating %s..." -msgstr "" - -#: ../nova/compute/monitor.py:289 -msgid "unexpected error during update" -msgstr "" - -#: ../nova/compute/monitor.py:356 -#, python-format -msgid "Cannot get blockstats for \"%(disk)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:379 -#, python-format -msgid "Cannot get ifstats for \"%(interface)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:414 -msgid "unexpected exception getting connection" -msgstr "" - -#: ../nova/compute/monitor.py:429 -#, python-format -msgid "Found instance: %s" -msgstr "" - #: ../nova/volume/san.py:67 #, python-format msgid "Could not find iSCSI export for volume %s" @@ -2270,10 +2215,6 @@ msgstr "" msgid "You must implement __call__" msgstr "" -#: ../bin/nova-instancemonitor.py:55 -msgid "Starting instance monitor" -msgstr "" - #: ../bin/nova-dhcpbridge.py:58 msgid "leasing ip" msgstr "" @@ -2892,3 +2833,21 @@ msgstr "" #~ msgid "Data store %s is unreachable. Trying again in %d seconds." #~ msgstr "" #~ "Datastore %s ist nicht erreichbar. Versuche es erneut in %d Sekunden." + +#~ msgid "Full set of FLAGS:" +#~ msgstr "Alle vorhandenen FLAGS:" + +#, python-format +#~ msgid "pidfile %s does not exist. Daemon not running?\n" +#~ msgstr "PID-Datei %s existiert nicht. Läuft der Daemon nicht?\n" + +#, python-format +#~ msgid "Starting %s" +#~ msgstr "%s wird gestartet" + +#~ msgid "No such process" +#~ msgstr "Kein passender Prozess gefunden" + +#, python-format +#~ msgid "Serving %s" +#~ msgstr "Bedient %s" diff --git a/po/en_AU.po b/po/en_AU.po index e53f9fc07..a51b9ff2d 100644 --- a/po/en_AU.po +++ b/po/en_AU.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-07-23 05:12+0000\n" -"X-Generator: Launchpad (build 13405)\n" +"X-Launchpad-Export-Date: 2011-08-03 04:44+0000\n" +"X-Generator: Launchpad (build 13573)\n" #: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55 #: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110 @@ -125,33 +125,6 @@ msgstr "" msgid "compute.api::resume %s" msgstr "" -#: ../nova/twistd.py:157 -msgid "Wrong number of arguments." -msgstr "" - -#: ../nova/twistd.py:209 -#, python-format -msgid "pidfile %s does not exist. Daemon not running?\n" -msgstr "" - -#: ../nova/twistd.py:221 -msgid "No such process" -msgstr "" - -#: ../nova/twistd.py:230 ../nova/service.py:224 -#, python-format -msgid "Serving %s" -msgstr "" - -#: ../nova/twistd.py:262 ../nova/service.py:225 -msgid "Full set of FLAGS:" -msgstr "" - -#: ../nova/twistd.py:266 -#, python-format -msgid "Starting %s" -msgstr "" - #: ../nova/virt/xenapi/volumeops.py:48 ../nova/virt/xenapi/volumeops.py:101 #: ../nova/db/sqlalchemy/api.py:731 ../nova/virt/libvirt_conn.py:741 #: ../nova/api/ec2/__init__.py:317 @@ -1778,34 +1751,6 @@ msgstr "" msgid "Got exception: %s" msgstr "" -#: ../nova/compute/monitor.py:259 -#, python-format -msgid "updating %s..." -msgstr "" - -#: ../nova/compute/monitor.py:289 -msgid "unexpected error during update" -msgstr "" - -#: ../nova/compute/monitor.py:356 -#, python-format -msgid "Cannot get blockstats for \"%(disk)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:379 -#, python-format -msgid "Cannot get ifstats for \"%(interface)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:414 -msgid "unexpected exception getting connection" -msgstr "" - -#: ../nova/compute/monitor.py:429 -#, python-format -msgid "Found instance: %s" -msgstr "" - #: ../nova/volume/san.py:67 #, python-format msgid "Could not find iSCSI export for volume %s" @@ -2263,10 +2208,6 @@ msgstr "" msgid "You must implement __call__" msgstr "" -#: ../bin/nova-instancemonitor.py:55 -msgid "Starting instance monitor" -msgstr "" - #: ../bin/nova-dhcpbridge.py:58 msgid "leasing ip" msgstr "" diff --git a/po/en_GB.po b/po/en_GB.po index 601f6170b..59247f4fa 100644 --- a/po/en_GB.po +++ b/po/en_GB.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-07-23 05:12+0000\n" -"X-Generator: Launchpad (build 13405)\n" +"X-Launchpad-Export-Date: 2011-08-03 04:44+0000\n" +"X-Generator: Launchpad (build 13573)\n" #: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55 #: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110 @@ -130,33 +130,6 @@ msgstr "compute.api::suspend %s" msgid "compute.api::resume %s" msgstr "compute.api::resume %s" -#: ../nova/twistd.py:157 -msgid "Wrong number of arguments." -msgstr "Wrong number of arguments." - -#: ../nova/twistd.py:209 -#, python-format -msgid "pidfile %s does not exist. Daemon not running?\n" -msgstr "pidfile %s does not exist. Daemon not running?\n" - -#: ../nova/twistd.py:221 -msgid "No such process" -msgstr "No such process" - -#: ../nova/twistd.py:230 ../nova/service.py:224 -#, python-format -msgid "Serving %s" -msgstr "Serving %s" - -#: ../nova/twistd.py:262 ../nova/service.py:225 -msgid "Full set of FLAGS:" -msgstr "Full set of FLAGS:" - -#: ../nova/twistd.py:266 -#, python-format -msgid "Starting %s" -msgstr "Starting %s" - #: ../nova/virt/xenapi/volumeops.py:48 ../nova/virt/xenapi/volumeops.py:101 #: ../nova/db/sqlalchemy/api.py:731 ../nova/virt/libvirt_conn.py:741 #: ../nova/api/ec2/__init__.py:317 @@ -1803,34 +1776,6 @@ msgstr "" msgid "Got exception: %s" msgstr "" -#: ../nova/compute/monitor.py:259 -#, python-format -msgid "updating %s..." -msgstr "" - -#: ../nova/compute/monitor.py:289 -msgid "unexpected error during update" -msgstr "" - -#: ../nova/compute/monitor.py:356 -#, python-format -msgid "Cannot get blockstats for \"%(disk)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:379 -#, python-format -msgid "Cannot get ifstats for \"%(interface)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:414 -msgid "unexpected exception getting connection" -msgstr "" - -#: ../nova/compute/monitor.py:429 -#, python-format -msgid "Found instance: %s" -msgstr "" - #: ../nova/volume/san.py:67 #, python-format msgid "Could not find iSCSI export for volume %s" @@ -2288,10 +2233,6 @@ msgstr "" msgid "You must implement __call__" msgstr "" -#: ../bin/nova-instancemonitor.py:55 -msgid "Starting instance monitor" -msgstr "" - #: ../bin/nova-dhcpbridge.py:58 msgid "leasing ip" msgstr "" @@ -2871,3 +2812,24 @@ msgstr "" #, python-format msgid "Removing user %(user)s from project %(project)s" msgstr "" + +#~ msgid "Wrong number of arguments." +#~ msgstr "Wrong number of arguments." + +#~ msgid "No such process" +#~ msgstr "No such process" + +#~ msgid "Full set of FLAGS:" +#~ msgstr "Full set of FLAGS:" + +#, python-format +#~ msgid "pidfile %s does not exist. Daemon not running?\n" +#~ msgstr "pidfile %s does not exist. Daemon not running?\n" + +#, python-format +#~ msgid "Starting %s" +#~ msgstr "Starting %s" + +#, python-format +#~ msgid "Serving %s" +#~ msgstr "Serving %s" diff --git a/po/es.po b/po/es.po index 05edc00c4..7371eae8c 100644 --- a/po/es.po +++ b/po/es.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-08-02 04:52+0000\n" -"X-Generator: Launchpad (build 13552)\n" +"X-Launchpad-Export-Date: 2011-08-03 04:44+0000\n" +"X-Generator: Launchpad (build 13573)\n" #: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55 #: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110 @@ -130,33 +130,6 @@ msgstr "compute.api::suspend %s" msgid "compute.api::resume %s" msgstr "compute.api::resume %s" -#: ../nova/twistd.py:157 -msgid "Wrong number of arguments." -msgstr "Cantidad de argumentos incorrecta" - -#: ../nova/twistd.py:209 -#, python-format -msgid "pidfile %s does not exist. Daemon not running?\n" -msgstr "El \"pidfile\" %s no existe. Quizás el servicio no este corriendo.\n" - -#: ../nova/twistd.py:221 -msgid "No such process" -msgstr "No existe el proceso" - -#: ../nova/twistd.py:230 ../nova/service.py:224 -#, python-format -msgid "Serving %s" -msgstr "Sirviendo %s" - -#: ../nova/twistd.py:262 ../nova/service.py:225 -msgid "Full set of FLAGS:" -msgstr "Conjunto completo de opciones (FLAGS):" - -#: ../nova/twistd.py:266 -#, python-format -msgid "Starting %s" -msgstr "Iniciando %s" - #: ../nova/virt/xenapi/volumeops.py:48 ../nova/virt/xenapi/volumeops.py:101 #: ../nova/db/sqlalchemy/api.py:731 ../nova/virt/libvirt_conn.py:741 #: ../nova/api/ec2/__init__.py:317 @@ -1819,34 +1792,6 @@ msgstr "" msgid "Got exception: %s" msgstr "Obtenida excepción %s" -#: ../nova/compute/monitor.py:259 -#, python-format -msgid "updating %s..." -msgstr "actualizando %s..." - -#: ../nova/compute/monitor.py:289 -msgid "unexpected error during update" -msgstr "error inesperado durante la actualización" - -#: ../nova/compute/monitor.py:356 -#, python-format -msgid "Cannot get blockstats for \"%(disk)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:379 -#, python-format -msgid "Cannot get ifstats for \"%(interface)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:414 -msgid "unexpected exception getting connection" -msgstr "excepción inexperada al obtener la conexión" - -#: ../nova/compute/monitor.py:429 -#, python-format -msgid "Found instance: %s" -msgstr "Encontrada interfaz: %s" - #: ../nova/volume/san.py:67 #, python-format msgid "Could not find iSCSI export for volume %s" @@ -2309,10 +2254,6 @@ msgstr "" msgid "You must implement __call__" msgstr "" -#: ../bin/nova-instancemonitor.py:55 -msgid "Starting instance monitor" -msgstr "" - #: ../bin/nova-dhcpbridge.py:58 msgid "leasing ip" msgstr "" @@ -2938,6 +2879,10 @@ msgstr "Eliminando el usuario %(user)s del proyecto %(project)s" #~ msgstr "" #~ "El almacen de datos %s es inalcanzable. Reintentandolo en %d segundos." +#, python-format +#~ msgid "Serving %s" +#~ msgstr "Sirviendo %s" + #, python-format #~ msgid "Couldn't get IP, using 127.0.0.1 %s" #~ msgstr "No puedo obtener IP, usando 127.0.0.1 %s" @@ -3098,10 +3043,24 @@ msgstr "Eliminando el usuario %(user)s del proyecto %(project)s" #~ msgid "Detach volume %s from mountpoint %s on instance %s" #~ msgstr "Desvinculando volumen %s del punto de montaje %s en la instancia %s" +#~ msgid "unexpected exception getting connection" +#~ msgstr "excepción inexperada al obtener la conexión" + +#~ msgid "unexpected error during update" +#~ msgstr "error inesperado durante la actualización" + #, python-format #~ msgid "Cannot get blockstats for \"%s\" on \"%s\"" #~ msgstr "No puedo obtener estadísticas del bloque para \"%s\" en \"%s\"" +#, python-format +#~ msgid "updating %s..." +#~ msgstr "actualizando %s..." + +#, python-format +#~ msgid "Found instance: %s" +#~ msgstr "Encontrada interfaz: %s" + #, python-format #~ msgid "Cannot get ifstats for \"%s\" on \"%s\"" #~ msgstr "No puedo obtener estadísticas de la interfaz para \"%s\" en \"%s\"" @@ -3380,3 +3339,20 @@ msgstr "Eliminando el usuario %(user)s del proyecto %(project)s" #, python-format #~ msgid "Spawning VM %s created %s." #~ msgstr "Iniciando VM %s creado %s." + +#~ msgid "No such process" +#~ msgstr "No existe el proceso" + +#~ msgid "Full set of FLAGS:" +#~ msgstr "Conjunto completo de opciones (FLAGS):" + +#~ msgid "Wrong number of arguments." +#~ msgstr "Cantidad de argumentos incorrecta" + +#, python-format +#~ msgid "pidfile %s does not exist. Daemon not running?\n" +#~ msgstr "El \"pidfile\" %s no existe. Quizás el servicio no este corriendo.\n" + +#, python-format +#~ msgid "Starting %s" +#~ msgstr "Iniciando %s" diff --git a/po/fr.po b/po/fr.po index 9dd789b3c..7cb298a94 100644 --- a/po/fr.po +++ b/po/fr.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-07-23 05:12+0000\n" -"X-Generator: Launchpad (build 13405)\n" +"X-Launchpad-Export-Date: 2011-08-03 04:43+0000\n" +"X-Generator: Launchpad (build 13573)\n" #: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55 #: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110 @@ -133,35 +133,6 @@ msgstr "compute.api::suspend %s" msgid "compute.api::resume %s" msgstr "compute.api::resume %s" -#: ../nova/twistd.py:157 -msgid "Wrong number of arguments." -msgstr "Nombre d'arguments incorrect." - -#: ../nova/twistd.py:209 -#, python-format -msgid "pidfile %s does not exist. Daemon not running?\n" -msgstr "" -"Le fichier pid %s n'existe pas. Est-ce que le processus est en cours " -"d'exécution ?\n" - -#: ../nova/twistd.py:221 -msgid "No such process" -msgstr "Aucun processus de ce type" - -#: ../nova/twistd.py:230 ../nova/service.py:224 -#, python-format -msgid "Serving %s" -msgstr "En train de servir %s" - -#: ../nova/twistd.py:262 ../nova/service.py:225 -msgid "Full set of FLAGS:" -msgstr "Ensemble de propriétés complet :" - -#: ../nova/twistd.py:266 -#, python-format -msgid "Starting %s" -msgstr "Démarrage de %s" - #: ../nova/virt/xenapi/volumeops.py:48 ../nova/virt/xenapi/volumeops.py:101 #: ../nova/db/sqlalchemy/api.py:731 ../nova/virt/libvirt_conn.py:741 #: ../nova/api/ec2/__init__.py:317 @@ -1865,34 +1836,6 @@ msgstr "Tâche [%(name)s] %(task)s état : %(status)s %(error_info)s" msgid "Got exception: %s" msgstr "Reçu exception : %s" -#: ../nova/compute/monitor.py:259 -#, python-format -msgid "updating %s..." -msgstr "mise à jour %s..." - -#: ../nova/compute/monitor.py:289 -msgid "unexpected error during update" -msgstr "erreur inopinée pendant la ise à jour" - -#: ../nova/compute/monitor.py:356 -#, python-format -msgid "Cannot get blockstats for \"%(disk)s\" on \"%(iid)s\"" -msgstr "Ne peut pas récupérer blockstats pour \"%(disk)s\" sur \"%(iid)s\"" - -#: ../nova/compute/monitor.py:379 -#, python-format -msgid "Cannot get ifstats for \"%(interface)s\" on \"%(iid)s\"" -msgstr "Ne peut pas récupérer ifstats pour \"%(interface)s\" sur \"%(iid)s\"" - -#: ../nova/compute/monitor.py:414 -msgid "unexpected exception getting connection" -msgstr "erreur inopinée pendant la connexion" - -#: ../nova/compute/monitor.py:429 -#, python-format -msgid "Found instance: %s" -msgstr "Instance trouvée : %s" - #: ../nova/volume/san.py:67 #, python-format msgid "Could not find iSCSI export for volume %s" @@ -2373,10 +2316,6 @@ msgstr "Démarrage %(arg0)s sur %(host)s:%(port)s" msgid "You must implement __call__" msgstr "Vous devez implémenter __call__" -#: ../bin/nova-instancemonitor.py:55 -msgid "Starting instance monitor" -msgstr "Démarrage du superviseur d'instance" - #: ../bin/nova-dhcpbridge.py:58 msgid "leasing ip" msgstr "Allocation IP" @@ -2990,3 +2929,51 @@ msgstr "Ajout de l'utilisateur %(user)s au projet %(project)s" #, python-format msgid "Removing user %(user)s from project %(project)s" msgstr "Suppression de l'utilisateur %(user)s du projet %(project)s" + +#~ msgid "Wrong number of arguments." +#~ msgstr "Nombre d'arguments incorrect." + +#~ msgid "No such process" +#~ msgstr "Aucun processus de ce type" + +#, python-format +#~ msgid "Starting %s" +#~ msgstr "Démarrage de %s" + +#~ msgid "Full set of FLAGS:" +#~ msgstr "Ensemble de propriétés complet :" + +#, python-format +#~ msgid "pidfile %s does not exist. Daemon not running?\n" +#~ msgstr "" +#~ "Le fichier pid %s n'existe pas. Est-ce que le processus est en cours " +#~ "d'exécution ?\n" + +#, python-format +#~ msgid "Serving %s" +#~ msgstr "En train de servir %s" + +#, python-format +#~ msgid "Cannot get blockstats for \"%(disk)s\" on \"%(iid)s\"" +#~ msgstr "Ne peut pas récupérer blockstats pour \"%(disk)s\" sur \"%(iid)s\"" + +#, python-format +#~ msgid "Cannot get ifstats for \"%(interface)s\" on \"%(iid)s\"" +#~ msgstr "Ne peut pas récupérer ifstats pour \"%(interface)s\" sur \"%(iid)s\"" + +#~ msgid "unexpected error during update" +#~ msgstr "erreur inopinée pendant la ise à jour" + +#, python-format +#~ msgid "updating %s..." +#~ msgstr "mise à jour %s..." + +#, python-format +#~ msgid "Found instance: %s" +#~ msgstr "Instance trouvée : %s" + +#~ msgid "unexpected exception getting connection" +#~ msgstr "erreur inopinée pendant la connexion" + +#~ msgid "Starting instance monitor" +#~ msgstr "Démarrage du superviseur d'instance" diff --git a/po/it.po b/po/it.po index 60c3a388a..14e9a6de6 100644 --- a/po/it.po +++ b/po/it.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-07-23 05:12+0000\n" -"X-Generator: Launchpad (build 13405)\n" +"X-Launchpad-Export-Date: 2011-08-03 04:44+0000\n" +"X-Generator: Launchpad (build 13573)\n" #: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55 #: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110 @@ -134,34 +134,6 @@ msgstr "compute.api::suspend %s" msgid "compute.api::resume %s" msgstr "compute.api::resume %s" -#: ../nova/twistd.py:157 -msgid "Wrong number of arguments." -msgstr "Numero errato di argomenti" - -#: ../nova/twistd.py:209 -#, python-format -msgid "pidfile %s does not exist. Daemon not running?\n" -msgstr "" -"Il pidfile %s non esiste. Assicurarsi che il demone é in esecuzione.\n" - -#: ../nova/twistd.py:221 -msgid "No such process" -msgstr "Nessun processo trovato" - -#: ../nova/twistd.py:230 ../nova/service.py:224 -#, python-format -msgid "Serving %s" -msgstr "Servire %s" - -#: ../nova/twistd.py:262 ../nova/service.py:225 -msgid "Full set of FLAGS:" -msgstr "Insieme di FLAGS:" - -#: ../nova/twistd.py:266 -#, python-format -msgid "Starting %s" -msgstr "Avvio di %s" - #: ../nova/virt/xenapi/volumeops.py:48 ../nova/virt/xenapi/volumeops.py:101 #: ../nova/db/sqlalchemy/api.py:731 ../nova/virt/libvirt_conn.py:741 #: ../nova/api/ec2/__init__.py:317 @@ -1791,34 +1763,6 @@ msgstr "" msgid "Got exception: %s" msgstr "" -#: ../nova/compute/monitor.py:259 -#, python-format -msgid "updating %s..." -msgstr "" - -#: ../nova/compute/monitor.py:289 -msgid "unexpected error during update" -msgstr "" - -#: ../nova/compute/monitor.py:356 -#, python-format -msgid "Cannot get blockstats for \"%(disk)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:379 -#, python-format -msgid "Cannot get ifstats for \"%(interface)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:414 -msgid "unexpected exception getting connection" -msgstr "" - -#: ../nova/compute/monitor.py:429 -#, python-format -msgid "Found instance: %s" -msgstr "" - #: ../nova/volume/san.py:67 #, python-format msgid "Could not find iSCSI export for volume %s" @@ -2278,10 +2222,6 @@ msgstr "" msgid "You must implement __call__" msgstr "" -#: ../bin/nova-instancemonitor.py:55 -msgid "Starting instance monitor" -msgstr "" - #: ../bin/nova-dhcpbridge.py:58 msgid "leasing ip" msgstr "" @@ -2896,3 +2836,25 @@ msgstr "" #, python-format #~ msgid "Data store %s is unreachable. Trying again in %d seconds." #~ msgstr "Datastore %s é irrangiungibile. Riprovare in %d seconds." + +#~ msgid "Full set of FLAGS:" +#~ msgstr "Insieme di FLAGS:" + +#, python-format +#~ msgid "pidfile %s does not exist. Daemon not running?\n" +#~ msgstr "" +#~ "Il pidfile %s non esiste. Assicurarsi che il demone é in esecuzione.\n" + +#, python-format +#~ msgid "Starting %s" +#~ msgstr "Avvio di %s" + +#, python-format +#~ msgid "Serving %s" +#~ msgstr "Servire %s" + +#~ msgid "Wrong number of arguments." +#~ msgstr "Numero errato di argomenti" + +#~ msgid "No such process" +#~ msgstr "Nessun processo trovato" diff --git a/po/ja.po b/po/ja.po index 09d17af3b..179302b55 100644 --- a/po/ja.po +++ b/po/ja.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-07-23 05:12+0000\n" -"X-Generator: Launchpad (build 13405)\n" +"X-Launchpad-Export-Date: 2011-08-03 04:44+0000\n" +"X-Generator: Launchpad (build 13573)\n" #: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55 #: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110 @@ -130,33 +130,6 @@ msgstr "例外: compute.api::suspend %s" msgid "compute.api::resume %s" msgstr "例外: compute.api::resume %s" -#: ../nova/twistd.py:157 -msgid "Wrong number of arguments." -msgstr "引数の数が異なります。" - -#: ../nova/twistd.py:209 -#, python-format -msgid "pidfile %s does not exist. Daemon not running?\n" -msgstr "pidfile %s が存在しません。デーモンは実行中ですか?\n" - -#: ../nova/twistd.py:221 -msgid "No such process" -msgstr "そのようなプロセスはありません" - -#: ../nova/twistd.py:230 ../nova/service.py:224 -#, python-format -msgid "Serving %s" -msgstr "%s サービスの開始" - -#: ../nova/twistd.py:262 ../nova/service.py:225 -msgid "Full set of FLAGS:" -msgstr "FLAGSの一覧:" - -#: ../nova/twistd.py:266 -#, python-format -msgid "Starting %s" -msgstr "%s を起動中" - #: ../nova/virt/xenapi/volumeops.py:48 ../nova/virt/xenapi/volumeops.py:101 #: ../nova/db/sqlalchemy/api.py:731 ../nova/virt/libvirt_conn.py:741 #: ../nova/api/ec2/__init__.py:317 @@ -1808,34 +1781,6 @@ msgstr "タスク [%(name)s] %(task)s 状態: %(status)s %(error_info)s" msgid "Got exception: %s" msgstr "例外 %s が発生しました。" -#: ../nova/compute/monitor.py:259 -#, python-format -msgid "updating %s..." -msgstr "%s の情報の更新…" - -#: ../nova/compute/monitor.py:289 -msgid "unexpected error during update" -msgstr "更新の最中に予期しないエラーが発生しました。" - -#: ../nova/compute/monitor.py:356 -#, python-format -msgid "Cannot get blockstats for \"%(disk)s\" on \"%(iid)s\"" -msgstr "\"%(iid)s\" 上の \"%(disk)s\" 用のブロック統計(blockstats)が取得できません" - -#: ../nova/compute/monitor.py:379 -#, python-format -msgid "Cannot get ifstats for \"%(interface)s\" on \"%(iid)s\"" -msgstr "\"%(iid)s\" 上の %(interface)s\" 用インターフェース統計(ifstats)が取得できません" - -#: ../nova/compute/monitor.py:414 -msgid "unexpected exception getting connection" -msgstr "接続に際し予期しないエラーが発生しました。" - -#: ../nova/compute/monitor.py:429 -#, python-format -msgid "Found instance: %s" -msgstr "インスタンス %s が見つかりました。" - #: ../nova/volume/san.py:67 #, python-format msgid "Could not find iSCSI export for volume %s" @@ -2311,10 +2256,6 @@ msgstr "%(host)s:%(port)s 上で %(arg0)s を開始しています" msgid "You must implement __call__" msgstr "__call__ を実装しなければなりません" -#: ../bin/nova-instancemonitor.py:55 -msgid "Starting instance monitor" -msgstr "インスタンスモニタを開始しています" - #: ../bin/nova-dhcpbridge.py:58 msgid "leasing ip" msgstr "IP アドレスをリースしました" @@ -2937,6 +2878,17 @@ msgstr "ユーザ %(user)s をプロジェクト %(project)s から削除しま #~ msgid "Data store %s is unreachable. Trying again in %d seconds." #~ msgstr "データストア %s に接続できません。 %d 秒後に再接続します。" +#, python-format +#~ msgid "Serving %s" +#~ msgstr "%s サービスの開始" + +#~ msgid "Full set of FLAGS:" +#~ msgstr "FLAGSの一覧:" + +#, python-format +#~ msgid "pidfile %s does not exist. Daemon not running?\n" +#~ msgstr "pidfile %s が存在しません。デーモンは実行中ですか?\n" + #, python-format #~ msgid "Couldn't get IP, using 127.0.0.1 %s" #~ msgstr "IPを取得できません。127.0.0.1 を %s として使います。" @@ -3097,6 +3049,13 @@ msgstr "ユーザ %(user)s をプロジェクト %(project)s から削除しま #~ msgid "Detach volume %s from mountpoint %s on instance %s" #~ msgstr "Detach volume: ボリューム %s をマウントポイント %s (インスタンス%s)からデタッチします。" +#, python-format +#~ msgid "updating %s..." +#~ msgstr "%s の情報の更新…" + +#~ msgid "unexpected error during update" +#~ msgstr "更新の最中に予期しないエラーが発生しました。" + #, python-format #~ msgid "Cannot get blockstats for \"%s\" on \"%s\"" #~ msgstr "ブロックデバイス \"%s\" の統計を \"%s\" について取得できません。" @@ -3105,6 +3064,13 @@ msgstr "ユーザ %(user)s をプロジェクト %(project)s から削除しま #~ msgid "Cannot get ifstats for \"%s\" on \"%s\"" #~ msgstr "インタフェース \"%s\" の統計を \"%s\" について取得できません。" +#~ msgid "unexpected exception getting connection" +#~ msgstr "接続に際し予期しないエラーが発生しました。" + +#, python-format +#~ msgid "Found instance: %s" +#~ msgstr "インスタンス %s が見つかりました。" + #, python-format #~ msgid "No service for %s, %s" #~ msgstr "%s, %s のserviceが存在しません。" @@ -3377,3 +3343,24 @@ msgstr "ユーザ %(user)s をプロジェクト %(project)s から削除しま #, python-format #~ msgid "volume %s: creating lv of size %sG" #~ msgstr "ボリューム%sの%sGのlv (論理ボリューム) を作成します。" + +#~ msgid "Wrong number of arguments." +#~ msgstr "引数の数が異なります。" + +#~ msgid "No such process" +#~ msgstr "そのようなプロセスはありません" + +#, python-format +#~ msgid "Cannot get blockstats for \"%(disk)s\" on \"%(iid)s\"" +#~ msgstr "\"%(iid)s\" 上の \"%(disk)s\" 用のブロック統計(blockstats)が取得できません" + +#, python-format +#~ msgid "Cannot get ifstats for \"%(interface)s\" on \"%(iid)s\"" +#~ msgstr "\"%(iid)s\" 上の %(interface)s\" 用インターフェース統計(ifstats)が取得できません" + +#~ msgid "Starting instance monitor" +#~ msgstr "インスタンスモニタを開始しています" + +#, python-format +#~ msgid "Starting %s" +#~ msgstr "%s を起動中" diff --git a/po/pt_BR.po b/po/pt_BR.po index a166c862b..d6d57a9b1 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-07-26 04:53+0000\n" -"X-Generator: Launchpad (build 13405)\n" +"X-Launchpad-Export-Date: 2011-08-03 04:44+0000\n" +"X-Generator: Launchpad (build 13573)\n" #: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55 #: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110 @@ -131,34 +131,6 @@ msgstr "compute.api::suspend %s" msgid "compute.api::resume %s" msgstr "compute.api::resume %s" -#: ../nova/twistd.py:157 -msgid "Wrong number of arguments." -msgstr "Número errado de argumentos." - -#: ../nova/twistd.py:209 -#, python-format -msgid "pidfile %s does not exist. Daemon not running?\n" -msgstr "" -"Arquivo do id do processo (pidfile) %s não existe. O Daemon está parado?\n" - -#: ../nova/twistd.py:221 -msgid "No such process" -msgstr "Processo inexistente" - -#: ../nova/twistd.py:230 ../nova/service.py:224 -#, python-format -msgid "Serving %s" -msgstr "Servindo %s" - -#: ../nova/twistd.py:262 ../nova/service.py:225 -msgid "Full set of FLAGS:" -msgstr "Conjunto completo de FLAGS:" - -#: ../nova/twistd.py:266 -#, python-format -msgid "Starting %s" -msgstr "Iniciando %s" - #: ../nova/virt/xenapi/volumeops.py:48 ../nova/virt/xenapi/volumeops.py:101 #: ../nova/db/sqlalchemy/api.py:731 ../nova/virt/libvirt_conn.py:741 #: ../nova/api/ec2/__init__.py:317 @@ -1812,34 +1784,6 @@ msgstr "" msgid "Got exception: %s" msgstr "" -#: ../nova/compute/monitor.py:259 -#, python-format -msgid "updating %s..." -msgstr "" - -#: ../nova/compute/monitor.py:289 -msgid "unexpected error during update" -msgstr "" - -#: ../nova/compute/monitor.py:356 -#, python-format -msgid "Cannot get blockstats for \"%(disk)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:379 -#, python-format -msgid "Cannot get ifstats for \"%(interface)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:414 -msgid "unexpected exception getting connection" -msgstr "" - -#: ../nova/compute/monitor.py:429 -#, python-format -msgid "Found instance: %s" -msgstr "" - #: ../nova/volume/san.py:67 #, python-format msgid "Could not find iSCSI export for volume %s" @@ -2298,10 +2242,6 @@ msgstr "" msgid "You must implement __call__" msgstr "" -#: ../bin/nova-instancemonitor.py:55 -msgid "Starting instance monitor" -msgstr "" - #: ../bin/nova-dhcpbridge.py:58 msgid "leasing ip" msgstr "" @@ -2925,6 +2865,17 @@ msgstr "" #~ "Repositório de dados %s não pode ser atingido. Tentando novamente em %d " #~ "segundos." +#~ msgid "Full set of FLAGS:" +#~ msgstr "Conjunto completo de FLAGS:" + +#, python-format +#~ msgid "Starting %s" +#~ msgstr "Iniciando %s" + +#, python-format +#~ msgid "Serving %s" +#~ msgstr "Servindo %s" + #, python-format #~ msgid "Couldn't get IP, using 127.0.0.1 %s" #~ msgstr "Não foi possível obter IP, usando 127.0.0.1 %s" @@ -3033,3 +2984,14 @@ msgstr "" #, python-format #~ msgid "Created user %s (admin: %r)" #~ msgstr "Criado usuário %s (administrador: %r)" + +#~ msgid "No such process" +#~ msgstr "Processo inexistente" + +#, python-format +#~ msgid "pidfile %s does not exist. Daemon not running?\n" +#~ msgstr "" +#~ "Arquivo do id do processo (pidfile) %s não existe. O Daemon está parado?\n" + +#~ msgid "Wrong number of arguments." +#~ msgstr "Número errado de argumentos." diff --git a/po/ru.po b/po/ru.po index 5d8532f2e..746db964a 100644 --- a/po/ru.po +++ b/po/ru.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-07-23 05:12+0000\n" -"X-Generator: Launchpad (build 13405)\n" +"X-Launchpad-Export-Date: 2011-08-03 04:44+0000\n" +"X-Generator: Launchpad (build 13573)\n" #: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55 #: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110 @@ -125,33 +125,6 @@ msgstr "" msgid "compute.api::resume %s" msgstr "" -#: ../nova/twistd.py:157 -msgid "Wrong number of arguments." -msgstr "Неверное число аргументов." - -#: ../nova/twistd.py:209 -#, python-format -msgid "pidfile %s does not exist. Daemon not running?\n" -msgstr "pidfile %s не обнаружен. Демон не запущен?\n" - -#: ../nova/twistd.py:221 -msgid "No such process" -msgstr "" - -#: ../nova/twistd.py:230 ../nova/service.py:224 -#, python-format -msgid "Serving %s" -msgstr "" - -#: ../nova/twistd.py:262 ../nova/service.py:225 -msgid "Full set of FLAGS:" -msgstr "" - -#: ../nova/twistd.py:266 -#, python-format -msgid "Starting %s" -msgstr "Запускается %s" - #: ../nova/virt/xenapi/volumeops.py:48 ../nova/virt/xenapi/volumeops.py:101 #: ../nova/db/sqlalchemy/api.py:731 ../nova/virt/libvirt_conn.py:741 #: ../nova/api/ec2/__init__.py:317 @@ -1779,34 +1752,6 @@ msgstr "" msgid "Got exception: %s" msgstr "" -#: ../nova/compute/monitor.py:259 -#, python-format -msgid "updating %s..." -msgstr "обновление %s..." - -#: ../nova/compute/monitor.py:289 -msgid "unexpected error during update" -msgstr "неожиданная ошибка во время обновления" - -#: ../nova/compute/monitor.py:356 -#, python-format -msgid "Cannot get blockstats for \"%(disk)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:379 -#, python-format -msgid "Cannot get ifstats for \"%(interface)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:414 -msgid "unexpected exception getting connection" -msgstr "" - -#: ../nova/compute/monitor.py:429 -#, python-format -msgid "Found instance: %s" -msgstr "" - #: ../nova/volume/san.py:67 #, python-format msgid "Could not find iSCSI export for volume %s" @@ -2264,10 +2209,6 @@ msgstr "" msgid "You must implement __call__" msgstr "" -#: ../bin/nova-instancemonitor.py:55 -msgid "Starting instance monitor" -msgstr "" - #: ../bin/nova-dhcpbridge.py:58 msgid "leasing ip" msgstr "" @@ -2848,6 +2789,10 @@ msgstr "" msgid "Removing user %(user)s from project %(project)s" msgstr "" +#, python-format +#~ msgid "Starting %s" +#~ msgstr "Запускается %s" + #, python-format #~ msgid "arg: %s\t\tval: %s" #~ msgstr "arg: %s\t\tval: %s" @@ -2900,6 +2845,13 @@ msgstr "" #~ msgid "Adding role %s to user %s in project %s" #~ msgstr "Добавление роли %s для пользователя %s в проект %s" +#~ msgid "unexpected error during update" +#~ msgstr "неожиданная ошибка во время обновления" + +#, python-format +#~ msgid "updating %s..." +#~ msgstr "обновление %s..." + #, python-format #~ msgid "Getting object: %s / %s" #~ msgstr "Получение объекта: %s / %s" @@ -2950,6 +2902,10 @@ msgstr "" #~ msgid "Couldn't get IP, using 127.0.0.1 %s" #~ msgstr "Не удалось получить IP, используем 127.0.0.1 %s" +#, python-format +#~ msgid "pidfile %s does not exist. Daemon not running?\n" +#~ msgstr "pidfile %s не обнаружен. Демон не запущен?\n" + #, python-format #~ msgid "Getting from %s: %s" #~ msgstr "Получение из %s: %s" @@ -2965,3 +2921,6 @@ msgstr "" #, python-format #~ msgid "Authenticated Request For %s:%s)" #~ msgstr "Запрос аутентификации для %s:%s)" + +#~ msgid "Wrong number of arguments." +#~ msgstr "Неверное число аргументов." diff --git a/po/tl.po b/po/tl.po index 7f600e8f1..84e9d26e6 100644 --- a/po/tl.po +++ b/po/tl.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-07-23 05:12+0000\n" -"X-Generator: Launchpad (build 13405)\n" +"X-Launchpad-Export-Date: 2011-08-03 04:44+0000\n" +"X-Generator: Launchpad (build 13573)\n" #: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55 #: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110 @@ -125,33 +125,6 @@ msgstr "" msgid "compute.api::resume %s" msgstr "" -#: ../nova/twistd.py:157 -msgid "Wrong number of arguments." -msgstr "" - -#: ../nova/twistd.py:209 -#, python-format -msgid "pidfile %s does not exist. Daemon not running?\n" -msgstr "" - -#: ../nova/twistd.py:221 -msgid "No such process" -msgstr "" - -#: ../nova/twistd.py:230 ../nova/service.py:224 -#, python-format -msgid "Serving %s" -msgstr "" - -#: ../nova/twistd.py:262 ../nova/service.py:225 -msgid "Full set of FLAGS:" -msgstr "" - -#: ../nova/twistd.py:266 -#, python-format -msgid "Starting %s" -msgstr "" - #: ../nova/virt/xenapi/volumeops.py:48 ../nova/virt/xenapi/volumeops.py:101 #: ../nova/db/sqlalchemy/api.py:731 ../nova/virt/libvirt_conn.py:741 #: ../nova/api/ec2/__init__.py:317 @@ -1778,34 +1751,6 @@ msgstr "" msgid "Got exception: %s" msgstr "" -#: ../nova/compute/monitor.py:259 -#, python-format -msgid "updating %s..." -msgstr "" - -#: ../nova/compute/monitor.py:289 -msgid "unexpected error during update" -msgstr "" - -#: ../nova/compute/monitor.py:356 -#, python-format -msgid "Cannot get blockstats for \"%(disk)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:379 -#, python-format -msgid "Cannot get ifstats for \"%(interface)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:414 -msgid "unexpected exception getting connection" -msgstr "" - -#: ../nova/compute/monitor.py:429 -#, python-format -msgid "Found instance: %s" -msgstr "" - #: ../nova/volume/san.py:67 #, python-format msgid "Could not find iSCSI export for volume %s" @@ -2265,10 +2210,6 @@ msgstr "" msgid "You must implement __call__" msgstr "" -#: ../bin/nova-instancemonitor.py:55 -msgid "Starting instance monitor" -msgstr "" - #: ../bin/nova-dhcpbridge.py:58 msgid "leasing ip" msgstr "" diff --git a/po/uk.po b/po/uk.po index bbd831de9..bcc53fed3 100644 --- a/po/uk.po +++ b/po/uk.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-07-23 05:12+0000\n" -"X-Generator: Launchpad (build 13405)\n" +"X-Launchpad-Export-Date: 2011-08-03 04:44+0000\n" +"X-Generator: Launchpad (build 13573)\n" #: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55 #: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110 @@ -125,33 +125,6 @@ msgstr "" msgid "compute.api::resume %s" msgstr "" -#: ../nova/twistd.py:157 -msgid "Wrong number of arguments." -msgstr "" - -#: ../nova/twistd.py:209 -#, python-format -msgid "pidfile %s does not exist. Daemon not running?\n" -msgstr "" - -#: ../nova/twistd.py:221 -msgid "No such process" -msgstr "" - -#: ../nova/twistd.py:230 ../nova/service.py:224 -#, python-format -msgid "Serving %s" -msgstr "Обслуговування %s" - -#: ../nova/twistd.py:262 ../nova/service.py:225 -msgid "Full set of FLAGS:" -msgstr "" - -#: ../nova/twistd.py:266 -#, python-format -msgid "Starting %s" -msgstr "Запускається %s" - #: ../nova/virt/xenapi/volumeops.py:48 ../nova/virt/xenapi/volumeops.py:101 #: ../nova/db/sqlalchemy/api.py:731 ../nova/virt/libvirt_conn.py:741 #: ../nova/api/ec2/__init__.py:317 @@ -1778,34 +1751,6 @@ msgstr "" msgid "Got exception: %s" msgstr "" -#: ../nova/compute/monitor.py:259 -#, python-format -msgid "updating %s..." -msgstr "" - -#: ../nova/compute/monitor.py:289 -msgid "unexpected error during update" -msgstr "" - -#: ../nova/compute/monitor.py:356 -#, python-format -msgid "Cannot get blockstats for \"%(disk)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:379 -#, python-format -msgid "Cannot get ifstats for \"%(interface)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:414 -msgid "unexpected exception getting connection" -msgstr "" - -#: ../nova/compute/monitor.py:429 -#, python-format -msgid "Found instance: %s" -msgstr "" - #: ../nova/volume/san.py:67 #, python-format msgid "Could not find iSCSI export for volume %s" @@ -2263,10 +2208,6 @@ msgstr "" msgid "You must implement __call__" msgstr "" -#: ../bin/nova-instancemonitor.py:55 -msgid "Starting instance monitor" -msgstr "" - #: ../bin/nova-dhcpbridge.py:58 msgid "leasing ip" msgstr "" @@ -2851,6 +2792,14 @@ msgstr "" #~ msgid "AMQP server on %s:%d is unreachable. Trying again in %d seconds." #~ msgstr "AMQP сервер %s:%d недоступний. Спроба під'єднання через %d секунд." +#, python-format +#~ msgid "Starting %s" +#~ msgstr "Запускається %s" + +#, python-format +#~ msgid "Serving %s" +#~ msgstr "Обслуговування %s" + #, python-format #~ msgid "Couldn't get IP, using 127.0.0.1 %s" #~ msgstr "Не вдалось отримати IP, використовуючи 127.0.0.1 %s" diff --git a/po/zh_CN.po b/po/zh_CN.po index c3d292a93..de7eb41e0 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -14,13 +14,12 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-07-23 05:12+0000\n" -"X-Generator: Launchpad (build 13405)\n" +"X-Launchpad-Export-Date: 2011-08-03 04:44+0000\n" +"X-Generator: Launchpad (build 13573)\n" -#: ../nova/twistd.py:266 #, python-format -msgid "Starting %s" -msgstr "启动 %s 中" +#~ msgid "Starting %s" +#~ msgstr "启动 %s 中" #: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55 #: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110 @@ -135,28 +134,6 @@ msgstr "" msgid "compute.api::resume %s" msgstr "" -#: ../nova/twistd.py:157 -msgid "Wrong number of arguments." -msgstr "错误参数个数。" - -#: ../nova/twistd.py:209 -#, python-format -msgid "pidfile %s does not exist. Daemon not running?\n" -msgstr "pidfile %s 不存在,守护进程是否运行?\n" - -#: ../nova/twistd.py:221 -msgid "No such process" -msgstr "没有该进程" - -#: ../nova/twistd.py:230 ../nova/service.py:224 -#, python-format -msgid "Serving %s" -msgstr "正在为 %s 服务" - -#: ../nova/twistd.py:262 ../nova/service.py:225 -msgid "Full set of FLAGS:" -msgstr "FLAGS全集:" - #: ../nova/virt/xenapi/volumeops.py:48 ../nova/virt/xenapi/volumeops.py:101 #: ../nova/db/sqlalchemy/api.py:731 ../nova/virt/libvirt_conn.py:741 #: ../nova/api/ec2/__init__.py:317 @@ -1785,34 +1762,6 @@ msgstr "" msgid "Got exception: %s" msgstr "" -#: ../nova/compute/monitor.py:259 -#, python-format -msgid "updating %s..." -msgstr "" - -#: ../nova/compute/monitor.py:289 -msgid "unexpected error during update" -msgstr "" - -#: ../nova/compute/monitor.py:356 -#, python-format -msgid "Cannot get blockstats for \"%(disk)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:379 -#, python-format -msgid "Cannot get ifstats for \"%(interface)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:414 -msgid "unexpected exception getting connection" -msgstr "" - -#: ../nova/compute/monitor.py:429 -#, python-format -msgid "Found instance: %s" -msgstr "" - #: ../nova/volume/san.py:67 #, python-format msgid "Could not find iSCSI export for volume %s" @@ -2270,10 +2219,6 @@ msgstr "" msgid "You must implement __call__" msgstr "" -#: ../bin/nova-instancemonitor.py:55 -msgid "Starting instance monitor" -msgstr "" - #: ../bin/nova-dhcpbridge.py:58 msgid "leasing ip" msgstr "" @@ -2888,6 +2833,9 @@ msgstr "从项目 %(project)s 中移除用户 %(user)s" #~ msgid "Data store %s is unreachable. Trying again in %d seconds." #~ msgstr "数据储存服务%s不可用。%d秒之后继续尝试。" +#~ msgid "Full set of FLAGS:" +#~ msgstr "FLAGS全集:" + #, python-format #~ msgid "(%s) publish (key: %s) %s" #~ msgstr "(%s)发布(键值:%s)%s" @@ -2945,3 +2893,17 @@ msgstr "从项目 %(project)s 中移除用户 %(user)s" #, python-format #~ msgid "Removing role %s from user %s for project %s" #~ msgstr "正将角色%s从用户%s在工程%s中移除" + +#~ msgid "No such process" +#~ msgstr "没有该进程" + +#, python-format +#~ msgid "Serving %s" +#~ msgstr "正在为 %s 服务" + +#, python-format +#~ msgid "pidfile %s does not exist. Daemon not running?\n" +#~ msgstr "pidfile %s 不存在,守护进程是否运行?\n" + +#~ msgid "Wrong number of arguments." +#~ msgstr "错误参数个数。" diff --git a/po/zh_TW.po b/po/zh_TW.po index ad14c0e32..a5a826aa0 100644 --- a/po/zh_TW.po +++ b/po/zh_TW.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-07-23 05:12+0000\n" -"X-Generator: Launchpad (build 13405)\n" +"X-Launchpad-Export-Date: 2011-08-03 04:44+0000\n" +"X-Generator: Launchpad (build 13573)\n" #: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55 #: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110 @@ -125,33 +125,6 @@ msgstr "" msgid "compute.api::resume %s" msgstr "" -#: ../nova/twistd.py:157 -msgid "Wrong number of arguments." -msgstr "" - -#: ../nova/twistd.py:209 -#, python-format -msgid "pidfile %s does not exist. Daemon not running?\n" -msgstr "pidfile %s 不存在. Daemon未啟動?\n" - -#: ../nova/twistd.py:221 -msgid "No such process" -msgstr "沒有此一程序" - -#: ../nova/twistd.py:230 ../nova/service.py:224 -#, python-format -msgid "Serving %s" -msgstr "" - -#: ../nova/twistd.py:262 ../nova/service.py:225 -msgid "Full set of FLAGS:" -msgstr "" - -#: ../nova/twistd.py:266 -#, python-format -msgid "Starting %s" -msgstr "正在啟動 %s" - #: ../nova/virt/xenapi/volumeops.py:48 ../nova/virt/xenapi/volumeops.py:101 #: ../nova/db/sqlalchemy/api.py:731 ../nova/virt/libvirt_conn.py:741 #: ../nova/api/ec2/__init__.py:317 @@ -1778,34 +1751,6 @@ msgstr "" msgid "Got exception: %s" msgstr "" -#: ../nova/compute/monitor.py:259 -#, python-format -msgid "updating %s..." -msgstr "" - -#: ../nova/compute/monitor.py:289 -msgid "unexpected error during update" -msgstr "" - -#: ../nova/compute/monitor.py:356 -#, python-format -msgid "Cannot get blockstats for \"%(disk)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:379 -#, python-format -msgid "Cannot get ifstats for \"%(interface)s\" on \"%(iid)s\"" -msgstr "" - -#: ../nova/compute/monitor.py:414 -msgid "unexpected exception getting connection" -msgstr "" - -#: ../nova/compute/monitor.py:429 -#, python-format -msgid "Found instance: %s" -msgstr "" - #: ../nova/volume/san.py:67 #, python-format msgid "Could not find iSCSI export for volume %s" @@ -2263,10 +2208,6 @@ msgstr "" msgid "You must implement __call__" msgstr "" -#: ../bin/nova-instancemonitor.py:55 -msgid "Starting instance monitor" -msgstr "" - #: ../bin/nova-dhcpbridge.py:58 msgid "leasing ip" msgstr "" @@ -2846,3 +2787,14 @@ msgstr "" #, python-format msgid "Removing user %(user)s from project %(project)s" msgstr "" + +#~ msgid "No such process" +#~ msgstr "沒有此一程序" + +#, python-format +#~ msgid "pidfile %s does not exist. Daemon not running?\n" +#~ msgstr "pidfile %s 不存在. Daemon未啟動?\n" + +#, python-format +#~ msgid "Starting %s" +#~ msgstr "正在啟動 %s" -- cgit From fa340e03bf33105053160d1d10973f5ea34cb0da Mon Sep 17 00:00:00 2001 From: Launchpad Translations on behalf of nova-core <> Date: Fri, 5 Aug 2011 04:47:13 +0000 Subject: Launchpad automatic translations update. --- po/zh_CN.po | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/po/zh_CN.po b/po/zh_CN.po index de7eb41e0..d2caff810 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: nova\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2011-02-21 10:03-0500\n" -"PO-Revision-Date: 2011-06-14 14:44+0000\n" -"Last-Translator: chong \n" +"PO-Revision-Date: 2011-08-04 05:50+0000\n" +"Last-Translator: zhangjunfeng \n" "Language-Team: Chinese (Simplified) \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-08-03 04:44+0000\n" -"X-Generator: Launchpad (build 13573)\n" +"X-Launchpad-Export-Date: 2011-08-05 04:47+0000\n" +"X-Generator: Launchpad (build 13604)\n" #, python-format #~ msgid "Starting %s" @@ -2670,12 +2670,12 @@ msgstr "用户 %s 不存在" #: ../nova/auth/ldapdriver.py:472 #, python-format msgid "Group can't be created because group %s already exists" -msgstr "" +msgstr "组不能被创建,因为组 %s 已经存在" #: ../nova/auth/ldapdriver.py:478 #, python-format msgid "Group can't be created because user %s doesn't exist" -msgstr "" +msgstr "组不能被创建,因为用户 %s 不存在" #: ../nova/auth/ldapdriver.py:495 #, python-format @@ -2690,50 +2690,50 @@ msgstr "" #: ../nova/auth/ldapdriver.py:510 ../nova/auth/ldapdriver.py:521 #, python-format msgid "The group at dn %s doesn't exist" -msgstr "" +msgstr "识别名为 %s 的组不存在" #: ../nova/auth/ldapdriver.py:513 #, python-format msgid "User %(uid)s is already a member of the group %(group_dn)s" -msgstr "" +msgstr "用户 %(uid)s 已经是 组 %(group_dn)s 中的成员" #: ../nova/auth/ldapdriver.py:524 #, python-format msgid "" "User %s can't be removed from the group because the user doesn't exist" -msgstr "" +msgstr "用户 %s 不能从组中删除,因为这个用户不存在" #: ../nova/auth/ldapdriver.py:528 #, python-format msgid "User %s is not a member of the group" -msgstr "" +msgstr "用户 %s 不是这个组的成员" #: ../nova/auth/ldapdriver.py:542 #, python-format msgid "" "Attempted to remove the last member of a group. Deleting the group at %s " "instead." -msgstr "" +msgstr "尝试删除组中最后一个成员,用删除组 %s 来代替。" #: ../nova/auth/ldapdriver.py:549 #, python-format msgid "User %s can't be removed from all because the user doesn't exist" -msgstr "" +msgstr "用户 %s 不能从系统中删除,因为这个用户不存在" #: ../nova/auth/ldapdriver.py:564 #, python-format msgid "Group at dn %s doesn't exist" -msgstr "" +msgstr "可识别名为 %s 的组不存在" #: ../nova/virt/xenapi/network_utils.py:40 #, python-format msgid "Found non-unique network for bridge %s" -msgstr "" +msgstr "发现网桥 %s 的网络不唯一" #: ../nova/virt/xenapi/network_utils.py:43 #, python-format msgid "Found no network for bridge %s" -msgstr "" +msgstr "发现网桥 %s 没有网络" #: ../nova/api/ec2/admin.py:97 #, python-format @@ -2748,22 +2748,22 @@ msgstr "删除用户: %s" #: ../nova/api/ec2/admin.py:127 #, python-format msgid "Adding role %(role)s to user %(user)s for project %(project)s" -msgstr "" +msgstr "添加角色 %(role)s 给项目 %(project)s 中的用户 %(user)s" #: ../nova/api/ec2/admin.py:131 #, python-format msgid "Adding sitewide role %(role)s to user %(user)s" -msgstr "" +msgstr "给用户 %(user)s 添加站点角色 %(role)s" #: ../nova/api/ec2/admin.py:137 #, python-format msgid "Removing role %(role)s from user %(user)s for project %(project)s" -msgstr "" +msgstr "删除项目 %(project)s中用户 %(user)s的角色 %(role)s" #: ../nova/api/ec2/admin.py:141 #, python-format msgid "Removing sitewide role %(role)s from user %(user)s" -msgstr "" +msgstr "删除用户 %(user)s 的站点角色 %(role)s" #: ../nova/api/ec2/admin.py:146 ../nova/api/ec2/admin.py:223 msgid "operation must be add or remove" @@ -2772,22 +2772,22 @@ msgstr "操作必须为添加或删除" #: ../nova/api/ec2/admin.py:159 #, python-format msgid "Getting x509 for user: %(name)s on project: %(project)s" -msgstr "" +msgstr "获得用户: %(name)s 在项目 :%(project)s中的x509" #: ../nova/api/ec2/admin.py:177 #, python-format msgid "Create project %(name)s managed by %(manager_user)s" -msgstr "" +msgstr "创建被%(manager_user)s 管理的项目 %(name)s" #: ../nova/api/ec2/admin.py:190 #, python-format msgid "Modify project: %(name)s managed by %(manager_user)s" -msgstr "" +msgstr "更改被 %(manager_user)s 管理的项目: %(name)s" #: ../nova/api/ec2/admin.py:200 #, python-format msgid "Delete project: %s" -msgstr "删除工程 %s" +msgstr "" #: ../nova/api/ec2/admin.py:214 #, python-format -- cgit From fb0b82c0d6af2d67ec9a88842d857b558eaec5d1 Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Fri, 5 Aug 2011 15:00:31 -0700 Subject: Refactored code to reduce lines of code and changed method signature --- nova/db/api.py | 12 ++++++------ nova/db/sqlalchemy/api.py | 14 ++------------ nova/network/manager.py | 31 +++++++++++++++++-------------- nova/tests/test_network.py | 32 ++++++++++++++++---------------- 4 files changed, 41 insertions(+), 48 deletions(-) diff --git a/nova/db/api.py b/nova/db/api.py index b98859ef9..789e9bc97 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -710,9 +710,9 @@ def network_get_all(context): return IMPL.network_get_all(context) -def network_get_requested_networks(context, requested_networks): - """Return all defined networks.""" - return IMPL.network_get_requested_networks(context, requested_networks) +def network_get_networks_by_ids(context, network_ids): + """Return networks by ids.""" + return IMPL.network_get_networks_by_ids(context, network_ids) # pylint: disable=C0103 @@ -1262,14 +1262,14 @@ def project_get_networks(context, project_id, associate=True): return IMPL.project_get_networks(context, project_id, associate) -def project_get_requested_networks(context, requested_networks): - """Return the network associated with the project. +def project_get_networks_by_ids(context, network_ids): + """Return the networks by ids associated with the project. If associate is true, it will attempt to associate a new network if one is not found, otherwise it returns None. """ - return IMPL.project_get_requested_networks(context, requested_networks) + return IMPL.project_get_networks_by_ids(context, network_ids) def project_get_networks_v6(context, project_id): diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 5de720205..63964a193 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1701,13 +1701,8 @@ def network_get_all(context): @require_admin_context -def network_get_requested_networks(context, requested_networks): +def network_get_networks_by_ids(context, network_ids): session = get_session() - - network_ids = [] - for id, fixed_ip in requested_networks: - network_ids.append(id) - result = session.query(models.Network).\ filter(models.Network.id.in_(network_ids)).\ filter_by(deleted=False).all() @@ -2873,13 +2868,8 @@ def project_get_networks(context, project_id, associate=True): @require_context -def project_get_requested_networks(context, requested_networks): +def project_get_networks_by_ids(context, network_ids): session = get_session() - - network_ids = [] - for id, fixed_ip in requested_networks: - network_ids.append(id) - result = session.query(models.Network).\ filter(models.Network.id.in_(network_ids)).\ filter_by(deleted=False).\ diff --git a/nova/network/manager.py b/nova/network/manager.py index 655d5800a..b5c961e2d 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -134,10 +134,9 @@ class RPCAllocateFixedIP(object): for network in networks: address = None if requested_networks is not None: - for id, fixed_ip in requested_networks: - if (network['id'] == id): - address = fixed_ip - break + for address in (fixed_ip for (id, fixed_ip) in \ + requested_networks if network['id'] == id): + break # NOTE(vish): if we are not multi_host pass to the network host if not network['multi_host']: @@ -387,8 +386,9 @@ class NetworkManager(manager.SchedulerDependentManager): # there is a better way to determine which networks # a non-vlan instance should connect to if requested_networks is not None and len(requested_networks) != 0: - networks = self.db.network_get_requested_networks(context, - requested_networks) + network_ids = [id for (id, fixed_ip) in requested_networks] + networks = self.db.network_get_networks_by_ids(context, + network_ids) else: try: networks = self.db.network_get_all(context) @@ -743,7 +743,8 @@ class NetworkManager(manager.SchedulerDependentManager): if networks is None or len(networks) == 0: return - result = self.db.network_get_requested_networks(context, networks) + network_ids = [id for (id, fixed_ip) in networks] + result = self.db.network_get_networks_by_ids(context, network_ids) for network_id, fixed_ip in networks: # check if the fixed IP address is valid and # it actually belongs to the network @@ -793,10 +794,10 @@ class FlatManager(NetworkManager): for network in networks: address = None if requested_networks is not None: - for id, fixed_ip in requested_networks: - if (network['id'] == id): - address = fixed_ip - break + for address in (fixed_ip for (id, fixed_ip) in \ + requested_networks if network['id'] == id): + break + self.allocate_fixed_ip(context, instance_id, network, address=address) @@ -913,8 +914,9 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): """Determine which networks an instance should connect to.""" # get networks associated with project if requested_networks is not None and len(requested_networks) != 0: - networks = self.db.project_get_requested_networks(context, - requested_networks) + network_ids = [id for (id, fixed_ip) in requested_networks] + networks = self.db.project_get_networks_by_ids(context, + network_ids) else: networks = self.db.project_get_networks(context, project_id) return networks @@ -973,7 +975,8 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): if networks is None or len(networks) == 0: return - result = self.db.project_get_requested_networks(context, networks) + network_ids = [id for (id, fixed_ip) in networks] + result = self.db.project_get_networks_by_ids(context, network_ids) for network_id, fixed_ip in networks: # check if the fixed IP address is valid and diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index 53abbeb4f..9608121bb 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -181,11 +181,11 @@ class FlatNetworkTestCase(test.TestCase): self.assertDictListMatch(nw[1]['ips'], check) def test_validate_networks(self): - self.mox.StubOutWithMock(db, 'network_get_requested_networks') + self.mox.StubOutWithMock(db, 'network_get_networks_by_ids') self.mox.StubOutWithMock(db, "fixed_ip_validate_by_network_address") requested_networks = [(1, "192.168.0.100")] - db.network_get_requested_networks(mox.IgnoreArg(), + db.network_get_networks_by_ids(mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(networks) db.fixed_ip_validate_by_network_address(mox.IgnoreArg(), mox.IgnoreArg(), @@ -204,9 +204,9 @@ class FlatNetworkTestCase(test.TestCase): self.network.validate_networks(None, requested_networks) def test_validate_networks_invalid_fixed_ip(self): - self.mox.StubOutWithMock(db, 'network_get_requested_networks') + self.mox.StubOutWithMock(db, 'network_get_networks_by_ids') requested_networks = [(1, "192.168.0.100.1")] - db.network_get_requested_networks(mox.IgnoreArg(), + db.network_get_networks_by_ids(mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(networks) self.mox.ReplayAll() @@ -215,10 +215,10 @@ class FlatNetworkTestCase(test.TestCase): requested_networks) def test_validate_networks_empty_fixed_ip(self): - self.mox.StubOutWithMock(db, 'network_get_requested_networks') + self.mox.StubOutWithMock(db, 'network_get_networks_by_ids') requested_networks = [(1, "")] - db.network_get_requested_networks(mox.IgnoreArg(), + db.network_get_networks_by_ids(mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(networks) self.mox.ReplayAll() @@ -227,10 +227,10 @@ class FlatNetworkTestCase(test.TestCase): None, requested_networks) def test_validate_networks_none_fixed_ip(self): - self.mox.StubOutWithMock(db, 'network_get_requested_networks') + self.mox.StubOutWithMock(db, 'network_get_networks_by_ids') requested_networks = [(1, None)] - db.network_get_requested_networks(mox.IgnoreArg(), + db.network_get_networks_by_ids(mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(networks) self.mox.ReplayAll() @@ -294,11 +294,11 @@ class VlanNetworkTestCase(test.TestCase): cidr='192.168.0.1/24', network_size=100) def test_validate_networks(self): - self.mox.StubOutWithMock(db, 'project_get_requested_networks') + self.mox.StubOutWithMock(db, 'project_get_networks_by_ids') self.mox.StubOutWithMock(db, "fixed_ip_validate_by_network_address") requested_networks = [(1, "192.168.0.100")] - db.project_get_requested_networks(mox.IgnoreArg(), + db.project_get_networks_by_ids(mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(networks) db.fixed_ip_validate_by_network_address(mox.IgnoreArg(), mox.IgnoreArg(), @@ -315,10 +315,10 @@ class VlanNetworkTestCase(test.TestCase): self.network.validate_networks(None, requested_networks) def test_validate_networks_invalid_fixed_ip(self): - self.mox.StubOutWithMock(db, 'project_get_requested_networks') + self.mox.StubOutWithMock(db, 'project_get_networks_by_ids') requested_networks = [(1, "192.168.0.100.1")] - db.project_get_requested_networks(mox.IgnoreArg(), + db.project_get_networks_by_ids(mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(networks) self.mox.ReplayAll() @@ -327,10 +327,10 @@ class VlanNetworkTestCase(test.TestCase): None, requested_networks) def test_validate_networks_empty_fixed_ip(self): - self.mox.StubOutWithMock(db, 'project_get_requested_networks') + self.mox.StubOutWithMock(db, 'project_get_networks_by_ids') requested_networks = [(1, "")] - db.project_get_requested_networks(mox.IgnoreArg(), + db.project_get_networks_by_ids(mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(networks) self.mox.ReplayAll() @@ -339,10 +339,10 @@ class VlanNetworkTestCase(test.TestCase): None, requested_networks) def test_validate_networks_none_fixed_ip(self): - self.mox.StubOutWithMock(db, 'project_get_requested_networks') + self.mox.StubOutWithMock(db, 'project_get_networks_by_ids') requested_networks = [(1, None)] - db.project_get_requested_networks(mox.IgnoreArg(), + db.project_get_networks_by_ids(mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(networks) self.mox.ReplayAll() -- cgit From f22cfa05f7c796fbda3d832e4bfadc325f8af6f5 Mon Sep 17 00:00:00 2001 From: Christopher MacGown Date: Thu, 11 Aug 2011 17:40:13 -0700 Subject: Updates to libvirt, write metadata, net, and key to the config drive --- nova/network/manager.py | 3 ++- nova/virt/disk.py | 33 +++++++++++++++++++++----- nova/virt/libvirt.xml.template | 14 +++++------ nova/virt/libvirt/connection.py | 51 ++++++++++++++++++++++++++--------------- 4 files changed, 68 insertions(+), 33 deletions(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index 75c3f668d..44bf662ce 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -893,7 +893,6 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): def _setup_network(self, context, network_ref): """Sets up network on this host.""" - network_ref['dhcp_server'] = self._get_dhcp_ip(context, network_ref) if not network_ref['vpn_public_address']: net = {} address = FLAGS.vpn_ip @@ -901,6 +900,8 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): network_ref = db.network_update(context, network_ref['id'], net) else: address = network_ref['vpn_public_address'] + + network_ref['dhcp_server'] = self._get_dhcp_ip(context, network_ref) self.driver.ensure_vlan_bridge(network_ref['vlan'], network_ref['bridge'], network_ref['bridge_interface'], diff --git a/nova/virt/disk.py b/nova/virt/disk.py index f8aea1f34..fda3f5f29 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -2,6 +2,9 @@ # Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. +# +# Copyright 2011, Piston Cloud Computing, Inc. +# # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -22,6 +25,7 @@ Includes injection of SSH PGP keys into authorized_keys file. """ +import json import os import tempfile import time @@ -60,7 +64,8 @@ def extend(image, size): utils.execute('resize2fs', image, check_exit_code=False) -def inject_data(image, key=None, net=None, partition=None, nbd=False): +def inject_data(image, key=None, net=None, metadata=None, + partition=None, nbd=False, tune2fs=True): """Injects a ssh key and optionally net data into a disk image. it will mount the image as a fully partitioned disk and attempt to inject @@ -89,9 +94,10 @@ def inject_data(image, key=None, net=None, partition=None, nbd=False): ' only inject raw disk images): %s' % mapped_device) - # Configure ext2fs so that it doesn't auto-check every N boots - out, err = utils.execute('sudo', 'tune2fs', - '-c', 0, '-i', 0, mapped_device) + if tune2fs: + # Configure ext2fs so that it doesn't auto-check every N boots + out, err = utils.execute('sudo', 'tune2fs', + '-c', 0, '-i', 0, mapped_device) tmpdir = tempfile.mkdtemp() try: @@ -103,7 +109,8 @@ def inject_data(image, key=None, net=None, partition=None, nbd=False): % err) try: - inject_data_into_fs(tmpdir, key, net, utils.execute) + inject_data_into_fs(tmpdir, key, net, metadata, + utils.execute) finally: # unmount device utils.execute('sudo', 'umount', mapped_device) @@ -155,6 +162,7 @@ def destroy_container(target, instance, nbd=False): def _link_device(image, nbd): """Link image to device using loopback or nbd""" + if nbd: device = _allocate_device() utils.execute('sudo', 'qemu-nbd', '-c', device, image) @@ -189,6 +197,7 @@ def _allocate_device(): # NOTE(vish): This assumes no other processes are allocating nbd devices. # It may race cause a race condition if multiple # workers are running on a given machine. + while True: if not _DEVICES: raise exception.Error(_('No free nbd devices')) @@ -202,7 +211,7 @@ def _free_device(device): _DEVICES.append(device) -def inject_data_into_fs(fs, key, net, execute): +def inject_data_into_fs(fs, key, net, metadata, execute): """Injects data into a filesystem already mounted by the caller. Virt connections can call this directly if they mount their fs in a different way to inject_data @@ -211,7 +220,19 @@ def inject_data_into_fs(fs, key, net, execute): _inject_key_into_fs(key, fs, execute=execute) if net: _inject_net_into_fs(net, fs, execute=execute) + if metadata: + _inject_metadata_into_fs(metadata, fs, execute=execute) + +def _inject_file_into_fs(injected_files, fs, execute=None): + for path, data in injected_files: + + +def _inject_metadata_into_fs(metadata, fs, execute=None): + metadata_path = os.path.join(fs, "meta.js") + metadata = dict([(m.key, m.value) for m in metadata]) + utils.execute('sudo', 'tee', '-a', metadata_path, + process_input=json.dumps(metadata)) def _inject_key_into_fs(key, fs, execute=None): """Add the given public ssh key to root's authorized_keys. diff --git a/nova/virt/libvirt.xml.template b/nova/virt/libvirt.xml.template index 4422f349f..1fa536ad1 100644 --- a/nova/virt/libvirt.xml.template +++ b/nova/virt/libvirt.xml.template @@ -55,13 +55,6 @@ #else - #if $getVar('config', False) - - - - - - #end if #if $getVar('rescue', False) @@ -96,6 +89,13 @@ #end for #end if + #if $getVar('config_drive', False) + + + + + + #end if #end if #for $nic in $nics diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index ad97dc796..35ce0dcde 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -781,6 +781,7 @@ class LibvirtConnection(driver.ComputeDriver): network_info=None, block_device_mapping=None): block_device_mapping = block_device_mapping or [] + if not suffix: suffix = '' @@ -857,7 +858,7 @@ class LibvirtConnection(driver.ComputeDriver): target=basepath('disk.local'), fname="local_%s" % inst_type['local_gb'], cow=FLAGS.use_cow_images, - local_gb=inst_type['local_gb']) + local_size=inst_type['local_gb']) # For now, we assume that if we're not using a kernel, we're using a # partitioned disk image where the target partition is the first @@ -866,20 +867,23 @@ class LibvirtConnection(driver.ComputeDriver): if not inst['kernel_id']: target_partition = "1" - if FLAGS.libvirt_type == 'lxc': + config_drive_id = inst.get('config_drive_id') + config_drive = inst.get('config_drive') + + if any((FLAGS.libvirt_type == 'lxc', config_drive, config_drive_id)): target_partition = None - else: - if inst['config_drive_id']: - fname = '%08x' % int(inst['config_drive_id']) - self._cache_image(fn=self._fetch_image, - target=basepath('config'), - fname=fname, - image_id=inst['config_drive_id'], - user=user, - project=project) - elif inst['config_drive']: - self._create_local(basepath('config'), 64, prefix="M", - fs_format='msdos') # 64MB + + if config_drive_id: + fname = '%08x' % int(config_drive_id) + self._cache_image(fn=self._fetch_image, + target=basepath('disk.config'), + fname=fname, + image_id=config_drive_id, + user=user, + project=project) + elif config_drive: + self._create_local(basepath('disk.config'), 64, prefix="M", + fs_format='msdos') # 64MB if inst['key_data']: key = str(inst['key_data']) @@ -924,15 +928,18 @@ class LibvirtConnection(driver.ComputeDriver): searchList=[{'interfaces': nets, 'use_ipv6': FLAGS.use_ipv6}])) - if any(key, net, inst['metadata']): + metadata = inst.get('metadata') + if any((key, net, metadata)): inst_name = inst['name'] - if inst['config_drive']: # Should be True or None by now. - injection_path = basepath('config') + if config_drive: # Should be True or None by now. + injection_path = basepath('disk.config') img_id = 'config-drive' + tune2fs = False else: injection_path = basepath('disk') img_id = inst.image_ref + tune2fs = True for injection in ('metadata', 'key', 'net'): if locals()[injection]: @@ -940,9 +947,10 @@ class LibvirtConnection(driver.ComputeDriver): '%(injection)s into image %(img_id)s' % locals())) try: - disk.inject_data(injection_path, key, net, inst['metadata'], + disk.inject_data(injection_path, key, net, metadata, partition=target_partition, - nbd=FLAGS.use_cow_images) + nbd=FLAGS.use_cow_images, + tune2fs=tune2fs) if FLAGS.libvirt_type == 'lxc': disk.setup_container(basepath('disk'), @@ -1043,6 +1051,11 @@ class LibvirtConnection(driver.ComputeDriver): 'ebs_root': ebs_root, 'volumes': block_device_mapping} + + config_drive = False + if instance.get('config_drive') or instance.get('config_drive_id'): + xml_info['config_drive'] = xml_info['basepath'] + "/disk.config" + if FLAGS.vnc_enabled and FLAGS.libvirt_type not in ('lxc', 'uml'): xml_info['vncserver_host'] = FLAGS.vncserver_host xml_info['vnc_keymap'] = FLAGS.vnc_keymap -- cgit From b776f19c21d1a56ac851435182c0c267166d49dd Mon Sep 17 00:00:00 2001 From: Christopher MacGown Date: Thu, 11 Aug 2011 17:59:41 -0700 Subject: Accidentally added inject_files to merge --- nova/virt/disk.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index fda3f5f29..63e09d014 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -223,9 +223,6 @@ def inject_data_into_fs(fs, key, net, metadata, execute): if metadata: _inject_metadata_into_fs(metadata, fs, execute=execute) -def _inject_file_into_fs(injected_files, fs, execute=None): - for path, data in injected_files: - def _inject_metadata_into_fs(metadata, fs, execute=None): metadata_path = os.path.join(fs, "meta.js") @@ -234,6 +231,7 @@ def _inject_metadata_into_fs(metadata, fs, execute=None): utils.execute('sudo', 'tee', '-a', metadata_path, process_input=json.dumps(metadata)) + def _inject_key_into_fs(key, fs, execute=None): """Add the given public ssh key to root's authorized_keys. -- cgit From 91eaa647506a2e343e8c689289529eafea0bc9d3 Mon Sep 17 00:00:00 2001 From: Christopher MacGown Date: Fri, 12 Aug 2011 14:33:27 -0700 Subject: Fix ugly little violations before someone says anything --- nova/api/openstack/create_instance_helper.py | 2 -- run_tests.sh | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 0ec455167..d776ae92d 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -178,8 +178,6 @@ class CreateInstanceHelper(object): def _handle_quota_error(self, error): """ Reraise quota errors as api-specific http exceptions - - """ if error.code == "OnsetFileLimitExceeded": expl = _("Personality file limit exceeded") diff --git a/run_tests.sh b/run_tests.sh index ba15242b6..871332b4a 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -67,7 +67,7 @@ function run_tests { ERRSIZE=`wc -l run_tests.log | awk '{print \$1}'` if [ "$ERRSIZE" -lt "40" ]; then - echo cat run_tests.log + cat run_tests.log fi fi return $RESULT -- cgit From 9081e8b62ea01828238ecaebdcf3e627ada3fe9a Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Tue, 16 Aug 2011 16:04:18 -0700 Subject: Added uuid for networks and made changes to the Create server API format to accept network as uuid instead of id --- bin/nova-manage | 37 +++++------ nova/api/openstack/create_instance_helper.py | 39 +++++------ nova/db/api.py | 23 ++----- nova/db/sqlalchemy/api.py | 40 +++--------- .../versions/037_add_uuid_to_networks.py | 43 +++++++++++++ nova/db/sqlalchemy/models.py | 1 + nova/exception.py | 4 +- nova/network/manager.py | 67 +++++++++++-------- .../api/openstack/contrib/test_createserverext.py | 75 ++++++++++------------ nova/tests/api/openstack/test_servers.py | 38 +++++------ nova/tests/test_network.py | 72 ++++++++++++--------- 11 files changed, 230 insertions(+), 209 deletions(-) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/037_add_uuid_to_networks.py diff --git a/bin/nova-manage b/bin/nova-manage index 41433f1d6..9592d5132 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -760,25 +760,26 @@ class NetworkCommands(object): def list(self): """List all created networks""" - print "%-5s\t%-18s\t%-15s\t%-15s\t%-15s\t%-15s\t%-15s\t%-15s" % ( - _('id'), - _('IPv4'), - _('IPv6'), - _('start address'), - _('DNS1'), - _('DNS2'), - _('VlanID'), - 'project') + _fmt = "%-5s\t%-18s\t%-15s\t%-15s\t%-15s\t%-15s\t%-15s\t%-15s\t%-15s" + print _fmt % (_('id'), + _('IPv4'), + _('IPv6'), + _('start address'), + _('DNS1'), + _('DNS2'), + _('VlanID'), + _('project'), + _("uuid")) for network in db.network_get_all(context.get_admin_context()): - print "%-5s\t%-18s\t%-15s\t%-15s\t%-15s\t%-15s\t%-15s\t%-15s" % ( - network.id, - network.cidr, - network.cidr_v6, - network.dhcp_start, - network.dns1, - network.dns2, - network.vlan, - network.project_id) + print _fmt % (network.id, + network.cidr, + network.cidr_v6, + network.dhcp_start, + network.dns1, + network.dns2, + network.vlan, + network.project_id, + network.uuid) @args('--network', dest="fixed_range", metavar='', help='Network to delete') diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index c1abd2eb6..8d5a9d2a3 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -304,15 +304,6 @@ class CreateInstanceHelper(object): raise exc.HTTPBadRequest(explanation=msg) return password - def _validate_fixed_ip(self, value): - if not isinstance(value, basestring): - msg = _("Fixed IP is not a string or unicode") - raise exc.HTTPBadRequest(explanation=msg) - - if value.strip() == '': - msg = _("Fixed IP is an empty string") - raise exc.HTTPBadRequest(explanation=msg) - def _get_requested_networks(self, requested_networks): """ Create a list of requested networks from the networks attribute @@ -320,31 +311,33 @@ class CreateInstanceHelper(object): networks = [] for network in requested_networks: try: - network_id = network['id'] - network_id = int(network_id) + network_uuid = network['uuid'] + + if not utils.is_uuid_like(network_uuid): + msg = _("Bad networks format: network uuid is not in" + " proper format (%s)") % network_uuid + raise exc.HTTPBadRequest(explanation=msg) + #fixed IP address is optional #if the fixed IP address is not provided then #it will use one of the available IP address from the network - fixed_ip = network.get('fixed_ip', None) - if fixed_ip is not None: - self._validate_fixed_ip(fixed_ip) + address = network.get('fixed_ip', None) + if address is not None and not utils.is_valid_ipv4(address): + msg = _("Invalid fixed IP address (%s)") % address + raise exc.HTTPBadRequest(explanation=msg) # check if the network id is already present in the list, # we don't want duplicate networks to be passed # at the boot time for id, ip in networks: - if id == network_id: + if id == network_uuid: expl = _("Duplicate networks (%s) are not allowed")\ - % network_id + % network_uuid raise exc.HTTPBadRequest(explanation=expl) - networks.append((network_id, fixed_ip)) + networks.append((network_uuid, address)) except KeyError as key: expl = _('Bad network format: missing %s') % key raise exc.HTTPBadRequest(explanation=expl) - except ValueError: - expl = _("Bad networks format: network id should " - "be integer (%s)") % network_id - raise exc.HTTPBadRequest(explanation=expl) except TypeError: expl = _('Bad networks format') raise exc.HTTPBadRequest(explanation=expl) @@ -543,8 +536,8 @@ class ServerXMLDeserializerV11(wsgi.MetadataXMLDeserializer): for network_node in self.find_children_named(node, "network"): item = {} - if network_node.hasAttribute("id"): - item["id"] = network_node.getAttribute("id") + if network_node.hasAttribute("uuid"): + item["uuid"] = network_node.getAttribute("uuid") if network_node.hasAttribute("fixed_ip"): item["fixed_ip"] = network_node.getAttribute("fixed_ip") networks.append(item) diff --git a/nova/db/api.py b/nova/db/api.py index b6b98daf4..6833e6312 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -404,14 +404,6 @@ def fixed_ip_update(context, address, values): """Create a fixed ip from the values dictionary.""" return IMPL.fixed_ip_update(context, address, values) - -def fixed_ip_validate_by_network_address(context, network_id, - address): - """validates if the address belongs to the network""" - return IMPL.fixed_ip_validate_by_network_address(context, network_id, - address) - - #################### @@ -695,9 +687,9 @@ def network_get_all(context): return IMPL.network_get_all(context) -def network_get_networks_by_ids(context, network_ids): +def network_get_networks_by_uuids(context, network_uuids): """Return networks by ids.""" - return IMPL.network_get_networks_by_ids(context, network_ids) + return IMPL.network_get_networks_by_uuids(context, network_uuids) # pylint: disable=C0103 @@ -1252,14 +1244,9 @@ def project_get_networks(context, project_id, associate=True): return IMPL.project_get_networks(context, project_id, associate) -def project_get_networks_by_ids(context, network_ids): - """Return the networks by ids associated with the project. - - If associate is true, it will attempt to associate a new - network if one is not found, otherwise it returns None. - - """ - return IMPL.project_get_networks_by_ids(context, network_ids) +def project_get_networks_by_uuids(context, network_uuids): + """Return the networks by uuids associated with the project.""" + return IMPL.project_get_networks_by_uuids(context, network_uuids) def project_get_networks_v6(context, project_id): diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index dde371820..21eb85b2c 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -824,26 +824,6 @@ def fixed_ip_get_by_address(context, address, session=None): return result -@require_context -def fixed_ip_validate_by_network_address(context, network_id, - address): - session = get_session() - fixed_ip_ref = session.query(models.FixedIp).\ - filter_by(address=address).\ - filter_by(reserved=False).\ - filter_by(network_id=network_id).\ - filter_by(deleted=can_read_deleted(context)).\ - first() - - if fixed_ip_ref is None: - raise exception.FixedIpNotFoundForNetwork(address=address, - network_id=network_id) - if fixed_ip_ref.instance is not None: - raise exception.FixedIpAlreadyInUse(address=address) - - return fixed_ip_ref - - @require_context def fixed_ip_get_by_instance(context, instance_id): session = get_session() @@ -1778,10 +1758,10 @@ def network_get_all(context): @require_admin_context -def network_get_networks_by_ids(context, network_ids): +def network_get_networks_by_uuids(context, network_uuids): session = get_session() result = session.query(models.Network).\ - filter(models.Network.id.in_(network_ids)).\ + filter(models.Network.uuid.in_(network_uuids)).\ filter_by(deleted=False).all() if not result: raise exception.NoNetworksFound() @@ -1794,14 +1774,14 @@ def network_get_networks_by_ids(context, network_ids): #check if the result contains all the networks #we are looking for - for network_id in network_ids: + for network_uuid in network_uuids: found = False for network in result: - if network['id'] == network_id: + if network['uuid'] == network_uuid: found = True break if not found: - raise exception.NetworkNotFound(network_id=network_id) + raise exception.NetworkNotFound(network_id=network_uuid) return result @@ -2962,10 +2942,10 @@ def project_get_networks(context, project_id, associate=True): @require_context -def project_get_networks_by_ids(context, network_ids): +def project_get_networks_by_uuids(context, network_uuids): session = get_session() result = session.query(models.Network).\ - filter(models.Network.id.in_(network_ids)).\ + filter(models.Network.uuid.in_(network_uuids)).\ filter_by(deleted=False).\ filter_by(project_id=context.project_id).all() @@ -2980,14 +2960,14 @@ def project_get_networks_by_ids(context, network_ids): #check if the result contains all the networks #we are looking for - for network_id in network_ids: + for uuid in network_uuids: found = False for network in result: - if network['id'] == network_id: + if network['uuid'] == uuid: found = True break if not found: - raise exception.NetworkNotFoundForProject(network_id=network_id, + raise exception.NetworkNotFoundForProject(network_uuid=uuid, project_id=context.project_id) return result diff --git a/nova/db/sqlalchemy/migrate_repo/versions/037_add_uuid_to_networks.py b/nova/db/sqlalchemy/migrate_repo/versions/037_add_uuid_to_networks.py new file mode 100644 index 000000000..38c543d51 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/037_add_uuid_to_networks.py @@ -0,0 +1,43 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# +# 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. + +from sqlalchemy import Column, Integer, MetaData, String, Table + +from nova import utils + + +meta = MetaData() + +networks = Table("networks", meta, + Column("id", Integer(), primary_key=True, nullable=False)) +uuid_column = Column("uuid", String(36)) + + +def upgrade(migrate_engine): + meta.bind = migrate_engine + networks.create_column(uuid_column) + + rows = migrate_engine.execute(networks.select()) + for row in rows: + networks_uuid = str(utils.gen_uuid()) + migrate_engine.execute(networks.update()\ + .where(networks.c.id == row[0])\ + .values(uuid=networks_uuid)) + + +def downgrade(migrate_engine): + meta.bind = migrate_engine + networks.drop_column(uuid_column) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index f2a4680b0..5cadd156d 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -557,6 +557,7 @@ class Network(BASE, NovaBase): project_id = Column(String(255)) host = Column(String(255)) # , ForeignKey('hosts.id')) + uuid = Column(String(36)) class VirtualInterface(BASE, NovaBase): diff --git a/nova/exception.py b/nova/exception.py index 0c07934cf..c68c89cad 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -428,7 +428,7 @@ class NoNetworksFound(NotFound): class NetworkNotFoundForProject(NotFound): - message = _("Either Network %(network_id)s is not present or " + message = _("Either Network uuid %(network_uuid)s is not present or " "is not assigned to the project %(project_id)s.") @@ -471,7 +471,7 @@ class FixedIpNotFoundForHost(FixedIpNotFound): class FixedIpNotFoundForNetwork(FixedIpNotFound): message = _("Fixed IP address (%(address)s) does not exist in " - "network (%(network_id)s).") + "network (%(network_uuid)s).") class FixedIpAlreadyInUse(AlreadyInUse): diff --git a/nova/network/manager.py b/nova/network/manager.py index f8a21fe4b..2f30f8ec1 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -135,8 +135,8 @@ class RPCAllocateFixedIP(object): for network in networks: address = None if requested_networks is not None: - for address in (fixed_ip for (id, fixed_ip) in \ - requested_networks if network['id'] == id): + for address in (fixed_ip for (uuid, fixed_ip) in \ + requested_networks if network['uuid'] == uuid): break # NOTE(vish): if we are not multi_host pass to the network host @@ -397,9 +397,9 @@ class NetworkManager(manager.SchedulerDependentManager): # there is a better way to determine which networks # a non-vlan instance should connect to if requested_networks is not None and len(requested_networks) != 0: - network_ids = [id for (id, fixed_ip) in requested_networks] - networks = self.db.network_get_networks_by_ids(context, - network_ids) + network_uuids = [uuid for (uuid, fixed_ip) in requested_networks] + networks = self.db.network_get_networks_by_uuids(context, + network_uuids) else: try: networks = self.db.network_get_all(context) @@ -827,18 +827,24 @@ class NetworkManager(manager.SchedulerDependentManager): if networks is None or len(networks) == 0: return - network_ids = [id for (id, fixed_ip) in networks] - result = self.db.network_get_networks_by_ids(context, network_ids) - for network_id, fixed_ip in networks: + network_uuids = [uuid for (uuid, fixed_ip) in networks] + + self.db.network_get_networks_by_uuids(context, network_uuids) + + for network_uuid, address in networks: # check if the fixed IP address is valid and # it actually belongs to the network - if fixed_ip is not None: - if not utils.is_valid_ipv4(fixed_ip): - raise exception.FixedIpInvalid(address=fixed_ip) + if address is not None: + if not utils.is_valid_ipv4(address): + raise exception.FixedIpInvalid(address=address) - self.db.fixed_ip_validate_by_network_address(context, - network_id, - fixed_ip) + fixed_ip_ref = self.db.fixed_ip_get_by_address(context, + address) + if fixed_ip_ref['network']['uuid'] != network_uuid: + raise exception.FixedIpNotFoundForNetwork(address=address, + network_uuid=network_uuid) + if fixed_ip_ref['instance'] is not None: + raise exception.FixedIpAlreadyInUse(address=address) class FlatManager(NetworkManager): @@ -878,8 +884,8 @@ class FlatManager(NetworkManager): for network in networks: address = None if requested_networks is not None: - for address in (fixed_ip for (id, fixed_ip) in \ - requested_networks if network['id'] == id): + for address in (fixed_ip for (uuid, fixed_ip) in \ + requested_networks if network['uuid'] == uuid): break self.allocate_fixed_ip(context, instance_id, @@ -999,9 +1005,9 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): """Determine which networks an instance should connect to.""" # get networks associated with project if requested_networks is not None and len(requested_networks) != 0: - network_ids = [id for (id, fixed_ip) in requested_networks] - networks = self.db.project_get_networks_by_ids(context, - network_ids) + network_uuids = [uuid for (uuid, fixed_ip) in requested_networks] + networks = self.db.project_get_networks_by_uuids(context, + network_uuids) else: networks = self.db.project_get_networks(context, project_id) return networks @@ -1060,19 +1066,24 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): if networks is None or len(networks) == 0: return - network_ids = [id for (id, fixed_ip) in networks] - result = self.db.project_get_networks_by_ids(context, network_ids) + network_uuids = [uuid for (uuid, fixed_ip) in networks] - for network_id, fixed_ip in networks: + self.db.project_get_networks_by_uuids(context, network_uuids) + + for network_uuid, address in networks: # check if the fixed IP address is valid and # it actually belongs to the network if fixed_ip is not None: - if not utils.is_valid_ipv4(fixed_ip): - raise exception.FixedIpInvalid(address=fixed_ip) - - self.db.fixed_ip_validate_by_network_address(context, - network_id, - fixed_ip) + if not utils.is_valid_ipv4(address): + raise exception.FixedIpInvalid(address=address) + + fixed_ip_ref = self.db.fixed_ip_get_by_address(context, + address) + if fixed_ip_ref['network']['uuid'] != network_uuid: + raise exception.FixedIpNotFoundForNetwork(address=address, + network_uuid=network_uuid) + if fixed_ip_ref['instance'] is not None: + raise exception.FixedIpAlreadyInUse(address=address) @property def _bottom_reserved_ips(self): diff --git a/nova/tests/api/openstack/contrib/test_createserverext.py b/nova/tests/api/openstack/contrib/test_createserverext.py index 33a1eec50..38415179a 100644 --- a/nova/tests/api/openstack/contrib/test_createserverext.py +++ b/nova/tests/api/openstack/contrib/test_createserverext.py @@ -43,6 +43,14 @@ FLAGS.verbose = True FAKE_UUID = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' +FAKE_NETWORKS = [('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', '10.0.1.12'), + ('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', '10.0.2.12')] + +DUPLICATE_NETWORKS = [('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', '10.0.1.12'), + ('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', '10.0.1.12')] + +INVALID_NETWORKS = [('invalid', 'invalid-ip-address')] + class CreateserverextTest(test.TestCase): @@ -106,8 +114,8 @@ class CreateserverextTest(test.TestCase): server['flavorRef'] = 1 if networks is not None: network_list = [] - for id, fixed_ip in networks: - network_list.append({'id': id, 'fixed_ip': fixed_ip}) + for uuid, fixed_ip in networks: + network_list.append({'uuid': uuid, 'fixed_ip': fixed_ip}) server['networks'] = network_list return {'server': server} @@ -148,8 +156,8 @@ class CreateserverextTest(test.TestCase): networks = server['networks'] body_parts.append('') for network in networks: - item = (network['id'], network['fixed_ip']) - body_parts.append('' + item = (network['uuid'], network['fixed_ip']) + body_parts.append('' % item) body_parts.append('') body_parts.append('') @@ -190,55 +198,44 @@ class CreateserverextTest(test.TestCase): self.assertEquals(networks, None) def test_create_instance_with_one_network(self): - id = 1 - fixed_ip = '10.0.1.12' - networks = [(id, fixed_ip)] request, response, networks = \ - self._create_instance_with_networks_json(networks) + self._create_instance_with_networks_json([FAKE_NETWORKS[0]]) self.assertEquals(response.status_int, 202) - self.assertEquals(networks, [(id, fixed_ip)]) + self.assertEquals(networks, [FAKE_NETWORKS[0]]) def test_create_instance_with_one_network_xml(self): - id = 1 - fixed_ip = '10.0.1.12' - networks = [(id, fixed_ip)] request, response, networks = \ - self._create_instance_with_networks_xml(networks) + self._create_instance_with_networks_xml([FAKE_NETWORKS[0]]) self.assertEquals(response.status_int, 202) - self.assertEquals(networks, [(id, fixed_ip)]) + self.assertEquals(networks, [FAKE_NETWORKS[0]]) def test_create_instance_with_two_networks(self): - networks = [(1, '10.0.1.12'), (2, '10.0.2.12')] request, response, networks = \ - self._create_instance_with_networks_json(networks) + self._create_instance_with_networks_json(FAKE_NETWORKS) self.assertEquals(response.status_int, 202) - self.assertEquals(networks, [(1, '10.0.1.12'), (2, '10.0.2.12')]) + self.assertEquals(networks, FAKE_NETWORKS) def test_create_instance_with_two_networks_xml(self): - networks = [(1, '10.0.1.12'), (2, '10.0.2.12')] request, response, networks = \ - self._create_instance_with_networks_xml(networks) + self._create_instance_with_networks_xml(FAKE_NETWORKS) self.assertEquals(response.status_int, 202) - self.assertEquals(networks, [(1, '10.0.1.12'), (2, '10.0.2.12')]) + self.assertEquals(networks, FAKE_NETWORKS) def test_create_instance_with_duplicate_networks(self): - networks = [(1, '10.0.1.12'), (1, '10.0.2.12')] request, response, networks = \ - self._create_instance_with_networks_json(networks) + self._create_instance_with_networks_json(DUPLICATE_NETWORKS) self.assertEquals(response.status_int, 400) self.assertEquals(networks, None) def test_create_instance_with_duplicate_networks_xml(self): - networks = [(1, '10.0.1.12'), (1, '10.0.2.12')] request, response, networks = \ - self._create_instance_with_networks_xml(networks) + self._create_instance_with_networks_xml(DUPLICATE_NETWORKS) self.assertEquals(response.status_int, 400) self.assertEquals(networks, None) def test_create_instance_with_network_no_id(self): - networks = [(1, '10.0.1.12')] - body_dict = self._create_networks_request_dict(networks) - del body_dict['server']['networks'][0]['id'] + body_dict = self._create_networks_request_dict([FAKE_NETWORKS[0]]) + del body_dict['server']['networks'][0]['uuid'] request = self._get_create_request_json(body_dict) compute_api, response = \ self._run_create_instance_with_mock_compute_api(request) @@ -246,26 +243,24 @@ class CreateserverextTest(test.TestCase): self.assertEquals(compute_api.networks, None) def test_create_instance_with_network_no_id_xml(self): - networks = [(1, '10.0.1.12')] - body_dict = self._create_networks_request_dict(networks) + body_dict = self._create_networks_request_dict([FAKE_NETWORKS[0]]) request = self._get_create_request_xml(body_dict) - request.body = request.body.replace(' id="1"', '') + uuid = ' uuid="aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"' + request.body = request.body.replace(uuid, '') compute_api, response = \ self._run_create_instance_with_mock_compute_api(request) self.assertEquals(response.status_int, 400) self.assertEquals(compute_api.networks, None) def test_create_instance_with_network_invalid_id(self): - networks = [('asd123', '10.0.1.12')] request, response, networks = \ - self._create_instance_with_networks_json(networks) + self._create_instance_with_networks_json(INVALID_NETWORKS) self.assertEquals(response.status_int, 400) self.assertEquals(networks, None) def test_create_instance_with_network_invalid_id_xml(self): - networks = [('asd123', '10.0.1.12')] request, response, networks = \ - self._create_instance_with_networks_xml(networks) + self._create_instance_with_networks_xml(INVALID_NETWORKS) self.assertEquals(response.status_int, 400) self.assertEquals(networks, None) @@ -291,21 +286,21 @@ class CreateserverextTest(test.TestCase): self.assertEquals(networks, None) def test_create_instance_with_network_no_fixed_ip(self): - networks = [(1, '10.0.1.12')] - body_dict = self._create_networks_request_dict(networks) + body_dict = self._create_networks_request_dict([FAKE_NETWORKS[0]]) del body_dict['server']['networks'][0]['fixed_ip'] request = self._get_create_request_json(body_dict) compute_api, response = \ self._run_create_instance_with_mock_compute_api(request) self.assertEquals(response.status_int, 202) - self.assertEquals(compute_api.networks, [(1, None)]) + self.assertEquals(compute_api.networks, + [('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', None)]) def test_create_instance_with_network_no_fixed_ip_xml(self): - networks = [(1, '10.0.1.12')] - body_dict = self._create_networks_request_dict(networks) + body_dict = self._create_networks_request_dict([FAKE_NETWORKS[0]]) request = self._get_create_request_xml(body_dict) request.body = request.body.replace(' fixed_ip="10.0.1.12"', '') compute_api, response = \ self._run_create_instance_with_mock_compute_api(request) self.assertEquals(response.status_int, 202) - self.assertEquals(compute_api.networks, [(1, None)]) + self.assertEquals(compute_api.networks, + [('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', None)]) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 08eb1eb3c..4cfe18c40 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -2662,7 +2662,7 @@ class TestServerCreateRequestXMLDeserializerV11(test.TestCase): - + """ request = self.deserializer.deserialize(serial_request, 'create') @@ -2670,7 +2670,7 @@ class TestServerCreateRequestXMLDeserializerV11(test.TestCase): "name": "new-server-test", "imageRef": "1", "flavorRef": "1", - "networks": [{"id": "1", "fixed_ip": "10.0.1.12"}], + "networks": [{"uuid": "1", "fixed_ip": "10.0.1.12"}], }} self.assertEquals(request['body'], expected) @@ -2679,8 +2679,8 @@ class TestServerCreateRequestXMLDeserializerV11(test.TestCase): - - + + """ request = self.deserializer.deserialize(serial_request, 'create') @@ -2688,8 +2688,8 @@ class TestServerCreateRequestXMLDeserializerV11(test.TestCase): "name": "new-server-test", "imageRef": "1", "flavorRef": "1", - "networks": [{"id": "1", "fixed_ip": "10.0.1.12"}, - {"id": "2", "fixed_ip": "10.0.2.12"}], + "networks": [{"uuid": "1", "fixed_ip": "10.0.1.12"}, + {"uuid": "2", "fixed_ip": "10.0.2.12"}], }} self.assertEquals(request['body'], expected) @@ -2698,10 +2698,10 @@ class TestServerCreateRequestXMLDeserializerV11(test.TestCase): - + - + """ request = self.deserializer.deserialize(serial_request, 'create') @@ -2709,7 +2709,7 @@ class TestServerCreateRequestXMLDeserializerV11(test.TestCase): "name": "new-server-test", "imageRef": "1", "flavorRef": "1", - "networks": [{"id": "1", "fixed_ip": "10.0.1.12"}], + "networks": [{"uuid": "1", "fixed_ip": "10.0.1.12"}], }} self.assertEquals(request['body'], expected) @@ -2735,7 +2735,7 @@ class TestServerCreateRequestXMLDeserializerV11(test.TestCase): - + """ request = self.deserializer.deserialize(serial_request, 'create') @@ -2743,7 +2743,7 @@ class TestServerCreateRequestXMLDeserializerV11(test.TestCase): "name": "new-server-test", "imageRef": "1", "flavorRef": "1", - "networks": [{"id": "1"}], + "networks": [{"uuid": "1"}], }} self.assertEquals(request['body'], expected) @@ -2752,7 +2752,7 @@ class TestServerCreateRequestXMLDeserializerV11(test.TestCase): - + """ request = self.deserializer.deserialize(serial_request, 'create') @@ -2760,7 +2760,7 @@ class TestServerCreateRequestXMLDeserializerV11(test.TestCase): "name": "new-server-test", "imageRef": "1", "flavorRef": "1", - "networks": [{"id": "", "fixed_ip": "10.0.1.12"}], + "networks": [{"uuid": "", "fixed_ip": "10.0.1.12"}], }} self.assertEquals(request['body'], expected) @@ -2769,7 +2769,7 @@ class TestServerCreateRequestXMLDeserializerV11(test.TestCase): - + """ request = self.deserializer.deserialize(serial_request, 'create') @@ -2777,7 +2777,7 @@ class TestServerCreateRequestXMLDeserializerV11(test.TestCase): "name": "new-server-test", "imageRef": "1", "flavorRef": "1", - "networks": [{"id": "1", "fixed_ip": ""}], + "networks": [{"uuid": "1", "fixed_ip": ""}], }} self.assertEquals(request['body'], expected) @@ -2786,8 +2786,8 @@ class TestServerCreateRequestXMLDeserializerV11(test.TestCase): - - + + """ request = self.deserializer.deserialize(serial_request, 'create') @@ -2795,8 +2795,8 @@ class TestServerCreateRequestXMLDeserializerV11(test.TestCase): "name": "new-server-test", "imageRef": "1", "flavorRef": "1", - "networks": [{"id": "1", "fixed_ip": "10.0.1.12"}, - {"id": "1", "fixed_ip": "10.0.2.12"}], + "networks": [{"uuid": "1", "fixed_ip": "10.0.1.12"}, + {"uuid": "1", "fixed_ip": "10.0.2.12"}], }} self.assertEquals(request['body'], expected) diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index f5ab5ae5a..1f01695a8 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -41,6 +41,7 @@ class FakeModel(dict): networks = [{'id': 0, + 'uuid': "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", 'label': 'test0', 'injected': False, 'multi_host': False, @@ -60,6 +61,7 @@ networks = [{'id': 0, 'project_id': 'fake_project', 'vpn_public_address': '192.168.0.2'}, {'id': 1, + 'uuid': "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", 'label': 'test1', 'injected': False, 'multi_host': False, @@ -179,15 +181,18 @@ class FlatNetworkTestCase(test.TestCase): self.assertDictListMatch(nw[1]['ips'], check) def test_validate_networks(self): - self.mox.StubOutWithMock(db, 'network_get_networks_by_ids') - self.mox.StubOutWithMock(db, "fixed_ip_validate_by_network_address") + self.mox.StubOutWithMock(db, 'network_get_networks_by_uuids') + self.mox.StubOutWithMock(db, "fixed_ip_get_by_address") - requested_networks = [(1, "192.168.0.100")] - db.network_get_networks_by_ids(mox.IgnoreArg(), + requested_networks = [("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", + "192.168.1.100")] + db.network_get_networks_by_uuids(mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(networks) - db.fixed_ip_validate_by_network_address(mox.IgnoreArg(), - mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn(fixed_ips[0]) + + fixed_ips[1]['network'] = FakeModel(**networks[1]) + fixed_ips[1]['instance'] = None + db.fixed_ip_get_by_address(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(fixed_ips[1]) self.mox.ReplayAll() self.network.validate_networks(None, requested_networks) @@ -202,9 +207,9 @@ class FlatNetworkTestCase(test.TestCase): self.network.validate_networks(None, requested_networks) def test_validate_networks_invalid_fixed_ip(self): - self.mox.StubOutWithMock(db, 'network_get_networks_by_ids') + self.mox.StubOutWithMock(db, 'network_get_networks_by_uuids') requested_networks = [(1, "192.168.0.100.1")] - db.network_get_networks_by_ids(mox.IgnoreArg(), + db.network_get_networks_by_uuids(mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(networks) self.mox.ReplayAll() @@ -213,10 +218,10 @@ class FlatNetworkTestCase(test.TestCase): requested_networks) def test_validate_networks_empty_fixed_ip(self): - self.mox.StubOutWithMock(db, 'network_get_networks_by_ids') + self.mox.StubOutWithMock(db, 'network_get_networks_by_uuids') requested_networks = [(1, "")] - db.network_get_networks_by_ids(mox.IgnoreArg(), + db.network_get_networks_by_uuids(mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(networks) self.mox.ReplayAll() @@ -225,10 +230,10 @@ class FlatNetworkTestCase(test.TestCase): None, requested_networks) def test_validate_networks_none_fixed_ip(self): - self.mox.StubOutWithMock(db, 'network_get_networks_by_ids') + self.mox.StubOutWithMock(db, 'network_get_networks_by_uuids') requested_networks = [(1, None)] - db.network_get_networks_by_ids(mox.IgnoreArg(), + db.network_get_networks_by_uuids(mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(networks) self.mox.ReplayAll() @@ -271,7 +276,8 @@ class VlanNetworkTestCase(test.TestCase): db.instance_get(mox.IgnoreArg(), mox.IgnoreArg()).AndReturn({'security_groups': [{'id': 0}]}) - db.fixed_ip_associate_pool(mox.IgnoreArg(), + db.fixed_ip_associate_by_address(mox.IgnoreArg(), + mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg()).AndReturn('192.168.0.1') db.fixed_ip_update(mox.IgnoreArg(), @@ -295,17 +301,20 @@ class VlanNetworkTestCase(test.TestCase): cidr='192.168.0.1/24', network_size=100) def test_validate_networks(self): - self.mox.StubOutWithMock(db, 'project_get_networks_by_ids') - self.mox.StubOutWithMock(db, "fixed_ip_validate_by_network_address") + self.mox.StubOutWithMock(db, 'project_get_networks_by_uuids') + self.mox.StubOutWithMock(db, "fixed_ip_get_by_address") - requested_networks = [(1, "192.168.0.100")] - db.project_get_networks_by_ids(mox.IgnoreArg(), + requested_networks = [("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", + "192.168.1.100")] + db.project_get_networks_by_uuids(mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(networks) - db.fixed_ip_validate_by_network_address(mox.IgnoreArg(), - mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn(fixed_ips[0]) - self.mox.ReplayAll() + fixed_ips[1]['network'] = FakeModel(**networks[1]) + fixed_ips[1]['instance'] = None + db.fixed_ip_get_by_address(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(fixed_ips[1]) + + self.mox.ReplayAll() self.network.validate_networks(None, requested_networks) def test_validate_networks_none_requested_networks(self): @@ -313,25 +322,26 @@ class VlanNetworkTestCase(test.TestCase): def test_validate_networks_empty_requested_networks(self): requested_networks = [] + self.mox.ReplayAll() + self.network.validate_networks(None, requested_networks) def test_validate_networks_invalid_fixed_ip(self): - self.mox.StubOutWithMock(db, 'project_get_networks_by_ids') - + self.mox.StubOutWithMock(db, 'project_get_networks_by_uuids') requested_networks = [(1, "192.168.0.100.1")] - db.project_get_networks_by_ids(mox.IgnoreArg(), + db.project_get_networks_by_uuids(mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(networks) self.mox.ReplayAll() self.assertRaises(exception.FixedIpInvalid, - self.network.validate_networks, - None, requested_networks) + self.network.validate_networks, None, + requested_networks) def test_validate_networks_empty_fixed_ip(self): - self.mox.StubOutWithMock(db, 'project_get_networks_by_ids') + self.mox.StubOutWithMock(db, 'project_get_networks_by_uuids') requested_networks = [(1, "")] - db.project_get_networks_by_ids(mox.IgnoreArg(), + db.project_get_networks_by_uuids(mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(networks) self.mox.ReplayAll() @@ -340,10 +350,10 @@ class VlanNetworkTestCase(test.TestCase): None, requested_networks) def test_validate_networks_none_fixed_ip(self): - self.mox.StubOutWithMock(db, 'project_get_networks_by_ids') + self.mox.StubOutWithMock(db, 'project_get_networks_by_uuids') requested_networks = [(1, None)] - db.project_get_networks_by_ids(mox.IgnoreArg(), + db.project_get_networks_by_uuids(mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(networks) self.mox.ReplayAll() -- cgit From 69c26210dd821df0d2160e51b10f147db2a40249 Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Wed, 17 Aug 2011 12:08:14 -0700 Subject: Fixed broken unit testcases --- nova/tests/api/openstack/test_extensions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/tests/api/openstack/test_extensions.py b/nova/tests/api/openstack/test_extensions.py index 9c29363c6..878ec00e5 100644 --- a/nova/tests/api/openstack/test_extensions.py +++ b/nova/tests/api/openstack/test_extensions.py @@ -85,7 +85,7 @@ class ExtensionControllerTest(test.TestCase): ext_path = os.path.join(os.path.dirname(__file__), "extensions") self.flags(osapi_extensions_path=ext_path) self.ext_list = [ - "Createserverext" + "Createserverext", "FlavorExtraSpecs", "Floating_ips", "Fox In Socks", -- cgit From 90650e5becb541790a8949edebaf0bff0ceb8f5b Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 17 Aug 2011 19:31:01 -0700 Subject: make admin context the default, clean up pipelib --- bin/nova-manage | 19 ++++++++++--------- etc/nova/api-paste.ini | 15 ++++++++++++--- nova/api/auth.py | 18 ++++++++++++++++++ nova/api/ec2/admin.py | 4 +++- nova/auth/manager.py | 3 +++ nova/cloudpipe/pipelib.py | 7 +++---- 6 files changed, 49 insertions(+), 17 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 8e6419c0b..7474b61cb 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -134,7 +134,7 @@ class VpnCommands(object): help='Project name') def list(self, project=None): """Print a listing of the VPN data for one or all projects.""" - + print "WARNING: This method only works with deprecated auth" print "%-12s\t" % 'project', print "%-20s\t" % 'ip:port', print "%-20s\t" % 'private_ip', @@ -170,17 +170,22 @@ class VpnCommands(object): def spawn(self): """Run all VPNs.""" + print "WARNING: This method only works with deprecated auth" for p in reversed(self.manager.get_projects()): if not self._vpn_for(p.id): print 'spawning %s' % p.id - self.pipe.launch_vpn_instance(p.id) + self.pipe.launch_vpn_instance(p.id, p.project_manager_id) time.sleep(10) @args('--project', dest="project_id", metavar='', help='Project name') - def run(self, project_id): - """Start the VPN for a given project.""" - self.pipe.launch_vpn_instance(project_id) + @args('--user', dest="user_id", metavar='', help='User name') + def run(self, project_id, user_id): + """Start the VPN for a given project and user.""" + if not user_id: + print "WARNING: This method only works with deprecated auth" + user_id = self.manager.get_project(project_id).project_manager_id + self.pipe.launch_vpn_instance(project_id, user_id) @args('--project', dest="project_id", metavar='', help='Project name') @@ -195,10 +200,6 @@ class VpnCommands(object): """ # TODO(tr3buchet): perhaps this shouldn't update all networks # associated with a project in the future - project = self.manager.get_project(project_id) - if not project: - print 'No project %s' % (project_id) - return admin_context = context.get_admin_context() networks = db.project_get_networks(admin_context, project_id) for network in networks: diff --git a/etc/nova/api-paste.ini b/etc/nova/api-paste.ini index b540509a2..108360466 100644 --- a/etc/nova/api-paste.ini +++ b/etc/nova/api-paste.ini @@ -19,7 +19,9 @@ use = egg:Paste#urlmap /1.0: ec2metadata [pipeline:ec2cloud] -pipeline = logrequest authenticate cloudrequest authorizer ec2executor +pipeline = logrequest admincontext cloudrequest authorizer ec2executor +# NOTE(vish): use the following pipeline for deprecated auth +#pipeline = logrequest authenticate cloudrequest authorizer ec2executor # NOTE(vish): use the following pipeline for keystone # pipeline = logrequest totoken authtoken keystonecontext cloudrequest authorizer ec2executor @@ -75,12 +77,16 @@ use = egg:Paste#urlmap /v1.1: openstackapi11 [pipeline:openstackapi10] -pipeline = faultwrap auth ratelimit osapiapp10 +pipeline = faultwrap admincontext ratelimit osapiapp10 +# NOTE(vish): use the following pipeline for deprecated auth +# pipeline = faultwrap auth ratelimit osapiapp10 # NOTE(vish): use the following pipeline for keystone #pipeline = faultwrap authtoken keystonecontext ratelimit osapiapp10 [pipeline:openstackapi11] -pipeline = faultwrap auth ratelimit extensions osapiapp11 +pipeline = faultwrap admincontext ratelimit extensions osapiapp11 +# NOTE(vish): use the following pipeline for deprecated auth +# pipeline = faultwrap auth ratelimit extensions osapiapp11 # NOTE(vish): use the following pipeline for keystone # pipeline = faultwrap authtoken keystonecontext ratelimit extensions osapiapp11 @@ -112,6 +118,9 @@ paste.app_factory = nova.api.openstack.versions:Versions.factory # Shared # ########## +[filter:admincontext] +paste.filter_factory = nova.api.auth:AdminContext.factory + [filter:keystonecontext] paste.filter_factory = nova.api.auth:KeystoneContext.factory diff --git a/nova/api/auth.py b/nova/api/auth.py index cd3e3e8a0..050216fd7 100644 --- a/nova/api/auth.py +++ b/nova/api/auth.py @@ -45,6 +45,24 @@ class InjectContext(wsgi.Middleware): return self.application +class AdminContext(wsgi.Middleware): + """Return an admin context no matter what""" + + @webob.dec.wsgify(RequestClass=wsgi.Request) + def __call__(self, req): + # Build a context, including the auth_token... + remote_address = req.remote_addr + if FLAGS.use_forwarded_for: + remote_address = req.headers.get('X-Forwarded-For', remote_address) + ctx = context.RequestContext('admin', + 'admin', + is_admin=True, + remote_address=remote_address) + + req.environ['nova.context'] = ctx + return self.application + + class KeystoneContext(wsgi.Middleware): """Make a request context from keystone headers""" diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index df7876b9d..dfbbc0a2b 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -283,8 +283,10 @@ class AdminController(object): # NOTE(vish) import delayed because of __init__.py from nova.cloudpipe import pipelib pipe = pipelib.CloudPipe() + proj = manager.AuthManager().get_project(project) + user_id = proj.project_manager_id try: - pipe.launch_vpn_instance(project) + pipe.launch_vpn_instance(project, user_id) except db.NoMoreNetworks: raise exception.ApiError("Unable to claim IP for VPN instance" ", ensure it isn't running, and try " diff --git a/nova/auth/manager.py b/nova/auth/manager.py index 6205cfb56..c9178c0dd 100644 --- a/nova/auth/manager.py +++ b/nova/auth/manager.py @@ -17,6 +17,9 @@ # under the License. """ +WARNING: This code is deprecated and will be removed. +Keystone is recommended is the recommended solution for auth management. + Nova authentication management """ diff --git a/nova/cloudpipe/pipelib.py b/nova/cloudpipe/pipelib.py index 2c4673f9e..1b742384c 100644 --- a/nova/cloudpipe/pipelib.py +++ b/nova/cloudpipe/pipelib.py @@ -93,11 +93,10 @@ class CloudPipe(object): zippy.close() return encoded - def launch_vpn_instance(self, project_id): + def launch_vpn_instance(self, project_id, user_id): LOG.debug(_("Launching VPN for %s") % (project_id)) - project = self.manager.get_project(project_id) - ctxt = context.RequestContext(user=project.project_manager_id, - project=project.id) + ctxt = context.RequestContext(user_id=user_id, + project_id=project_id) key_name = self.setup_key_pair(ctxt) group_name = self.setup_security_group(ctxt) -- cgit From 41819d8d048b889f2e7f5e4ee0ff2873bfdef904 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 17 Aug 2011 20:22:30 -0700 Subject: fix integration tests --- etc/nova/api-paste.ini | 7 +- nova/api/openstack/auth.py | 17 +++++ nova/tests/integrated/integrated_helpers.py | 109 ++++------------------------ nova/tests/integrated/test_login.py | 33 --------- nova/tests/integrated/test_servers.py | 2 +- 5 files changed, 36 insertions(+), 132 deletions(-) diff --git a/etc/nova/api-paste.ini b/etc/nova/api-paste.ini index 108360466..a9ae0abf6 100644 --- a/etc/nova/api-paste.ini +++ b/etc/nova/api-paste.ini @@ -77,14 +77,14 @@ use = egg:Paste#urlmap /v1.1: openstackapi11 [pipeline:openstackapi10] -pipeline = faultwrap admincontext ratelimit osapiapp10 +pipeline = faultwrap noauth admincontext ratelimit osapiapp10 # NOTE(vish): use the following pipeline for deprecated auth # pipeline = faultwrap auth ratelimit osapiapp10 # NOTE(vish): use the following pipeline for keystone #pipeline = faultwrap authtoken keystonecontext ratelimit osapiapp10 [pipeline:openstackapi11] -pipeline = faultwrap admincontext ratelimit extensions osapiapp11 +pipeline = faultwrap noauth admincontext ratelimit extensions osapiapp11 # NOTE(vish): use the following pipeline for deprecated auth # pipeline = faultwrap auth ratelimit extensions osapiapp11 # NOTE(vish): use the following pipeline for keystone @@ -96,6 +96,9 @@ paste.filter_factory = nova.api.openstack:FaultWrapper.factory [filter:auth] paste.filter_factory = nova.api.openstack.auth:AuthMiddleware.factory +[filter:noauth] +paste.filter_factory = nova.api.openstack.auth:NoAuthMiddleware.factory + [filter:ratelimit] paste.filter_factory = nova.api.openstack.limits:RateLimitingMiddleware.factory diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index d42abe1f8..f4a50fc46 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -34,6 +34,23 @@ LOG = logging.getLogger('nova.api.openstack') FLAGS = flags.FLAGS +class NoAuthMiddleware(wsgi.Middleware): + """Return a fake token if one isn't specified.""" + + @webob.dec.wsgify(RequestClass=wsgi.Request) + def __call__(self, req): + if 'X-Auth-Token' in req.headers: + return self.application + logging.debug("Got no auth token, returning fake info.") + res = webob.Response() + res.headers['X-Auth-Token'] = 'fake' + res.headers['X-Server-Management-Url'] = req.url + res.headers['X-Storage-Url'] = '' + res.headers['X-CDN-Management-Url'] = '' + res.content_type = 'text/plain' + res.status = '204' + return res + class AuthMiddleware(wsgi.Middleware): """Authorize the openstack API request or return an HTTP Forbidden.""" diff --git a/nova/tests/integrated/integrated_helpers.py b/nova/tests/integrated/integrated_helpers.py index fb2f88502..343190427 100644 --- a/nova/tests/integrated/integrated_helpers.py +++ b/nova/tests/integrated/integrated_helpers.py @@ -22,10 +22,8 @@ Provides common functionality for integrated unit tests import random import string -from nova import exception from nova import service from nova import test # For the flags -from nova.auth import manager import nova.image.glance from nova.log import logging from nova.tests.integrated.api import client @@ -58,90 +56,6 @@ def generate_new_element(items, prefix, numeric=False): LOG.debug("Random collision on %s" % candidate) -class TestUser(object): - def __init__(self, name, secret, auth_url): - self.name = name - self.secret = secret - self.auth_url = auth_url - - if not auth_url: - raise exception.Error("auth_url is required") - self.openstack_api = client.TestOpenStackClient(self.name, - self.secret, - self.auth_url) - - def get_unused_server_name(self): - servers = self.openstack_api.get_servers() - server_names = [server['name'] for server in servers] - return generate_new_element(server_names, 'server') - - def get_invalid_image(self): - images = self.openstack_api.get_images() - image_ids = [image['id'] for image in images] - return generate_new_element(image_ids, '', numeric=True) - - def get_valid_image(self, create=False): - images = self.openstack_api.get_images() - if create and not images: - # TODO(justinsb): No way currently to create an image through API - #created_image = self.openstack_api.post_image(image) - #images.append(created_image) - raise exception.Error("No way to create an image through API") - - if images: - return images[0] - return None - - -class IntegratedUnitTestContext(object): - def __init__(self, auth_url): - self.auth_manager = manager.AuthManager() - - self.auth_url = auth_url - self.project_name = None - - self.test_user = None - - self.setup() - - def setup(self): - self._create_test_user() - - def _create_test_user(self): - self.test_user = self._create_unittest_user() - - # No way to currently pass this through the OpenStack API - self.project_name = 'openstack' - self._configure_project(self.project_name, self.test_user) - - def cleanup(self): - self.test_user = None - - def _create_unittest_user(self): - users = self.auth_manager.get_users() - user_names = [user.name for user in users] - auth_name = generate_new_element(user_names, 'unittest_user_') - auth_key = generate_random_alphanumeric(16) - - # Right now there's a bug where auth_name and auth_key are reversed - # bug732907 - auth_key = auth_name - - self.auth_manager.create_user(auth_name, auth_name, auth_key, False) - return TestUser(auth_name, auth_key, self.auth_url) - - def _configure_project(self, project_name, user): - projects = self.auth_manager.get_projects() - project_names = [project.name for project in projects] - if not project_name in project_names: - project = self.auth_manager.create_project(project_name, - user.name, - description=None, - member_users=None) - else: - self.auth_manager.add_to_project(user.name, project_name) - - class _IntegratedTestBase(test.TestCase): def setUp(self): super(_IntegratedTestBase, self).setUp() @@ -163,10 +77,7 @@ class _IntegratedTestBase(test.TestCase): self._start_api_service() - self.context = IntegratedUnitTestContext(self.auth_url) - - self.user = self.context.test_user - self.api = self.user.openstack_api + self.api = client.TestOpenStackClient('fake', 'fake', self.auth_url) def _start_api_service(self): osapi = service.WSGIService("osapi") @@ -174,10 +85,6 @@ class _IntegratedTestBase(test.TestCase): self.auth_url = 'http://%s:%s/v1.1' % (osapi.host, osapi.port) LOG.warn(self.auth_url) - def tearDown(self): - self.context.cleanup() - super(_IntegratedTestBase, self).tearDown() - def _get_flags(self): """An opportunity to setup flags, before the services are started.""" f = {} @@ -190,10 +97,20 @@ class _IntegratedTestBase(test.TestCase): f['fake_network'] = True return f + def get_unused_server_name(self): + servers = self.api.get_servers() + server_names = [server['name'] for server in servers] + return generate_new_element(server_names, 'server') + + def get_invalid_image(self): + images = self.api.get_images() + image_ids = [image['id'] for image in images] + return generate_new_element(image_ids, '', numeric=True) + def _build_minimal_create_server_request(self): server = {} - image = self.user.get_valid_image(create=True) + image = self.api.get_images()[0] LOG.debug("Image: %s" % image) if 'imageRef' in image: @@ -211,7 +128,7 @@ class _IntegratedTestBase(test.TestCase): server['flavorRef'] = 'http://fake.server/%s' % flavor['id'] # Set a valid server name - server_name = self.user.get_unused_server_name() + server_name = self.get_unused_server_name() server['name'] = server_name return server diff --git a/nova/tests/integrated/test_login.py b/nova/tests/integrated/test_login.py index 06359a52f..3a863d0f9 100644 --- a/nova/tests/integrated/test_login.py +++ b/nova/tests/integrated/test_login.py @@ -15,11 +15,9 @@ # License for the specific language governing permissions and limitations # under the License. -import unittest from nova.log import logging from nova.tests.integrated import integrated_helpers -from nova.tests.integrated.api import client LOG = logging.getLogger('nova.tests.integrated') @@ -31,34 +29,3 @@ class LoginTest(integrated_helpers._IntegratedTestBase): flavors = self.api.get_flavors() for flavor in flavors: LOG.debug(_("flavor: %s") % flavor) - - def test_bad_login_password(self): - """Test that I get a 401 with a bad username.""" - bad_credentials_api = client.TestOpenStackClient(self.user.name, - "notso_password", - self.user.auth_url) - - self.assertRaises(client.OpenStackApiAuthenticationException, - bad_credentials_api.get_flavors) - - def test_bad_login_username(self): - """Test that I get a 401 with a bad password.""" - bad_credentials_api = client.TestOpenStackClient("notso_username", - self.user.secret, - self.user.auth_url) - - self.assertRaises(client.OpenStackApiAuthenticationException, - bad_credentials_api.get_flavors) - - def test_bad_login_both_bad(self): - """Test that I get a 401 with both bad username and bad password.""" - bad_credentials_api = client.TestOpenStackClient("notso_username", - "notso_password", - self.user.auth_url) - - self.assertRaises(client.OpenStackApiAuthenticationException, - bad_credentials_api.get_flavors) - - -if __name__ == "__main__": - unittest.main() diff --git a/nova/tests/integrated/test_servers.py b/nova/tests/integrated/test_servers.py index 725f6d529..c2f800689 100644 --- a/nova/tests/integrated/test_servers.py +++ b/nova/tests/integrated/test_servers.py @@ -51,7 +51,7 @@ class ServersTest(integrated_helpers._IntegratedTestBase): self.api.post_server, post) # With an invalid imageRef, this throws 500. - server['imageRef'] = self.user.get_invalid_image() + server['imageRef'] = self.get_invalid_image() # TODO(justinsb): Check whatever the spec says should be thrown here self.assertRaises(client.OpenStackApiException, self.api.post_server, post) -- cgit From a1ceed43d6ab871d3dea721b855bd7eabec48433 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 17 Aug 2011 20:24:52 -0700 Subject: clean up fake auth from server actions test --- nova/tests/api/openstack/test_server_actions.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/nova/tests/api/openstack/test_server_actions.py b/nova/tests/api/openstack/test_server_actions.py index 687a19390..3b419dec5 100644 --- a/nova/tests/api/openstack/test_server_actions.py +++ b/nova/tests/api/openstack/test_server_actions.py @@ -1,17 +1,13 @@ import base64 import json -import unittest -from xml.dom import minidom import stubout import webob from nova import context -from nova import db from nova import utils from nova import flags from nova.api.openstack import create_instance_helper -from nova.compute import instance_types from nova.compute import power_state import nova.db.api from nova import test @@ -103,8 +99,6 @@ class ServerActionsTest(test.TestCase): super(ServerActionsTest, self).setUp() self.flags(verbose=True) self.stubs = stubout.StubOutForTesting() - fakes.FakeAuthManager.reset_fake_data() - fakes.FakeAuthDatabase.data = {} fakes.stub_out_auth(self.stubs) self.stubs.Set(nova.db.api, 'instance_get', return_server_by_id) self.stubs.Set(nova.db.api, 'instance_update', instance_update) @@ -468,8 +462,6 @@ class ServerActionsTestV11(test.TestCase): self.maxDiff = None super(ServerActionsTestV11, self).setUp() self.stubs = stubout.StubOutForTesting() - fakes.FakeAuthManager.reset_fake_data() - fakes.FakeAuthDatabase.data = {} fakes.stub_out_auth(self.stubs) self.stubs.Set(nova.db.api, 'instance_get', return_server_by_id) self.stubs.Set(nova.db.api, 'instance_update', instance_update) -- cgit From 6c256a9a5c013f8674776adb2005b4f541f705b5 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 17 Aug 2011 20:26:33 -0700 Subject: remove extra reference in pipelib --- nova/cloudpipe/pipelib.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/nova/cloudpipe/pipelib.py b/nova/cloudpipe/pipelib.py index 1b742384c..3eb372844 100644 --- a/nova/cloudpipe/pipelib.py +++ b/nova/cloudpipe/pipelib.py @@ -34,7 +34,6 @@ from nova import exception from nova import flags from nova import log as logging from nova import utils -from nova.auth import manager # TODO(eday): Eventually changes these to something not ec2-specific from nova.api.ec2 import cloud @@ -57,7 +56,6 @@ LOG = logging.getLogger('nova.cloudpipe') class CloudPipe(object): def __init__(self): self.controller = cloud.CloudController() - self.manager = manager.AuthManager() def get_encoded_zip(self, project_id): # Make a payload.zip -- cgit From 6d87608cf835e1c27f3b6b6b31e6b41b0aa90b90 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 17 Aug 2011 20:35:54 -0700 Subject: pep8 --- nova/api/openstack/auth.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index f4a50fc46..b37f9aade 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -51,6 +51,7 @@ class NoAuthMiddleware(wsgi.Middleware): res.status = '204' return res + class AuthMiddleware(wsgi.Middleware): """Authorize the openstack API request or return an HTTP Forbidden.""" -- cgit From 794d73a916a6bf97cbad4d0cfd5cf359e0418af0 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Fri, 19 Aug 2011 09:53:31 +0200 Subject: Move documentation from nova.virt.fake to nova.virt.driver. --- nova/virt/driver.py | 247 +++++++++++++++++++++++++++++++++++++++++++--- nova/virt/fake.py | 278 +--------------------------------------------------- 2 files changed, 238 insertions(+), 287 deletions(-) diff --git a/nova/virt/driver.py b/nova/virt/driver.py index 20af2666d..72fe118b4 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -62,11 +62,41 @@ def block_device_info_get_mapping(block_device_info): class ComputeDriver(object): """Base class for compute drivers. - Lots of documentation is currently on fake.py. + The interface to this class talks in terms of 'instances' (Amazon EC2 and + internal Nova terminology), by which we mean 'running virtual machine' + (XenAPI terminology) or domain (Xen or libvirt terminology). + + An instance has an ID, which is the identifier chosen by Nova to represent + the instance further up the stack. This is unfortunately also called a + 'name' elsewhere. As far as this layer is concerned, 'instance ID' and + 'instance name' are synonyms. + + Note that the instance ID or name is not human-readable or + customer-controlled -- it's an internal ID chosen by Nova. At the + nova.virt layer, instances do not have human-readable names at all -- such + things are only known higher up the stack. + + Most virtualization platforms will also have their own identity schemes, + to uniquely identify a VM or domain. These IDs must stay internal to the + platform-specific layer, and never escape the connection interface. The + platform-specific layer is responsible for keeping track of which instance + ID maps to which platform-specific ID, and vice versa. + + In contrast, the list_disks and list_interfaces calls may return + platform-specific IDs. These identify a specific virtual disk or specific + virtual network interface, and these IDs are opaque to the rest of Nova. + + Some methods here take an instance of nova.compute.service.Instance. This + is the datastructure used by nova.compute to store details regarding an + instance, and pass them into this layer. This layer is responsible for + translating that generic datastructure into terms that are specific to the + virtualization platform. + """ def init_host(self, host): - """Adopt existing VM's running here""" + """Initialize anything that is necessary for the driver to function, + including catching up with currently running VM's on the given host.""" # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() @@ -84,6 +114,10 @@ class ComputeDriver(object): raise NotImplementedError() def list_instances(self): + """ + Return the names of all the instances known to the virtualization + layer, as a list. + """ # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() @@ -94,7 +128,20 @@ class ComputeDriver(object): def spawn(self, context, instance, network_info=None, block_device_info=None): - """Launch a VM for the specified instance""" + """ + Create a new instance/VM/domain on the virtualization platform. + + The given parameter is an instance of nova.compute.service.Instance. + This function should use the data there to guide the creation of + the new instance. + + Once this successfully completes, the instance should be + running (power_state.RUNNING). + + If this fails, any partial instance should be completely + cleaned up, and the virtualization platform should be in the state + that it was before this call began. + """ raise NotImplementedError() def destroy(self, instance, network_info, cleanup=True): @@ -115,7 +162,11 @@ class ComputeDriver(object): raise NotImplementedError() def reboot(self, instance, network_info): - """Reboot specified VM""" + """Reboot the specified instance. + + The given parameter is an instance of nova.compute.service.Instance, + and so the instance is being specified as instance.name. + """ # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() @@ -140,26 +191,40 @@ class ComputeDriver(object): raise NotImplementedError() def get_host_ip_addr(self): + """ + Retrieves the IP address of the dom0 + """ # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() def attach_volume(self, context, instance_id, volume_id, mountpoint): + """Attach the disk at device_path to the instance at mountpoint""" raise NotImplementedError() def detach_volume(self, context, instance_id, volume_id): + """Detach the disk attached to the instance at mountpoint""" raise NotImplementedError() - def compare_cpu(self, context, cpu_info): + def compare_cpu(self, cpu_info): raise NotImplementedError() def migrate_disk_and_power_off(self, instance, dest): - """Transfers the VHD of a running instance to another host, then shuts - off the instance copies over the COW disk""" + """ + Transfers the disk of a running instance in multiple phases, turning + off the instance before the end. + """ # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() def snapshot(self, context, instance, image_id): - """Create snapshot from a running VM instance.""" + """ + Snapshots the specified instance. + + The given parameter is an instance of nova.compute.service.Instance, + and so the instance is being specified as instance.name. + + The second parameter is the name of the snapshot. + """ raise NotImplementedError() def finish_migration(self, context, instance, disk_info, network_info, @@ -173,7 +238,7 @@ class ComputeDriver(object): raise NotImplementedError() def pause(self, instance, callback): - """Pause VM instance""" + """Pause the specified instance.""" # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() @@ -235,15 +300,69 @@ class ComputeDriver(object): raise NotImplementedError() def refresh_security_group_rules(self, security_group_id): + """This method is called after a change to security groups. + + All security groups and their associated rules live in the datastore, + and calling this method should apply the updated rules to instances + running the specified security group. + + An error should be raised if the operation cannot complete. + + """ # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() def refresh_security_group_members(self, security_group_id): + """This method is called when a security group is added to an instance. + + This message is sent to the virtualization drivers on hosts that are + running an instance that belongs to a security group that has a rule + that references the security group identified by `security_group_id`. + It is the responsiblity of this method to make sure any rules + that authorize traffic flow with members of the security group are + updated and any new members can communicate, and any removed members + cannot. + + Scenario: + * we are running on host 'H0' and we have an instance 'i-0'. + * instance 'i-0' is a member of security group 'speaks-b' + * group 'speaks-b' has an ingress rule that authorizes group 'b' + * another host 'H1' runs an instance 'i-1' + * instance 'i-1' is a member of security group 'b' + + When 'i-1' launches or terminates we will recieve the message + to update members of group 'b', at which time we will make + any changes needed to the rules for instance 'i-0' to allow + or deny traffic coming from 'i-1', depending on if it is being + added or removed from the group. + + In this scenario, 'i-1' could just as easily have been running on our + host 'H0' and this method would still have been called. The point was + that this method isn't called on the host where instances of that + group are running (as is the case with + :method:`refresh_security_group_rules`) but is called where references + are made to authorizing those instances. + + An error should be raised if the operation cannot complete. + + """ # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() def refresh_provider_fw_rules(self, security_group_id): - """See: nova/virt/fake.py for docs.""" + """This triggers a firewall update based on database changes. + + When this is called, rules have either been added or removed from the + datastore. You can retrieve rules with + :method:`nova.db.api.provider_fw_rule_get_all`. + + Provider rules take precedence over security group rules. If an IP + would be allowed by a security group ingress rule, but blocked by + a provider rule, then packets from the IP are dropped. This includes + intra-project traffic in the case of the allow_project_net_traffic + flag for the libvirt-derived classes. + + """ # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() @@ -284,18 +403,38 @@ class ComputeDriver(object): raise NotImplementedError() def set_admin_password(self, context, instance_id, new_pass=None): - """Set the root/admin password for an instance on this server.""" + """ + Set the root password on the specified instance. + + The first parameter is an instance of nova.compute.service.Instance, + and so the instance is being specified as instance.name. The second + parameter is the value of the new password. + """ raise NotImplementedError() def inject_file(self, instance, b64_path, b64_contents): - """Create a file on the VM instance. The file path and contents - should be base64-encoded. + """ + Writes a file on the specified instance. + + The first parameter is an instance of nova.compute.service.Instance, + and so the instance is being specified as instance.name. The second + parameter is the base64-encoded path to which the file is to be + written on the instance; the third is the contents of the file, also + base64-encoded. """ # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() def agent_update(self, instance, url, md5hash): - """Update agent on the VM instance.""" + """ + Update agent on the specified instance. + + The first parameter is an instance of nova.compute.service.Instance, + and so the instance is being specified as instance.name. The second + parameter is the URL of the agent to be fetched and updated on the + instance; the third is the md5 hash of the file for verification + purposes. + """ # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() @@ -322,3 +461,83 @@ class ComputeDriver(object): """Plugs in VIFs to networks.""" # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() + + def update_host_status(self): + """Refresh host stats""" + raise NotImplementedError() + + def get_host_stats(self, refresh=False): + """Return currently known host stats""" + raise NotImplementedError() + + def list_disks(self, instance_name): + """ + Return the IDs of all the virtual disks attached to the specified + instance, as a list. These IDs are opaque to the caller (they are + only useful for giving back to this layer as a parameter to + disk_stats). These IDs only need to be unique for a given instance. + + Note that this function takes an instance ID. + """ + raise NotImplementedError() + + def list_interfaces(self, instance_name): + """ + Return the IDs of all the virtual network interfaces attached to the + specified instance, as a list. These IDs are opaque to the caller + (they are only useful for giving back to this layer as a parameter to + interface_stats). These IDs only need to be unique for a given + instance. + + Note that this function takes an instance ID. + """ + raise NotImplementedError() + + def resize(self, instance, flavor): + """ + Resizes/Migrates the specified instance. + + The flavor parameter determines whether or not the instance RAM and + disk space are modified, and if so, to what size. + """ + raise NotImplementedError() + + def block_stats(self, instance_name, disk_id): + """ + Return performance counters associated with the given disk_id on the + given instance_name. These are returned as [rd_req, rd_bytes, wr_req, + wr_bytes, errs], where rd indicates read, wr indicates write, req is + the total number of I/O requests made, bytes is the total number of + bytes transferred, and errs is the number of requests held up due to a + full pipeline. + + All counters are long integers. + + This method is optional. On some platforms (e.g. XenAPI) performance + statistics can be retrieved directly in aggregate form, without Nova + having to do the aggregation. On those platforms, this method is + unused. + + Note that this function takes an instance ID. + """ + raise NotImplementedError() + + def interface_stats(self, instance_name, iface_id): + """ + Return performance counters associated with the given iface_id on the + given instance_id. These are returned as [rx_bytes, rx_packets, + rx_errs, rx_drop, tx_bytes, tx_packets, tx_errs, tx_drop], where rx + indicates receive, tx indicates transmit, bytes and packets indicate + the total number of bytes or packets transferred, and errs and dropped + is the total number of packets failed / dropped. + + All counters are long integers. + + This method is optional. On some platforms (e.g. XenAPI) performance + statistics can be retrieved directly in aggregate form, without Nova + having to do the aggregation. On those platforms, this method is + unused. + + Note that this function takes an instance ID. + """ + raise NotImplementedError() diff --git a/nova/virt/fake.py b/nova/virt/fake.py index dc0628772..67cc1ea29 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -48,37 +48,7 @@ class FakeInstance(object): class FakeConnection(driver.ComputeDriver): - """ - The interface to this class talks in terms of 'instances' (Amazon EC2 and - internal Nova terminology), by which we mean 'running virtual machine' - (XenAPI terminology) or domain (Xen or libvirt terminology). - - An instance has an ID, which is the identifier chosen by Nova to represent - the instance further up the stack. This is unfortunately also called a - 'name' elsewhere. As far as this layer is concerned, 'instance ID' and - 'instance name' are synonyms. - - Note that the instance ID or name is not human-readable or - customer-controlled -- it's an internal ID chosen by Nova. At the - nova.virt layer, instances do not have human-readable names at all -- such - things are only known higher up the stack. - - Most virtualization platforms will also have their own identity schemes, - to uniquely identify a VM or domain. These IDs must stay internal to the - platform-specific layer, and never escape the connection interface. The - platform-specific layer is responsible for keeping track of which instance - ID maps to which platform-specific ID, and vice versa. - - In contrast, the list_disks and list_interfaces calls may return - platform-specific IDs. These identify a specific virtual disk or specific - virtual network interface, and these IDs are opaque to the rest of Nova. - - Some methods here take an instance of nova.compute.service.Instance. This - is the datastructure used by nova.compute to store details regarding an - instance, and pass them into this layer. This layer is responsible for - translating that generic datastructure into terms that are specific to the - virtualization platform. - """ + """Fake hypervisor driver""" def __init__(self): self.instances = {} @@ -105,17 +75,9 @@ class FakeConnection(driver.ComputeDriver): return cls._instance def init_host(self, host): - """ - Initialize anything that is necessary for the driver to function, - including catching up with currently running VM's on the given host. - """ return def list_instances(self): - """ - Return the names of all the instances known to the virtualization - layer, as a list. - """ return self.instances.keys() def _map_to_instance_info(self, instance): @@ -131,167 +93,54 @@ class FakeConnection(driver.ComputeDriver): def spawn(self, context, instance, network_info=None, block_device_info=None): - """ - Create a new instance/VM/domain on the virtualization platform. - - The given parameter is an instance of nova.compute.service.Instance. - This function should use the data there to guide the creation of - the new instance. - - The work will be done asynchronously. This function returns a - task that allows the caller to detect when it is complete. - - Once this successfully completes, the instance should be - running (power_state.RUNNING). - - If this fails, any partial instance should be completely - cleaned up, and the virtualization platform should be in the state - that it was before this call began. - """ - name = instance.name state = power_state.RUNNING fake_instance = FakeInstance(name, state) self.instances[name] = fake_instance def snapshot(self, context, instance, name): - """ - Snapshots the specified instance. - - The given parameter is an instance of nova.compute.service.Instance, - and so the instance is being specified as instance.name. - - The second parameter is the name of the snapshot. - - The work will be done asynchronously. This function returns a - task that allows the caller to detect when it is complete. - """ pass def reboot(self, instance, network_info): - """ - Reboot the specified instance. - - The given parameter is an instance of nova.compute.service.Instance, - and so the instance is being specified as instance.name. - - The work will be done asynchronously. This function returns a - task that allows the caller to detect when it is complete. - """ pass def get_host_ip_addr(self): - """ - Retrieves the IP address of the dom0 - """ - pass + return '192.168.0.1' def resize(self, instance, flavor): - """ - Resizes/Migrates the specified instance. - - The flavor parameter determines whether or not the instance RAM and - disk space are modified, and if so, to what size. - - The work will be done asynchronously. This function returns a task - that allows the caller to detect when it is complete. - """ pass def set_admin_password(self, instance, new_pass): - """ - Set the root password on the specified instance. - - The first parameter is an instance of nova.compute.service.Instance, - and so the instance is being specified as instance.name. The second - parameter is the value of the new password. - - The work will be done asynchronously. This function returns a - task that allows the caller to detect when it is complete. - """ pass def inject_file(self, instance, b64_path, b64_contents): - """ - Writes a file on the specified instance. - - The first parameter is an instance of nova.compute.service.Instance, - and so the instance is being specified as instance.name. The second - parameter is the base64-encoded path to which the file is to be - written on the instance; the third is the contents of the file, also - base64-encoded. - - The work will be done asynchronously. This function returns a - task that allows the caller to detect when it is complete. - """ pass def agent_update(self, instance, url, md5hash): - """ - Update agent on the specified instance. - - The first parameter is an instance of nova.compute.service.Instance, - and so the instance is being specified as instance.name. The second - parameter is the URL of the agent to be fetched and updated on the - instance; the third is the md5 hash of the file for verification - purposes. - - The work will be done asynchronously. This function returns a - task that allows the caller to detect when it is complete. - """ pass def rescue(self, context, instance, callback, network_info): - """ - Rescue the specified instance. - """ pass def unrescue(self, instance, callback, network_info): - """ - Unrescue the specified instance. - """ pass def poll_rescued_instances(self, timeout): - """Poll for rescued instances""" pass def migrate_disk_and_power_off(self, instance, dest): - """ - Transfers the disk of a running instance in multiple phases, turning - off the instance before the end. - """ - pass - - def attach_disk(self, instance, disk_info): - """ - Attaches the disk to an instance given the metadata disk_info - """ pass def pause(self, instance, callback): - """ - Pause the specified instance. - """ pass def unpause(self, instance, callback): - """ - Unpause the specified instance. - """ pass def suspend(self, instance, callback): - """ - suspend the specified instance - """ pass def resume(self, instance, callback): - """ - resume the specified instance - """ pass def destroy(self, instance, network_info, cleanup=True): @@ -303,25 +152,14 @@ class FakeConnection(driver.ComputeDriver): (key, self.instances)) def attach_volume(self, instance_name, device_path, mountpoint): - """Attach the disk at device_path to the instance at mountpoint""" + self._mounts[mountpoint] = device_path return True def detach_volume(self, instance_name, mountpoint): - """Detach the disk attached to the instance at mountpoint""" + del self._mounts[mountpoint] return True def get_info(self, instance_name): - """ - Get a block of information about the given instance. This is returned - as a dictionary containing 'state': The power_state of the instance, - 'max_mem': The maximum memory for the instance, in KiB, 'mem': The - current memory the instance has, in KiB, 'num_cpu': The current number - of virtual CPUs the instance has, 'cpu_time': The total CPU time used - by the instance, in nanoseconds. - - This method should raise exception.NotFound if the hypervisor has no - knowledge of the instance - """ if instance_name not in self.instances: raise exception.InstanceNotFound(instance_id=instance_name) i = self.instances[instance_name] @@ -332,69 +170,18 @@ class FakeConnection(driver.ComputeDriver): 'cpu_time': 0} def get_diagnostics(self, instance_name): - pass + return {} def list_disks(self, instance_name): - """ - Return the IDs of all the virtual disks attached to the specified - instance, as a list. These IDs are opaque to the caller (they are - only useful for giving back to this layer as a parameter to - disk_stats). These IDs only need to be unique for a given instance. - - Note that this function takes an instance ID. - """ return ['A_DISK'] def list_interfaces(self, instance_name): - """ - Return the IDs of all the virtual network interfaces attached to the - specified instance, as a list. These IDs are opaque to the caller - (they are only useful for giving back to this layer as a parameter to - interface_stats). These IDs only need to be unique for a given - instance. - - Note that this function takes an instance ID. - """ return ['A_VIF'] def block_stats(self, instance_name, disk_id): - """ - Return performance counters associated with the given disk_id on the - given instance_name. These are returned as [rd_req, rd_bytes, wr_req, - wr_bytes, errs], where rd indicates read, wr indicates write, req is - the total number of I/O requests made, bytes is the total number of - bytes transferred, and errs is the number of requests held up due to a - full pipeline. - - All counters are long integers. - - This method is optional. On some platforms (e.g. XenAPI) performance - statistics can be retrieved directly in aggregate form, without Nova - having to do the aggregation. On those platforms, this method is - unused. - - Note that this function takes an instance ID. - """ return [0L, 0L, 0L, 0L, None] def interface_stats(self, instance_name, iface_id): - """ - Return performance counters associated with the given iface_id on the - given instance_id. These are returned as [rx_bytes, rx_packets, - rx_errs, rx_drop, tx_bytes, tx_packets, tx_errs, tx_drop], where rx - indicates receive, tx indicates transmit, bytes and packets indicate - the total number of bytes or packets transferred, and errs and dropped - is the total number of packets failed / dropped. - - All counters are long integers. - - This method is optional. On some platforms (e.g. XenAPI) performance - statistics can be retrieved directly in aggregate form, without Nova - having to do the aggregation. On those platforms, this method is - unused. - - Note that this function takes an instance ID. - """ return [0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L] def get_console_output(self, instance): @@ -416,67 +203,12 @@ class FakeConnection(driver.ComputeDriver): 'password': 'fakepassword'} def refresh_security_group_rules(self, security_group_id): - """This method is called after a change to security groups. - - All security groups and their associated rules live in the datastore, - and calling this method should apply the updated rules to instances - running the specified security group. - - An error should be raised if the operation cannot complete. - - """ return True def refresh_security_group_members(self, security_group_id): - """This method is called when a security group is added to an instance. - - This message is sent to the virtualization drivers on hosts that are - running an instance that belongs to a security group that has a rule - that references the security group identified by `security_group_id`. - It is the responsiblity of this method to make sure any rules - that authorize traffic flow with members of the security group are - updated and any new members can communicate, and any removed members - cannot. - - Scenario: - * we are running on host 'H0' and we have an instance 'i-0'. - * instance 'i-0' is a member of security group 'speaks-b' - * group 'speaks-b' has an ingress rule that authorizes group 'b' - * another host 'H1' runs an instance 'i-1' - * instance 'i-1' is a member of security group 'b' - - When 'i-1' launches or terminates we will recieve the message - to update members of group 'b', at which time we will make - any changes needed to the rules for instance 'i-0' to allow - or deny traffic coming from 'i-1', depending on if it is being - added or removed from the group. - - In this scenario, 'i-1' could just as easily have been running on our - host 'H0' and this method would still have been called. The point was - that this method isn't called on the host where instances of that - group are running (as is the case with - :method:`refresh_security_group_rules`) but is called where references - are made to authorizing those instances. - - An error should be raised if the operation cannot complete. - - """ return True def refresh_provider_fw_rules(self): - """This triggers a firewall update based on database changes. - - When this is called, rules have either been added or removed from the - datastore. You can retrieve rules with - :method:`nova.db.api.provider_fw_rule_get_all`. - - Provider rules take precedence over security group rules. If an IP - would be allowed by a security group ingress rule, but blocked by - a provider rule, then packets from the IP are dropped. This includes - intra-project traffic in the case of the allow_project_net_traffic - flag for the libvirt-derived classes. - - """ pass def update_available_resource(self, ctxt, host): -- cgit From a2c7e0f7fc6e25828ab32f133965b300e37d9264 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Fri, 19 Aug 2011 13:44:32 +0200 Subject: Start improving documentation. --- nova/virt/driver.py | 57 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/nova/virt/driver.py b/nova/virt/driver.py index 72fe118b4..6d3af7e0a 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -104,6 +104,7 @@ class ComputeDriver(object): """Get the current status of an instance, by name (not ID!) Returns a dict containing: + :state: the running state, one of the power_state codes :max_mem: (int) the maximum memory in KBytes allowed :mem: (int) the memory in KBytes used by the domain @@ -131,16 +132,20 @@ class ComputeDriver(object): """ Create a new instance/VM/domain on the virtualization platform. - The given parameter is an instance of nova.compute.service.Instance. - This function should use the data there to guide the creation of - the new instance. - Once this successfully completes, the instance should be running (power_state.RUNNING). If this fails, any partial instance should be completely cleaned up, and the virtualization platform should be in the state that it was before this call began. + + :param context: security context + :param instance: Instance of {nova.compute.service.Instance}. + This function should use the data there to guide + the creation of the new instance. + :param network_info: + :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info` + :param block_device_info: """ raise NotImplementedError() @@ -148,15 +153,17 @@ class ComputeDriver(object): """Destroy (shutdown and delete) the specified instance. The given parameter is an instance of nova.compute.service.Instance, - and so the instance is being specified as instance.name. - - The work will be done asynchronously. This function returns a - task that allows the caller to detect when it is complete. If the instance is not found (for example if networking failed), this function should still succeed. It's probably a good idea to log a warning in that case. + :param instance: Instance of {nova.compute.service.Instance} and so + the instance is being specified as instance.name. + :param network_info: + :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info` + :param cleanup: + """ # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() @@ -164,8 +171,10 @@ class ComputeDriver(object): def reboot(self, instance, network_info): """Reboot the specified instance. - The given parameter is an instance of nova.compute.service.Instance, - and so the instance is being specified as instance.name. + :param instance: Instance of {nova.compute.service.Instance} and so + the instance is being specified as instance.name. + :param network_info: + :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info` """ # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() @@ -206,6 +215,17 @@ class ComputeDriver(object): raise NotImplementedError() def compare_cpu(self, cpu_info): + """Compares given cpu info against host + + Before attempting to migrate a VM to this host, + compare_cpu is called to ensure that the VM will + actually run here. + + :param cpu_info: (str) JSON structure describing the source CPU. + :returns: None if migration is acceptable + :raises: :py:class:`~nova.exception.InvalidCPUInfo` if migration + is not acceptable. + """ raise NotImplementedError() def migrate_disk_and_power_off(self, instance, dest): @@ -229,7 +249,11 @@ class ComputeDriver(object): def finish_migration(self, context, instance, disk_info, network_info, resize_instance): - """Completes a resize, turning on the migrated instance""" + """Completes a resize, turning on the migrated instance + + :param network_info: + :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info` + """ raise NotImplementedError() def revert_migration(self, instance): @@ -283,15 +307,15 @@ class ComputeDriver(object): post_method, recover_method): """Spawning live_migration operation for distributing high-load. - :params ctxt: security context - :params instance_ref: + :param ctxt: security context + :param instance_ref: nova.db.sqlalchemy.models.Instance object instance object that is migrated. - :params dest: destination host - :params post_method: + :param dest: destination host + :param post_method: post operation method. expected nova.compute.manager.post_live_migration. - :params recover_method: + :param recover_method: recovery method when any exception occurs. expected nova.compute.manager.recover_live_migration. @@ -541,3 +565,4 @@ class ComputeDriver(object): Note that this function takes an instance ID. """ raise NotImplementedError() + -- cgit From d1f5d1fae10bb8a1d0e445ec2b9153542eb025f4 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Fri, 19 Aug 2011 13:54:18 +0200 Subject: pep8 --- nova/virt/driver.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nova/virt/driver.py b/nova/virt/driver.py index 6d3af7e0a..93290aba7 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -565,4 +565,3 @@ class ComputeDriver(object): Note that this function takes an instance ID. """ raise NotImplementedError() - -- cgit From 83d4c5b9b1f7ed9b75ae04464423b7ca4b5d627d Mon Sep 17 00:00:00 2001 From: Christopher MacGown Date: Fri, 19 Aug 2011 08:08:23 -0700 Subject: Fix config_drive migration, per Matt Dietz. --- .../versions/037_add_config_drive_to_instances.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/037_add_config_drive_to_instances.py b/nova/db/sqlalchemy/migrate_repo/versions/037_add_config_drive_to_instances.py index 65ea012dd..36a6af16f 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/037_add_config_drive_to_instances.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/037_add_config_drive_to_instances.py @@ -23,20 +23,15 @@ meta = MetaData() instances = Table("instances", meta, Column("id", Integer(), primary_key=True, nullable=False)) -config_drive_column = Column("config_drive", String(255)) # matches image_ref + +# matches the size of an image_ref +config_drive_column = Column("config_drive", String(255), nullable=True) def upgrade(migrate_engine): meta.bind = migrate_engine instances.create_column(config_drive_column) - rows = migrate_engine.execute(instances.select()) - for row in rows: - instance_config_drive = None # pre-existing instances don't have one. - migrate_engine.execute(instances.update()\ - .where(instances.c.id == row[0])\ - .values(config_drive=instance_config_drive)) - def downgrade(migrate_engine): meta.bind = migrate_engine -- cgit From 276403dcb6a8c7802c456b88f8dad249b7513e64 Mon Sep 17 00:00:00 2001 From: Christopher MacGown Date: Fri, 19 Aug 2011 08:16:17 -0700 Subject: Define FLAGS.default_local_format. By default it's None, to match current expected _create_local --- nova/virt/libvirt/connection.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 1b48fd795..a715da66e 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -126,6 +126,10 @@ flags.DEFINE_string('libvirt_vif_type', 'bridge', flags.DEFINE_string('libvirt_vif_driver', 'nova.virt.libvirt.vif.LibvirtBridgeDriver', 'The libvirt VIF driver to configure the VIFs.') +flags.DEFINE_string('default_local_format', + None, + 'The default format a local_volume will be formatted with ' + 'on creation.') def get_connection(read_only): -- cgit From c4fc9f0737ec9f8d5c950b850fed9930a68164f4 Mon Sep 17 00:00:00 2001 From: Christopher MacGown Date: Fri, 19 Aug 2011 08:44:14 -0700 Subject: Add copyright notices --- nova/api/openstack/create_instance_helper.py | 1 + nova/api/openstack/views/servers.py | 1 + nova/compute/api.py | 1 + .../migrate_repo/versions/037_add_config_drive_to_instances.py | 4 ++-- nova/db/sqlalchemy/models.py | 1 + nova/scheduler/simple.py | 1 - nova/tests/api/openstack/test_servers.py | 1 + nova/tests/test_compute.py | 1 + nova/virt/libvirt/connection.py | 1 + nova/virt/xenapi/vm_utils.py | 1 + 10 files changed, 10 insertions(+), 3 deletions(-) diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index d776ae92d..563ef1c42 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -1,4 +1,5 @@ # Copyright 2011 OpenStack LLC. +# Copyright 2011 Piston Cloud Computing, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py index c7bc03bcb..19acb0899 100644 --- a/nova/api/openstack/views/servers.py +++ b/nova/api/openstack/views/servers.py @@ -1,6 +1,7 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2010-2011 OpenStack LLC. +# Copyright 2011 Piston Cloud Computing, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may diff --git a/nova/compute/api.py b/nova/compute/api.py index e42a5bbad..ccf29bd1a 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -2,6 +2,7 @@ # Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. +# Copyright 2011 Piston Cloud Computing, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may diff --git a/nova/db/sqlalchemy/migrate_repo/versions/037_add_config_drive_to_instances.py b/nova/db/sqlalchemy/migrate_repo/versions/037_add_config_drive_to_instances.py index 36a6af16f..d3058f00d 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/037_add_config_drive_to_instances.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/037_add_config_drive_to_instances.py @@ -1,6 +1,6 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. +# +# Copyright 2011 Piston Cloud Computing, Inc. # # 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 diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 8a6e2f673..c454cfcc3 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -2,6 +2,7 @@ # Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. +# Copyright 2011 Piston Cloud Computing, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may diff --git a/nova/scheduler/simple.py b/nova/scheduler/simple.py index 61c76b35d..fc1b3142a 100644 --- a/nova/scheduler/simple.py +++ b/nova/scheduler/simple.py @@ -41,7 +41,6 @@ class SimpleScheduler(chance.ChanceScheduler): def _schedule_instance(self, context, instance_id, *_args, **_kwargs): """Picks a host that is up and has the fewest running instances.""" - instance_ref = db.instance_get(context, instance_id) if (instance_ref['availability_zone'] and ':' in instance_ref['availability_zone'] diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 7d8b222cc..0a46c3fd1 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -1,6 +1,7 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2010-2011 OpenStack LLC. +# Copyright 2011 Piston Cloud Computing, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 227b42fd7..8d1b95f74 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -2,6 +2,7 @@ # Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. +# Copyright 2011 Piston Cloud Computing, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index a715da66e..ea47fa99d 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -4,6 +4,7 @@ # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. # Copyright (c) 2010 Citrix Systems, Inc. +# Copyright (c) 2011 Piston Cloud Computing, Inc # # 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 diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 206f522c9..3861f6bd8 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -1,6 +1,7 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright (c) 2010 Citrix Systems, Inc. +# Copyright 2011 Piston Cloud Computing, Inc. # # 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 -- cgit From 25ec08a208474e1cf5827c1eb8b231d631c489e9 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Fri, 19 Aug 2011 20:09:31 +0200 Subject: Revert irrelevant changes that accidentally crept into this patch :( --- nova/virt/fake.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 67cc1ea29..13b7aeab5 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -152,11 +152,9 @@ class FakeConnection(driver.ComputeDriver): (key, self.instances)) def attach_volume(self, instance_name, device_path, mountpoint): - self._mounts[mountpoint] = device_path return True def detach_volume(self, instance_name, mountpoint): - del self._mounts[mountpoint] return True def get_info(self, instance_name): -- cgit From d27a4d4a59a0103762ece2776ddd484629a72d54 Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Fri, 19 Aug 2011 14:25:41 -0700 Subject: Fixed review comments --- nova/db/api.py | 21 +---- nova/db/sqlalchemy/api.py | 104 ++++++--------------- .../versions/037_add_uuid_to_networks.py | 43 --------- .../versions/039_add_uuid_to_networks.py | 43 +++++++++ nova/exception.py | 6 +- nova/network/manager.py | 68 ++++++-------- nova/tests/test_network.py | 81 ++++++++-------- 7 files changed, 151 insertions(+), 215 deletions(-) delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/037_add_uuid_to_networks.py create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/039_add_uuid_to_networks.py diff --git a/nova/db/api.py b/nova/db/api.py index 6833e6312..34d560ca3 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -323,13 +323,13 @@ def migration_get_by_instance_and_status(context, instance_uuid, status): #################### -def fixed_ip_associate(context, address, instance_id): +def fixed_ip_associate(context, address, instance_id, network_id=None): """Associate fixed ip to instance. Raises if fixed ip is not available. """ - return IMPL.fixed_ip_associate(context, address, instance_id) + return IMPL.fixed_ip_associate(context, address, instance_id, network_id) def fixed_ip_associate_pool(context, network_id, instance_id=None, host=None): @@ -342,14 +342,6 @@ def fixed_ip_associate_pool(context, network_id, instance_id=None, host=None): instance_id, host) -def fixed_ip_associate_by_address(context, network_id, instance_id, address): - """check if the address is free and is in the network - and it is not associated to any instance. - """ - return IMPL.fixed_ip_associate_by_address(context, network_id, - instance_id, address) - - def fixed_ip_create(context, values): """Create a fixed ip from the values dictionary.""" return IMPL.fixed_ip_create(context, values) @@ -687,9 +679,9 @@ def network_get_all(context): return IMPL.network_get_all(context) -def network_get_networks_by_uuids(context, network_uuids): +def network_get_all_by_uuids(context, network_uuids, project_id=None): """Return networks by ids.""" - return IMPL.network_get_networks_by_uuids(context, network_uuids) + return IMPL.network_get_all_by_uuids(context, network_uuids, project_id) # pylint: disable=C0103 @@ -1244,11 +1236,6 @@ def project_get_networks(context, project_id, associate=True): return IMPL.project_get_networks(context, project_id, associate) -def project_get_networks_by_uuids(context, network_uuids): - """Return the networks by uuids associated with the project.""" - return IMPL.project_get_networks_by_uuids(context, network_uuids) - - def project_get_networks_v6(context, project_id): return IMPL.project_get_networks_v6(context, project_id) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 5aa49213a..005500058 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -266,7 +266,7 @@ def service_get_all_network_sorted(context): session = get_session() with session.begin(): topic = 'network' - label = 'network_' + label = 'network_count' subq = session.query(models.Network.host, func.count(models.Network.id).label(label)).\ filter_by(deleted=False).\ @@ -652,23 +652,36 @@ def floating_ip_update(context, address, values): ################### -@require_context -def fixed_ip_associate(context, address, instance_id): +@require_admin_context +def fixed_ip_associate(context, address, instance_id, network_id=None): session = get_session() with session.begin(): - instance = instance_get(context, instance_id, session=session) + network_or_none = or_(models.FixedIp.network_id == network_id, + models.FixedIp.network_id == None) fixed_ip_ref = session.query(models.FixedIp).\ - filter_by(address=address).\ + filter(network_or_none).\ + filter_by(reserved=False).\ filter_by(deleted=False).\ - filter_by(instance=None).\ + filter_by(address=address).\ with_lockmode('update').\ first() # NOTE(vish): if with_lockmode isn't supported, as in sqlite, # then this has concurrency issues - if not fixed_ip_ref: - raise exception.NoMoreFixedIps() - fixed_ip_ref.instance = instance + if fixed_ip_ref is None: + raise exception.FixedIpNotFoundForNetwork(address=address, + network_id=network_id) + if fixed_ip_ref.instance is not None: + raise exception.FixedIpAlreadyInUse(address=address) + + if not fixed_ip_ref.network: + fixed_ip_ref.network = network_get(context, + network_id, + session=session) + fixed_ip_ref.instance = instance_get(context, + instance_id, + session=session) session.add(fixed_ip_ref) + return fixed_ip_ref['address'] @require_admin_context @@ -703,40 +716,6 @@ def fixed_ip_associate_pool(context, network_id, instance_id=None, host=None): return fixed_ip_ref['address'] -@require_admin_context -def fixed_ip_associate_by_address(context, network_id, instance_id, - address): - if address is None: - return fixed_ip_associate_pool(context, network_id, instance_id) - - session = get_session() - with session.begin(): - fixed_ip_ref = session.query(models.FixedIp).\ - filter_by(reserved=False).\ - filter_by(deleted=False).\ - filter_by(network_id=network_id).\ - filter_by(address=address).\ - with_lockmode('update').\ - first() - # NOTE(vish): if with_lockmode isn't supported, as in sqlite, - # then this has concurrency issues - if fixed_ip_ref is None: - raise exception.FixedIpNotFoundForNetwork(address=address, - network_id=network_id) - if fixed_ip_ref.instance is not None: - raise exception.FixedIpAlreadyInUse(address=address) - - if not fixed_ip_ref.network: - fixed_ip_ref.network = network_get(context, - network_id, - session=session) - fixed_ip_ref.instance = instance_get(context, - instance_id, - session=session) - session.add(fixed_ip_ref) - return fixed_ip_ref['address'] - - @require_context def fixed_ip_create(_context, values): fixed_ip_ref = models.FixedIp() @@ -1777,10 +1756,13 @@ def network_get_all(context): @require_admin_context -def network_get_networks_by_uuids(context, network_uuids): +def network_get_all_by_uuids(context, network_uuids, project_id=None): session = get_session() + project_or_none = or_(models.Network.project_id == project_id, + models.Network.project_id == None) result = session.query(models.Network).\ filter(models.Network.uuid.in_(network_uuids)).\ + filter(project_or_none).\ filter_by(deleted=False).all() if not result: raise exception.NoNetworksFound() @@ -1800,6 +1782,9 @@ def network_get_networks_by_uuids(context, network_uuids): found = True break if not found: + if project_id: + raise exception.NetworkNotFoundForProject(network_uuid=uuid, + project_id=context.project_id) raise exception.NetworkNotFound(network_id=network_uuid) return result @@ -2961,37 +2946,6 @@ def project_get_networks(context, project_id, associate=True): return result -@require_context -def project_get_networks_by_uuids(context, network_uuids): - session = get_session() - result = session.query(models.Network).\ - filter(models.Network.uuid.in_(network_uuids)).\ - filter_by(deleted=False).\ - filter_by(project_id=context.project_id).all() - - if not result: - raise exception.NoNetworksFound() - - #check if host is set to all of the networks - # returned in the result - for network in result: - if network['host'] is None: - raise exception.NetworkHostNotSet(network_id=network['id']) - - #check if the result contains all the networks - #we are looking for - for uuid in network_uuids: - found = False - for network in result: - if network['uuid'] == uuid: - found = True - break - if not found: - raise exception.NetworkNotFoundForProject(network_uuid=uuid, - project_id=context.project_id) - return result - - @require_context def project_get_networks_v6(context, project_id): return project_get_networks(context, project_id) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/037_add_uuid_to_networks.py b/nova/db/sqlalchemy/migrate_repo/versions/037_add_uuid_to_networks.py deleted file mode 100644 index 38c543d51..000000000 --- a/nova/db/sqlalchemy/migrate_repo/versions/037_add_uuid_to_networks.py +++ /dev/null @@ -1,43 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. -# -# 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. - -from sqlalchemy import Column, Integer, MetaData, String, Table - -from nova import utils - - -meta = MetaData() - -networks = Table("networks", meta, - Column("id", Integer(), primary_key=True, nullable=False)) -uuid_column = Column("uuid", String(36)) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - networks.create_column(uuid_column) - - rows = migrate_engine.execute(networks.select()) - for row in rows: - networks_uuid = str(utils.gen_uuid()) - migrate_engine.execute(networks.update()\ - .where(networks.c.id == row[0])\ - .values(uuid=networks_uuid)) - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - networks.drop_column(uuid_column) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/039_add_uuid_to_networks.py b/nova/db/sqlalchemy/migrate_repo/versions/039_add_uuid_to_networks.py new file mode 100644 index 000000000..38c543d51 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/039_add_uuid_to_networks.py @@ -0,0 +1,43 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# +# 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. + +from sqlalchemy import Column, Integer, MetaData, String, Table + +from nova import utils + + +meta = MetaData() + +networks = Table("networks", meta, + Column("id", Integer(), primary_key=True, nullable=False)) +uuid_column = Column("uuid", String(36)) + + +def upgrade(migrate_engine): + meta.bind = migrate_engine + networks.create_column(uuid_column) + + rows = migrate_engine.execute(networks.select()) + for row in rows: + networks_uuid = str(utils.gen_uuid()) + migrate_engine.execute(networks.update()\ + .where(networks.c.id == row[0])\ + .values(uuid=networks_uuid)) + + +def downgrade(migrate_engine): + meta.bind = migrate_engine + networks.drop_column(uuid_column) diff --git a/nova/exception.py b/nova/exception.py index 97275fa25..262122990 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -185,10 +185,6 @@ class Invalid(NovaException): message = _("Unacceptable parameters.") -class AlreadyInUse(NovaException): - message = _("Already is in use.") - - class InvalidSignature(Invalid): message = _("Invalid signature %(signature)s for user %(user)s.") @@ -474,7 +470,7 @@ class FixedIpNotFoundForNetwork(FixedIpNotFound): "network (%(network_uuid)s).") -class FixedIpAlreadyInUse(AlreadyInUse): +class FixedIpAlreadyInUse(NovaException): message = _("Fixed IP address %(address)s is already in use.") diff --git a/nova/network/manager.py b/nova/network/manager.py index 9565c879f..aa2a3700c 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -399,8 +399,8 @@ class NetworkManager(manager.SchedulerDependentManager): # a non-vlan instance should connect to if requested_networks is not None and len(requested_networks) != 0: network_uuids = [uuid for (uuid, fixed_ip) in requested_networks] - networks = self.db.network_get_networks_by_uuids(context, - network_uuids) + networks = self.db.network_get_all_by_uuids(context, + network_uuids) else: try: networks = self.db.network_get_all(context) @@ -588,10 +588,15 @@ class NetworkManager(manager.SchedulerDependentManager): # network_get_by_compute_host address = None if network['cidr']: - address = self.db.fixed_ip_associate_by_address(context.elevated(), - network['id'], - instance_id, - kwargs.get('address', None)) + address = kwargs.get('address', None) + if address: + address = self.db.fixed_ip_associate(context, + address, instance_id, + network['id']) + else: + address = self.db.fixed_ip_associate_pool(context.elevated(), + network['id'], + instance_id) self._do_trigger_security_group_members_refresh_for_instance( instance_id) get_vif = self.db.virtual_interface_get_by_instance_and_network @@ -826,7 +831,7 @@ class NetworkManager(manager.SchedulerDependentManager): network_uuids = [uuid for (uuid, fixed_ip) in networks] - self.db.network_get_networks_by_uuids(context, network_uuids) + self._get_networks_by_uuids(context, network_uuids) for network_uuid, address in networks: # check if the fixed IP address is valid and @@ -843,6 +848,9 @@ class NetworkManager(manager.SchedulerDependentManager): if fixed_ip_ref['instance'] is not None: raise exception.FixedIpAlreadyInUse(address=address) + def _get_networks_by_uuids(self, context, network_uuids): + return self.db.network_get_all_by_uuids(context, network_uuids) + class FlatManager(NetworkManager): """Basic network where no vlans are used. @@ -980,10 +988,15 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): address, instance_id) else: - address = self.db.fixed_ip_associate_by_address(context, - network['id'], - instance_id, - kwargs.get('address', None)) + address = kwargs.get('address', None) + if address: + address = self.db.fixed_ip_associate(context, address, + instance_id, + network['id']) + else: + address = self.db.fixed_ip_associate_pool(context, + network['id'], + instance_id) self._do_trigger_security_group_members_refresh_for_instance( instance_id) vif = self.db.virtual_interface_get_by_instance_and_network(context, @@ -1005,8 +1018,9 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): # get networks associated with project if requested_networks is not None and len(requested_networks) != 0: network_uuids = [uuid for (uuid, fixed_ip) in requested_networks] - networks = self.db.project_get_networks_by_uuids(context, - network_uuids) + networks = self.db.network_get_all_by_uuids(context, + network_uuids, + project_id) else: networks = self.db.project_get_networks(context, project_id) return networks @@ -1058,31 +1072,9 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): self.db.network_update(context, network_ref['id'], {'gateway_v6': gateway}) - def validate_networks(self, context, networks): - """check if the networks exists and host - is set to each network. - """ - if networks is None or len(networks) == 0: - return - - network_uuids = [uuid for (uuid, fixed_ip) in networks] - - self.db.project_get_networks_by_uuids(context, network_uuids) - - for network_uuid, address in networks: - # check if the fixed IP address is valid and - # it actually belongs to the network - if fixed_ip is not None: - if not utils.is_valid_ipv4(address): - raise exception.FixedIpInvalid(address=address) - - fixed_ip_ref = self.db.fixed_ip_get_by_address(context, - address) - if fixed_ip_ref['network']['uuid'] != network_uuid: - raise exception.FixedIpNotFoundForNetwork(address=address, - network_uuid=network_uuid) - if fixed_ip_ref['instance'] is not None: - raise exception.FixedIpAlreadyInUse(address=address) + def _get_networks_by_uuids(self, context, network_uuids): + return self.db.network_get_all_by_uuids(context, network_uuids, + context.project_id) @property def _bottom_reserved_ips(self): diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index 86fa9ee7f..0b8539442 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -15,6 +15,7 @@ # License for the specific language governing permissions and limitations # under the License. +from nova import context from nova import db from nova import exception from nova import log as logging @@ -128,6 +129,8 @@ class FlatNetworkTestCase(test.TestCase): super(FlatNetworkTestCase, self).setUp() self.network = network_manager.FlatManager(host=HOST) self.network.db = db + self.context = context.RequestContext('testuser', 'testproject', + is_admin=False) def test_get_instance_nw_info(self): self.mox.StubOutWithMock(db, 'fixed_ip_get_by_instance') @@ -186,13 +189,13 @@ class FlatNetworkTestCase(test.TestCase): self.assertDictListMatch(nw[1]['ips'], check) def test_validate_networks(self): - self.mox.StubOutWithMock(db, 'network_get_networks_by_uuids') + self.mox.StubOutWithMock(db, 'network_get_all_by_uuids') self.mox.StubOutWithMock(db, "fixed_ip_get_by_address") requested_networks = [("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", "192.168.1.100")] - db.network_get_networks_by_uuids(mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn(networks) + db.network_get_all_by_uuids(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(networks) fixed_ips[1]['network'] = FakeModel(**networks[1]) fixed_ips[1]['instance'] = None @@ -200,22 +203,22 @@ class FlatNetworkTestCase(test.TestCase): mox.IgnoreArg()).AndReturn(fixed_ips[1]) self.mox.ReplayAll() - self.network.validate_networks(None, requested_networks) + self.network.validate_networks(self.context, requested_networks) def test_validate_networks_none_requested_networks(self): - self.network.validate_networks(None, None) + self.network.validate_networks(self.context, None) def test_validate_networks_empty_requested_networks(self): requested_networks = [] self.mox.ReplayAll() - self.network.validate_networks(None, requested_networks) + self.network.validate_networks(self.context, requested_networks) def test_validate_networks_invalid_fixed_ip(self): - self.mox.StubOutWithMock(db, 'network_get_networks_by_uuids') + self.mox.StubOutWithMock(db, 'network_get_all_by_uuids') requested_networks = [(1, "192.168.0.100.1")] - db.network_get_networks_by_uuids(mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn(networks) + db.network_get_all_by_uuids(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(networks) self.mox.ReplayAll() self.assertRaises(exception.FixedIpInvalid, @@ -223,11 +226,11 @@ class FlatNetworkTestCase(test.TestCase): requested_networks) def test_validate_networks_empty_fixed_ip(self): - self.mox.StubOutWithMock(db, 'network_get_networks_by_uuids') + self.mox.StubOutWithMock(db, 'network_get_all_by_uuids') requested_networks = [(1, "")] - db.network_get_networks_by_uuids(mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn(networks) + db.network_get_all_by_uuids(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(networks) self.mox.ReplayAll() self.assertRaises(exception.FixedIpInvalid, @@ -235,10 +238,10 @@ class FlatNetworkTestCase(test.TestCase): None, requested_networks) def test_validate_networks_none_fixed_ip(self): - self.mox.StubOutWithMock(db, 'network_get_networks_by_uuids') + self.mox.StubOutWithMock(db, 'network_get_all_by_uuids') requested_networks = [(1, None)] - db.network_get_networks_by_uuids(mox.IgnoreArg(), + db.network_get_all_by_uuids(mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(networks) self.mox.ReplayAll() @@ -250,6 +253,8 @@ class VlanNetworkTestCase(test.TestCase): super(VlanNetworkTestCase, self).setUp() self.network = network_manager.VlanManager(host=HOST) self.network.db = db + self.context = context.RequestContext('testuser', 'testproject', + is_admin=False) def test_vpn_allocate_fixed_ip(self): self.mox.StubOutWithMock(db, 'fixed_ip_associate') @@ -272,7 +277,7 @@ class VlanNetworkTestCase(test.TestCase): self.network.allocate_fixed_ip(None, 0, network, vpn=True) def test_allocate_fixed_ip(self): - self.mox.StubOutWithMock(db, 'fixed_ip_associate_by_address') + self.mox.StubOutWithMock(db, 'fixed_ip_associate_pool') self.mox.StubOutWithMock(db, 'fixed_ip_update') self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance_and_network') @@ -281,8 +286,7 @@ class VlanNetworkTestCase(test.TestCase): db.instance_get(mox.IgnoreArg(), mox.IgnoreArg()).AndReturn({'security_groups': [{'id': 0}]}) - db.fixed_ip_associate_by_address(mox.IgnoreArg(), - mox.IgnoreArg(), + db.fixed_ip_associate_pool(mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg()).AndReturn('192.168.0.1') db.fixed_ip_update(mox.IgnoreArg(), @@ -294,7 +298,7 @@ class VlanNetworkTestCase(test.TestCase): network = dict(networks[0]) network['vpn_private_address'] = '192.168.0.2' - self.network.allocate_fixed_ip(None, 0, network) + self.network.allocate_fixed_ip(self.context, 0, network) def test_create_networks_too_big(self): self.assertRaises(ValueError, self.network.create_networks, None, @@ -306,13 +310,14 @@ class VlanNetworkTestCase(test.TestCase): cidr='192.168.0.1/24', network_size=100) def test_validate_networks(self): - self.mox.StubOutWithMock(db, 'project_get_networks_by_uuids') + self.mox.StubOutWithMock(db, 'network_get_all_by_uuids') self.mox.StubOutWithMock(db, "fixed_ip_get_by_address") requested_networks = [("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", "192.168.1.100")] - db.project_get_networks_by_uuids(mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn(networks) + db.network_get_all_by_uuids(mox.IgnoreArg(), + mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(networks) fixed_ips[1]['network'] = FakeModel(**networks[1]) fixed_ips[1]['instance'] = None @@ -320,49 +325,51 @@ class VlanNetworkTestCase(test.TestCase): mox.IgnoreArg()).AndReturn(fixed_ips[1]) self.mox.ReplayAll() - self.network.validate_networks(None, requested_networks) + self.network.validate_networks(self.context, requested_networks) def test_validate_networks_none_requested_networks(self): - self.network.validate_networks(None, None) + self.network.validate_networks(self.context, None) def test_validate_networks_empty_requested_networks(self): requested_networks = [] self.mox.ReplayAll() - self.network.validate_networks(None, requested_networks) + self.network.validate_networks(self.context, requested_networks) def test_validate_networks_invalid_fixed_ip(self): - self.mox.StubOutWithMock(db, 'project_get_networks_by_uuids') + self.mox.StubOutWithMock(db, 'network_get_all_by_uuids') requested_networks = [(1, "192.168.0.100.1")] - db.project_get_networks_by_uuids(mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn(networks) + db.network_get_all_by_uuids(mox.IgnoreArg(), + mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(networks) self.mox.ReplayAll() self.assertRaises(exception.FixedIpInvalid, - self.network.validate_networks, None, + self.network.validate_networks, self.context, requested_networks) def test_validate_networks_empty_fixed_ip(self): - self.mox.StubOutWithMock(db, 'project_get_networks_by_uuids') + self.mox.StubOutWithMock(db, 'network_get_all_by_uuids') requested_networks = [(1, "")] - db.project_get_networks_by_uuids(mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn(networks) + db.network_get_all_by_uuids(mox.IgnoreArg(), + mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(networks) self.mox.ReplayAll() self.assertRaises(exception.FixedIpInvalid, self.network.validate_networks, - None, requested_networks) + self.context, requested_networks) def test_validate_networks_none_fixed_ip(self): - self.mox.StubOutWithMock(db, 'project_get_networks_by_uuids') + self.mox.StubOutWithMock(db, 'network_get_all_by_uuids') requested_networks = [(1, None)] - db.project_get_networks_by_uuids(mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn(networks) + db.network_get_all_by_uuids(mox.IgnoreArg(), + mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(networks) self.mox.ReplayAll() - - self.network.validate_networks(None, requested_networks) + self.network.validate_networks(self.context, requested_networks) class CommonNetworkTestCase(test.TestCase): -- cgit From f2fdb6a3028172d9085d0759d4b6770da9e71cb7 Mon Sep 17 00:00:00 2001 From: Launchpad Translations on behalf of nova-core <> Date: Sat, 20 Aug 2011 05:06:37 +0000 Subject: Launchpad automatic translations update. --- po/zh_CN.po | 122 ++++++++---------------------------------------------------- 1 file changed, 15 insertions(+), 107 deletions(-) diff --git a/po/zh_CN.po b/po/zh_CN.po index d2caff810..6284ee46c 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: nova\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2011-02-21 10:03-0500\n" -"PO-Revision-Date: 2011-08-04 05:50+0000\n" +"PO-Revision-Date: 2011-08-19 09:26+0000\n" "Last-Translator: zhangjunfeng \n" "Language-Team: Chinese (Simplified) \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-08-05 04:47+0000\n" -"X-Generator: Launchpad (build 13604)\n" +"X-Launchpad-Export-Date: 2011-08-20 05:06+0000\n" +"X-Generator: Launchpad (build 13697)\n" #, python-format #~ msgid "Starting %s" @@ -48,7 +48,7 @@ msgstr "" #: ../nova/exception.py:107 msgid "DB exception wrapped" -msgstr "" +msgstr "数据库异常" #. exc_type, exc_value, exc_traceback = sys.exc_info() #: ../nova/exception.py:120 @@ -88,7 +88,7 @@ msgstr "获取外网IP失败" #: ../nova/api/openstack/servers.py:152 #, python-format msgid "%(param)s property not found for image %(_image_id)s" -msgstr "" +msgstr "没有找到镜像文件%(_image_id)s 的属性 %(param)s" #: ../nova/api/openstack/servers.py:168 msgid "No keypairs defined" @@ -97,55 +97,55 @@ msgstr "未定义密钥对" #: ../nova/api/openstack/servers.py:238 #, python-format msgid "Compute.api::lock %s" -msgstr "" +msgstr "compute.api::加锁 %s" #: ../nova/api/openstack/servers.py:253 #, python-format msgid "Compute.api::unlock %s" -msgstr "" +msgstr "compute.api::解锁 %s" #: ../nova/api/openstack/servers.py:267 #, python-format msgid "Compute.api::get_lock %s" -msgstr "" +msgstr "Compute.api::得到锁 %s" #: ../nova/api/openstack/servers.py:281 #, python-format msgid "Compute.api::reset_network %s" -msgstr "" +msgstr "Compute.api::重置网络 %s" #: ../nova/api/openstack/servers.py:292 #, python-format msgid "Compute.api::pause %s" -msgstr "" +msgstr "Compute.api::暂停 %s" #: ../nova/api/openstack/servers.py:303 #, python-format msgid "Compute.api::unpause %s" -msgstr "" +msgstr "Compute.api::继续 %s" #: ../nova/api/openstack/servers.py:314 #, python-format msgid "compute.api::suspend %s" -msgstr "" +msgstr "compute.api::挂起 %s" #: ../nova/api/openstack/servers.py:325 #, python-format msgid "compute.api::resume %s" -msgstr "" +msgstr "compute.api::回复 %s" #: ../nova/virt/xenapi/volumeops.py:48 ../nova/virt/xenapi/volumeops.py:101 #: ../nova/db/sqlalchemy/api.py:731 ../nova/virt/libvirt_conn.py:741 #: ../nova/api/ec2/__init__.py:317 #, python-format msgid "Instance %s not found" -msgstr "" +msgstr "实例 %s 没有找到" #. NOTE: No Resource Pool concept so far #: ../nova/virt/xenapi/volumeops.py:51 #, python-format msgid "Attach_volume: %(instance_name)s, %(device_path)s, %(mountpoint)s" -msgstr "" +msgstr "挂载卷:%(instance_name)s, %(device_path)s, %(mountpoint)s" #: ../nova/virt/xenapi/volumeops.py:69 #, python-format @@ -2799,101 +2799,9 @@ msgstr "添加用户 %(user)s 到项目 %(project)s 中" msgid "Removing user %(user)s from project %(project)s" msgstr "从项目 %(project)s 中移除用户 %(user)s" -#, python-format -#~ msgid "" -#~ "%s\n" -#~ "Command: %s\n" -#~ "Exit code: %s\n" -#~ "Stdout: %r\n" -#~ "Stderr: %r" -#~ msgstr "" -#~ "%s\n" -#~ "命令:%s\n" -#~ "退出代码:%s\n" -#~ "标准输出(stdout):%r\n" -#~ "标准错误(stderr):%r" - -#, python-format -#~ msgid "Binding %s to %s with key %s" -#~ msgstr "将%s绑定到%s(以%s键值)" - -#, python-format -#~ msgid "AMQP server on %s:%d is unreachable. Trying again in %d seconds." -#~ msgstr "位于%s:%d的AMQP服务器不可用。%d秒后重试。" - -#, python-format -#~ msgid "Getting from %s: %s" -#~ msgstr "从%s获得如下内容:%s" - -#, python-format -#~ msgid "Starting %s node" -#~ msgstr "启动%s节点" - -#, python-format -#~ msgid "Data store %s is unreachable. Trying again in %d seconds." -#~ msgstr "数据储存服务%s不可用。%d秒之后继续尝试。" - #~ msgid "Full set of FLAGS:" #~ msgstr "FLAGS全集:" -#, python-format -#~ msgid "(%s) publish (key: %s) %s" -#~ msgstr "(%s)发布(键值:%s)%s" - -#, python-format -#~ msgid "Couldn't get IP, using 127.0.0.1 %s" -#~ msgstr "不能获取IP,将使用 127.0.0.1 %s" - -#, python-format -#~ msgid "" -#~ "Access key %s has had %d failed authentications and will be locked out for " -#~ "%d minutes." -#~ msgstr "访问键 %s时,存在%d个失败的认证,将于%d分钟后解锁" - -#, python-format -#~ msgid "Authenticated Request For %s:%s)" -#~ msgstr "为%s:%s申请认证" - -#, python-format -#~ msgid "arg: %s\t\tval: %s" -#~ msgstr "键为: %s\t\t值为: %s" - -#, python-format -#~ msgid "Getting x509 for user: %s on project: %s" -#~ msgstr "为用户 %s从工程%s中获取 x509" - -#, python-format -#~ msgid "Create project %s managed by %s" -#~ msgstr "创建工程%s,此工程由%s管理" - -#, python-format -#~ msgid "Unsupported API request: controller = %s,action = %s" -#~ msgstr "不支持的API请求: 控制器 = %s,执行 = %s" - -#, python-format -#~ msgid "Adding sitewide role %s to user %s" -#~ msgstr "增加站点范围的 %s角色给用户 %s" - -#, python-format -#~ msgid "Adding user %s to project %s" -#~ msgstr "增加用户%s到%s工程" - -#, python-format -#~ msgid "Unauthorized request for controller=%s and action=%s" -#~ msgstr "对控制器=%s及动作=%s未经授权" - -#, python-format -#~ msgid "Removing user %s from project %s" -#~ msgstr "正将用户%s从工程%s中移除" - -#, python-format -#~ msgid "Adding role %s to user %s for project %s" -#~ msgstr "正将%s角色赋予用户%s(在工程%s中)" - -#, python-format -#~ msgid "Removing role %s from user %s for project %s" -#~ msgstr "正将角色%s从用户%s在工程%s中移除" - #~ msgid "No such process" #~ msgstr "没有该进程" -- cgit From 6d6ea16b2840d876120f7cad2aa679eee0370cb9 Mon Sep 17 00:00:00 2001 From: Launchpad Translations on behalf of nova-core <> Date: Mon, 22 Aug 2011 04:48:50 +0000 Subject: Launchpad automatic translations update. --- po/it.po | 83 +++++++++++++++++++--------------------------------------------- 1 file changed, 24 insertions(+), 59 deletions(-) diff --git a/po/it.po b/po/it.po index 14e9a6de6..e166297f1 100644 --- a/po/it.po +++ b/po/it.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: nova\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2011-02-21 10:03-0500\n" -"PO-Revision-Date: 2011-02-22 19:34+0000\n" -"Last-Translator: Armando Migliaccio \n" +"PO-Revision-Date: 2011-08-21 22:50+0000\n" +"Last-Translator: Guido Davide Dall'Olio \n" "Language-Team: Italian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-08-03 04:44+0000\n" -"X-Generator: Launchpad (build 13573)\n" +"X-Launchpad-Export-Date: 2011-08-22 04:48+0000\n" +"X-Generator: Launchpad (build 13697)\n" #: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55 #: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110 @@ -449,24 +449,24 @@ msgstr "" #: ../nova/scheduler/simple.py:53 #, python-format msgid "Host %s is not alive" -msgstr "" +msgstr "L'host %s non è attivo" #: ../nova/scheduler/simple.py:65 msgid "All hosts have too many cores" -msgstr "" +msgstr "Gli host hanno troppi core" #: ../nova/scheduler/simple.py:87 #, python-format msgid "Host %s not available" -msgstr "" +msgstr "Host %s non disponibile" #: ../nova/scheduler/simple.py:99 msgid "All hosts have too many gigabytes" -msgstr "" +msgstr "Gli Host hanno troppy gigabyte" #: ../nova/scheduler/simple.py:119 msgid "All hosts have too many networks" -msgstr "" +msgstr "Gli host hanno troppe reti" #: ../nova/volume/manager.py:85 #, python-format @@ -496,7 +496,7 @@ msgstr "" #: ../nova/volume/manager.py:123 #, python-format msgid "volume %s: created successfully" -msgstr "" +msgstr "volume %s: creato con successo" #: ../nova/volume/manager.py:131 msgid "Volume is still attached" @@ -514,12 +514,12 @@ msgstr "" #: ../nova/volume/manager.py:138 #, python-format msgid "volume %s: deleting" -msgstr "" +msgstr "volume %s: rimuovendo" #: ../nova/volume/manager.py:147 #, python-format msgid "volume %s: deleted successfully" -msgstr "" +msgstr "volume %s: rimosso con successo" #: ../nova/virt/xenapi/fake.py:74 #, python-format @@ -529,7 +529,7 @@ msgstr "" #: ../nova/virt/xenapi/fake.py:304 ../nova/virt/xenapi/fake.py:404 #: ../nova/virt/xenapi/fake.py:422 ../nova/virt/xenapi/fake.py:478 msgid "Raising NotImplemented" -msgstr "" +msgstr "Sollevando NotImplemented" #: ../nova/virt/xenapi/fake.py:306 #, python-format @@ -539,7 +539,7 @@ msgstr "" #: ../nova/virt/xenapi/fake.py:341 #, python-format msgid "Calling %(localname)s %(impl)s" -msgstr "" +msgstr "Chiamando %(localname)s %(impl)s" #: ../nova/virt/xenapi/fake.py:346 #, python-format @@ -564,17 +564,17 @@ msgstr "" #: ../nova/virt/connection.py:73 msgid "Failed to open connection to the hypervisor" -msgstr "" +msgstr "Fallita l'apertura della connessione verso l'hypervisor" #: ../nova/network/linux_net.py:187 #, python-format msgid "Starting VLAN inteface %s" -msgstr "" +msgstr "Avviando l'interfaccia VLAN %s" #: ../nova/network/linux_net.py:208 #, python-format msgid "Starting Bridge interface for %s" -msgstr "" +msgstr "Avviando l'interfaccia Bridge per %s" #. pylint: disable=W0703 #: ../nova/network/linux_net.py:314 @@ -632,7 +632,7 @@ msgstr "Il risultato é %s" #: ../nova/utils.py:159 #, python-format msgid "Running cmd (SSH): %s" -msgstr "" +msgstr "Eseguendo cmd (SSH): %s" #: ../nova/utils.py:217 #, python-format @@ -642,7 +642,7 @@ msgstr "debug in callback: %s" #: ../nova/utils.py:222 #, python-format msgid "Running %s" -msgstr "" +msgstr "Eseguendo %s" #: ../nova/utils.py:262 #, python-format @@ -697,12 +697,12 @@ msgstr "" #: ../nova/virt/xenapi/vm_utils.py:135 ../nova/virt/hyperv.py:171 #, python-format msgid "Created VM %s..." -msgstr "" +msgstr "Creata VM %s.." #: ../nova/virt/xenapi/vm_utils.py:138 #, python-format msgid "Created VM %(instance_name)s as %(vm_ref)s." -msgstr "" +msgstr "Creata VM %(instance_name)s come %(vm_ref)s" #: ../nova/virt/xenapi/vm_utils.py:168 #, python-format @@ -771,7 +771,7 @@ msgstr "" #: ../nova/virt/xenapi/vm_utils.py:332 #, python-format msgid "Glance image %s" -msgstr "" +msgstr "Immagine Glance %s" #. we need to invoke a plugin for copying VDI's #. content into proper path @@ -783,7 +783,7 @@ msgstr "" #: ../nova/virt/xenapi/vm_utils.py:352 #, python-format msgid "Kernel/Ramdisk VDI %s destroyed" -msgstr "" +msgstr "Kernel/Ramdisk VDI %s distrutti" #: ../nova/virt/xenapi/vm_utils.py:361 #, python-format @@ -793,7 +793,7 @@ msgstr "" #: ../nova/virt/xenapi/vm_utils.py:386 ../nova/virt/xenapi/vm_utils.py:402 #, python-format msgid "Looking up vdi %s for PV kernel" -msgstr "" +msgstr "Cercando vdi %s per kernel PV" #: ../nova/virt/xenapi/vm_utils.py:397 #, python-format @@ -2802,41 +2802,6 @@ msgstr "" msgid "Removing user %(user)s from project %(project)s" msgstr "" -#, python-format -#~ msgid "" -#~ "%s\n" -#~ "Command: %s\n" -#~ "Exit code: %s\n" -#~ "Stdout: %r\n" -#~ "Stderr: %r" -#~ msgstr "" -#~ "%s\n" -#~ "Comando: %s\n" -#~ "Exit code: %s\n" -#~ "Stdout: %r\n" -#~ "Stderr: %r" - -#, python-format -#~ msgid "(%s) publish (key: %s) %s" -#~ msgstr "(%s) pubblica (chiave: %s) %s" - -#, python-format -#~ msgid "AMQP server on %s:%d is unreachable. Trying again in %d seconds." -#~ msgstr "" -#~ "Il server AMQP su %s:%d non é raggiungibile. Riprovare in %d secondi." - -#, python-format -#~ msgid "Binding %s to %s with key %s" -#~ msgstr "Collegando %s a %s con la chiave %s" - -#, python-format -#~ msgid "Starting %s node" -#~ msgstr "Avviando il nodo %s" - -#, python-format -#~ msgid "Data store %s is unreachable. Trying again in %d seconds." -#~ msgstr "Datastore %s é irrangiungibile. Riprovare in %d seconds." - #~ msgid "Full set of FLAGS:" #~ msgstr "Insieme di FLAGS:" -- cgit From 8d62d47a1148cc79c0ef0330e0c2d70177ea71c8 Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Mon, 22 Aug 2011 16:39:05 -0400 Subject: Stubbed out the database in order to improve tests --- nova/tests/test_nova_manage.py | 42 +++++++++++------------------------------- 1 file changed, 11 insertions(+), 31 deletions(-) diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py index 9c6563f14..3a6e48dec 100644 --- a/nova/tests/test_nova_manage.py +++ b/nova/tests/test_nova_manage.py @@ -28,55 +28,35 @@ sys.dont_write_bytecode = True import imp nova_manage = imp.load_source('nova_manage.py', NOVA_MANAGE_PATH) sys.dont_write_bytecode = False +import mox +import stubout -import netaddr from nova import context from nova import db -from nova import flags +from nova import exception from nova import test - -FLAGS = flags.FLAGS +from nova.tests.db import fakes as db_fakes class FixedIpCommandsTestCase(test.TestCase): def setUp(self): super(FixedIpCommandsTestCase, self).setUp() - cidr = '10.0.0.0/24' - net = netaddr.IPNetwork(cidr) - net_info = {'bridge': 'fakebr', - 'bridge_interface': 'fakeeth', - 'dns': FLAGS.flat_network_dns, - 'cidr': cidr, - 'netmask': str(net.netmask), - 'gateway': str(net[1]), - 'broadcast': str(net.broadcast), - 'dhcp_start': str(net[2])} - self.network = db.network_create_safe(context.get_admin_context(), - net_info) - num_ips = len(net) - for index in range(num_ips): - address = str(net[index]) - reserved = (index == 1 or index == 2) - db.fixed_ip_create(context.get_admin_context(), - {'network_id': self.network['id'], - 'address': address, - 'reserved': reserved}) + self.stubs = stubout.StubOutForTesting() + db_fakes.stub_out_db_network_api(self.stubs) self.commands = nova_manage.FixedIpCommands() def tearDown(self): - db.network_delete_safe(context.get_admin_context(), self.network['id']) super(FixedIpCommandsTestCase, self).tearDown() + self.stubs.UnsetAll() def test_reserve(self): - self.commands.reserve('10.0.0.100') + self.commands.reserve('192.168.0.100') address = db.fixed_ip_get_by_address(context.get_admin_context(), - '10.0.0.100') + '192.168.0.100') self.assertEqual(address['reserved'], True) def test_unreserve(self): - db.fixed_ip_update(context.get_admin_context(), '10.0.0.100', - {'reserved': True}) - self.commands.unreserve('10.0.0.100') + self.commands.unreserve('192.168.0.100') address = db.fixed_ip_get_by_address(context.get_admin_context(), - '10.0.0.100') + '192.168.0.100') self.assertEqual(address['reserved'], False) -- cgit From 77f15157c5ca7013df397abc22a8866cce02976d Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Mon, 22 Aug 2011 17:08:11 -0400 Subject: Ensure that reserve and unreserve exit when an address is not found --- bin/nova-manage | 2 ++ nova/tests/test_nova_manage.py | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/bin/nova-manage b/bin/nova-manage index 8e6419c0b..3d9a40afb 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -611,6 +611,8 @@ class FixedIpCommands(object): try: fixed_ip = db.fixed_ip_get_by_address(ctxt, address) + if fixed_ip is None: + raise exception.NotFound('Could not find address') db.fixed_ip_update(ctxt, fixed_ip['address'], {'reserved': reserved}) except exception.NotFound as ex: diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py index 3a6e48dec..f5ea68a03 100644 --- a/nova/tests/test_nova_manage.py +++ b/nova/tests/test_nova_manage.py @@ -55,8 +55,18 @@ class FixedIpCommandsTestCase(test.TestCase): '192.168.0.100') self.assertEqual(address['reserved'], True) + def test_reserve_nonexistent_address(self): + self.assertRaises(SystemExit, + self.commands.reserve, + '55.55.55.55') + def test_unreserve(self): self.commands.unreserve('192.168.0.100') address = db.fixed_ip_get_by_address(context.get_admin_context(), '192.168.0.100') self.assertEqual(address['reserved'], False) + + def test_unreserve_nonexistent_address(self): + self.assertRaises(SystemExit, + self.commands.unreserve, + '55.55.55.55') -- cgit From c3ed01d7d53dbade412122743078d60131adbf9f Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 22 Aug 2011 14:24:59 -0700 Subject: change NoAuth to actually use a tenant and user --- etc/nova/api-paste.ini | 12 +++++------ nova/api/auth.py | 19 +---------------- nova/api/ec2/__init__.py | 21 +++++++++++++++++++ nova/api/openstack/auth.py | 52 ++++++++++++++++++++++++++++++++++------------ 4 files changed, 67 insertions(+), 37 deletions(-) diff --git a/etc/nova/api-paste.ini b/etc/nova/api-paste.ini index a9ae0abf6..dafdef877 100644 --- a/etc/nova/api-paste.ini +++ b/etc/nova/api-paste.ini @@ -19,7 +19,7 @@ use = egg:Paste#urlmap /1.0: ec2metadata [pipeline:ec2cloud] -pipeline = logrequest admincontext cloudrequest authorizer ec2executor +pipeline = logrequest ec2noauth cloudrequest authorizer ec2executor # NOTE(vish): use the following pipeline for deprecated auth #pipeline = logrequest authenticate cloudrequest authorizer ec2executor # NOTE(vish): use the following pipeline for keystone @@ -43,6 +43,9 @@ paste.filter_factory = nova.api.ec2:Lockout.factory [filter:totoken] paste.filter_factory = nova.api.ec2:ToToken.factory +[filter:ec2noauth] +paste.filter_factory = nova.api.ec2:NoAuth.factory + [filter:authenticate] paste.filter_factory = nova.api.ec2:Authenticate.factory @@ -77,14 +80,14 @@ use = egg:Paste#urlmap /v1.1: openstackapi11 [pipeline:openstackapi10] -pipeline = faultwrap noauth admincontext ratelimit osapiapp10 +pipeline = faultwrap noauth ratelimit osapiapp10 # NOTE(vish): use the following pipeline for deprecated auth # pipeline = faultwrap auth ratelimit osapiapp10 # NOTE(vish): use the following pipeline for keystone #pipeline = faultwrap authtoken keystonecontext ratelimit osapiapp10 [pipeline:openstackapi11] -pipeline = faultwrap noauth admincontext ratelimit extensions osapiapp11 +pipeline = faultwrap noauth ratelimit extensions osapiapp11 # NOTE(vish): use the following pipeline for deprecated auth # pipeline = faultwrap auth ratelimit extensions osapiapp11 # NOTE(vish): use the following pipeline for keystone @@ -121,9 +124,6 @@ paste.app_factory = nova.api.openstack.versions:Versions.factory # Shared # ########## -[filter:admincontext] -paste.filter_factory = nova.api.auth:AdminContext.factory - [filter:keystonecontext] paste.filter_factory = nova.api.auth:KeystoneContext.factory diff --git a/nova/api/auth.py b/nova/api/auth.py index 050216fd7..cd0d38b3f 100644 --- a/nova/api/auth.py +++ b/nova/api/auth.py @@ -45,24 +45,6 @@ class InjectContext(wsgi.Middleware): return self.application -class AdminContext(wsgi.Middleware): - """Return an admin context no matter what""" - - @webob.dec.wsgify(RequestClass=wsgi.Request) - def __call__(self, req): - # Build a context, including the auth_token... - remote_address = req.remote_addr - if FLAGS.use_forwarded_for: - remote_address = req.headers.get('X-Forwarded-For', remote_address) - ctx = context.RequestContext('admin', - 'admin', - is_admin=True, - remote_address=remote_address) - - req.environ['nova.context'] = ctx - return self.application - - class KeystoneContext(wsgi.Middleware): """Make a request context from keystone headers""" @@ -80,6 +62,7 @@ class KeystoneContext(wsgi.Middleware): req.headers.get('X_STORAGE_TOKEN')) # Build a context, including the auth_token... + remote_address = getattr(req, 'remote_address', '127.0.0.1') remote_address = req.remote_addr if FLAGS.use_forwarded_for: remote_address = req.headers.get('X-Forwarded-For', remote_address) diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index 17969099d..5430f443d 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -183,6 +183,27 @@ class ToToken(wsgi.Middleware): return self.application +class NoAuth(wsgi.Middleware): + """Add user:project as 'nova.context' to WSGI environ.""" + + @webob.dec.wsgify(RequestClass=wsgi.Request) + def __call__(self, req): + if 'AWSAccessKeyId' not in req.params: + raise webob.exc.HTTPBadRequest() + user_id, _sep, project_id = req.params['AWSAccessKeyId'].partition(':') + project_id = project_id or user_id + remote_address = getattr(req, 'remote_address', '127.0.0.1') + if FLAGS.use_forwarded_for: + remote_address = req.headers.get('X-Forwarded-For', remote_address) + ctx = context.RequestContext(user_id, + project_id, + is_admin=True, + remote_address=remote_address) + + req.environ['nova.context'] = ctx + return self.application + + class Authenticate(wsgi.Middleware): """Authenticate an EC2 request and add 'nova.context' to WSGI environ.""" diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index 0d9c7562a..f2dc89094 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -33,6 +33,7 @@ from nova.api.openstack import faults LOG = logging.getLogger('nova.api.openstack') FLAGS = flags.FLAGS +flags.DECLARE('use_forwarded_for', 'nova.api.auth') class NoAuthMiddleware(wsgi.Middleware): @@ -40,17 +41,36 @@ class NoAuthMiddleware(wsgi.Middleware): @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): - if 'X-Auth-Token' in req.headers: + if 'X-Auth-Token' not in req.headers: + os_url = req.url + version = common.get_version_from_href(os_url) + user_id = req.headers.get('X-Auth-User', 'admin') + project_id = req.headers.get('X-Auth-Project-Id', 'admin') + if version == '1.1': + os_url += '/' + project_id + res = webob.Response() + res.headers['X-Auth-Token'] = '%s:%s' % (user_id, project_id) + res.headers['X-Server-Management-Url'] = os_url + res.headers['X-Storage-Url'] = '' + res.headers['X-CDN-Management-Url'] = '' + res.content_type = 'text/plain' + res.status = '204' + return res + else: + token = req.headers['X-Auth-Token'] + user_id, _sep, project_id = token.partition(':') + project_id = project_id or user_id + remote_address = getattr(req, 'remote_address', '127.0.0.1') + if FLAGS.use_forwarded_for: + remote_address = req.headers.get('X-Forwarded-For', + remote_address) + ctx = context.RequestContext(user_id, + project_id, + is_admin=True, + remote_address=remote_address) + + req.environ['nova.context'] = ctx return self.application - logging.debug("Got no auth token, returning fake info.") - res = webob.Response() - res.headers['X-Auth-Token'] = 'fake' - res.headers['X-Server-Management-Url'] = req.url - res.headers['X-Storage-Url'] = '' - res.headers['X-CDN-Management-Url'] = '' - res.content_type = 'text/plain' - res.status = '204' - return res class AuthMiddleware(wsgi.Middleware): @@ -103,9 +123,15 @@ class AuthMiddleware(wsgi.Middleware): project_id = projects[0].id is_admin = self.auth.is_admin(user_id) - req.environ['nova.context'] = context.RequestContext(user_id, - project_id, - is_admin) + remote_address = getattr(req, 'remote_address', '127.0.0.1') + if FLAGS.use_forwarded_for: + remote_address = req.headers.get('X-Forwarded-For', remote_address) + ctx = context.RequestContext(user_id, + project_id, + is_admin=is_admin, + remote_address=remote_address) + req.environ['nova.context'] = ctx + if not is_admin and not self.auth.is_project_member(user_id, project_id): msg = _("%(user_id)s must be an admin or a " -- cgit From 43add36446e6b4172dc8ed5043e11187a9992474 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 22 Aug 2011 14:26:41 -0700 Subject: fix comment --- nova/auth/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/auth/manager.py b/nova/auth/manager.py index c9178c0dd..85227bea0 100644 --- a/nova/auth/manager.py +++ b/nova/auth/manager.py @@ -18,7 +18,7 @@ """ WARNING: This code is deprecated and will be removed. -Keystone is recommended is the recommended solution for auth management. +Keystone is the recommended solution for auth management. Nova authentication management """ -- cgit From 6f3610042452cc1cb6b1e0c204a127c0c48794f0 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 22 Aug 2011 19:25:22 -0700 Subject: unindented per review, added a note about auth v2 --- nova/api/openstack/auth.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index f2dc89094..6754fea27 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -49,6 +49,9 @@ class NoAuthMiddleware(wsgi.Middleware): if version == '1.1': os_url += '/' + project_id res = webob.Response() + # NOTE(vish): This is expecting and returning Auth(1.1), whereas + # keystone uses 2.0 auth. We should probably allow + # 2.0 auth here as well. res.headers['X-Auth-Token'] = '%s:%s' % (user_id, project_id) res.headers['X-Server-Management-Url'] = os_url res.headers['X-Storage-Url'] = '' @@ -56,21 +59,20 @@ class NoAuthMiddleware(wsgi.Middleware): res.content_type = 'text/plain' res.status = '204' return res - else: - token = req.headers['X-Auth-Token'] - user_id, _sep, project_id = token.partition(':') - project_id = project_id or user_id - remote_address = getattr(req, 'remote_address', '127.0.0.1') - if FLAGS.use_forwarded_for: - remote_address = req.headers.get('X-Forwarded-For', - remote_address) - ctx = context.RequestContext(user_id, - project_id, - is_admin=True, - remote_address=remote_address) - - req.environ['nova.context'] = ctx - return self.application + + token = req.headers['X-Auth-Token'] + user_id, _sep, project_id = token.partition(':') + project_id = project_id or user_id + remote_address = getattr(req, 'remote_address', '127.0.0.1') + if FLAGS.use_forwarded_for: + remote_address = req.headers.get('X-Forwarded-For', remote_address) + ctx = context.RequestContext(user_id, + project_id, + is_admin=True, + remote_address=remote_address) + + req.environ['nova.context'] = ctx + return self.application class AuthMiddleware(wsgi.Middleware): -- cgit From 4c2674516897b6cce0441efe4ebb005c01cb3411 Mon Sep 17 00:00:00 2001 From: Christopher MacGown Date: Mon, 22 Aug 2011 21:06:47 -0700 Subject: Added the fixes suggested by Eric Windisch from cloudscaling... --- nova/virt/disk.py | 2 +- nova/virt/xenapi/vm_utils.py | 2 +- nova/virt/xenapi/vmops.py | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index 54b191fa9..809d3323c 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -228,7 +228,7 @@ def _inject_metadata_into_fs(metadata, fs, execute=None): metadata_path = os.path.join(fs, "meta.js") metadata = dict([(m.key, m.value) for m in metadata]) - utils.execute('sudo', 'tee', '-a', metadata_path, + utils.execute('sudo', 'tee', metadata_path, process_input=json.dumps(metadata)) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 3861f6bd8..e517dcf28 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -641,7 +641,7 @@ class VMHelper(HelperBase): # everything mount_required = False key, net, metadata = _prepare_injectables(instance, network_info) - mount_required = key or net + mount_required = key or net or metadata if not mount_required: return diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index b1522729a..606041d12 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -251,8 +251,9 @@ class VMOps(object): bootable=False) userdevice += 1 - # Alter the image before VM start for, e.g. network injection - if FLAGS.flat_injected: + # Alter the image before VM start for, e.g. network injection also + # alter the image if there's metadata. + if FLAGS.flat_injected or instance['metadata']: VMHelper.preconfigure_instance(self._session, instance, first_vdi_ref, network_info) -- cgit From 7f1adb50cfab91a553f2d129b9b2eef1e5b2145b Mon Sep 17 00:00:00 2001 From: Christopher MacGown Date: Mon, 22 Aug 2011 22:17:51 -0700 Subject: Moved migration and fixed tests from upstream --- nova/compute/api.py | 2 +- .../versions/041_add_config_drive_to_instances.py | 38 ++++++++++++++++++++++ nova/tests/api/openstack/test_servers.py | 28 +++++++++------- nova/tests/test_compute.py | 2 +- nova/virt/disk.py | 2 +- nova/virt/libvirt/connection.py | 7 ++-- nova/virt/xenapi/vm_utils.py | 2 +- 7 files changed, 61 insertions(+), 20 deletions(-) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/041_add_config_drive_to_instances.py diff --git a/nova/compute/api.py b/nova/compute/api.py index 74149f17d..69f76bf40 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -526,7 +526,7 @@ class API(base.Base): availability_zone, user_data, metadata, injected_files, admin_password, zone_blob, reservation_id, access_ip_v4, access_ip_v6, - requested_networks, config_drive + requested_networks, config_drive) block_device_mapping = block_device_mapping or [] instances = [] diff --git a/nova/db/sqlalchemy/migrate_repo/versions/041_add_config_drive_to_instances.py b/nova/db/sqlalchemy/migrate_repo/versions/041_add_config_drive_to_instances.py new file mode 100644 index 000000000..d3058f00d --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/041_add_config_drive_to_instances.py @@ -0,0 +1,38 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright 2011 Piston Cloud Computing, Inc. +# +# 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. + +from sqlalchemy import Column, Integer, MetaData, String, Table + +from nova import utils + + +meta = MetaData() + +instances = Table("instances", meta, + Column("id", Integer(), primary_key=True, nullable=False)) + +# matches the size of an image_ref +config_drive_column = Column("config_drive", String(255), nullable=True) + + +def upgrade(migrate_engine): + meta.bind = migrate_engine + instances.create_column(config_drive_column) + + +def downgrade(migrate_engine): + meta.bind = migrate_engine + instances.drop_column(config_drive_column) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index f854a500c..aec2ad947 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -1776,8 +1776,8 @@ class ServersTest(test.TestCase): self.config_drive = True self._setup_for_create_instance() - image_href = 'http://localhost/v1.1/images/2' - flavor_ref = 'http://localhost/v1.1/flavors/3' + image_href = 'http://localhost/v1.1/123/images/2' + flavor_ref = 'http://localhost/v1.1/123/flavors/3' body = { 'server': { 'name': 'config_drive_test', @@ -1792,12 +1792,13 @@ class ServersTest(test.TestCase): }, } - req = webob.Request.blank('/v1.1/servers') + req = webob.Request.blank('/v1.1/123/servers') req.method = 'POST' req.body = json.dumps(body) req.headers["content-type"] = "application/json" res = req.get_response(fakes.wsgi_app()) + print res self.assertEqual(res.status_int, 202) server = json.loads(res.body)['server'] self.assertEqual(1, server['id']) @@ -1807,8 +1808,8 @@ class ServersTest(test.TestCase): self.config_drive = 2 self._setup_for_create_instance() - image_href = 'http://localhost/v1.1/images/2' - flavor_ref = 'http://localhost/v1.1/flavors/3' + image_href = 'http://localhost/v1.1/123/images/2' + flavor_ref = 'http://localhost/v1.1/123/flavors/3' body = { 'server': { 'name': 'config_drive_test', @@ -1823,7 +1824,7 @@ class ServersTest(test.TestCase): }, } - req = webob.Request.blank('/v1.1/servers') + req = webob.Request.blank('/v1.1/123/servers') req.method = 'POST' req.body = json.dumps(body) req.headers["content-type"] = "application/json" @@ -1840,8 +1841,8 @@ class ServersTest(test.TestCase): self.config_drive = "asdf" self._setup_for_create_instance() - image_href = 'http://localhost/v1.1/images/2' - flavor_ref = 'http://localhost/v1.1/flavors/3' + image_href = 'http://localhost/v1.1/123/images/2' + flavor_ref = 'http://localhost/v1.1/123/flavors/3' body = { 'server': { 'name': 'config_drive_test', @@ -1856,7 +1857,7 @@ class ServersTest(test.TestCase): }, } - req = webob.Request.blank('/v1.1/servers') + req = webob.Request.blank('/v1.1/123/servers') req.method = 'POST' req.body = json.dumps(body) req.headers["content-type"] = "application/json" @@ -1867,8 +1868,8 @@ class ServersTest(test.TestCase): def test_create_instance_without_config_drive_v1_1(self): self._setup_for_create_instance() - image_href = 'http://localhost/v1.1/images/2' - flavor_ref = 'http://localhost/v1.1/flavors/3' + image_href = 'http://localhost/v1.1/123/images/2' + flavor_ref = 'http://localhost/v1.1/123/flavors/3' body = { 'server': { 'name': 'config_drive_test', @@ -1883,7 +1884,7 @@ class ServersTest(test.TestCase): }, } - req = webob.Request.blank('/v1.1/servers') + req = webob.Request.blank('/v1.1/123/servers') req.method = 'POST' req.body = json.dumps(body) req.headers["content-type"] = "application/json" @@ -3588,6 +3589,7 @@ class ServersViewBuilderV11Test(test.TestCase): "id": 1, "uuid": self.instance['uuid'], "name": "test_server", + "config_drive": None, "links": [ { "rel": "self", @@ -3747,6 +3749,7 @@ class ServersViewBuilderV11Test(test.TestCase): }, "addresses": {}, "metadata": {}, + "config_drive": None, "accessIPv4": "1.2.3.4", "accessIPv6": "", "links": [ @@ -3801,6 +3804,7 @@ class ServersViewBuilderV11Test(test.TestCase): }, "addresses": {}, "metadata": {}, + "config_drive": None, "accessIPv4": "", "accessIPv6": "fead::1234", "links": [ diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index b75e25dda..0523d73b6 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -163,7 +163,7 @@ class ComputeTestCase(test.TestCase): def test_create_instance_associates_config_drive(self): """Make sure create associates a config drive.""" - instance_id = self._create_instance(params={'config_drive': True,}) + instance_id = self._create_instance(params={'config_drive': True, }) try: self.compute.run_instance(self.context, instance_id) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index 809d3323c..52b2881e8 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -228,7 +228,7 @@ def _inject_metadata_into_fs(metadata, fs, execute=None): metadata_path = os.path.join(fs, "meta.js") metadata = dict([(m.key, m.value) for m in metadata]) - utils.execute('sudo', 'tee', metadata_path, + utils.execute('sudo', 'tee', metadata_path, process_input=json.dumps(metadata)) diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 23fa86f65..4388291db 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -131,8 +131,8 @@ flags.DEFINE_string('libvirt_vif_type', 'bridge', flags.DEFINE_string('libvirt_vif_driver', 'nova.virt.libvirt.vif.LibvirtBridgeDriver', 'The libvirt VIF driver to configure the VIFs.') -flags.DEFINE_string('default_local_format', - None, +flags.DEFINE_string('default_local_format', + None, 'The default format a local_volume will be formatted with ' 'on creation.') @@ -970,7 +970,7 @@ class LibvirtConnection(driver.ComputeDriver): for injection in ('metadata', 'key', 'net'): if locals()[injection]: LOG.info(_('instance %(inst_name)s: injecting ' - '%(injection)s into image %(img_id)s' + '%(injection)s into image %(img_id)s' % locals())) try: disk.inject_data(injection_path, key, net, metadata, @@ -1106,7 +1106,6 @@ class LibvirtConnection(driver.ComputeDriver): block_device_info)): xml_info['swap_device'] = self.default_swap_device - config_drive = False if instance.get('config_drive') or instance.get('config_drive_id'): xml_info['config_drive'] = xml_info['basepath'] + "/disk.config" diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 18fe84e6c..efbea7076 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -741,7 +741,7 @@ class VMHelper(HelperBase): # if at all, so determine whether it's required first, and then do # everything mount_required = False - key, net, metadata = _prepare_injectables(instance, network_info) + key, net, metadata = _prepare_injectables(instance, network_info) mount_required = key or net or metadata if not mount_required: return -- cgit From 909e0ea5c61ba66e5c07b91ff225d64adf60f960 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Tue, 23 Aug 2011 10:21:07 -0400 Subject: Move use_ipv6 into flags. Its used in multiple places (network manager and the OSAPI) and should be defined at the top level. --- nova/flags.py | 2 ++ nova/network/manager.py | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/flags.py b/nova/flags.py index 48d5e8168..f822ae61a 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -402,3 +402,5 @@ DEFINE_bool('resume_guests_state_on_host_boot', False, DEFINE_string('root_helper', 'sudo', 'Command prefix to use for running commands as root') + +DEFINE_bool('use_ipv6', False, 'use the ipv6') diff --git a/nova/network/manager.py b/nova/network/manager.py index aa2a3700c..404a3180e 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -106,8 +106,6 @@ flags.DEFINE_integer('create_unique_mac_address_attempts', 5, 'Number of attempts to create unique mac address') flags.DEFINE_bool('auto_assign_floating_ip', False, 'Autoassigning floating ip to VM') -flags.DEFINE_bool('use_ipv6', False, - 'use the ipv6') flags.DEFINE_string('network_host', socket.gethostname(), 'Network host to use for ip allocation in flat modes') flags.DEFINE_bool('fake_call', False, -- cgit From e1c27761863a50bf33a2dcfffa96e911ae9b5b55 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Tue, 23 Aug 2011 10:31:34 -0400 Subject: 'use the ipv6' -- 'use ipv6' --- nova/flags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/flags.py b/nova/flags.py index f822ae61a..ce5356723 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -403,4 +403,4 @@ DEFINE_bool('resume_guests_state_on_host_boot', False, DEFINE_string('root_helper', 'sudo', 'Command prefix to use for running commands as root') -DEFINE_bool('use_ipv6', False, 'use the ipv6') +DEFINE_bool('use_ipv6', False, 'use ipv6') -- cgit