summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTushar Patil <tushar.vitthal.patil@gmail.com>2011-07-11 13:34:39 -0700
committerTushar Patil <tushar.vitthal.patil@gmail.com>2011-07-11 13:34:39 -0700
commit2a6f97940f71c056b4bfb0cd9a86f5d676abc4e1 (patch)
treeacfae7cd5239939dd59dd8b9f766c489a725ae9d
parent6843421be9cdef1fc12d3480889bdcfd96821e1b (diff)
downloadnova-2a6f97940f71c056b4bfb0cd9a86f5d676abc4e1.tar.gz
nova-2a6f97940f71c056b4bfb0cd9a86f5d676abc4e1.tar.xz
nova-2a6f97940f71c056b4bfb0cd9a86f5d676abc4e1.zip
add optional parameter networks to the Create server OS API
-rwxr-xr-xbin/nova-manage6
-rw-r--r--nova/api/openstack/create_instance_helper.py55
-rw-r--r--nova/compute/api.py34
-rw-r--r--nova/compute/manager.py4
-rw-r--r--nova/db/api.py32
-rw-r--r--nova/db/sqlalchemy/api.py125
-rw-r--r--nova/exception.py26
-rw-r--r--nova/network/api.py9
-rw-r--r--nova/network/manager.py125
-rw-r--r--nova/utils.py16
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."""