diff options
-rwxr-xr-x | bin/nova-manage | 31 | ||||
-rw-r--r-- | nova/api/ec2/cloud.py | 11 | ||||
-rw-r--r-- | nova/api/openstack/servers.py | 35 | ||||
-rw-r--r-- | nova/auth/manager.py | 15 | ||||
-rw-r--r-- | nova/compute/manager.py | 2 | ||||
-rw-r--r-- | nova/db/api.py | 45 | ||||
-rw-r--r-- | nova/db/sqlalchemy/api.py | 115 | ||||
-rw-r--r-- | nova/db/sqlalchemy/models.py | 33 | ||||
-rw-r--r-- | nova/network/linux_net.py | 6 | ||||
-rw-r--r-- | nova/network/manager.py | 173 | ||||
-rw-r--r-- | nova/test.py | 14 | ||||
-rw-r--r-- | nova/tests/compute_unittest.py | 3 | ||||
-rw-r--r-- | nova/tests/network_unittest.py | 31 | ||||
-rw-r--r-- | nova/tests/scheduler_unittest.py | 1 | ||||
-rw-r--r-- | nova/virt/libvirt_conn.py | 4 |
15 files changed, 275 insertions, 244 deletions
diff --git a/bin/nova-manage b/bin/nova-manage index e19bf70b7..d36b0f53a 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -73,6 +73,7 @@ from nova import flags from nova import quota from nova import utils from nova.auth import manager +from nova.network import manager as network_manager from nova.cloudpipe import pipelib @@ -377,6 +378,29 @@ class FloatingIpCommands(object): floating_ip['address'], instance) +class NetworkCommands(object): + """Class for managing networks.""" + + def create(self, fixed_range=None, num_networks=None, + network_size=None, vlan_start=None, vpn_start=None): + """Creates fixed ips for host by range + arguments: [fixed_range=FLAG], [num_networks=FLAG], + [network_size=FLAG], [vlan_start=FLAG], + [vpn_start=FLAG]""" + if not fixed_range: + fixed_range = FLAGS.fixed_range + if not num_networks: + num_networks = FLAGS.num_networks + if not network_size: + network_size = FLAGS.network_size + if not vlan_start: + vlan_start = FLAGS.vlan_start + if not vpn_start: + vpn_start = FLAGS.vpn_start + net_manager = utils.import_object(FLAGS.network_manager) + net_manager.create_networks(None, fixed_range, int(num_networks), + int(network_size), int(vlan_start), + int(vpn_start)) CATEGORIES = [ ('user', UserCommands), @@ -384,7 +408,8 @@ CATEGORIES = [ ('role', RoleCommands), ('shell', ShellCommands), ('vpn', VpnCommands), - ('floating', FloatingIpCommands) + ('floating', FloatingIpCommands), + ('network', NetworkCommands) ] @@ -454,9 +479,9 @@ def main(): fn(*argv) sys.exit(0) except TypeError: - print "Wrong number of arguments supplied" + print "Possible wrong number of arguments supplied" print "%s %s: %s" % (category, action, fn.__doc__) - sys.exit(2) + raise if __name__ == '__main__': main() diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 7839dc92c..278055be1 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -734,13 +734,13 @@ class CloudController(object): def _get_network_topic(self, context): """Retrieves the network host for a project""" - network_ref = db.project_get_network(context, context.project.id) + network_ref = self.network_manager.get_network(context) host = network_ref['host'] if not host: host = rpc.call(FLAGS.network_topic, {"method": "set_network_host", "args": {"context": None, - "project_id": context.project.id}}) + "network_id": network_ref['id']}}) return db.queue_get_for(context, FLAGS.network_topic, host) def _ensure_default_security_group(self, context): @@ -851,12 +851,13 @@ class CloudController(object): ec2_id = internal_id_to_ec2_id(internal_id) inst['hostname'] = ec2_id db.instance_update(context, inst_id, inst) + # TODO(vish): This probably should be done in the scheduler + # or in compute as a call. The network should be + # allocated after the host is assigned and setup + # can happen at the same time. address = self.network_manager.allocate_fixed_ip(context, inst_id, vpn) - - # TODO(vish): This probably should be done in the scheduler - # network is setup when host is assigned network_topic = self._get_network_topic(context) rpc.call(network_topic, {"method": "setup_fixed_ip", diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 5d1ed9822..1a0792bf8 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -48,9 +48,9 @@ def _entity_list(entities): return dict(servers=entities) def _entity_detail(inst): - """ Maps everything to valid attributes for return""" - power_mapping = { - power_state.NOSTATE: 'build', + """ Maps everything to Rackspace-like attributes for return""" + power_mapping = { + power_state.NOSTATE: 'build', power_state.RUNNING: 'active', power_state.BLOCKED: 'active', power_state.PAUSED: 'suspended', @@ -60,7 +60,7 @@ def _entity_detail(inst): } inst_dict = {} - mapped_keys = dict(status='state', imageId='image_id', + mapped_keys = dict(status='state', imageId='image_id', flavorId='instance_type', name='server_name', id='id') for k, v in mapped_keys.iteritems(): @@ -83,7 +83,7 @@ class Controller(wsgi.Controller): _serialization_metadata = { 'application/xml': { "attributes": { - "server": [ "id", "imageId", "name", "flavorId", "hostId", + "server": [ "id", "imageId", "name", "flavorId", "hostId", "status", "progress", "progress" ] } } @@ -155,7 +155,7 @@ class Controller(wsgi.Controller): user_id = req.environ['nova.context']['user']['id'] inst_dict = self._deserialize(req.body, req) - + if not inst_dict: return faults.Fault(exc.HTTPUnprocessableEntity()) @@ -163,12 +163,12 @@ class Controller(wsgi.Controller): if not instance or instance.user_id != user_id: return faults.Fault(exc.HTTPNotFound()) - self.db_driver.instance_update(None, int(id), + self.db_driver.instance_update(None, int(id), _filter_params(inst_dict['server'])) return faults.Fault(exc.HTTPNoContent()) def action(self, req, id): - """ multi-purpose method used to reboot, rebuild, and + """ multi-purpose method used to reboot, rebuild, and resize a server """ user_id = req.environ['nova.context']['user']['id'] input_dict = self._deserialize(req.body, req) @@ -195,12 +195,11 @@ class Controller(wsgi.Controller): if v['flavorid'] == flavor_id][0] image_id = env['server']['imageId'] - img_service = utils.import_object(FLAGS.image_service) image = img_service.show(image_id) - if not image: + if not image: raise Exception, "Image not found" inst['server_name'] = env['server']['name'] @@ -236,15 +235,14 @@ class Controller(wsgi.Controller): ref = self.db_driver.instance_create(None, inst) inst['id'] = ref.internal_id - # TODO(dietz): this isn't explicitly necessary, but the networking # calls depend on an object with a project_id property, and therefore # should be cleaned up later api_context = context.APIRequestContext(user_id) - + inst['mac_address'] = utils.generate_mac() - - #TODO(dietz) is this necessary? + + #TODO(dietz) is this necessary? inst['launch_index'] = 0 inst['hostname'] = str(ref.internal_id) @@ -256,21 +254,20 @@ class Controller(wsgi.Controller): # TODO(vish): This probably should be done in the scheduler # network is setup when host is assigned - network_topic = self._get_network_topic(user_id) + network_topic = self._get_network_topic(None) rpc.call(network_topic, {"method": "setup_fixed_ip", "args": {"context": None, "address": address}}) return inst - def _get_network_topic(self, user_id): + def _get_network_topic(self, context): """Retrieves the network host for a project""" - network_ref = self.db_driver.project_get_network(None, - user_id) + network_ref = self.network_manager.get_network(context) host = network_ref['host'] if not host: host = rpc.call(FLAGS.network_topic, {"method": "set_network_host", "args": {"context": None, - "project_id": user_id}}) + "network_id": network_ref['id']}}) return self.db_driver.queue_get_for(None, FLAGS.network_topic, host) diff --git a/nova/auth/manager.py b/nova/auth/manager.py index 58e33969b..9c499c98d 100644 --- a/nova/auth/manager.py +++ b/nova/auth/manager.py @@ -484,13 +484,6 @@ class AuthManager(object): member_users) if project_dict: project = Project(**project_dict) - try: - self.network_manager.allocate_network(context, - project.id) - except: - drv.delete_project(project.id) - raise - return project def modify_project(self, project, manager_user=None, description=None): @@ -559,14 +552,6 @@ class AuthManager(object): def delete_project(self, project, context=None): """Deletes a project""" - try: - network_ref = db.project_get_network(context, - Project.safe_id(project)) - db.network_destroy(context, network_ref['id']) - except: - logging.exception('Could not destroy network for %s', - project) - with self.driver() as drv: drv.delete_project(Project.safe_id(project)) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index ef7e9da6f..f36e14aa2 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -76,7 +76,7 @@ class ComputeManager(manager.Manager): raise exception.Error("Instance has already been created") logging.debug("instance %s: starting...", instance_id) project_id = instance_ref['project_id'] - self.network_manager.setup_compute_network(context, project_id) + self.network_manager.setup_compute_network(context, instance_id) self.db.instance_update(context, instance_id, {'host': self.host}) diff --git a/nova/db/api.py b/nova/db/api.py index 4be8df397..a655e6a8a 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -340,6 +340,11 @@ def key_pair_get_all_by_user(context, user_id): #################### +def network_associate(context, project_id): + """Associate a free network to a project.""" + return IMPL.network_associate(context, project_id) + + def network_count(context): """Return the number of networks.""" return IMPL.network_count(context) @@ -360,9 +365,12 @@ def network_count_reserved_ips(context, network_id): return IMPL.network_count_reserved_ips(context, network_id) -def network_create(context, values): - """Create a network from the values dictionary.""" - return IMPL.network_create(context, values) +def network_create_safe(context, values): + """Create a network from the values dict + + The network is only returned if the create succeeds. If the create violates + constraints because the network already exists, no exception is raised.""" + return IMPL.network_create_safe(context, values) def network_create_fixed_ips(context, network_id, num_vpn_clients): @@ -370,9 +378,14 @@ def network_create_fixed_ips(context, network_id, num_vpn_clients): return IMPL.network_create_fixed_ips(context, network_id, num_vpn_clients) -def network_destroy(context, network_id): - """Destroy the network or raise if it does not exist.""" - return IMPL.network_destroy(context, network_id) +def network_disassociate(context, network_id): + """Disassociate the network from project or raise if it does not exist.""" + return IMPL.network_disassociate(context, network_id) + + +def network_disassociate_all(context): + """Disassociate all networks from projects.""" + return IMPL.network_disassociate_all(context) def network_get(context, network_id): @@ -387,10 +400,15 @@ def network_get_associated_fixed_ips(context, network_id): def network_get_by_bridge(context, bridge): - """Get an network or raise if it does not exist.""" + """Get a network by bridge or raise if it does not exist.""" return IMPL.network_get_by_bridge(context, bridge) +def network_get_by_instance(context, instance_id): + """Get a network by instance id or raise if it does not exist.""" + return IMPL.network_get_by_instance(context, instance_id) + + def network_get_index(context, network_id): """Get non-conflicting index for network""" return IMPL.network_get_index(context, network_id) @@ -401,19 +419,6 @@ def network_get_vpn_ip(context, network_id): return IMPL.network_get_vpn_ip(context, network_id) -def network_index_count(context): - """Return count of network indexes""" - return IMPL.network_index_count(context) - - -def network_index_create_safe(context, values): - """Create a network index from the values dict - - The index is not returned. If the create violates the unique - constraints because the index already exists, no exception is raised.""" - return IMPL.network_index_create_safe(context, values) - - def network_set_cidr(context, network_id, cidr): """Set the Classless Inner Domain Routing for the network""" return IMPL.network_set_cidr(context, network_id, cidr) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 50d802774..14714d4b1 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -805,6 +805,24 @@ def key_pair_get_all_by_user(context, user_id): @require_admin_context +def network_associate(context, project_id): + session = get_session() + with session.begin(): + network_ref = session.query(models.Network + ).filter_by(deleted=False + ).filter_by(project_id=None + ).with_lockmode('update' + ).first() + # NOTE(vish): if with_lockmode isn't supported, as in sqlite, + # then this has concurrency issues + if not network_ref: + raise db.NoMoreNetworks() + network_ref['project_id'] = project_id + session.add(network_ref) + return network_ref + + +@require_admin_context def network_count(context): session = get_session() return session.query(models.Network @@ -844,31 +862,26 @@ def network_count_reserved_ips(context, network_id): @require_admin_context -def network_create(context, values): +def network_create_safe(context, values): network_ref = models.Network() for (key, value) in values.iteritems(): network_ref[key] = value - network_ref.save() - return network_ref + try: + network_ref.save() + return network_ref + except IntegrityError: + return None @require_admin_context -def network_destroy(context, network_id): +def network_disassociate(context, network_id): + network_update(context, network_id, {'project_id': None}) + + +@require_admin_context +def network_disassociate_all(context): session = get_session() - with session.begin(): - # TODO(vish): do we have to use sql here? - session.execute('update networks set deleted=1 where id=:id', - {'id': network_id}) - session.execute('update fixed_ips set deleted=1 where network_id=:id', - {'id': network_id}) - session.execute('update floating_ips set deleted=1 ' - 'where fixed_ip_id in ' - '(select id from fixed_ips ' - 'where network_id=:id)', - {'id': network_id}) - session.execute('update network_indexes set network_id=NULL ' - 'where network_id=:id', - {'id': network_id}) + session.execute('update networks set project_id=NULL') @require_context @@ -918,48 +931,21 @@ def network_get_by_bridge(context, bridge): if not result: raise exception.NotFound('No network for bridge %s' % bridge) - return result @require_admin_context -def network_get_index(context, network_id): +def network_get_by_instance(_context, instance_id): session = get_session() - with session.begin(): - network_index = session.query(models.NetworkIndex - ).filter_by(network_id=None - ).filter_by(deleted=False - ).with_lockmode('update' - ).first() - - if not network_index: - raise db.NoMoreNetworks() - - network_index['network'] = network_get(context, - network_id, - session=session) - session.add(network_index) - - return network_index['index'] - - -@require_admin_context -def network_index_count(context): - session = get_session() - return session.query(models.NetworkIndex - ).filter_by(deleted=can_read_deleted(context) - ).count() - - -@require_admin_context -def network_index_create_safe(context, values): - network_index_ref = models.NetworkIndex() - for (key, value) in values.iteritems(): - network_index_ref[key] = value - try: - network_index_ref.save() - except IntegrityError: - pass + rv = session.query(models.Network + ).filter_by(deleted=False + ).join(models.Network.fixed_ips + ).filter_by(instance_id=instance_id + ).filter_by(deleted=False + ).first() + if not rv: + raise exception.NotFound('No network for instance %s' % instance_id) + return rv @require_admin_context @@ -999,15 +985,22 @@ def network_update(context, network_id, values): @require_context def project_get_network(context, project_id): session = get_session() - result= session.query(models.Network + rv = session.query(models.Network ).filter_by(project_id=project_id ).filter_by(deleted=False ).first() - - if not result: - raise exception.NotFound('No network for project: %s' % project_id) - - return result + if not rv: + try: + return network_associate(context, project_id) + except IntegrityError: + # NOTE(vish): We hit this if there is a race and two + # processes are attempting to allocate the + # network at the same time + rv = session.query(models.Network + ).filter_by(project_id=project_id + ).filter_by(deleted=False + ).first() + return rv ################### diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 0eb9a8f18..eed8f0578 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -25,7 +25,7 @@ import datetime # TODO(vish): clean up these imports from sqlalchemy.orm import relationship, backref, exc, object_mapper -from sqlalchemy import Column, Integer, String +from sqlalchemy import Column, Integer, String, schema from sqlalchemy import ForeignKey, DateTime, Boolean, Text from sqlalchemy.exc import IntegrityError from sqlalchemy.ext.declarative import declarative_base @@ -363,10 +363,13 @@ class KeyPair(BASE, NovaBase): class Network(BASE, NovaBase): """Represents a network""" __tablename__ = 'networks' + __table_args__ = (schema.UniqueConstraint("vpn_public_address", + "vpn_public_port"), + {'mysql_engine': 'InnoDB'}) id = Column(Integer, primary_key=True) injected = Column(Boolean, default=False) - cidr = Column(String(255)) + cidr = Column(String(255), unique=True) netmask = Column(String(255)) bridge = Column(String(255)) gateway = Column(String(255)) @@ -379,28 +382,13 @@ class Network(BASE, NovaBase): vpn_private_address = Column(String(255)) dhcp_start = Column(String(255)) - project_id = Column(String(255)) + # NOTE(vish): The unique constraint below helps avoid a race condition + # when associating a network, but it also means that we + # can't associate two networks with one project. + project_id = Column(String(255), unique=True) host = Column(String(255)) # , ForeignKey('hosts.id')) -class NetworkIndex(BASE, NovaBase): - """Represents a unique offset for a network - - Currently vlan number, vpn port, and fixed ip ranges are keyed off of - this index. These may ultimately need to be converted to separate - pools. - """ - __tablename__ = 'network_indexes' - id = Column(Integer, primary_key=True) - index = Column(Integer, unique=True) - network_id = Column(Integer, ForeignKey('networks.id'), nullable=True) - network = relationship(Network, - backref=backref('network_index', uselist=False), - foreign_keys=network_id, - primaryjoin='and_(NetworkIndex.network_id==Network.id,' - 'NetworkIndex.deleted==False)') - - class AuthToken(BASE, NovaBase): """Represents an authorization token for all API transactions. Fields are a string representing the actual token and a user id for mapping @@ -413,7 +401,6 @@ class AuthToken(BASE, NovaBase): cdn_management_url = Column(String(255)) - # TODO(vish): can these both come from the same baseclass? class FixedIp(BASE, NovaBase): """Represents a fixed ip for an instance""" @@ -517,7 +504,7 @@ def register_models(): """Register Models and create metadata""" from sqlalchemy import create_engine models = (Service, Instance, Volume, ExportDevice, FixedIp, - FloatingIp, Network, NetworkIndex, SecurityGroup, + FloatingIp, Network, SecurityGroup, SecurityGroupIngressRule, SecurityGroupInstanceAssociation, AuthToken, User, Project) # , Image, Host engine = create_engine(FLAGS.sql_connection, echo=False) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 37f9c8253..0891c02b1 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -65,12 +65,12 @@ def init_host(): # SNAT rule for outbound traffic. _confirm_rule("POSTROUTING", "-t nat -s %s " "-j SNAT --to-source %s" - % (FLAGS.private_range, FLAGS.routing_source_ip)) + % (FLAGS.fixed_range, FLAGS.routing_source_ip)) _confirm_rule("POSTROUTING", "-t nat -s %s -j MASQUERADE" % - FLAGS.private_range) + FLAGS.fixed_range) _confirm_rule("POSTROUTING", "-t nat -s %(range)s -d %(range)s -j ACCEPT" % - {'range': FLAGS.private_range}) + {'range': FLAGS.fixed_range}) def bind_floating_ip(floating_ip): """Bind ip to public interface""" diff --git a/nova/network/manager.py b/nova/network/manager.py index 093a6be9a..a1d10c119 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -37,17 +37,6 @@ from nova import utils FLAGS = flags.FLAGS flags.DEFINE_string('flat_network_bridge', 'br100', 'Bridge for simple network instances') -flags.DEFINE_list('flat_network_ips', - ['192.168.0.2', '192.168.0.3', '192.168.0.4'], - 'Available ips for simple network') -flags.DEFINE_string('flat_network_network', '192.168.0.0', - 'Network for simple network') -flags.DEFINE_string('flat_network_netmask', '255.255.255.0', - 'Netmask for simple network') -flags.DEFINE_string('flat_network_gateway', '192.168.0.1', - 'Broadcast for simple network') -flags.DEFINE_string('flat_network_broadcast', '192.168.0.255', - 'Broadcast for simple network') flags.DEFINE_string('flat_network_dns', '8.8.4.4', 'Dns for simple network') flags.DEFINE_integer('vlan_start', 100, 'First VLAN for private networks') @@ -57,8 +46,8 @@ flags.DEFINE_string('vpn_ip', utils.get_my_ip(), flags.DEFINE_integer('vpn_start', 1000, 'First Vpn port for private networks') flags.DEFINE_integer('network_size', 256, 'Number of addresses in each private subnet') -flags.DEFINE_string('public_range', '4.4.4.0/24', 'Public IP address block') -flags.DEFINE_string('private_range', '10.0.0.0/8', 'Private IP address block') +flags.DEFINE_string('floating_range', '4.4.4.0/24', 'Floating IP address block') +flags.DEFINE_string('fixed_range', '10.0.0.0/8', 'Fixed IP address block') flags.DEFINE_integer('cnt_vpn_clients', 5, 'Number of addresses reserved for vpn clients') flags.DEFINE_string('network_driver', 'nova.network.linux_net', @@ -91,13 +80,9 @@ class NetworkManager(manager.Manager): for network in self.db.host_get_networks(None, self.host): self._on_set_network_host(None, network['id']) - def set_network_host(self, context, project_id): - """Safely sets the host of the projects network""" + def set_network_host(self, context, network_id): + """Safely sets the host of the network""" logging.debug("setting network host") - network_ref = self.db.project_get_network(context, project_id) - # TODO(vish): can we minimize db access by just getting the - # id here instead of the ref? - network_id = network_ref['id'] host = self.db.network_set_host(None, network_id, self.host) @@ -117,10 +102,10 @@ class NetworkManager(manager.Manager): raise NotImplementedError() def _on_set_network_host(self, context, network_id): - """Called when this host becomes the host for a project""" + """Called when this host becomes the host for a network""" raise NotImplementedError() - def setup_compute_network(self, context, project_id): + def setup_compute_network(self, context, instance_id): """Sets up matching network for compute hosts""" raise NotImplementedError() @@ -150,6 +135,16 @@ class NetworkManager(manager.Manager): """Returns an floating ip to the pool""" self.db.floating_ip_deallocate(context, floating_address) + def get_network(self, context): + """Get the network for the current context""" + raise NotImplementedError() + + def create_networks(self, context, num_networks, network_size, + *args, **kwargs): + """Create networks based on parameters""" + raise NotImplementedError() + + @property def _bottom_reserved_ips(self): # pylint: disable-msg=R0201 """Number of reserved ips at the bottom of the range""" @@ -163,7 +158,7 @@ class NetworkManager(manager.Manager): def _create_fixed_ips(self, context, network_id): """Create all fixed ips for network""" network_ref = self.db.network_get(context, network_id) - # NOTE(vish): should these be properties of the network as opposed + # NOTE(vish): Should these be properties of the network as opposed # to properties of the manager class? bottom_reserved = self._bottom_reserved_ips top_reserved = self._top_reserved_ips @@ -185,8 +180,13 @@ class FlatManager(NetworkManager): def allocate_fixed_ip(self, context, instance_id, *args, **kwargs): """Gets a fixed ip from the pool""" - network_ref = self.db.project_get_network(context, context.project.id) - address = self.db.fixed_ip_associate_pool(context, + # TODO(vish): when this is called by compute, we can associate compute + # 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 + network_ref = self.db.network_get_by_bridge(None, + FLAGS.flat_network_bridge) + address = self.db.fixed_ip_associate_pool(None, network_ref['id'], instance_id) self.db.fixed_ip_update(context, address, {'allocated': True}) @@ -195,9 +195,9 @@ class FlatManager(NetworkManager): def deallocate_fixed_ip(self, context, address, *args, **kwargs): """Returns a fixed ip to the pool""" self.db.fixed_ip_update(context, address, {'allocated': False}) - self.db.fixed_ip_disassociate(context, address) + self.db.fixed_ip_disassociate(None, address) - def setup_compute_network(self, context, project_id): + def setup_compute_network(self, context, instance_id): """Network is created manually""" pass @@ -205,24 +205,42 @@ class FlatManager(NetworkManager): """Currently no setup""" pass + def create_networks(self, context, cidr, num_networks, network_size, + *args, **kwargs): + """Create networks based on parameters""" + fixed_net = IPy.IP(cidr) + for index in range(num_networks): + start = index * network_size + significant_bits = 32 - int(math.log(network_size, 2)) + cidr = "%s/%s" % (fixed_net[start], significant_bits) + project_net = IPy.IP(cidr) + net = {} + net['cidr'] = cidr + net['netmask'] = str(project_net.netmask()) + net['gateway'] = str(project_net[1]) + net['broadcast'] = str(project_net.broadcast()) + net['dhcp_start'] = str(project_net[2]) + network_ref = self.db.network_create_safe(context, net) + if network_ref: + self._create_fixed_ips(context, network_ref['id']) + + def get_network(self, context): + """Get the network for the current context""" + # NOTE(vish): To support mutilple network hosts, This could randomly + # select from multiple networks instead of just + # returning the one. It could also potentially be done + # in the scheduler. + return self.db.network_get_by_bridge(context, + FLAGS.flat_network_bridge) + def _on_set_network_host(self, context, network_id): - """Called when this host becomes the host for a project""" - # NOTE(vish): should there be two types of network objects - # in the datastore? + """Called when this host becomes the host for a network""" net = {} net['injected'] = True - net['netmask'] = FLAGS.flat_network_netmask net['bridge'] = FLAGS.flat_network_bridge - net['gateway'] = FLAGS.flat_network_gateway - net['broadcast'] = FLAGS.flat_network_broadcast net['dns'] = FLAGS.flat_network_dns self.db.network_update(context, network_id, net) - # NOTE(vish): Rignt now we are putting all of the fixed ips in - # one large pool, but ultimately it may be better to - # have each network manager have its own network that - # it is responsible for and its own pool of ips. - for address in FLAGS.flat_network_ips: - self.db.fixed_ip_create(context, {'address': address}) + class VlanManager(NetworkManager): @@ -250,10 +268,13 @@ class VlanManager(NetworkManager): def allocate_fixed_ip(self, context, instance_id, *args, **kwargs): """Gets a fixed ip from the pool""" + # TODO(vish): This should probably be getting project_id from + # the instance, but it is another trip to the db. + # Perhaps this method should take an instance_ref. network_ref = self.db.project_get_network(context, context.project.id) if kwargs.get('vpn', None): address = network_ref['vpn_private_address'] - self.db.fixed_ip_associate(context, address, instance_id) + self.db.fixed_ip_associate(None, address, instance_id) else: address = self.db.fixed_ip_associate_pool(None, network_ref['id'], @@ -319,38 +340,9 @@ class VlanManager(NetworkManager): network_ref = self.db.fixed_ip_get_network(context, address) self.driver.update_dhcp(context, network_ref['id']) - def allocate_network(self, context, project_id): - """Set up the network""" - self._ensure_indexes(context) - network_ref = db.network_create(context, {'project_id': project_id}) - network_id = network_ref['id'] - private_net = IPy.IP(FLAGS.private_range) - index = db.network_get_index(context, network_id) - vlan = FLAGS.vlan_start + index - start = index * FLAGS.network_size - significant_bits = 32 - int(math.log(FLAGS.network_size, 2)) - cidr = "%s/%s" % (private_net[start], significant_bits) - project_net = IPy.IP(cidr) - - net = {} - net['cidr'] = cidr - # NOTE(vish): we could turn these into properties - net['netmask'] = str(project_net.netmask()) - net['gateway'] = str(project_net[1]) - net['broadcast'] = str(project_net.broadcast()) - net['vpn_private_address'] = str(project_net[2]) - net['dhcp_start'] = str(project_net[3]) - net['vlan'] = vlan - net['bridge'] = 'br%s' % vlan - net['vpn_public_address'] = FLAGS.vpn_ip - net['vpn_public_port'] = FLAGS.vpn_start + index - db.network_update(context, network_id, net) - self._create_fixed_ips(context, network_id) - return network_id - - def setup_compute_network(self, context, project_id): + def setup_compute_network(self, context, instance_id): """Sets up matching network for compute hosts""" - network_ref = self.db.project_get_network(context, project_id) + network_ref = db.network_get_by_instance(context, instance_id) self.driver.ensure_vlan_bridge(network_ref['vlan'], network_ref['bridge']) @@ -359,17 +351,42 @@ class VlanManager(NetworkManager): # TODO(vish): Implement this pass - def _ensure_indexes(self, context): - """Ensure the indexes for the network exist - - This could use a manage command instead of keying off of a flag""" - if not self.db.network_index_count(context): - for index in range(FLAGS.num_networks): - self.db.network_index_create_safe(context, {'index': index}) + def create_networks(self, context, cidr, num_networks, network_size, + vlan_start, vpn_start): + """Create networks based on parameters""" + fixed_net = IPy.IP(cidr) + for index in range(num_networks): + vlan = vlan_start + index + start = index * network_size + significant_bits = 32 - int(math.log(network_size, 2)) + cidr = "%s/%s" % (fixed_net[start], significant_bits) + project_net = IPy.IP(cidr) + net = {} + net['cidr'] = cidr + net['netmask'] = str(project_net.netmask()) + net['gateway'] = str(project_net[1]) + net['broadcast'] = str(project_net.broadcast()) + net['vpn_private_address'] = str(project_net[2]) + net['dhcp_start'] = str(project_net[3]) + net['vlan'] = vlan + net['bridge'] = 'br%s' % vlan + # NOTE(vish): This makes ports unique accross the cloud, a more + # robust solution would be to make them unique per ip + net['vpn_public_port'] = vpn_start + index + network_ref = self.db.network_create_safe(context, net) + if network_ref: + self._create_fixed_ips(context, network_ref['id']) + + def get_network(self, context): + """Get the network for the current context""" + return self.db.project_get_network(None, context.project.id) def _on_set_network_host(self, context, network_id): - """Called when this host becomes the host for a project""" + """Called when this host becomes the host for a network""" network_ref = self.db.network_get(context, network_id) + net = {} + net['vpn_public_address'] = FLAGS.vpn_ip + db.network_update(context, network_id, net) self.driver.ensure_vlan_bridge(network_ref['vlan'], network_ref['bridge'], network_ref) diff --git a/nova/test.py b/nova/test.py index 08e1dea2d..5cf2abd53 100644 --- a/nova/test.py +++ b/nova/test.py @@ -35,6 +35,7 @@ from nova import db from nova import fakerabbit from nova import flags from nova import rpc +from nova.network import manager as network_manager FLAGS = flags.FLAGS @@ -58,6 +59,15 @@ class TrialTestCase(unittest.TestCase): def setUp(self): # pylint: disable-msg=C0103 """Run before each test method to initialize test environment""" super(TrialTestCase, self).setUp() + # NOTE(vish): We need a better method for creating fixtures for tests + # now that we have some required db setup for the system + # to work properly. + if db.network_count(None) != 5: + network_manager.VlanManager().create_networks(None, + FLAGS.fixed_range, + 5, 16, + FLAGS.vlan_start, + FLAGS.vpn_start) # emulate some of the mox stuff, we can't use the metaclass # because it screws with our generators @@ -74,7 +84,7 @@ class TrialTestCase(unittest.TestCase): self.stubs.UnsetAll() self.stubs.SmartUnsetAll() self.mox.VerifyAll() - + db.network_disassociate_all(None) rpc.Consumer.attach_to_twisted = self.originalAttach for x in self.injected: try: @@ -140,7 +150,7 @@ class TrialTestCase(unittest.TestCase): class BaseTestCase(TrialTestCase): # TODO(jaypipes): Can this be moved into the TrialTestCase class? """Base test case class for all unit tests. - + DEPRECATED: This is being removed once Tornado is gone, use TrialTestCase. """ def setUp(self): # pylint: disable-msg=C0103 diff --git a/nova/tests/compute_unittest.py b/nova/tests/compute_unittest.py index 1e2bb113b..5a7f170f3 100644 --- a/nova/tests/compute_unittest.py +++ b/nova/tests/compute_unittest.py @@ -40,7 +40,8 @@ class ComputeTestCase(test.TrialTestCase): def setUp(self): # pylint: disable-msg=C0103 logging.getLogger().setLevel(logging.DEBUG) super(ComputeTestCase, self).setUp() - self.flags(connection_type='fake') + self.flags(connection_type='fake', + network_manager='nova.network.manager.FlatManager') self.compute = utils.import_object(FLAGS.compute_manager) self.manager = manager.AuthManager() self.user = self.manager.create_user('fake', 'fake', 'fake') diff --git a/nova/tests/network_unittest.py b/nova/tests/network_unittest.py index 59b0a36e4..c81f93bb3 100644 --- a/nova/tests/network_unittest.py +++ b/nova/tests/network_unittest.py @@ -52,13 +52,13 @@ class NetworkTestCase(test.TrialTestCase): self.context = context.APIRequestContext(project=None, user=self.user) for i in range(5): name = 'project%s' % i - self.projects.append(self.manager.create_project(name, - 'netuser', - name)) + project = self.manager.create_project(name, 'netuser', name) + self.projects.append(project) # create the necessary network data for the project - user_context = context.get_admin_context(user=self.user) - - self.network.set_network_host(user_context, self.projects[i].id) + user_context = context.APIRequestContext(project=self.projects[i], + user=self.user) + network_ref = self.network.get_network(user_context) + self.network.set_network_host(user_context, network_ref['id']) instance_ref = self._create_instance(0) self.instance_id = instance_ref['id'] instance_ref = self._create_instance(1) @@ -99,7 +99,7 @@ class NetworkTestCase(test.TrialTestCase): """Makes sure that we can allocaate a public ip""" # TODO(vish): better way of adding floating ips self.context.project = self.projects[0] - pubnet = IPy.IP(flags.FLAGS.public_range) + pubnet = IPy.IP(flags.FLAGS.floating_range) address = str(pubnet[0]) try: db.floating_ip_get_by_address(None, address) @@ -109,6 +109,7 @@ class NetworkTestCase(test.TrialTestCase): float_addr = self.network.allocate_floating_ip(self.context, self.projects[0].id) fix_addr = self._create_address(0) + lease_ip(fix_addr) self.assertEqual(float_addr, str(pubnet[0])) self.network.associate_floating_ip(self.context, float_addr, fix_addr) address = db.instance_get_floating_address(None, self.instance_id) @@ -118,6 +119,7 @@ class NetworkTestCase(test.TrialTestCase): self.assertEqual(address, None) self.network.deallocate_floating_ip(self.context, float_addr) self.network.deallocate_fixed_ip(self.context, fix_addr) + release_ip(fix_addr) def test_allocate_deallocate_fixed_ip(self): """Makes sure that we can allocate and deallocate a fixed ip""" @@ -190,8 +192,10 @@ class NetworkTestCase(test.TrialTestCase): release_ip(address3) for instance_id in instance_ids: db.instance_destroy(None, instance_id) - release_ip(first) + self.context.project = self.projects[0] + self.network.deallocate_fixed_ip(self.context, first) self._deallocate_address(0, first) + release_ip(first) def test_vpn_ip_and_port_looks_valid(self): """Ensure the vpn ip and port are reasonable""" @@ -207,10 +211,13 @@ class NetworkTestCase(test.TrialTestCase): for i in range(networks_left): project = self.manager.create_project('many%s' % i, self.user) projects.append(project) + db.project_get_network(None, project.id) + project = self.manager.create_project('last', self.user) + projects.append(project) self.assertRaises(db.NoMoreNetworks, - self.manager.create_project, - 'boom', - self.user) + db.project_get_network, + None, + project.id) for project in projects: self.manager.delete_project(project) @@ -223,7 +230,9 @@ class NetworkTestCase(test.TrialTestCase): address2 = self._create_address(0) self.assertEqual(address, address2) + lease_ip(address) self.network.deallocate_fixed_ip(self.context, address2) + release_ip(address) def test_available_ips(self): """Make sure the number of available ips for the network is correct diff --git a/nova/tests/scheduler_unittest.py b/nova/tests/scheduler_unittest.py index 53a8be144..80100fc2f 100644 --- a/nova/tests/scheduler_unittest.py +++ b/nova/tests/scheduler_unittest.py @@ -75,6 +75,7 @@ class SimpleDriverTestCase(test.TrialTestCase): self.flags(connection_type='fake', max_cores=4, max_gigabytes=4, + network_manager='nova.network.manager.FlatManager', volume_driver='nova.volume.driver.FakeAOEDriver', scheduler_driver='nova.scheduler.simple.SimpleScheduler') self.scheduler = manager.SchedulerManager() diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 319f7d2af..6ef5aa472 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -287,7 +287,7 @@ class LibvirtConnection(object): key = str(inst['key_data']) net = None - network_ref = db.project_get_network(None, project.id) + network_ref = db.network_get_by_instance(None, inst['id']) if network_ref['injected']: address = db.instance_get_fixed_address(None, inst['id']) with open(FLAGS.injected_network_template) as f: @@ -320,7 +320,7 @@ class LibvirtConnection(object): def to_xml(self, instance): # TODO(termie): cache? logging.debug('instance %s: starting toXML method', instance['name']) - network = db.project_get_network(None, instance['project_id']) + network = db.instance_get_fixed_by_instance(None, inst['id']) # FIXME(vish): stick this in db instance_type = instance_types.INSTANCE_TYPES[instance['instance_type']] ip_address = db.instance_get_fixed_address({}, instance['id']) |