diff options
| author | Tushar Patil <tushar.vitthal.patil@gmail.com> | 2011-07-11 13:34:39 -0700 |
|---|---|---|
| committer | Tushar Patil <tushar.vitthal.patil@gmail.com> | 2011-07-11 13:34:39 -0700 |
| commit | 2a6f97940f71c056b4bfb0cd9a86f5d676abc4e1 (patch) | |
| tree | acfae7cd5239939dd59dd8b9f766c489a725ae9d | |
| parent | 6843421be9cdef1fc12d3480889bdcfd96821e1b (diff) | |
| download | nova-2a6f97940f71c056b4bfb0cd9a86f5d676abc4e1.tar.gz nova-2a6f97940f71c056b4bfb0cd9a86f5d676abc4e1.tar.xz nova-2a6f97940f71c056b4bfb0cd9a86f5d676abc4e1.zip | |
add optional parameter networks to the Create server OS API
| -rwxr-xr-x | bin/nova-manage | 6 | ||||
| -rw-r--r-- | nova/api/openstack/create_instance_helper.py | 55 | ||||
| -rw-r--r-- | nova/compute/api.py | 34 | ||||
| -rw-r--r-- | nova/compute/manager.py | 4 | ||||
| -rw-r--r-- | nova/db/api.py | 32 | ||||
| -rw-r--r-- | nova/db/sqlalchemy/api.py | 125 | ||||
| -rw-r--r-- | nova/exception.py | 26 | ||||
| -rw-r--r-- | nova/network/api.py | 9 | ||||
| -rw-r--r-- | nova/network/manager.py | 125 | ||||
| -rw-r--r-- | 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() @@ -772,6 +806,26 @@ def fixed_ip_get_by_address(context, address, session=None): @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() rv = session.query(models.FixedIp).\ @@ -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 @@ -2728,6 +2815,42 @@ def project_get_networks(context, project_id, associate=True): @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.""" |
