diff options
-rwxr-xr-x | bin/nova-manage | 96 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/networks.py | 29 | ||||
-rw-r--r-- | nova/network/manager.py | 90 | ||||
-rw-r--r-- | nova/network/quantum/nova_ipam_lib.py | 2 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_networks.py | 62 | ||||
-rw-r--r-- | nova/tests/fake_flags.py | 1 | ||||
-rw-r--r-- | nova/tests/network/test_linux_net.py | 1 | ||||
-rw-r--r-- | nova/tests/test_nova_manage.py | 4 |
8 files changed, 195 insertions, 90 deletions
diff --git a/bin/nova-manage b/bin/nova-manage index 9a09a8b5a..01b27ef2d 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -83,6 +83,7 @@ from nova import db from nova.db import migration from nova import exception from nova import flags +from nova.api.openstack.compute.contrib import networks from nova.openstack.common import cfg from nova.openstack.common import importutils from nova.openstack.common import log as logging @@ -94,6 +95,8 @@ from nova import utils from nova import version from nova.volume import volume_types +from webob import exc + FLAGS = flags.FLAGS flags.DECLARE('flat_network_bridge', 'nova.network.manager') flags.DECLARE('num_networks', 'nova.network.manager') @@ -434,7 +437,7 @@ class NetworkCommands(object): @args('--label', dest="label", metavar='<label>', help='Label for network (ex: public)') - @args('--fixed_range_v4', dest="fixed_range_v4", metavar='<x.x.x.x/yy>', + @args('--fixed_range_v4', dest="cidr", metavar='<x.x.x.x/yy>', help='IPv4 subnet (ex: 10.0.0.0/8)') @args('--num_networks', dest="num_networks", metavar='<number>', help='Number of networks to create') @@ -442,7 +445,7 @@ class NetworkCommands(object): help='Number of IPs per network') @args('--vlan', dest="vlan_start", metavar='<vlan id>', help='vlan id') @args('--vpn', dest="vpn_start", help='vpn start') - @args('--fixed_range_v6', dest="fixed_range_v6", + @args('--fixed_range_v6', dest="cidr_v6", help='IPv6 subnet (ex: fe80::/64') @args('--gateway', dest="gateway", help='gateway') @args('--gateway_v6', dest="gateway_v6", help='ipv6 gateway') @@ -464,91 +467,22 @@ class NetworkCommands(object): help='Project id') @args('--priority', dest="priority", metavar="<number>", help='Network interface priority') - def create(self, label=None, fixed_range_v4=None, num_networks=None, + def create(self, label=None, cidr=None, num_networks=None, network_size=None, multi_host=None, vlan_start=None, - vpn_start=None, fixed_range_v6=None, gateway=None, + vpn_start=None, cidr_v6=None, gateway=None, gateway_v6=None, bridge=None, bridge_interface=None, dns1=None, dns2=None, project_id=None, priority=None, uuid=None, fixed_cidr=None): """Creates fixed ips for host by range""" - - # check for certain required inputs - if not label: - raise exception.NetworkNotCreated(req='--label') - # Size of "label" column in nova.networks is 255, hence the restriction - if len(label) > 255: - reason = _("Maximum allowed length for 'label' is 255.") - raise exception.InvalidInput(reason=reason) - if not (fixed_range_v4 or fixed_range_v6): - req = '--fixed_range_v4 or --fixed_range_v6' - raise exception.NetworkNotCreated(req=req) - - bridge = bridge or FLAGS.flat_network_bridge - if not bridge: - bridge_required = ['nova.network.manager.FlatManager', - 'nova.network.manager.FlatDHCPManager'] - if FLAGS.network_manager in bridge_required: - raise exception.NetworkNotCreated(req='--bridge') - - bridge_interface = (bridge_interface or FLAGS.flat_interface or - FLAGS.vlan_interface) - if not bridge_interface: - interface_required = ['nova.network.manager.VlanManager'] - if FLAGS.network_manager in interface_required: - raise exception.NetworkNotCreated(req='--bridge_interface') - - # sanitize other input using FLAGS if necessary - if not num_networks: - num_networks = FLAGS.num_networks - if not network_size and fixed_range_v4: - fixnet = netaddr.IPNetwork(fixed_range_v4) - each_subnet_size = fixnet.size / int(num_networks) - if each_subnet_size > FLAGS.network_size: - network_size = FLAGS.network_size - subnet = 32 - int(math.log(network_size, 2)) - oversize_msg = _('Subnet(s) too large, defaulting to /%s.' - ' To override, specify network_size flag.') % subnet - print oversize_msg - else: - network_size = fixnet.size - if not multi_host: - multi_host = FLAGS.multi_host - else: - multi_host = multi_host == 'T' - if not vlan_start: - vlan_start = FLAGS.vlan_start - if not vpn_start: - vpn_start = FLAGS.vpn_start - if not dns1 and FLAGS.flat_network_dns: - dns1 = FLAGS.flat_network_dns - - if not network_size: - network_size = FLAGS.network_size - - if fixed_cidr: - fixed_cidr = netaddr.IPNetwork(fixed_cidr) - - # create the network + kwargs = dict(((k, v) for k, v in locals().iteritems() + if v and k != "self")) + if multi_host is not None: + kwargs['multi_host'] = multi_host == 'T' net_manager = importutils.import_object(FLAGS.network_manager) - net_manager.create_networks(context.get_admin_context(), - label=label, - cidr=fixed_range_v4, - multi_host=multi_host, - num_networks=int(num_networks), - network_size=int(network_size), - vlan_start=int(vlan_start), - vpn_start=int(vpn_start), - cidr_v6=fixed_range_v6, - gateway=gateway, - gateway_v6=gateway_v6, - bridge=bridge, - bridge_interface=bridge_interface, - dns1=dns1, - dns2=dns2, - project_id=project_id, - priority=priority, - uuid=uuid, - fixed_cidr=fixed_cidr) + try: + net_manager.create_networks(context.get_admin_context(), **kwargs) + except exc.HTTPBadRequest as ex: + print ex def list(self): """List all created networks""" diff --git a/nova/api/openstack/compute/contrib/networks.py b/nova/api/openstack/compute/contrib/networks.py index fb1d3d77e..f673bf43a 100644 --- a/nova/api/openstack/compute/contrib/networks.py +++ b/nova/api/openstack/compute/contrib/networks.py @@ -16,7 +16,7 @@ # License for the specific language governing permissions and limitations # under the License. - +import netaddr import webob from webob import exc @@ -111,8 +111,31 @@ class NetworkController(object): raise exc.HTTPNotFound(_("Network not found")) return exc.HTTPAccepted() - def create(self, req, id, body=None): - raise exc.HTTPNotImplemented() + def create(self, req, body): + context = req.environ['nova.context'] + authorize(context) + + def bad(e): + return exc.HTTPUnprocessableEntity(explanation=e) + + if not (body and body.get("network")): + raise bad(_("Missing network in body")) + + params = body["network"] + if not params.get("label"): + raise bad(_("Network label is required")) + + cidr = params.get("cidr") or params.get("cidr_v6") + if not cidr: + raise bad(_("Network cidr or cidr_v6 is required")) + + LOG.debug(_("Creating network with label %s") % params["label"]) + + params["num_networks"] = 1 + params["network_size"] = netaddr.IPNetwork(cidr).size + + network = self.network_api.create(context, **params)[0] + return {"network": network_dict(context, network)} def add(self, req, body): context = req.environ['nova.context'] diff --git a/nova/network/manager.py b/nova/network/manager.py index c1f3a6418..862fb6f78 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -769,6 +769,8 @@ class NetworkManager(manager.SchedulerDependentManager): timeout_fixed_ips = True + required_create_args = [] + def __init__(self, network_driver=None, *args, **kwargs): if not network_driver: network_driver = FLAGS.network_driver @@ -1348,10 +1350,86 @@ class NetworkManager(manager.SchedulerDependentManager): if not fixed_ip['allocated']: self.db.fixed_ip_disassociate(context, address) - def create_networks(self, context, label, cidr, multi_host, num_networks, - network_size, cidr_v6, gateway, gateway_v6, bridge, - bridge_interface, dns1=None, dns2=None, + def create_networks(self, context, + label, cidr=None, multi_host=None, num_networks=None, + network_size=None, cidr_v6=None, + gateway=None, gateway_v6=None, bridge=None, + bridge_interface=None, dns1=None, dns2=None, fixed_cidr=None, **kwargs): + arg_names = ("label", "cidr", "multi_host", "num_networks", + "network_size", "cidr_v6", + "gateway", "gateway_v6", "bridge", + "bridge_interface", "dns1", "dns2", + "fixed_cidr") + for name in arg_names: + kwargs[name] = locals()[name] + int_args = ("network_size", "num_networks", + "vlan_start", "vpn_start") + for key in int_args: + try: + kwargs[key] = int(kwargs[key]) + except ValueError: + raise ValueError(_("%s must be an integer") % key) + except KeyError: + pass + + # check for certain required inputs + label = kwargs["label"] + if not label: + raise exception.NetworkNotCreated(req="label") + + # Size of "label" column in nova.networks is 255, hence the restriction + if len(label) > 255: + raise ValueError(_("Maximum allowed length for 'label' is 255.")) + + if not (kwargs["cidr"] or kwargs["cidr_v6"]): + raise exception.NetworkNotCreated(req="cidr or cidr_v6") + + kwargs["bridge"] = kwargs["bridge"] or FLAGS.flat_network_bridge + kwargs["bridge_interface"] = (kwargs["bridge_interface"] or + FLAGS.flat_interface) + + for fld in self.required_create_args: + if not kwargs[fld]: + raise exception.NetworkNotCreated(req=fld) + + num_networks = kwargs["num_networks"] or FLAGS.num_networks + network_size = kwargs["network_size"] + cidr = kwargs["cidr"] + if not network_size and cidr: + fixnet = netaddr.IPNetwork(cidr) + each_subnet_size = fixnet.size / num_networks + if each_subnet_size > FLAGS.network_size: + network_size = FLAGS.network_size + subnet = 32 - int(math.log(network_size, 2)) + oversize_msg = _( + 'Subnet(s) too large, defaulting to /%s.' + ' To override, specify network_size flag.') % subnet + LOG.warn(oversize_msg) + else: + network_size = fixnet.size + kwargs["num_networks"] = num_networks + kwargs["network_size"] = network_size + + kwargs["multi_host"] = (FLAGS.multi_host + if kwargs["multi_host"] is None + else + utils.bool_from_str(kwargs["multi_host"])) + kwargs["vlan_start"] = kwargs.get("vlan_start") or FLAGS.vlan_start + kwargs["vpn_start"] = kwargs.get("vpn_start") or FLAGS.vpn_start + kwargs["dns1"] = kwargs["dns1"] or FLAGS.flat_network_dns + kwargs["network_size"] = kwargs["network_size"] or FLAGS.network_size + + if kwargs["fixed_cidr"]: + kwargs["fixed_cidr"] = netaddr.IPNetwork(kwargs["fixed_cidr"]) + + return self._do_create_networks(context, **kwargs) + + def _do_create_networks(self, context, + label, cidr, multi_host, num_networks, + network_size, cidr_v6, gateway, gateway_v6, bridge, + bridge_interface, dns1=None, dns2=None, + fixed_cidr=None, **kwargs): """Create networks based on parameters.""" # NOTE(jkoelker): these are dummy values to make sure iter works # TODO(tr3buchet): disallow carving up networks @@ -1719,6 +1797,8 @@ class FlatManager(NetworkManager): timeout_fixed_ips = False + required_create_args = ['bridge'] + def _allocate_fixed_ips(self, context, instance_id, host, networks, **kwargs): """Calls allocate_fixed_ip once for each network.""" @@ -1793,6 +1873,7 @@ class FlatDHCPManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): SHOULD_CREATE_BRIDGE = True DHCP = True + required_create_args = ['bridge'] def init_host(self): """Do any initialization that needs to be run if this is a @@ -1862,6 +1943,7 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): SHOULD_CREATE_BRIDGE = True SHOULD_CREATE_VLAN = True DHCP = True + required_create_args = ['bridge_interface'] def init_host(self): """Do any initialization that needs to be run if this is a @@ -1941,6 +2023,8 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): '%(num_networks)s. Network size is %(network_size)s') % kwargs) + kwargs['bridge_interface'] = (kwargs.get('bridge_interface') or + FLAGS.vlan_interface) return NetworkManager.create_networks( self, context, vpn=True, **kwargs) diff --git a/nova/network/quantum/nova_ipam_lib.py b/nova/network/quantum/nova_ipam_lib.py index f398815e5..02157bf7c 100644 --- a/nova/network/quantum/nova_ipam_lib.py +++ b/nova/network/quantum/nova_ipam_lib.py @@ -59,7 +59,7 @@ class QuantumNovaIPAMLib(object): """ admin_context = context.elevated() subnet_size = len(netaddr.IPNetwork(cidr)) - networks = manager.FlatManager.create_networks(self.net_manager, + networks = manager.FlatManager._do_create_networks(self.net_manager, admin_context, label, cidr, False, 1, subnet_size, cidr_v6, gateway, gateway_v6, quantum_net_id, None, dns1, dns2, diff --git a/nova/tests/api/openstack/compute/contrib/test_networks.py b/nova/tests/api/openstack/compute/contrib/test_networks.py index efbb7ceba..808493f1b 100644 --- a/nova/tests/api/openstack/compute/contrib/test_networks.py +++ b/nova/tests/api/openstack/compute/contrib/test_networks.py @@ -15,6 +15,10 @@ # under the License. import copy +import itertools +import math +import netaddr +import uuid import webob @@ -23,6 +27,11 @@ from nova import exception from nova import test from nova.tests.api.openstack import fakes +from nova import flags + + +FLAGS = flags.FLAGS + FAKE_NETWORKS = [ { @@ -75,6 +84,15 @@ FAKE_USER_NETWORKS = [ }, ] +NEW_NETWORK = { + "network": { + "bridge_interface": "eth0", + "cidr": "10.20.105.0/24", + "label": "new net 111", + "vlan_start": 111, + } +} + class FakeNetworkAPI(object): @@ -117,6 +135,32 @@ class FakeNetworkAPI(object): return network raise exception.NetworkNotFound() + def create(self, context, **kwargs): + subnet_bits = int(math.ceil(math.log(kwargs.get( + 'network_size', FLAGS.network_size), 2))) + fixed_net_v4 = netaddr.IPNetwork(kwargs['cidr']) + prefixlen_v4 = 32 - subnet_bits + subnets_v4 = list(fixed_net_v4.subnet( + prefixlen_v4, + count=kwargs.get('num_networks', FLAGS.num_networks))) + new_networks = [] + new_id = max((net['id'] for net in self.networks)) + for index, subnet_v4 in enumerate(subnets_v4): + new_id += 1 + net = {'id': new_id, 'uuid': str(uuid.uuid4())} + + net['cidr'] = str(subnet_v4) + net['netmask'] = str(subnet_v4.netmask) + net['gateway'] = kwargs.get('gateway') or str(subnet_v4[1]) + net['broadcast'] = str(subnet_v4.broadcast) + net['dhcp_start'] = str(subnet_v4[2]) + + for key in FAKE_NETWORKS[0].iterkeys(): + net.setdefault(key, kwargs.get(key)) + new_networks.append(net) + self.networks += new_networks + return new_networks + class NetworksTest(test.TestCase): @@ -204,3 +248,21 @@ class NetworksTest(test.TestCase): req.environ["nova.context"].is_admin = True res_dict = self.controller.show(req, uuid) self.assertEqual(res_dict['network']['project_id'], 'fake') + + def test_network_create(self): + req = fakes.HTTPRequest.blank('/v2/1234/os-networks') + res_dict = self.controller.create(req, NEW_NETWORK) + self.assertTrue('network' in res_dict) + uuid = res_dict['network']['id'] + req = fakes.HTTPRequest.blank('/v2/1234/os-networks/%s' % uuid) + res_dict = self.controller.show(req, uuid) + self.assertTrue(res_dict['network']['label']. + startswith(NEW_NETWORK['network']['label'])) + + def test_network_create_large(self): + req = fakes.HTTPRequest.blank('/v2/1234/os-networks') + large_network = copy.deepcopy(NEW_NETWORK) + large_network['network']['cidr'] = '128.0.0.0/4' + res_dict = self.controller.create(req, large_network) + self.assertEqual(res_dict['network']['cidr'], + large_network['network']['cidr']) diff --git a/nova/tests/fake_flags.py b/nova/tests/fake_flags.py index f6d9496b1..f0fde3588 100644 --- a/nova/tests/fake_flags.py +++ b/nova/tests/fake_flags.py @@ -39,6 +39,7 @@ def set_defaults(conf): conf.set_default('iscsi_num_targets', 8) conf.set_default('network_size', 8) conf.set_default('num_networks', 2) + conf.set_default('vlan_interface', 'eth0') conf.set_default('rpc_backend', 'nova.openstack.common.rpc.impl_fake') conf.set_default('sql_connection', "sqlite://") conf.set_default('sqlite_synchronous', False) diff --git a/nova/tests/network/test_linux_net.py b/nova/tests/network/test_linux_net.py index 94b2ac8d9..47b853a8a 100644 --- a/nova/tests/network/test_linux_net.py +++ b/nova/tests/network/test_linux_net.py @@ -373,6 +373,7 @@ class LinuxNetworkTestCase(test.TestCase): "bridge_interface": "base_interface", "vlan": "fake" } + self.flags(vlan_interface="") driver.plug(network, "fakemac") self.assertEqual(info['passed_interface'], "base_interface") self.flags(vlan_interface="override_interface") diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py index 805ac8e0d..6cadeb2ef 100644 --- a/nova/tests/test_nova_manage.py +++ b/nova/tests/test_nova_manage.py @@ -171,13 +171,13 @@ class NetworkCommandsTestCase(test.TestCase): fake_create_networks) self.commands.create( label='Test', - fixed_range_v4='10.2.0.0/24', + cidr='10.2.0.0/24', num_networks=1, network_size=256, multi_host='F', vlan_start=200, vpn_start=2000, - fixed_range_v6='fd00:2::/120', + cidr_v6='fd00:2::/120', gateway='10.2.0.1', gateway_v6='fd00:2::22', bridge='br200', |