From c1e0abd5be3ac8473aaf255f77fb2357b5771ea9 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 4 Aug 2010 18:00:13 -0700 Subject: fixed circular reference and tests --- nova/auth/manager.py | 4 +- nova/network/model.py | 89 ------------------------------- nova/network/networkdata.py | 116 +++++++++++++++++++++++++++++++++++++++++ nova/network/service.py | 5 +- nova/tests/auth_unittest.py | 14 ----- nova/tests/network_unittest.py | 15 ++++++ 6 files changed, 136 insertions(+), 107 deletions(-) create mode 100644 nova/network/networkdata.py diff --git a/nova/auth/manager.py b/nova/auth/manager.py index dacdeb383..463cfdf4a 100644 --- a/nova/auth/manager.py +++ b/nova/auth/manager.py @@ -37,7 +37,7 @@ from nova import objectstore # for flags from nova import utils from nova.auth import ldapdriver # for flags from nova.auth import signer -from nova.network import model +from nova.network import networkdata FLAGS = flags.FLAGS @@ -529,7 +529,7 @@ class AuthManager(object): @return: A tuple containing (ip, port) or None, None if vpn has not been allocated for user. """ - network_data = model.NetworkData.lookup(Project.safe_id(project)) + network_data = networkdata.NetworkData.lookup(Project.safe_id(project)) if not network_data: raise exception.NotFound('project network data has not been set') return (network_data.ip, network_data.port) diff --git a/nova/network/model.py b/nova/network/model.py index b065d174e..daac035e4 100644 --- a/nova/network/model.py +++ b/nova/network/model.py @@ -53,13 +53,6 @@ flags.DEFINE_integer('cnt_vpn_clients', 5, flags.DEFINE_integer('cloudpipe_start_port', 12000, 'Starting port for mapped CloudPipe external ports') -flags.DEFINE_string('vpn_ip', utils.get_my_ip(), - 'Public IP for the cloudpipe VPN servers') -flags.DEFINE_integer('vpn_start_port', 1000, - 'Start port for the cloudpipe VPN servers') -flags.DEFINE_integer('vpn_end_port', 2000, - 'End port for the cloudpipe VPN servers') - logging.getLogger().setLevel(logging.DEBUG) @@ -381,88 +374,6 @@ class PublicAddress(datastore.BasicModel): return addr -class NoMorePorts(exception.Error): - pass - - -class NetworkData(datastore.BasicModel): - """Manages network host, and vpn ip and port for projects""" - def __init__(self, project_id): - self.project_id = project_id - super(NetworkData, self).__init__() - - @property - def identifier(self): - """Identifier used for key in redis""" - return self.project_id - - @classmethod - def create(cls, project_id): - """Creates a vpn for project - - This method finds a free ip and port and stores the associated - values in the datastore. - """ - # TODO(vish): will we ever need multiiple ips per host? - port = cls.find_free_port_for_ip(FLAGS.vpn_ip) - network_data = cls(project_id) - # save ip for project - network_data['host'] = FLAGS.node_name - network_data['project'] = project_id - network_data['ip'] = FLAGS.vpn_ip - network_data['port'] = port - network_data.save() - return network_data - - @classmethod - def find_free_port_for_ip(cls, ip): - """Finds a free port for a given ip from the redis set""" - # TODO(vish): these redis commands should be generalized and - # placed into a base class. Conceptually, it is - # similar to an association, but we are just - # storing a set of values instead of keys that - # should be turned into objects. - redis = datastore.Redis.instance() - key = 'ip:%s:ports' % ip - # TODO(vish): these ports should be allocated through an admin - # command instead of a flag - if (not redis.exists(key) and - not redis.exists(cls._redis_association_name('ip', ip))): - for i in range(FLAGS.vpn_start_port, FLAGS.vpn_end_port + 1): - redis.sadd(key, i) - - port = redis.spop(key) - if not port: - raise NoMorePorts() - return port - - @classmethod - def num_ports_for_ip(cls, ip): - """Calculates the number of free ports for a given ip""" - return datastore.Redis.instance().scard('ip:%s:ports' % ip) - - @property - def ip(self): - """The ip assigned to the project""" - return self['ip'] - - @property - def port(self): - """The port assigned to the project""" - return int(self['port']) - - def save(self): - """Saves the association to the given ip""" - self.associate_with('ip', self.ip) - super(NetworkData, self).save() - - def destroy(self): - """Cleans up datastore and adds port back to pool""" - self.unassociate_with('ip', self.ip) - datastore.Redis.instance().sadd('ip:%s:ports' % self.ip, self.port) - super(NetworkData, self).destroy() - - DEFAULT_PORTS = [("tcp",80), ("tcp",22), ("udp",1194), ("tcp",443)] class PublicNetworkController(BaseNetwork): override_type = 'network' diff --git a/nova/network/networkdata.py b/nova/network/networkdata.py new file mode 100644 index 000000000..cec84287c --- /dev/null +++ b/nova/network/networkdata.py @@ -0,0 +1,116 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""Network Data for projects""" + +from nova import datastore +from nova import exception +from nova import flags +from nova import utils + +FLAGS = flags.FLAGS + + +flags.DEFINE_string('vpn_ip', utils.get_my_ip(), + 'Public IP for the cloudpipe VPN servers') +flags.DEFINE_integer('vpn_start_port', 1000, + 'Start port for the cloudpipe VPN servers') +flags.DEFINE_integer('vpn_end_port', 2000, + 'End port for the cloudpipe VPN servers') + +class NoMorePorts(exception.Error): + pass + + +class NetworkData(datastore.BasicModel): + """Manages network host, and vpn ip and port for projects""" + def __init__(self, project_id): + self.project_id = project_id + super(NetworkData, self).__init__() + + @property + def identifier(self): + """Identifier used for key in redis""" + return self.project_id + + @classmethod + def create(cls, project_id): + """Creates a vpn for project + + This method finds a free ip and port and stores the associated + values in the datastore. + """ + # TODO(vish): will we ever need multiiple ips per host? + port = cls.find_free_port_for_ip(FLAGS.vpn_ip) + network_data = cls(project_id) + # save ip for project + network_data['host'] = FLAGS.node_name + network_data['project'] = project_id + network_data['ip'] = FLAGS.vpn_ip + network_data['port'] = port + network_data.save() + return network_data + + @classmethod + def find_free_port_for_ip(cls, ip): + """Finds a free port for a given ip from the redis set""" + # TODO(vish): these redis commands should be generalized and + # placed into a base class. Conceptually, it is + # similar to an association, but we are just + # storing a set of values instead of keys that + # should be turned into objects. + redis = datastore.Redis.instance() + key = 'ip:%s:ports' % ip + # TODO(vish): these ports should be allocated through an admin + # command instead of a flag + if (not redis.exists(key) and + not redis.exists(cls._redis_association_name('ip', ip))): + for i in range(FLAGS.vpn_start_port, FLAGS.vpn_end_port + 1): + redis.sadd(key, i) + + port = redis.spop(key) + if not port: + raise NoMorePorts() + return port + + @classmethod + def num_ports_for_ip(cls, ip): + """Calculates the number of free ports for a given ip""" + return datastore.Redis.instance().scard('ip:%s:ports' % ip) + + @property + def ip(self): + """The ip assigned to the project""" + return self['ip'] + + @property + def port(self): + """The port assigned to the project""" + return int(self['port']) + + def save(self): + """Saves the association to the given ip""" + self.associate_with('ip', self.ip) + super(NetworkData, self).save() + + def destroy(self): + """Cleans up datastore and adds port back to pool""" + self.unassociate_with('ip', self.ip) + datastore.Redis.instance().sadd('ip:%s:ports' % self.ip, self.port) + super(NetworkData, self).destroy() + diff --git a/nova/network/service.py b/nova/network/service.py index 243f9a720..afc20c0d5 100644 --- a/nova/network/service.py +++ b/nova/network/service.py @@ -28,6 +28,7 @@ from nova.auth import manager from nova.exception import NotFound from nova.network import exception from nova.network import model +from nova.network import networkdata FLAGS = flags.FLAGS @@ -81,7 +82,7 @@ class BaseNetworkService(service.Service): self.network = model.PublicNetworkController() def set_network_host(self, user_id, project_id, *args, **kwargs): - """Safely becomes the host of the projects network""" + """Safely sets the host of the projects network""" redis = datastore.Redis.instance() key = _host_key(project_id) if redis.setnx(key, FLAGS.node_name): @@ -214,7 +215,7 @@ class VlanNetworkService(BaseNetworkService): def _on_set_network_host(self, user_id, project_id, *args, **kwargs): """Called when this host becomes the host for a project""" - model.NetworkData.create(project_id) + networkdata.NetworkData.create(project_id) @classmethod def setup_compute_network(self, user_id, project_id, security_group, diff --git a/nova/tests/auth_unittest.py b/nova/tests/auth_unittest.py index 2167c2385..3bd0a432c 100644 --- a/nova/tests/auth_unittest.py +++ b/nova/tests/auth_unittest.py @@ -179,20 +179,6 @@ class AuthTestCase(test.BaseTestCase): self.manager.remove_role('test1', 'sysadmin') self.assertFalse(project.has_role('test1', 'sysadmin')) - def test_212_vpn_ip_and_port_looks_valid(self): - project = self.manager.get_project('testproj') - self.assert_(project.vpn_ip) - self.assert_(project.vpn_port >= FLAGS.vpn_start_port) - self.assert_(project.vpn_port <= FLAGS.vpn_end_port) - - def test_213_too_many_vpns(self): - vpns = [] - for i in xrange(manager.Vpn.num_ports_for_ip(FLAGS.vpn_ip)): - vpns.append(manager.Vpn.create("vpnuser%s" % i)) - self.assertRaises(manager.NoMorePorts, manager.Vpn.create, "boom") - for vpn in vpns: - vpn.destroy() - def test_214_can_retrieve_project_by_user(self): project = self.manager.create_project('testproj2', 'test2', 'Another test project', ['test2']) self.assert_(len(self.manager.get_projects()) > 1) diff --git a/nova/tests/network_unittest.py b/nova/tests/network_unittest.py index 42cae327f..49147d4ec 100644 --- a/nova/tests/network_unittest.py +++ b/nova/tests/network_unittest.py @@ -25,6 +25,7 @@ from nova import test from nova import utils from nova.auth import manager from nova.network import model +from nova.network import networkdata from nova.network import service from nova.network.exception import NoMoreAddresses @@ -154,6 +155,20 @@ class NetworkTestCase(test.TrialTestCase): rv = self.service.deallocate_fixed_ip(firstaddress) self.dnsmasq.release_ip(mac, firstaddress, hostname, net.bridge_name) + def test_212_vpn_ip_and_port_looks_valid(self): + networkdata.NetworkData.create(self.projects[0].id) + self.assert_(self.projects[0].vpn_ip) + self.assert_(self.projects[0].vpn_port >= FLAGS.vpn_start_port) + self.assert_(self.projects[0].vpn_port <= FLAGS.vpn_end_port) + + def test_too_many_vpns(self): + vpns = [] + for i in xrange(networkdata.NetworkData.num_ports_for_ip(FLAGS.vpn_ip)): + vpns.append(networkdata.NetworkData.create("vpnuser%s" % i)) + self.assertRaises(networkdata.NoMorePorts, networkdata.NetworkData.create, "boom") + for vpn in vpns: + vpn.destroy() + def test_release_before_deallocate(self): pass -- cgit