From 9df460a5cb2de96133028949ad12ac7c16dbd7fc Mon Sep 17 00:00:00 2001 From: andy Date: Wed, 8 Sep 2010 19:57:29 +0200 Subject: Remove tornado-related code from almost everything. Left it in api where it is still being used pending gundlach's changes. --- nova/rpc.py | 16 +--------------- nova/test.py | 7 ++++--- nova/tests/access_unittest.py | 2 +- nova/tests/auth_unittest.py | 2 +- nova/tests/cloud_unittest.py | 14 ++++++++------ nova/tests/objectstore_unittest.py | 2 +- nova/tests/rpc_unittest.py | 4 ++-- 7 files changed, 18 insertions(+), 29 deletions(-) diff --git a/nova/rpc.py b/nova/rpc.py index 84a9b5590..d83dd7a7c 100644 --- a/nova/rpc.py +++ b/nova/rpc.py @@ -81,21 +81,6 @@ class Consumer(messaging.Consumer): self.failed_connection = False super(Consumer, self).__init__(*args, **kwargs) - # TODO(termie): it would be nice to give these some way of automatically - # cleaning up after themselves - def attach_to_tornado(self, io_inst=None): - """Attach a callback to tornado that fires 10 times a second""" - from tornado import ioloop - if io_inst is None: - io_inst = ioloop.IOLoop.instance() - - injected = ioloop.PeriodicCallback( - lambda: self.fetch(enable_callbacks=True), 100, io_loop=io_inst) - injected.start() - return injected - - attachToTornado = attach_to_tornado - def fetch(self, no_ack=None, auto_ack=None, enable_callbacks=False): """Wraps the parent fetch with some logic for failed connections""" # TODO(vish): the logic for failed connections and logging should be @@ -123,6 +108,7 @@ class Consumer(messaging.Consumer): """Attach a callback to twisted that fires 10 times a second""" loop = task.LoopingCall(self.fetch, enable_callbacks=True) loop.start(interval=0.1) + return loop class Publisher(messaging.Publisher): diff --git a/nova/test.py b/nova/test.py index c392c8a84..5380d5743 100644 --- a/nova/test.py +++ b/nova/test.py @@ -62,6 +62,7 @@ class TrialTestCase(unittest.TestCase): self.mox = mox.Mox() self.stubs = stubout.StubOutForTesting() self.flag_overrides = {} + self.injected = [] def tearDown(self): # pylint: disable-msg=C0103 """Runs after each test method to finalize/tear down test environment""" @@ -72,6 +73,9 @@ class TrialTestCase(unittest.TestCase): self.stubs.SmartUnsetAll() self.mox.VerifyAll() + for x in self.injected: + x.stop() + if FLAGS.fake_rabbit: fakerabbit.reset_all() @@ -99,7 +103,6 @@ class BaseTestCase(TrialTestCase): super(BaseTestCase, self).setUp() # TODO(termie): we could possibly keep a more global registry of # the injected listeners... this is fine for now though - self.injected = [] self.ioloop = ioloop.IOLoop.instance() self._waiting = None @@ -109,8 +112,6 @@ class BaseTestCase(TrialTestCase): def tearDown(self):# pylint: disable-msg=C0103 """Runs after each test method to finalize/tear down test environment""" super(BaseTestCase, self).tearDown() - for x in self.injected: - x.stop() if FLAGS.fake_rabbit: fakerabbit.reset_all() diff --git a/nova/tests/access_unittest.py b/nova/tests/access_unittest.py index fa0a090a0..4cdf42091 100644 --- a/nova/tests/access_unittest.py +++ b/nova/tests/access_unittest.py @@ -30,7 +30,7 @@ FLAGS = flags.FLAGS class Context(object): pass -class AccessTestCase(test.BaseTestCase): +class AccessTestCase(test.TrialTestCase): def setUp(self): super(AccessTestCase, self).setUp() FLAGS.connection_type = 'fake' diff --git a/nova/tests/auth_unittest.py b/nova/tests/auth_unittest.py index 0b404bfdc..b219d0e3c 100644 --- a/nova/tests/auth_unittest.py +++ b/nova/tests/auth_unittest.py @@ -31,7 +31,7 @@ from nova.endpoint import cloud FLAGS = flags.FLAGS -class AuthTestCase(test.BaseTestCase): +class AuthTestCase(test.TrialTestCase): flush_db = False def setUp(self): super(AuthTestCase, self).setUp() diff --git a/nova/tests/cloud_unittest.py b/nova/tests/cloud_unittest.py index 900ff5a97..0add53937 100644 --- a/nova/tests/cloud_unittest.py +++ b/nova/tests/cloud_unittest.py @@ -19,7 +19,6 @@ import logging import StringIO import time -from tornado import ioloop from twisted.internet import defer import unittest from xml.etree import ElementTree @@ -36,7 +35,7 @@ from nova.endpoint import cloud FLAGS = flags.FLAGS -class CloudTestCase(test.BaseTestCase): +class CloudTestCase(test.TrialTestCase): def setUp(self): super(CloudTestCase, self).setUp() self.flags(connection_type='fake', @@ -51,18 +50,21 @@ class CloudTestCase(test.BaseTestCase): # set up a service self.compute = service.ComputeService() self.compute_consumer = rpc.AdapterConsumer(connection=self.conn, - topic=FLAGS.compute_topic, - proxy=self.compute) - self.injected.append(self.compute_consumer.attach_to_tornado(self.ioloop)) + topic=FLAGS.compute_topic, + proxy=self.compute) + self.injected.append(self.compute_consumer.attach_to_twisted()) try: manager.AuthManager().create_user('admin', 'admin', 'admin') except: pass admin = manager.AuthManager().get_user('admin') project = manager.AuthManager().create_project('proj', 'admin', 'proj') - self.context = api.APIRequestContext(handler=None,project=project,user=admin) + self.context = api.APIRequestContext(handler=None, + project=project, + user=admin) def tearDown(self): + super(CloudTestCase, self).tearDown() manager.AuthManager().delete_project('proj') manager.AuthManager().delete_user('admin') diff --git a/nova/tests/objectstore_unittest.py b/nova/tests/objectstore_unittest.py index dece4b5d5..b5970d405 100644 --- a/nova/tests/objectstore_unittest.py +++ b/nova/tests/objectstore_unittest.py @@ -53,7 +53,7 @@ os.makedirs(os.path.join(OSS_TEMPDIR, 'images')) os.makedirs(os.path.join(OSS_TEMPDIR, 'buckets')) -class ObjectStoreTestCase(test.BaseTestCase): +class ObjectStoreTestCase(test.TrialTestCase): """Test objectstore API directly.""" def setUp(self): # pylint: disable-msg=C0103 diff --git a/nova/tests/rpc_unittest.py b/nova/tests/rpc_unittest.py index e12a28fbc..e11967987 100644 --- a/nova/tests/rpc_unittest.py +++ b/nova/tests/rpc_unittest.py @@ -30,7 +30,7 @@ from nova import test FLAGS = flags.FLAGS -class RpcTestCase(test.BaseTestCase): +class RpcTestCase(test.TrialTestCase): """Test cases for rpc""" def setUp(self): # pylint: disable-msg=C0103 super(RpcTestCase, self).setUp() @@ -40,7 +40,7 @@ class RpcTestCase(test.BaseTestCase): topic='test', proxy=self.receiver) - self.injected.append(self.consumer.attach_to_tornado(self.ioloop)) + self.injected.append(self.consumer.attach_to_twisted()) def test_call_succeed(self): """Get a value through rpc call""" -- cgit From bd550806950bcfdcd32172a896f04bc3b1a76392 Mon Sep 17 00:00:00 2001 From: andy Date: Wed, 8 Sep 2010 20:06:27 +0200 Subject: Missed an instance of attach_to_tornado. Kind of crappy because testing didn't catch it, the test code certainly appears to be testing those features, however. --- nova/rpc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/rpc.py b/nova/rpc.py index d83dd7a7c..7a89ca8d0 100644 --- a/nova/rpc.py +++ b/nova/rpc.py @@ -264,7 +264,7 @@ def call(topic, msg): return d.callback(data['result']) consumer.register_callback(deferred_receive) - injected = consumer.attach_to_tornado() + injected = consumer.attach_to_twisted() # clean up after the injected listened and return x d.addCallback(lambda x: injected.stop() and x or x) -- cgit From 387671f9bc0299116ffbab7acfc47127afb989aa Mon Sep 17 00:00:00 2001 From: andy Date: Wed, 8 Sep 2010 22:43:54 +0200 Subject: Tests turn things into inlineCallbacks. This is to duplicate the old behavior of BaseTestCase and to generally prevent the bad situation in that tests appear to be passing when in fact they haven't run because @defer.inlineCallbacks was forgotten. --- nova/rpc.py | 1 - nova/test.py | 51 ++++++++++++++++++++++++++++++++++++++++---- nova/tests/cloud_unittest.py | 2 +- nova/tests/rpc_unittest.py | 3 +-- 4 files changed, 49 insertions(+), 8 deletions(-) diff --git a/nova/rpc.py b/nova/rpc.py index 7a89ca8d0..210d3cccf 100644 --- a/nova/rpc.py +++ b/nova/rpc.py @@ -250,7 +250,6 @@ def call(topic, msg): msg_id = uuid.uuid4().hex msg.update({'_msg_id': msg_id}) LOG.debug("MSG_ID is %s" % (msg_id)) - conn = Connection.instance() d = defer.Deferred() consumer = DirectConsumer(connection=conn, msg_id=msg_id) diff --git a/nova/test.py b/nova/test.py index 5380d5743..1f4b33272 100644 --- a/nova/test.py +++ b/nova/test.py @@ -33,6 +33,7 @@ from twisted.trial import unittest from nova import fakerabbit from nova import flags +from nova import rpc FLAGS = flags.FLAGS @@ -63,22 +64,28 @@ class TrialTestCase(unittest.TestCase): self.stubs = stubout.StubOutForTesting() self.flag_overrides = {} self.injected = [] + self._monkeyPatchAttach() def tearDown(self): # pylint: disable-msg=C0103 """Runs after each test method to finalize/tear down test environment""" - super(TrialTestCase, self).tearDown() self.reset_flags() self.mox.UnsetStubs() self.stubs.UnsetAll() self.stubs.SmartUnsetAll() self.mox.VerifyAll() - + + rpc.Consumer.attach_to_twisted = self.originalAttach for x in self.injected: - x.stop() + try: + x.stop() + except AssertionError: + pass if FLAGS.fake_rabbit: fakerabbit.reset_all() + super(TrialTestCase, self).tearDown() + def flags(self, **kw): """Override flag variables for a test""" for k, v in kw.iteritems(): @@ -94,10 +101,46 @@ class TrialTestCase(unittest.TestCase): for k, v in self.flag_overrides.iteritems(): setattr(FLAGS, k, v) + def run(self, result=None): + test_method = getattr(self, self._testMethodName) + setattr(self, + self._testMethodName, + self._maybeInlineCallbacks(test_method, result)) + rv = super(TrialTestCase, self).run(result) + setattr(self, self._testMethodName, test_method) + return rv + + def _maybeInlineCallbacks(self, func, result): + def _wrapped(): + g = func() + if isinstance(g, defer.Deferred): + return g + if not hasattr(g, 'send'): + return defer.succeed(g) + + inlined = defer.inlineCallbacks(func) + d = inlined() + return d + _wrapped.func_name = func.func_name + return _wrapped + + def _monkeyPatchAttach(self): + self.originalAttach = rpc.Consumer.attach_to_twisted + def _wrapped(innerSelf): + rv = self.originalAttach(innerSelf) + self.injected.append(rv) + return rv + + _wrapped.func_name = self.originalAttach.func_name + rpc.Consumer.attach_to_twisted = _wrapped + class BaseTestCase(TrialTestCase): # TODO(jaypipes): Can this be moved into the TrialTestCase class? - """Base test case class for all unit tests.""" + """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 """Run before each test method to initialize test environment""" super(BaseTestCase, self).setUp() diff --git a/nova/tests/cloud_unittest.py b/nova/tests/cloud_unittest.py index 0add53937..893717fe4 100644 --- a/nova/tests/cloud_unittest.py +++ b/nova/tests/cloud_unittest.py @@ -52,7 +52,7 @@ class CloudTestCase(test.TrialTestCase): self.compute_consumer = rpc.AdapterConsumer(connection=self.conn, topic=FLAGS.compute_topic, proxy=self.compute) - self.injected.append(self.compute_consumer.attach_to_twisted()) + self.compute_consumer.attach_to_twisted() try: manager.AuthManager().create_user('admin', 'admin', 'admin') diff --git a/nova/tests/rpc_unittest.py b/nova/tests/rpc_unittest.py index e11967987..1bb2405c7 100644 --- a/nova/tests/rpc_unittest.py +++ b/nova/tests/rpc_unittest.py @@ -39,8 +39,7 @@ class RpcTestCase(test.TrialTestCase): self.consumer = rpc.AdapterConsumer(connection=self.conn, topic='test', proxy=self.receiver) - - self.injected.append(self.consumer.attach_to_twisted()) + self.consumer.attach_to_twisted() def test_call_succeed(self): """Get a value through rpc call""" -- cgit From 3dbb3fa96779fbed89251a7c24455acbddc013e5 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sat, 11 Sep 2010 20:05:32 -0700 Subject: fixed reversed args in nova-manage project environment --- bin/nova-manage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/nova-manage b/bin/nova-manage index 325245ac4..1c6dc03ac 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -252,7 +252,7 @@ class ProjectCommands(object): def environment(self, project_id, user_id, filename='novarc'): """Exports environment variables to an sourcable file arguments: project_id user_id [filename='novarc]""" - rc = self.manager.get_environment_rc(project_id, user_id) + rc = self.manager.get_environment_rc(user_id, project_id) with open(filename, 'w') as f: f.write(rc) -- cgit From ab0e61a1e05bdec8b39c22ff9d93845526a0a02b Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sat, 11 Sep 2010 20:22:19 -0700 Subject: implement floating_ip_get_all_by_project and renamed db methods that get more then one to get_all_by instead of get_by --- nova/db/api.py | 19 ++++++++++++------- nova/db/sqlalchemy/api.py | 14 +++++++++++--- nova/endpoint/cloud.py | 16 ++++++++-------- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/nova/db/api.py b/nova/db/api.py index d749ae50a..1529888ba 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -122,10 +122,15 @@ def floating_ip_get_all(context): def floating_ip_get_all_by_host(context, host): - """Get all floating ips.""" + """Get all floating ips by host.""" return IMPL.floating_ip_get_all_by_host(context, host) +def floating_ip_get_all_by_project(context, project_id): + """Get all floating ips by project.""" + return IMPL.floating_ip_get_all_by_project(context, project_id) + + def floating_ip_get_by_address(context, address): """Get a floating ip by address or raise if it doesn't exist.""" return IMPL.floating_ip_get_by_address(context, address) @@ -208,14 +213,14 @@ def instance_get_all(context): return IMPL.instance_get_all(context) -def instance_get_by_project(context, project_id): +def instance_get_all_by_project(context, project_id): """Get all instance belonging to a project.""" - return IMPL.instance_get_by_project(context, project_id) + return IMPL.instance_get_all_by_project(context, project_id) -def instance_get_by_reservation(context, reservation_id): +def instance_get_all_by_reservation(context, reservation_id): """Get all instance belonging to a reservation.""" - return IMPL.instance_get_by_reservation(context, reservation_id) + return IMPL.instance_get_all_by_reservation(context, reservation_id) def instance_get_fixed_address(context, instance_id): @@ -417,9 +422,9 @@ def volume_get_instance(context, volume_id): return IMPL.volume_get_instance(context, volume_id) -def volume_get_by_project(context, project_id): +def volume_get_all_by_project(context, project_id): """Get all volumes belonging to a project.""" - return IMPL.volume_get_by_project(context, project_id) + return IMPL.volume_get_all_by_project(context, project_id) def volume_get_by_str(context, str_id): diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 485dca2b0..948bca224 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -159,6 +159,14 @@ def floating_ip_get_all_by_host(_context, host): ).filter_by(deleted=False ).all() +def floating_ip_get_all_by_project(_context, project_id): + session = get_session() + return session.query(models.FloatingIp + ).options(joinedload_all('fixed_ip.instance') + ).filter_by(project_id=project_id + ).filter_by(deleted=False + ).all() + def floating_ip_get_by_address(_context, address): return models.FloatingIp.find_by_str(address) @@ -288,7 +296,7 @@ def instance_get_all(context): ).all() -def instance_get_by_project(context, project_id): +def instance_get_all_by_project(context, project_id): session = get_session() return session.query(models.Instance ).options(joinedload_all('fixed_ip.floating_ips') @@ -297,7 +305,7 @@ def instance_get_by_project(context, project_id): ).all() -def instance_get_by_reservation(_context, reservation_id): +def instance_get_all_by_reservation(_context, reservation_id): session = get_session() return session.query(models.Instance ).options(joinedload_all('fixed_ip.floating_ips') @@ -606,7 +614,7 @@ def volume_get_all(context): return models.Volume.all(deleted=_deleted(context)) -def volume_get_by_project(context, project_id): +def volume_get_all_by_project(context, project_id): session = get_session() return session.query(models.Volume ).filter_by(project_id=project_id diff --git a/nova/endpoint/cloud.py b/nova/endpoint/cloud.py index 622b4e2a4..00aa2ede8 100644 --- a/nova/endpoint/cloud.py +++ b/nova/endpoint/cloud.py @@ -84,7 +84,7 @@ class CloudController(object): def _get_mpi_data(self, project_id): result = {} - for instance in db.instance_get_by_project(None, project_id): + for instance in db.instance_get_all_by_project(None, project_id): if instance['fixed_ip']: line = '%s slots=%d' % (instance['fixed_ip']['str_id'], INSTANCE_TYPES[instance['instance_type']]['vcpus']) @@ -244,7 +244,7 @@ class CloudController(object): if context.user.is_admin(): volumes = db.volume_get_all(context) else: - volumes = db.volume_get_by_project(context, context.project.id) + volumes = db.volume_get_all_by_project(context, context.project.id) volumes = [self._format_volume(context, v) for v in volumes] @@ -363,14 +363,14 @@ class CloudController(object): def _format_instances(self, context, reservation_id=None): reservations = {} if reservation_id: - instances = db.instance_get_by_reservation(context, - reservation_id) + instances = db.instance_get_all_by_reservation(context, + reservation_id) else: if not context.user.is_admin(): instances = db.instance_get_all(context) else: - instances = db.instance_get_by_project(context, - context.project.id) + instances = db.instance_get_all_by_project(context, + context.project.id) for instance in instances: if not context.user.is_admin(): if instance['image_id'] == FLAGS.vpn_image_id: @@ -421,8 +421,8 @@ class CloudController(object): if context.user.is_admin(): iterator = db.floating_ip_get_all(context) else: - iterator = db.floating_ip_get_by_project(context, - context.project.id) + iterator = db.floating_ip_get_all_by_project(context, + context.project.id) for floating_ip_ref in iterator: address = floating_ip_ref['str_id'] instance_id = None -- cgit From 19040b7b2b908b2816bc7aca54a8437d54badd26 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sat, 11 Sep 2010 21:12:01 -0700 Subject: fixed reference to misnamed method --- nova/endpoint/cloud.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nova/endpoint/cloud.py b/nova/endpoint/cloud.py index 00aa2ede8..41f23618f 100644 --- a/nova/endpoint/cloud.py +++ b/nova/endpoint/cloud.py @@ -464,14 +464,15 @@ class CloudController(object): @defer.inlineCallbacks def associate_address(self, context, instance_id, public_ip, **kwargs): instance_ref = db.instance_get_by_str(context, instance_id) - fixed_ip_ref = db.fixed_ip_get_by_instance(context, instance_ref['id']) + fixed_address = db.instance_get_fixed_address(context, + instance_ref['id']) floating_ip_ref = db.floating_ip_get_by_address(context, public_ip) network_topic = yield self._get_network_topic(context) rpc.cast(network_topic, {"method": "associate_floating_ip", "args": {"context": None, "floating_address": floating_ip_ref['str_id'], - "fixed_address": fixed_ip_ref['str_id']}}) + "fixed_address": fixed_address}}) defer.returnValue({'associateResponse': ["Address associated."]}) @rbac.allow('netadmin') -- cgit From 53bba81d1e9774eefadd4f0f2b25638838a7ad07 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sat, 11 Sep 2010 22:54:47 -0700 Subject: don't allocate the same floating ip multiple times --- nova/db/sqlalchemy/api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 948bca224..cc496e558 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -80,6 +80,7 @@ def floating_ip_allocate_address(_context, host, project_id): floating_ip_ref = session.query(models.FloatingIp ).filter_by(host=host ).filter_by(fixed_ip_id=None + ).filter_by(project_id=None ).filter_by(deleted=False ).with_lockmode('update' ).first() -- cgit From e45a5dd2cbcfe5d43cc59c6a20e3d065d43ee161 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sat, 11 Sep 2010 23:32:03 -0700 Subject: speed up generation of dhcp_hosts and don't run into None errors if instance is deleted --- nova/db/sqlalchemy/api.py | 1 + nova/network/linux_net.py | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index cc496e558..4cc086006 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -437,6 +437,7 @@ def network_get(_context, network_id): def network_get_associated_fixed_ips(_context, network_id): session = get_session() return session.query(models.FixedIp + ).options(joinedload_all('instance') ).filter_by(network_id=network_id ).filter(models.FixedIp.instance_id != None ).filter_by(deleted=False diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 41aeb5da7..621ae54ea 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -132,8 +132,9 @@ def ensure_bridge(bridge, interface, net_attrs=None): def get_dhcp_hosts(context, network_id): """Get a string containing a network's hosts config in dnsmasq format""" hosts = [] - for fixed_ip in db.network_get_associated_fixed_ips(context, network_id): - hosts.append(_host_dhcp(fixed_ip['str_id'])) + for fixed_ip_ref in db.network_get_associated_fixed_ips(context, + network_id): + hosts.append(_host_dhcp(fixed_ip_ref)) return '\n'.join(hosts) @@ -171,12 +172,12 @@ def update_dhcp(context, network_id): _execute(command, addl_env=env) -def _host_dhcp(address): +def _host_dhcp(fixed_ip_ref): """Return a host string for an address""" - instance_ref = db.fixed_ip_get_instance(None, address) + instance_ref = fixed_ip_ref['instance'] return "%s,%s.novalocal,%s" % (instance_ref['mac_address'], instance_ref['hostname'], - address) + fixed_ip_ref['str_id']) def _execute(cmd, *args, **kwargs): -- cgit From 85876fc1fa3dc7cb289e59712ab5a2b20877fc58 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sat, 11 Sep 2010 23:34:32 -0700 Subject: disassociate floating is supposed to take floating_address --- nova/endpoint/cloud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/endpoint/cloud.py b/nova/endpoint/cloud.py index 41f23618f..5fff72642 100644 --- a/nova/endpoint/cloud.py +++ b/nova/endpoint/cloud.py @@ -605,7 +605,7 @@ class CloudController(object): rpc.cast(network_topic, {"method": "disassociate_floating_ip", "args": {"context": None, - "address": address}}) + "floating_address": address}}) address = db.instance_get_fixed_address(context, instance_ref['id']) -- cgit From be1b1e320c17630430cfa567d8685f8cfc5773e4 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sat, 11 Sep 2010 23:51:28 -0700 Subject: make fixed_ip_get_by_address return the instance as well so we don't run into concurrency issues where it is disassociated in between --- nova/db/sqlalchemy/api.py | 16 ++++++++++++++-- nova/network/manager.py | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 4cc086006..3d3b766fc 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -19,13 +19,15 @@ Implementation of SQLAlchemy backend """ +import sys + from nova import db from nova import exception from nova import flags from nova.db.sqlalchemy import models from nova.db.sqlalchemy.session import get_session from sqlalchemy import or_ -from sqlalchemy.orm import joinedload_all +from sqlalchemy.orm import exc, joinedload_all FLAGS = flags.FLAGS @@ -243,7 +245,17 @@ def fixed_ip_disassociate(_context, address): def fixed_ip_get_by_address(_context, address): - return models.FixedIp.find_by_str(address) + session = get_session() + with session.begin(): + try: + return session.query(models.FixedIp + ).options(joinedload_all('instance') + ).filter_by(address=address + ).filter_by(deleted=False + ).one() + except exc.NoResultFound: + new_exc = exception.NotFound("No model for address %s" % address) + raise new_exc.__class__, new_exc, sys.exc_info()[2] def fixed_ip_get_instance(_context, address): diff --git a/nova/network/manager.py b/nova/network/manager.py index 7a3bcfc2f..9564a3e33 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -262,7 +262,7 @@ class VlanManager(NetworkManager): if not fixed_ip_ref['allocated']: logging.warn("IP %s leased that was already deallocated", address) return - instance_ref = self.db.fixed_ip_get_instance(context, address) + instance_ref = fixed_ip_ref['instance'] if not instance_ref: raise exception.Error("IP %s leased that isn't associated" % address) @@ -280,7 +280,7 @@ class VlanManager(NetworkManager): if not fixed_ip_ref['leased']: logging.warn("IP %s released that was not leased", address) return - instance_ref = self.db.fixed_ip_get_instance(context, address) + instance_ref = fixed_ip_ref['instance'] if not instance_ref: raise exception.Error("IP %s released that isn't associated" % address) -- cgit From 970654239267fc702f767bbaff3e22207576d0cd Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 13 Sep 2010 01:58:40 -0700 Subject: multiple network controllers will not create duplicate indexes --- nova/db/api.py | 9 ++++++--- nova/db/sqlalchemy/api.py | 8 ++++++-- nova/db/sqlalchemy/models.py | 2 +- nova/network/manager.py | 2 +- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/nova/db/api.py b/nova/db/api.py index 9f6ff99c3..a775dc301 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -365,9 +365,12 @@ def network_index_count(context): return IMPL.network_index_count(context) -def network_index_create(context, values): - """Create a network index from the values dict""" - return IMPL.network_index_create(context, values) +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): diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index d612fe669..35585b1b0 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -25,6 +25,7 @@ from nova import flags from nova.db.sqlalchemy import models from nova.db.sqlalchemy.session import get_session from sqlalchemy import or_ +from sqlalchemy.exc import IntegrityError from sqlalchemy.orm import joinedload_all from sqlalchemy.sql import func @@ -567,11 +568,14 @@ def network_index_count(_context): return models.NetworkIndex.count() -def network_index_create(_context, values): +def network_index_create_safe(_context, values): network_index_ref = models.NetworkIndex() for (key, value) in values.iteritems(): network_index_ref[key] = value - network_index_ref.save() + try: + network_index_ref.save() + except IntegrityError: + pass def network_set_host(_context, network_id, host_id): diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 41013f41b..6ff0decb5 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -355,7 +355,7 @@ class NetworkIndex(BASE, NovaBase): """ __tablename__ = 'network_indexes' id = Column(Integer, primary_key=True) - index = Column(Integer) + index = Column(Integer, unique=True) network_id = Column(Integer, ForeignKey('networks.id'), nullable=True) network = relationship(Network, backref=backref('network_index', uselist=False)) diff --git a/nova/network/manager.py b/nova/network/manager.py index 191c1d364..570d5f8d8 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -343,7 +343,7 @@ class VlanManager(NetworkManager): 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(context, {'index': index}) + self.db.network_index_create_safe(context, {'index': index}) def _on_set_network_host(self, context, network_id): """Called when this host becomes the host for a project""" -- cgit From 01a757ee7bc3624c17dbbcfd3bc65d3e2f674b03 Mon Sep 17 00:00:00 2001 From: Devin Carlen Date: Wed, 15 Sep 2010 17:40:12 -0700 Subject: Added iptables host initial configuration --- bin/nova-manage | 1 + nova/endpoint/api.py | 3 +- nova/flags.py | 5 ++- nova/manager.py | 10 +++++ nova/network/linux_net.py | 44 +++++++++++++++------- nova/network/manager.py | 7 ++++ nova/service.py | 1 + tools/setup_ipchains.sh | 94 +++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 148 insertions(+), 17 deletions(-) create mode 100644 tools/setup_ipchains.sh diff --git a/bin/nova-manage b/bin/nova-manage index 325245ac4..909435ede 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -384,3 +384,4 @@ def main(): if __name__ == '__main__': main() + diff --git a/nova/endpoint/api.py b/nova/endpoint/api.py index 40be00bb7..6de3698e1 100755 --- a/nova/endpoint/api.py +++ b/nova/endpoint/api.py @@ -42,8 +42,6 @@ from nova.endpoint import cloud FLAGS = flags.FLAGS -flags.DEFINE_integer('cc_port', 8773, 'cloud controller port') - _log = logging.getLogger("api") _log.setLevel(logging.DEBUG) @@ -342,3 +340,4 @@ class APIServerApplication(tornado.web.Application): (r'/1.0/([-A-Za-z0-9/]*)', MetadataRequestHandler), ], pool=multiprocessing.Pool(4)) self.controllers = controllers + diff --git a/nova/flags.py b/nova/flags.py index 7b0c95a3c..55b452fc3 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -184,7 +184,9 @@ DEFINE_string('rabbit_userid', 'guest', 'rabbit userid') DEFINE_string('rabbit_password', 'guest', 'rabbit password') DEFINE_string('rabbit_virtual_host', '/', 'rabbit virtual host') DEFINE_string('control_exchange', 'nova', 'the main exchange to connect to') -DEFINE_string('ec2_url', 'http://127.0.0.1:8773/services/Cloud', +DEFINE_string('cc_ip', '127.0.0.1', 'ip of api server') +DEFINE_integer('cc_port', 8773, 'cloud controller port') +DEFINE_string('ec2_url', 'http://%s:%s/services/Cloud' % (FLAGS.cc_ip, FLAGS.cc_port), 'Url to ec2 api server') DEFINE_string('default_image', 'ami-11111', @@ -220,3 +222,4 @@ DEFINE_string('host', socket.gethostname(), # UNUSED DEFINE_string('node_availability_zone', 'nova', 'availability zone of this node') + diff --git a/nova/manager.py b/nova/manager.py index e9aa50c56..495b1f0d1 100644 --- a/nova/manager.py +++ b/nova/manager.py @@ -37,3 +37,13 @@ class Manager(object): if not db_driver: db_driver = FLAGS.db_driver self.db = utils.import_object(db_driver) # pylint: disable-msg=C0103 + + def init_host(self): + """Do any initialization that needs to be run if this is a standalone service. + + Child classes should override this method. + """ + + + + diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 41aeb5da7..604d11c93 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -36,13 +36,28 @@ flags.DEFINE_string('dhcpbridge_flagfile', flags.DEFINE_string('networks_path', utils.abspath('../networks'), 'Location to keep network config files') flags.DEFINE_string('public_interface', 'vlan1', - 'Interface for public IP addresses') + 'Interface for public IP addresses') flags.DEFINE_string('bridge_dev', 'eth0', - 'network device for bridges') - + 'network device for bridges') +flags.DEFINE_string('routing_source_ip', utils.get_my_ip(), + 'Public IP of network host') DEFAULT_PORTS = [("tcp", 80), ("tcp", 22), ("udp", 1194), ("tcp", 443)] +def init_host(): + """Basic networking setup goes here""" + # NOTE(devcamcar): Cloud public DNAT entries, CloudPipe port + # forwarding entries and a default DNAT entry. + _confirm_rule("-t nat -A nova_prerouting -s 0.0.0.0/0 " + "-d 169.254.169.254/32 -p tcp -m tcp --dport 80 -j DNAT " + "--to-destination %s:%s" % (FLAGS.cc_ip, FLAGS.cc_port)) + + # NOTE(devcamcar): Cloud public SNAT entries and the default + # SNAT rule for outbound traffic. + _confirm_rule("-t nat -A nova_postrouting -s %s " + "-j SNAT --to-source %s" + % (FLAGS.private_range, FLAGS.routing_source_ip)) + def bind_floating_ip(floating_ip): """Bind ip to public interface""" @@ -58,37 +73,37 @@ def unbind_floating_ip(floating_ip): def ensure_vlan_forward(public_ip, port, private_ip): """Sets up forwarding rules for vlan""" - _confirm_rule("FORWARD -d %s -p udp --dport 1194 -j ACCEPT" % private_ip) + _confirm_rule("nova_forward -d %s -p udp --dport 1194 -j ACCEPT" % private_ip) _confirm_rule( - "PREROUTING -t nat -d %s -p udp --dport %s -j DNAT --to %s:1194" + "nova_prerouting -t nat -d %s -p udp --dport %s -j DNAT --to %s:1194" % (public_ip, port, private_ip)) def ensure_floating_forward(floating_ip, fixed_ip): """Ensure floating ip forwarding rule""" - _confirm_rule("PREROUTING -t nat -d %s -j DNAT --to %s" + _confirm_rule("nova_prerouting -t nat -d %s -j DNAT --to %s" % (floating_ip, fixed_ip)) - _confirm_rule("POSTROUTING -t nat -s %s -j SNAT --to %s" + _confirm_rule("nova_postrouting -t nat -s %s -j SNAT --to %s" % (fixed_ip, floating_ip)) # TODO(joshua): Get these from the secgroup datastore entries - _confirm_rule("FORWARD -d %s -p icmp -j ACCEPT" + _confirm_rule("nova_forward -d %s -p icmp -j ACCEPT" % (fixed_ip)) for (protocol, port) in DEFAULT_PORTS: _confirm_rule( - "FORWARD -d %s -p %s --dport %s -j ACCEPT" + "nova_forward -d %s -p %s --dport %s -j ACCEPT" % (fixed_ip, protocol, port)) def remove_floating_forward(floating_ip, fixed_ip): """Remove forwarding for floating ip""" - _remove_rule("PREROUTING -t nat -d %s -j DNAT --to %s" + _remove_rule("nova_prerouting -t nat -d %s -j DNAT --to %s" % (floating_ip, fixed_ip)) - _remove_rule("POSTROUTING -t nat -s %s -j SNAT --to %s" + _remove_rule("nova_postrouting -t nat -s %s -j SNAT --to %s" % (fixed_ip, floating_ip)) - _remove_rule("FORWARD -d %s -p icmp -j ACCEPT" + _remove_rule("nova_forward -d %s -p icmp -j ACCEPT" % (fixed_ip)) for (protocol, port) in DEFAULT_PORTS: - _remove_rule("FORWARD -d %s -p %s --dport %s -j ACCEPT" + _remove_rule("nova_forward -d %s -p %s --dport %s -j ACCEPT" % (fixed_ip, protocol, port)) @@ -124,7 +139,7 @@ def ensure_bridge(bridge, interface, net_attrs=None): net_attrs['gateway'], net_attrs['broadcast'], net_attrs['netmask'])) - _confirm_rule("FORWARD --in-interface %s -j ACCEPT" % bridge) + _confirm_rule("nova_forward --in-interface %s -j ACCEPT" % bridge) else: _execute("sudo ifconfig %s up" % bridge) @@ -256,3 +271,4 @@ def _dnsmasq_pid_for(vlan): if os.path.exists(pid_file): with open(pid_file, 'r') as f: return int(f.read()) + diff --git a/nova/network/manager.py b/nova/network/manager.py index 7a3bcfc2f..87c3d8e46 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -218,6 +218,12 @@ class FlatManager(NetworkManager): class VlanManager(NetworkManager): """Vlan network with dhcp""" + + def init_host(self): + """Do any initialization that needs to be run if this is a standalone service. + """ + driver.init_host() + 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) @@ -363,3 +369,4 @@ class VlanManager(NetworkManager): parent_reserved = super(VlanManager, self)._top_reserved_ips return parent_reserved + FLAGS.cnt_vpn_clients + diff --git a/nova/service.py b/nova/service.py index 870dd6ceb..8f1db1b8e 100644 --- a/nova/service.py +++ b/nova/service.py @@ -158,3 +158,4 @@ class Service(object, service.Service): self.model_disconnected = True logging.exception("model server went away") yield + diff --git a/tools/setup_ipchains.sh b/tools/setup_ipchains.sh new file mode 100644 index 000000000..b1ab1c6f7 --- /dev/null +++ b/tools/setup_ipchains.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash + +CMD="global" +IP="XXX" +PRIVATE_RANGE="10.128.0.0/12" + +if [ -n "$1" ]; then + CMD=$1 +fi + +if [ -n "$2" ]; then + IP=$2 +fi + +if [ -n "$3" ]; then + PRIVATE_RANGE=$3 +fi + +if [ "$CMD" == "global" ]; then + iptables -P INPUT DROP + iptables -A INPUT -m state --state INVALID -j DROP + iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -A INPUT -m tcp -p tcp -d $MGMT_IP --dport 22 -j ACCEPT + iptables -A INPUT -m udp -p udp --dport 123 -j ACCEPT + iptables -N nova_input + iptables -A INPUT -j nova_input + iptables -A INPUT -p icmp -j ACCEPT + iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset + iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable + + iptables -P FORWARD DROP + iptables -A FORWARD -m state --state INVALID -j DROP + iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu + iptables -N nova_forward + iptables -A FORWARD -j nova_forward + + iptables -P OUTPUT DROP + iptables -A OUTPUT -m state --state INVALID -j DROP + iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -N nova_output + iptables -A OUTPUT -j nova_output + + iptables -t nat -N nova_prerouting + iptables -t nat -A PREROUTING -j nova_prerouting + + iptables -t nat -N nova_postrouting + iptables -t nat -A POSTROUTING -j nova_postrouting + + iptables -t nat -N nova_output + iptables -t nat -A OUTPUT -j nova_output + + # ganglia (all hosts) + iptables -A nova_input -m tcp -p tcp -d $IP --dport 8649 -j ACCEPT + iptables -A nova_input -m udp -p udp -d $IP --dport 8649 -j ACCEPT +fi + +if [ "$CMD" == "dashboard" ]; then + # dashboard + iptables -A nova_input -m tcp -p tcp -d $IP --dport 80 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $IP --dport 443 -j ACCEPT +fi + +if [ "$CMD" == "objectstore" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 3333 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $IP --dport 8773 -j ACCEPT +fi + +if [ "$CMD" == "redis" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 6379 -j ACCEPT +fi + +if [ "$CMD" == "mysql" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 3306 -j ACCEPT +fi + +if [ "$CMD" == "rabbitmq" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 4369 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $IP --dport 5672 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $IP --dport 53284 -j ACCEPT +fi + +if [ "$CMD" == "dnsmasq" ]; then + # NOTE(vish): this could theoretically be setup per network + # for each host, but it seems like overkill + iptables -A nova_input -m tcp -p tcp -s $PRIVATE_RANGE --dport 53 -j ACCEPT + iptables -A nova_input -m udp -p udp -s $PRIVATE_RANGE --dport 53 -j ACCEPT + iptables -A nova_input -m udp -p udp --dport 67 -j ACCEPT + +if [ "$CMD" == "ldap" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 389 -j ACCEPT +fi + + -- cgit From bc2641148359352ed83d4190baaf1e208e00a6b9 Mon Sep 17 00:00:00 2001 From: Devin Carlen Date: Wed, 15 Sep 2010 17:49:15 -0700 Subject: Added iptables host initial configuration --- nova/network/manager.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index 87c3d8e46..25a9ba474 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -219,10 +219,11 @@ class FlatManager(NetworkManager): class VlanManager(NetworkManager): """Vlan network with dhcp""" - def init_host(self): - """Do any initialization that needs to be run if this is a standalone service. - """ - driver.init_host() + def init_host(self): + """Do any initialization that needs to be run if this is a + standalone service. + """ + driver.init_host() def allocate_fixed_ip(self, context, instance_id, *args, **kwargs): """Gets a fixed ip from the pool""" -- cgit From e1ddec70bc7522a75b4a50953a0f4b20ace6cce1 Mon Sep 17 00:00:00 2001 From: Devin Carlen Date: Thu, 16 Sep 2010 11:40:04 -0700 Subject: Added missing masquerade rules --- nova/network/linux_net.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 604d11c93..75acf2afc 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -58,6 +58,8 @@ def init_host(): "-j SNAT --to-source %s" % (FLAGS.private_range, FLAGS.routing_source_ip)) + _confirm_rule("-A nova_postrouting -s %s MASQUERADE" % FLAGS.private_range) + _confirm_rule("-A nova_postrouting -s %(range)s -d %(range)s" % {'range': FLAGS.private_range}) def bind_floating_ip(floating_ip): """Bind ip to public interface""" -- cgit From d0708205759880e7fb78fbb1df33df939f669413 Mon Sep 17 00:00:00 2001 From: Devin Carlen Date: Thu, 16 Sep 2010 11:44:51 -0700 Subject: Whitespace fixes --- bin/nova-manage | 1 - nova/manager.py | 3 --- 2 files changed, 4 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 909435ede..325245ac4 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -384,4 +384,3 @@ def main(): if __name__ == '__main__': main() - diff --git a/nova/manager.py b/nova/manager.py index 495b1f0d1..b7b97bced 100644 --- a/nova/manager.py +++ b/nova/manager.py @@ -44,6 +44,3 @@ class Manager(object): Child classes should override this method. """ - - - -- cgit From 68633fadeb92a5a26d1ab613bed6094ddfa2a014 Mon Sep 17 00:00:00 2001 From: Devin Carlen Date: Mon, 20 Sep 2010 15:35:44 -0700 Subject: Whitespace fixes --- nova/endpoint/api.py | 1 - nova/flags.py | 1 - nova/network/linux_net.py | 1 - nova/network/manager.py | 1 - nova/service.py | 1 - 5 files changed, 5 deletions(-) diff --git a/nova/endpoint/api.py b/nova/endpoint/api.py index 6de3698e1..56481b2c0 100755 --- a/nova/endpoint/api.py +++ b/nova/endpoint/api.py @@ -340,4 +340,3 @@ class APIServerApplication(tornado.web.Application): (r'/1.0/([-A-Za-z0-9/]*)', MetadataRequestHandler), ], pool=multiprocessing.Pool(4)) self.controllers = controllers - diff --git a/nova/flags.py b/nova/flags.py index 55b452fc3..ce30d5033 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -222,4 +222,3 @@ DEFINE_string('host', socket.gethostname(), # UNUSED DEFINE_string('node_availability_zone', 'nova', 'availability zone of this node') - diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 75acf2afc..65dcf51ee 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -273,4 +273,3 @@ def _dnsmasq_pid_for(vlan): if os.path.exists(pid_file): with open(pid_file, 'r') as f: return int(f.read()) - diff --git a/nova/network/manager.py b/nova/network/manager.py index 25a9ba474..c7bcfa175 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -370,4 +370,3 @@ class VlanManager(NetworkManager): parent_reserved = super(VlanManager, self)._top_reserved_ips return parent_reserved + FLAGS.cnt_vpn_clients - diff --git a/nova/service.py b/nova/service.py index 8f1db1b8e..870dd6ceb 100644 --- a/nova/service.py +++ b/nova/service.py @@ -158,4 +158,3 @@ class Service(object, service.Service): self.model_disconnected = True logging.exception("model server went away") yield - -- cgit From e74b8070f73d8bada01cfe2d26223e5180ab67fb Mon Sep 17 00:00:00 2001 From: Devin Carlen Date: Tue, 21 Sep 2010 00:03:53 -0700 Subject: Renamed cc_ip flag to cc_host --- nova/flags.py | 4 ++-- nova/network/linux_net.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/nova/flags.py b/nova/flags.py index ce30d5033..9d27d336d 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -184,9 +184,9 @@ DEFINE_string('rabbit_userid', 'guest', 'rabbit userid') DEFINE_string('rabbit_password', 'guest', 'rabbit password') DEFINE_string('rabbit_virtual_host', '/', 'rabbit virtual host') DEFINE_string('control_exchange', 'nova', 'the main exchange to connect to') -DEFINE_string('cc_ip', '127.0.0.1', 'ip of api server') +DEFINE_string('cc_host', '127.0.0.1', 'ip of api server') DEFINE_integer('cc_port', 8773, 'cloud controller port') -DEFINE_string('ec2_url', 'http://%s:%s/services/Cloud' % (FLAGS.cc_ip, FLAGS.cc_port), +DEFINE_string('ec2_url', 'http://%s:%s/services/Cloud' % (FLAGS.cc_host, FLAGS.cc_port), 'Url to ec2 api server') DEFINE_string('default_image', 'ami-11111', diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 65dcf51ee..53fb2df94 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -50,7 +50,7 @@ def init_host(): # forwarding entries and a default DNAT entry. _confirm_rule("-t nat -A nova_prerouting -s 0.0.0.0/0 " "-d 169.254.169.254/32 -p tcp -m tcp --dport 80 -j DNAT " - "--to-destination %s:%s" % (FLAGS.cc_ip, FLAGS.cc_port)) + "--to-destination %s:%s" % (FLAGS.cc_host, FLAGS.cc_port)) # NOTE(devcamcar): Cloud public SNAT entries and the default # SNAT rule for outbound traffic. -- cgit From b68ab98d6718d5a7237f5620e8caffc770dfe822 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 21 Sep 2010 19:24:19 -0400 Subject: User updatable name & description for images. --- nova/endpoint/cloud.py | 12 ++++++++++++ nova/endpoint/images.py | 6 ++++++ nova/objectstore/handler.py | 19 +++++++++++++++---- nova/objectstore/image.py | 12 ++++++++++++ nova/tests/cloud_unittest.py | 26 +++++++++++++++++++++++++- nova/tests/objectstore_unittest.py | 6 ++++++ 6 files changed, 76 insertions(+), 5 deletions(-) diff --git a/nova/endpoint/cloud.py b/nova/endpoint/cloud.py index e29d09a89..b1678ecce 100644 --- a/nova/endpoint/cloud.py +++ b/nova/endpoint/cloud.py @@ -752,3 +752,15 @@ class CloudController(object): raise exception.ApiError('operation_type must be add or remove') result = images.modify(context, image_id, operation_type) return defer.succeed(result) + + @rbac.allow('projectmanager', 'sysadmin') + def set_image_name(self, context, image_id, name): + result = images.update_user_editable_field(context, image_id, + 'displayName', name) + return defer.succeed(result) + + @rbac.allow('projectmanager', 'sysadmin') + def set_image_description(self, context, image_id, name): + result = images.update_user_editable_field(context, image_id, + 'displayDescription', name) + return defer.succeed(result) diff --git a/nova/endpoint/images.py b/nova/endpoint/images.py index 4579cd81a..a63c059bb 100644 --- a/nova/endpoint/images.py +++ b/nova/endpoint/images.py @@ -43,6 +43,12 @@ def modify(context, image_id, operation): return True +def update_user_editable_field(context, image_id, field, value): + conn(context).make_request( + method='POST', + bucket='_images', + query_args=qs({'image_id': image_id, 'field': field, 'value': value})) + return True def register(context, image_location): """ rpc call to register a new image based from a manifest """ diff --git a/nova/objectstore/handler.py b/nova/objectstore/handler.py index 5c3dc286b..f4596e982 100644 --- a/nova/objectstore/handler.py +++ b/nova/objectstore/handler.py @@ -382,15 +382,26 @@ class ImagesResource(resource.Resource): def render_POST(self, request): # pylint: disable-msg=R0201 """Update image attributes: public/private""" + # image_id required for all requests image_id = get_argument(request, 'image_id', u'') - operation = get_argument(request, 'operation', u'') - image_object = image.Image(image_id) - if not image_object.is_authorized(request.context): + logging.debug("not authorized for handle_POST in images") raise exception.NotAuthorized - image_object.set_public(operation=='add') + operation = get_argument(request, 'operation', u'') + field = get_argument(request, 'field', u'') + value = get_argument(request, 'value', u'') + if operation: + # operation implies publicity toggle + logging.debug("handling publicity toggle") + image_object.set_public(operation=='add') + elif field: + # field implies user field editing (value can be blank) + logging.debug("update user field") + image_object.update_user_editable_field(field, value) + else: + logging.debug("unknown action for handle_POST in images") return '' diff --git a/nova/objectstore/image.py b/nova/objectstore/image.py index f3c02a425..ad1745af6 100644 --- a/nova/objectstore/image.py +++ b/nova/objectstore/image.py @@ -82,6 +82,18 @@ class Image(object): with open(os.path.join(self.path, 'info.json'), 'w') as f: json.dump(md, f) + def update_user_editable_field(self, field, value): + fields = ['displayName', 'displayDescription'] + if field not in fields: + raise KeyError("Invalid field: %s" % field) + info = self.metadata + if value: + info[field] = value + elif field in info: + del info[field] + with open(os.path.join(self.path, 'info.json'), 'w') as f: + json.dump(info, f) + @staticmethod def all(): images = [] diff --git a/nova/tests/cloud_unittest.py b/nova/tests/cloud_unittest.py index c36d5a34f..2e97180be 100644 --- a/nova/tests/cloud_unittest.py +++ b/nova/tests/cloud_unittest.py @@ -16,9 +16,13 @@ # License for the specific language governing permissions and limitations # under the License. +import json import logging +import os import StringIO +import tempfile import time + from tornado import ioloop from twisted.internet import defer import unittest @@ -32,15 +36,22 @@ from nova.auth import manager from nova.compute import power_state from nova.endpoint import api from nova.endpoint import cloud +from nova.objectstore import image FLAGS = flags.FLAGS +# Temp dirs for working with image attributes through the cloud controller +# (stole this from objectstore_unittest.py) +OSS_TEMPDIR = tempfile.mkdtemp(prefix='test_oss-') +IMAGES_PATH = os.path.join(OSS_TEMPDIR, 'images') +os.makedirs(IMAGES_PATH) + class CloudTestCase(test.BaseTestCase): def setUp(self): super(CloudTestCase, self).setUp() - self.flags(connection_type='fake') + self.flags(connection_type='fake', images_path=IMAGES_PATH) self.conn = rpc.Connection.instance() logging.getLogger().setLevel(logging.DEBUG) @@ -156,3 +167,16 @@ class CloudTestCase(test.BaseTestCase): #for i in xrange(4): # data = self.cloud.get_metadata(instance(i)['private_dns_name']) # self.assert_(data['meta-data']['ami-id'] == 'ami-%s' % i) + + def test_user_editable_endpoint(self): + pathdir = os.path.join(FLAGS.images_path, 'i-testing') + os.mkdir(pathdir) + info = {} + with open(os.path.join(pathdir, 'info.json'), 'w') as f: + json.dump(info, f) + yield self.cloud.set_image_description(self.context, 'i-testing', + 'Foo Img') + img = image.Image('i-testing') + self.assertEqual('Foo Img', img.metadata['displayDescription']) + self.cloud.set_image_description(self.context, 'i-testing', '') + self.assert_(not 'displayDescription' in img.metadata) diff --git a/nova/tests/objectstore_unittest.py b/nova/tests/objectstore_unittest.py index dece4b5d5..82c6e2c49 100644 --- a/nova/tests/objectstore_unittest.py +++ b/nova/tests/objectstore_unittest.py @@ -164,6 +164,12 @@ class ObjectStoreTestCase(test.BaseTestCase): self.context.project = self.auth_manager.get_project('proj2') self.assertFalse(my_img.is_authorized(self.context)) + # change user-editable fields + my_img.update_user_editable_field('displayName', 'my cool image') + self.assertEqual('my cool image', my_img.metadata['displayName']) + my_img.update_user_editable_field('displayName', '') + self.assert_(not 'displayName' in my_img.metadata) + class TestHTTPChannel(http.HTTPChannel): """Dummy site required for twisted.web""" -- cgit From 71c41338e4aac98ec03eec1c90ee99e43d51bcb7 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Wed, 22 Sep 2010 16:42:35 -0400 Subject: Add user display fields to instances & volumes. --- nova/db/sqlalchemy/models.py | 9 ++++++++- nova/endpoint/cloud.py | 32 ++++++++++++++++++++++++++++++++ nova/tests/cloud_unittest.py | 43 ++++++++++++++++++++++++++++++++++++++----- 3 files changed, 78 insertions(+), 6 deletions(-) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 41013f41b..359087db0 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -233,7 +233,6 @@ class Instance(BASE, NovaBase): vcpus = Column(Integer) local_gb = Column(Integer) - hostname = Column(String(255)) host = Column(String(255)) # , ForeignKey('hosts.id')) @@ -247,6 +246,10 @@ class Instance(BASE, NovaBase): scheduled_at = Column(DateTime) launched_at = Column(DateTime) terminated_at = Column(DateTime) + + display_name = Column(String(255)) + display_description = Column(String(255)) + # TODO(vish): see Ewan's email about state improvements, probably # should be in a driver base class or some such # vmstate_state = running, halted, suspended, paused @@ -282,6 +285,10 @@ class Volume(BASE, NovaBase): launched_at = Column(DateTime) terminated_at = Column(DateTime) + display_name = Column(String(255)) + display_description = Column(String(255)) + + class Quota(BASE, NovaBase): """Represents quota overrides for a project""" __tablename__ = 'quotas' diff --git a/nova/endpoint/cloud.py b/nova/endpoint/cloud.py index b1678ecce..50fdee60b 100644 --- a/nova/endpoint/cloud.py +++ b/nova/endpoint/cloud.py @@ -280,6 +280,10 @@ class CloudController(object): 'volume_id': volume['str_id']}] else: v['attachmentSet'] = [{}] + if 'display_name' in volume: + v['display_name'] = volume['display_name'] + if 'display_description' in volume: + v['display_description'] = volume['display_description'] return v @rbac.allow('projectmanager', 'sysadmin') @@ -299,6 +303,8 @@ class CloudController(object): vol['availability_zone'] = FLAGS.storage_availability_zone vol['status'] = "creating" vol['attach_status'] = "detached" + vol['display_name'] = kwargs.get('display_name') + vol['display_description'] = kwargs.get('display_description') volume_ref = db.volume_create(context, vol) rpc.cast(FLAGS.scheduler_topic, @@ -367,6 +373,17 @@ class CloudController(object): lst = [lst] return [{label: x} for x in lst] + @rbac.allow('projectmanager', 'sysadmin') + def update_volume(self, context, volume_id, **kwargs): + updatable_fields = ['display_name', 'display_description'] + changes = {} + for field in updatable_fields: + if field in kwargs: + changes[field] = kwargs[field] + if changes: + db.volume_update(context, volume_id, kwargs) + return defer.succeed(True) + @rbac.allow('all') def describe_instances(self, context, **kwargs): return defer.succeed(self._format_describe_instances(context)) @@ -420,6 +437,8 @@ class CloudController(object): i['instanceType'] = instance['instance_type'] i['launchTime'] = instance['created_at'] i['amiLaunchIndex'] = instance['launch_index'] + i['displayName'] = instance['display_name'] + i['displayDescription'] = instance['display_description'] if not reservations.has_key(instance['reservation_id']): r = {} r['reservationId'] = instance['reservation_id'] @@ -589,6 +608,8 @@ class CloudController(object): base_options['user_data'] = kwargs.get('user_data', '') base_options['security_group'] = security_group base_options['instance_type'] = instance_type + base_options['display_name'] = kwargs.get('display_name') + base_options['display_description'] = kwargs.get('display_description') type_data = INSTANCE_TYPES[instance_type] base_options['memory_mb'] = type_data['memory_mb'] @@ -689,6 +710,17 @@ class CloudController(object): "instance_id": instance_ref['id']}}) return defer.succeed(True) + @rbac.allow('projectmanager', 'sysadmin') + def update_instance(self, context, instance_id, **kwargs): + updatable_fields = ['display_name', 'display_description'] + changes = {} + for field in updatable_fields: + if field in kwargs: + changes[field] = kwargs[field] + if changes: + db.instance_update(context, instance_id, kwargs) + return defer.succeed(True) + @rbac.allow('projectmanager', 'sysadmin') def delete_volume(self, context, volume_id, **kwargs): # TODO: return error if not authorized diff --git a/nova/tests/cloud_unittest.py b/nova/tests/cloud_unittest.py index 2e97180be..18ad19ede 100644 --- a/nova/tests/cloud_unittest.py +++ b/nova/tests/cloud_unittest.py @@ -28,6 +28,7 @@ from twisted.internet import defer import unittest from xml.etree import ElementTree +from nova import db from nova import flags from nova import rpc from nova import test @@ -168,15 +169,47 @@ class CloudTestCase(test.BaseTestCase): # data = self.cloud.get_metadata(instance(i)['private_dns_name']) # self.assert_(data['meta-data']['ami-id'] == 'ami-%s' % i) - def test_user_editable_endpoint(self): - pathdir = os.path.join(FLAGS.images_path, 'i-testing') + def test_user_editable_image_endpoint(self): + pathdir = os.path.join(FLAGS.images_path, 'ami-testing') os.mkdir(pathdir) info = {} with open(os.path.join(pathdir, 'info.json'), 'w') as f: json.dump(info, f) - yield self.cloud.set_image_description(self.context, 'i-testing', + yield self.cloud.set_image_description(self.context, 'ami-testing', 'Foo Img') - img = image.Image('i-testing') + img = image.Image('ami-testing') self.assertEqual('Foo Img', img.metadata['displayDescription']) - self.cloud.set_image_description(self.context, 'i-testing', '') + self.cloud.set_image_description(self.context, 'ami-testing', '') self.assert_(not 'displayDescription' in img.metadata) + + def test_update_of_instance_display_fields(self): + inst = db.instance_create({}, {}) + self.cloud.update_instance(self.context, inst['id'], + display_name='c00l 1m4g3') + inst = db.instance_get({}, inst['id']) + self.assertEqual('c00l 1m4g3', inst['display_name']) + db.instance_destroy({}, inst['id']) + + def test_update_of_instance_wont_update_private_fields(self): + inst = db.instance_create({}, {}) + self.cloud.update_instance(self.context, inst['id'], + mac_address='DE:AD:BE:EF') + inst = db.instance_get({}, inst['id']) + self.assertEqual(None, inst['mac_address']) + db.instance_destroy({}, inst['id']) + + def test_update_of_volume_display_fields(self): + vol = db.volume_create({}, {}) + self.cloud.update_volume(self.context, vol['id'], + display_name='c00l v0lum3') + vol = db.volume_get({}, vol['id']) + self.assertEqual('c00l v0lum3', vol['display_name']) + db.volume_destroy({}, vol['id']) + + def test_update_of_volume_wont_update_private_fields(self): + vol = db.volume_create({}, {}) + self.cloud.update_volume(self.context, vol['id'], + mountpoint='/not/here') + vol = db.volume_get({}, vol['id']) + self.assertEqual(None, vol['mountpoint']) + db.volume_destroy({}, vol['id']) -- cgit From 4f2edd43ca2c4a175b4d9dce23ae9e28941122e2 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 23 Sep 2010 11:23:32 -0700 Subject: renamed ipchains to iptables --- setup_iptables.sh | 94 +++++++++++++++++++++++++++++++++++++++++++++++++ tools/setup_ipchains.sh | 94 ------------------------------------------------- 2 files changed, 94 insertions(+), 94 deletions(-) create mode 100644 setup_iptables.sh delete mode 100644 tools/setup_ipchains.sh diff --git a/setup_iptables.sh b/setup_iptables.sh new file mode 100644 index 000000000..b1ab1c6f7 --- /dev/null +++ b/setup_iptables.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash + +CMD="global" +IP="XXX" +PRIVATE_RANGE="10.128.0.0/12" + +if [ -n "$1" ]; then + CMD=$1 +fi + +if [ -n "$2" ]; then + IP=$2 +fi + +if [ -n "$3" ]; then + PRIVATE_RANGE=$3 +fi + +if [ "$CMD" == "global" ]; then + iptables -P INPUT DROP + iptables -A INPUT -m state --state INVALID -j DROP + iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -A INPUT -m tcp -p tcp -d $MGMT_IP --dport 22 -j ACCEPT + iptables -A INPUT -m udp -p udp --dport 123 -j ACCEPT + iptables -N nova_input + iptables -A INPUT -j nova_input + iptables -A INPUT -p icmp -j ACCEPT + iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset + iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable + + iptables -P FORWARD DROP + iptables -A FORWARD -m state --state INVALID -j DROP + iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu + iptables -N nova_forward + iptables -A FORWARD -j nova_forward + + iptables -P OUTPUT DROP + iptables -A OUTPUT -m state --state INVALID -j DROP + iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -N nova_output + iptables -A OUTPUT -j nova_output + + iptables -t nat -N nova_prerouting + iptables -t nat -A PREROUTING -j nova_prerouting + + iptables -t nat -N nova_postrouting + iptables -t nat -A POSTROUTING -j nova_postrouting + + iptables -t nat -N nova_output + iptables -t nat -A OUTPUT -j nova_output + + # ganglia (all hosts) + iptables -A nova_input -m tcp -p tcp -d $IP --dport 8649 -j ACCEPT + iptables -A nova_input -m udp -p udp -d $IP --dport 8649 -j ACCEPT +fi + +if [ "$CMD" == "dashboard" ]; then + # dashboard + iptables -A nova_input -m tcp -p tcp -d $IP --dport 80 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $IP --dport 443 -j ACCEPT +fi + +if [ "$CMD" == "objectstore" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 3333 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $IP --dport 8773 -j ACCEPT +fi + +if [ "$CMD" == "redis" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 6379 -j ACCEPT +fi + +if [ "$CMD" == "mysql" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 3306 -j ACCEPT +fi + +if [ "$CMD" == "rabbitmq" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 4369 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $IP --dport 5672 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $IP --dport 53284 -j ACCEPT +fi + +if [ "$CMD" == "dnsmasq" ]; then + # NOTE(vish): this could theoretically be setup per network + # for each host, but it seems like overkill + iptables -A nova_input -m tcp -p tcp -s $PRIVATE_RANGE --dport 53 -j ACCEPT + iptables -A nova_input -m udp -p udp -s $PRIVATE_RANGE --dport 53 -j ACCEPT + iptables -A nova_input -m udp -p udp --dport 67 -j ACCEPT + +if [ "$CMD" == "ldap" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 389 -j ACCEPT +fi + + diff --git a/tools/setup_ipchains.sh b/tools/setup_ipchains.sh deleted file mode 100644 index b1ab1c6f7..000000000 --- a/tools/setup_ipchains.sh +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env bash - -CMD="global" -IP="XXX" -PRIVATE_RANGE="10.128.0.0/12" - -if [ -n "$1" ]; then - CMD=$1 -fi - -if [ -n "$2" ]; then - IP=$2 -fi - -if [ -n "$3" ]; then - PRIVATE_RANGE=$3 -fi - -if [ "$CMD" == "global" ]; then - iptables -P INPUT DROP - iptables -A INPUT -m state --state INVALID -j DROP - iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT - iptables -A INPUT -m tcp -p tcp -d $MGMT_IP --dport 22 -j ACCEPT - iptables -A INPUT -m udp -p udp --dport 123 -j ACCEPT - iptables -N nova_input - iptables -A INPUT -j nova_input - iptables -A INPUT -p icmp -j ACCEPT - iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset - iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable - - iptables -P FORWARD DROP - iptables -A FORWARD -m state --state INVALID -j DROP - iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT - iptables -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu - iptables -N nova_forward - iptables -A FORWARD -j nova_forward - - iptables -P OUTPUT DROP - iptables -A OUTPUT -m state --state INVALID -j DROP - iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT - iptables -N nova_output - iptables -A OUTPUT -j nova_output - - iptables -t nat -N nova_prerouting - iptables -t nat -A PREROUTING -j nova_prerouting - - iptables -t nat -N nova_postrouting - iptables -t nat -A POSTROUTING -j nova_postrouting - - iptables -t nat -N nova_output - iptables -t nat -A OUTPUT -j nova_output - - # ganglia (all hosts) - iptables -A nova_input -m tcp -p tcp -d $IP --dport 8649 -j ACCEPT - iptables -A nova_input -m udp -p udp -d $IP --dport 8649 -j ACCEPT -fi - -if [ "$CMD" == "dashboard" ]; then - # dashboard - iptables -A nova_input -m tcp -p tcp -d $IP --dport 80 -j ACCEPT - iptables -A nova_input -m tcp -p tcp -d $IP --dport 443 -j ACCEPT -fi - -if [ "$CMD" == "objectstore" ]; then - iptables -A nova_input -m tcp -p tcp -d $IP --dport 3333 -j ACCEPT - iptables -A nova_input -m tcp -p tcp -d $IP --dport 8773 -j ACCEPT -fi - -if [ "$CMD" == "redis" ]; then - iptables -A nova_input -m tcp -p tcp -d $IP --dport 6379 -j ACCEPT -fi - -if [ "$CMD" == "mysql" ]; then - iptables -A nova_input -m tcp -p tcp -d $IP --dport 3306 -j ACCEPT -fi - -if [ "$CMD" == "rabbitmq" ]; then - iptables -A nova_input -m tcp -p tcp -d $IP --dport 4369 -j ACCEPT - iptables -A nova_input -m tcp -p tcp -d $IP --dport 5672 -j ACCEPT - iptables -A nova_input -m tcp -p tcp -d $IP --dport 53284 -j ACCEPT -fi - -if [ "$CMD" == "dnsmasq" ]; then - # NOTE(vish): this could theoretically be setup per network - # for each host, but it seems like overkill - iptables -A nova_input -m tcp -p tcp -s $PRIVATE_RANGE --dport 53 -j ACCEPT - iptables -A nova_input -m udp -p udp -s $PRIVATE_RANGE --dport 53 -j ACCEPT - iptables -A nova_input -m udp -p udp --dport 67 -j ACCEPT - -if [ "$CMD" == "ldap" ]; then - iptables -A nova_input -m tcp -p tcp -d $IP --dport 389 -j ACCEPT -fi - - -- cgit From 44a3fe22d72f7359f57e7eb9ce443c974391991c Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 23 Sep 2010 11:40:09 -0700 Subject: fixed a couple of typos --- nova/manager.py | 3 ++- nova/network/manager.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/nova/manager.py b/nova/manager.py index b7b97bced..65300354b 100644 --- a/nova/manager.py +++ b/nova/manager.py @@ -40,7 +40,8 @@ class Manager(object): def init_host(self): """Do any initialization that needs to be run if this is a standalone service. - + Child classes should override this method. """ + pass diff --git a/nova/network/manager.py b/nova/network/manager.py index dcca21127..abe4dcebc 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -218,12 +218,12 @@ class FlatManager(NetworkManager): class VlanManager(NetworkManager): """Vlan network with dhcp""" - + def init_host(self): """Do any initialization that needs to be run if this is a standalone service. """ - driver.init_host() + self.driver.init_host() def allocate_fixed_ip(self, context, instance_id, *args, **kwargs): """Gets a fixed ip from the pool""" -- cgit From 47a957acb176d108aac4183cbf5a882149d7462d Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 23 Sep 2010 11:58:33 -0700 Subject: put setup_iptables in the right dir --- setup_iptables.sh | 94 ------------------------------------------------- tools/setup_iptables.sh | 94 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 94 deletions(-) delete mode 100644 setup_iptables.sh create mode 100644 tools/setup_iptables.sh diff --git a/setup_iptables.sh b/setup_iptables.sh deleted file mode 100644 index b1ab1c6f7..000000000 --- a/setup_iptables.sh +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env bash - -CMD="global" -IP="XXX" -PRIVATE_RANGE="10.128.0.0/12" - -if [ -n "$1" ]; then - CMD=$1 -fi - -if [ -n "$2" ]; then - IP=$2 -fi - -if [ -n "$3" ]; then - PRIVATE_RANGE=$3 -fi - -if [ "$CMD" == "global" ]; then - iptables -P INPUT DROP - iptables -A INPUT -m state --state INVALID -j DROP - iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT - iptables -A INPUT -m tcp -p tcp -d $MGMT_IP --dport 22 -j ACCEPT - iptables -A INPUT -m udp -p udp --dport 123 -j ACCEPT - iptables -N nova_input - iptables -A INPUT -j nova_input - iptables -A INPUT -p icmp -j ACCEPT - iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset - iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable - - iptables -P FORWARD DROP - iptables -A FORWARD -m state --state INVALID -j DROP - iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT - iptables -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu - iptables -N nova_forward - iptables -A FORWARD -j nova_forward - - iptables -P OUTPUT DROP - iptables -A OUTPUT -m state --state INVALID -j DROP - iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT - iptables -N nova_output - iptables -A OUTPUT -j nova_output - - iptables -t nat -N nova_prerouting - iptables -t nat -A PREROUTING -j nova_prerouting - - iptables -t nat -N nova_postrouting - iptables -t nat -A POSTROUTING -j nova_postrouting - - iptables -t nat -N nova_output - iptables -t nat -A OUTPUT -j nova_output - - # ganglia (all hosts) - iptables -A nova_input -m tcp -p tcp -d $IP --dport 8649 -j ACCEPT - iptables -A nova_input -m udp -p udp -d $IP --dport 8649 -j ACCEPT -fi - -if [ "$CMD" == "dashboard" ]; then - # dashboard - iptables -A nova_input -m tcp -p tcp -d $IP --dport 80 -j ACCEPT - iptables -A nova_input -m tcp -p tcp -d $IP --dport 443 -j ACCEPT -fi - -if [ "$CMD" == "objectstore" ]; then - iptables -A nova_input -m tcp -p tcp -d $IP --dport 3333 -j ACCEPT - iptables -A nova_input -m tcp -p tcp -d $IP --dport 8773 -j ACCEPT -fi - -if [ "$CMD" == "redis" ]; then - iptables -A nova_input -m tcp -p tcp -d $IP --dport 6379 -j ACCEPT -fi - -if [ "$CMD" == "mysql" ]; then - iptables -A nova_input -m tcp -p tcp -d $IP --dport 3306 -j ACCEPT -fi - -if [ "$CMD" == "rabbitmq" ]; then - iptables -A nova_input -m tcp -p tcp -d $IP --dport 4369 -j ACCEPT - iptables -A nova_input -m tcp -p tcp -d $IP --dport 5672 -j ACCEPT - iptables -A nova_input -m tcp -p tcp -d $IP --dport 53284 -j ACCEPT -fi - -if [ "$CMD" == "dnsmasq" ]; then - # NOTE(vish): this could theoretically be setup per network - # for each host, but it seems like overkill - iptables -A nova_input -m tcp -p tcp -s $PRIVATE_RANGE --dport 53 -j ACCEPT - iptables -A nova_input -m udp -p udp -s $PRIVATE_RANGE --dport 53 -j ACCEPT - iptables -A nova_input -m udp -p udp --dport 67 -j ACCEPT - -if [ "$CMD" == "ldap" ]; then - iptables -A nova_input -m tcp -p tcp -d $IP --dport 389 -j ACCEPT -fi - - diff --git a/tools/setup_iptables.sh b/tools/setup_iptables.sh new file mode 100644 index 000000000..b1ab1c6f7 --- /dev/null +++ b/tools/setup_iptables.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash + +CMD="global" +IP="XXX" +PRIVATE_RANGE="10.128.0.0/12" + +if [ -n "$1" ]; then + CMD=$1 +fi + +if [ -n "$2" ]; then + IP=$2 +fi + +if [ -n "$3" ]; then + PRIVATE_RANGE=$3 +fi + +if [ "$CMD" == "global" ]; then + iptables -P INPUT DROP + iptables -A INPUT -m state --state INVALID -j DROP + iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -A INPUT -m tcp -p tcp -d $MGMT_IP --dport 22 -j ACCEPT + iptables -A INPUT -m udp -p udp --dport 123 -j ACCEPT + iptables -N nova_input + iptables -A INPUT -j nova_input + iptables -A INPUT -p icmp -j ACCEPT + iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset + iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable + + iptables -P FORWARD DROP + iptables -A FORWARD -m state --state INVALID -j DROP + iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu + iptables -N nova_forward + iptables -A FORWARD -j nova_forward + + iptables -P OUTPUT DROP + iptables -A OUTPUT -m state --state INVALID -j DROP + iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -N nova_output + iptables -A OUTPUT -j nova_output + + iptables -t nat -N nova_prerouting + iptables -t nat -A PREROUTING -j nova_prerouting + + iptables -t nat -N nova_postrouting + iptables -t nat -A POSTROUTING -j nova_postrouting + + iptables -t nat -N nova_output + iptables -t nat -A OUTPUT -j nova_output + + # ganglia (all hosts) + iptables -A nova_input -m tcp -p tcp -d $IP --dport 8649 -j ACCEPT + iptables -A nova_input -m udp -p udp -d $IP --dport 8649 -j ACCEPT +fi + +if [ "$CMD" == "dashboard" ]; then + # dashboard + iptables -A nova_input -m tcp -p tcp -d $IP --dport 80 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $IP --dport 443 -j ACCEPT +fi + +if [ "$CMD" == "objectstore" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 3333 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $IP --dport 8773 -j ACCEPT +fi + +if [ "$CMD" == "redis" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 6379 -j ACCEPT +fi + +if [ "$CMD" == "mysql" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 3306 -j ACCEPT +fi + +if [ "$CMD" == "rabbitmq" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 4369 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $IP --dport 5672 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $IP --dport 53284 -j ACCEPT +fi + +if [ "$CMD" == "dnsmasq" ]; then + # NOTE(vish): this could theoretically be setup per network + # for each host, but it seems like overkill + iptables -A nova_input -m tcp -p tcp -s $PRIVATE_RANGE --dport 53 -j ACCEPT + iptables -A nova_input -m udp -p udp -s $PRIVATE_RANGE --dport 53 -j ACCEPT + iptables -A nova_input -m udp -p udp --dport 67 -j ACCEPT + +if [ "$CMD" == "ldap" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 389 -j ACCEPT +fi + + -- cgit From 564105a3f0087f31a879460d70e73bc358e0e8c0 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 23 Sep 2010 12:18:56 -0700 Subject: made use of nova_ chains a flag and fixed a few typos --- nova/network/linux_net.py | 46 ++++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 53fb2df94..149848750 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -41,6 +41,8 @@ flags.DEFINE_string('bridge_dev', 'eth0', 'network device for bridges') flags.DEFINE_string('routing_source_ip', utils.get_my_ip(), 'Public IP of network host') +flags.DEFINE_string('use_nova_chains', False, + 'use the nova_ routing chains instead of default') DEFAULT_PORTS = [("tcp", 80), ("tcp", 22), ("udp", 1194), ("tcp", 443)] @@ -48,18 +50,18 @@ def init_host(): """Basic networking setup goes here""" # NOTE(devcamcar): Cloud public DNAT entries, CloudPipe port # forwarding entries and a default DNAT entry. - _confirm_rule("-t nat -A nova_prerouting -s 0.0.0.0/0 " + _confirm_rule("PREROUTING", "-t nat -s 0.0.0.0/0 " "-d 169.254.169.254/32 -p tcp -m tcp --dport 80 -j DNAT " "--to-destination %s:%s" % (FLAGS.cc_host, FLAGS.cc_port)) # NOTE(devcamcar): Cloud public SNAT entries and the default # SNAT rule for outbound traffic. - _confirm_rule("-t nat -A nova_postrouting -s %s " + _confirm_rule("POSTROUTING", "-t nat -s %s " "-j SNAT --to-source %s" % (FLAGS.private_range, FLAGS.routing_source_ip)) - _confirm_rule("-A nova_postrouting -s %s MASQUERADE" % FLAGS.private_range) - _confirm_rule("-A nova_postrouting -s %(range)s -d %(range)s" % {'range': FLAGS.private_range}) + _confirm_rule("POSTROUTING", "-t nat -s %s MASQUERADE" % FLAGS.private_range) + _confirm_rule("POSTROUTING", "-t nat -s %(range)s -d %(range)s" % {'range': FLAGS.private_range}) def bind_floating_ip(floating_ip): """Bind ip to public interface""" @@ -75,37 +77,37 @@ def unbind_floating_ip(floating_ip): def ensure_vlan_forward(public_ip, port, private_ip): """Sets up forwarding rules for vlan""" - _confirm_rule("nova_forward -d %s -p udp --dport 1194 -j ACCEPT" % private_ip) + _confirm_rule("FORWARD", "-d %s -p udp --dport 1194 -j ACCEPT" % private_ip) _confirm_rule( - "nova_prerouting -t nat -d %s -p udp --dport %s -j DNAT --to %s:1194" + "PREROUTING -t nat -d %s -p udp --dport %s -j DNAT --to %s:1194" % (public_ip, port, private_ip)) def ensure_floating_forward(floating_ip, fixed_ip): """Ensure floating ip forwarding rule""" - _confirm_rule("nova_prerouting -t nat -d %s -j DNAT --to %s" + _confirm_rule("PREROUTING", "-t nat -d %s -j DNAT --to %s" % (floating_ip, fixed_ip)) - _confirm_rule("nova_postrouting -t nat -s %s -j SNAT --to %s" + _confirm_rule("POSTROUTING", "-t nat -s %s -j SNAT --to %s" % (fixed_ip, floating_ip)) # TODO(joshua): Get these from the secgroup datastore entries - _confirm_rule("nova_forward -d %s -p icmp -j ACCEPT" + _confirm_rule("FORWARD", "-d %s -p icmp -j ACCEPT" % (fixed_ip)) for (protocol, port) in DEFAULT_PORTS: _confirm_rule( - "nova_forward -d %s -p %s --dport %s -j ACCEPT" + "FORWARD -d %s -p %s --dport %s -j ACCEPT" % (fixed_ip, protocol, port)) def remove_floating_forward(floating_ip, fixed_ip): """Remove forwarding for floating ip""" - _remove_rule("nova_prerouting -t nat -d %s -j DNAT --to %s" + _remove_rule("PREROUTING", "-t nat -d %s -j DNAT --to %s" % (floating_ip, fixed_ip)) - _remove_rule("nova_postrouting -t nat -s %s -j SNAT --to %s" + _remove_rule("POSTROUTING", "-t nat -s %s -j SNAT --to %s" % (fixed_ip, floating_ip)) - _remove_rule("nova_forward -d %s -p icmp -j ACCEPT" + _remove_rule("FORWARD", "-d %s -p icmp -j ACCEPT" % (fixed_ip)) for (protocol, port) in DEFAULT_PORTS: - _remove_rule("nova_forward -d %s -p %s --dport %s -j ACCEPT" + _remove_rule("FORWARD", "-d %s -p %s --dport %s -j ACCEPT" % (fixed_ip, protocol, port)) @@ -141,7 +143,7 @@ def ensure_bridge(bridge, interface, net_attrs=None): net_attrs['gateway'], net_attrs['broadcast'], net_attrs['netmask'])) - _confirm_rule("nova_forward --in-interface %s -j ACCEPT" % bridge) + _confirm_rule("FORWARD", "--in-interface %s -j ACCEPT" % bridge) else: _execute("sudo ifconfig %s up" % bridge) @@ -211,15 +213,19 @@ def _device_exists(device): return not err -def _confirm_rule(cmd): +def _confirm_rule(chain, cmd): """Delete and re-add iptables rule""" - _execute("sudo iptables --delete %s" % (cmd), check_exit_code=False) - _execute("sudo iptables -I %s" % (cmd)) + if FLAGS.use_nova_chains: + chain = "nova_%s" % chain.lower() + _execute("sudo iptables --delete %s %s" % (chain, cmd), check_exit_code=False) + _execute("sudo iptables -I %s %s" % (chain, cmd)) -def _remove_rule(cmd): +def _remove_rule(chain, cmd): """Remove iptables rule""" - _execute("sudo iptables --delete %s" % (cmd)) + if FLAGS.use_nova_chains: + chain = "%S" % chain.lower() + _execute("sudo iptables --delete %s %s" % (chain, cmd)) def _dnsmasq_cmd(net): -- cgit From 81fc2078ca3d3e07728a39b6cdec47af871f2f2f Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 23 Sep 2010 12:20:40 -0700 Subject: removed extra line in manage --- nova/manager.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nova/manager.py b/nova/manager.py index 65300354b..94e4ae959 100644 --- a/nova/manager.py +++ b/nova/manager.py @@ -44,4 +44,3 @@ class Manager(object): Child classes should override this method. """ pass - -- cgit From 065257fb0686d848fcf20235a4e04b76872a5b01 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 23 Sep 2010 12:43:41 -0700 Subject: fixed a few missing params from iptables rules --- nova/network/linux_net.py | 4 ++-- nova/service.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 149848750..38a616e83 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -60,8 +60,8 @@ def init_host(): "-j SNAT --to-source %s" % (FLAGS.private_range, FLAGS.routing_source_ip)) - _confirm_rule("POSTROUTING", "-t nat -s %s MASQUERADE" % FLAGS.private_range) - _confirm_rule("POSTROUTING", "-t nat -s %(range)s -d %(range)s" % {'range': FLAGS.private_range}) + _confirm_rule("POSTROUTING", "-t nat -s %s -j MASQUERADE" % FLAGS.private_range) + _confirm_rule("POSTROUTING", "-t nat -s %(range)s -d %(range)s -j ACCEPT" % {'range': FLAGS.private_range}) def bind_floating_ip(floating_ip): """Bind ip to public interface""" diff --git a/nova/service.py b/nova/service.py index 870dd6ceb..dcd2a09ef 100644 --- a/nova/service.py +++ b/nova/service.py @@ -50,6 +50,7 @@ class Service(object, service.Service): self.topic = topic manager_class = utils.import_class(manager) self.manager = manager_class(host=host, *args, **kwargs) + self.manager.init_host() self.model_disconnected = False super(Service, self).__init__(*args, **kwargs) try: -- cgit From 15c2678d3e3899e7ab6180dce457ae6d3e54937d Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 24 Sep 2010 18:21:58 -0700 Subject: improved the shell script for iptables --- tools/setup_iptables.sh | 124 ++++++++++++++++++++++++++++++------------------ 1 file changed, 78 insertions(+), 46 deletions(-) mode change 100644 => 100755 tools/setup_iptables.sh diff --git a/tools/setup_iptables.sh b/tools/setup_iptables.sh old mode 100644 new mode 100755 index b1ab1c6f7..fd32f6f82 --- a/tools/setup_iptables.sh +++ b/tools/setup_iptables.sh @@ -1,93 +1,125 @@ #!/usr/bin/env bash - -CMD="global" -IP="XXX" -PRIVATE_RANGE="10.128.0.0/12" +# 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. if [ -n "$1" ]; then CMD=$1 +else + CMD="all" fi if [ -n "$2" ]; then IP=$2 +else + # NOTE(vish): this will just get the first ip in the list, so if you + # have more than one eth device set up, this will fail + IP=`ifconfig | grep -m 1 'inet addr:'| cut -d: -f2 | awk '{print $1}'` fi if [ -n "$3" ]; then PRIVATE_RANGE=$3 +else + PRIVATE_RANGE="10.0.0.0/12" +fi + + +if [ -n "$4" ]; then + MGMT_IP=$4 +else + MGMT_IP="$IP" fi -if [ "$CMD" == "global" ]; then - iptables -P INPUT DROP - iptables -A INPUT -m state --state INVALID -j DROP - iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT - iptables -A INPUT -m tcp -p tcp -d $MGMT_IP --dport 22 -j ACCEPT - iptables -A INPUT -m udp -p udp --dport 123 -j ACCEPT - iptables -N nova_input - iptables -A INPUT -j nova_input - iptables -A INPUT -p icmp -j ACCEPT - iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset - iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable - - iptables -P FORWARD DROP - iptables -A FORWARD -m state --state INVALID -j DROP - iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT - iptables -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu - iptables -N nova_forward - iptables -A FORWARD -j nova_forward - - iptables -P OUTPUT DROP - iptables -A OUTPUT -m state --state INVALID -j DROP - iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT - iptables -N nova_output - iptables -A OUTPUT -j nova_output - - iptables -t nat -N nova_prerouting - iptables -t nat -A PREROUTING -j nova_prerouting - - iptables -t nat -N nova_postrouting - iptables -t nat -A POSTROUTING -j nova_postrouting - - iptables -t nat -N nova_output - iptables -t nat -A OUTPUT -j nova_output - - # ganglia (all hosts) +iptables -F +iptables -P INPUT DROP +iptables -A INPUT -m state --state INVALID -j DROP +iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT +iptables -A INPUT -m tcp -p tcp -d $MGMT_IP --dport 22 -j ACCEPT +iptables -A INPUT -m udp -p udp --dport 123 -j ACCEPT +iptables -N nova_input +iptables -A INPUT -j nova_input +iptables -A INPUT -p icmp -j ACCEPT +iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset +iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable + +iptables -P FORWARD DROP +iptables -A FORWARD -m state --state INVALID -j DROP +iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT +iptables -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu +iptables -N nova_forward +iptables -A FORWARD -j nova_forward + +iptables -P OUTPUT DROP +iptables -A OUTPUT -m state --state INVALID -j DROP +iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT +iptables -N nova_output +iptables -A OUTPUT -j nova_output + +iptables -t nat -N nova_prerouting +iptables -t nat -A PREROUTING -j nova_prerouting + +iptables -t nat -N nova_postrouting +iptables -t nat -A POSTROUTING -j nova_postrouting + +iptables -t nat -N nova_output +iptables -t nat -A OUTPUT -j nova_output + +if [ "$CMD" == "ganglia" ] || [ "$CMD" == "all" ]; then iptables -A nova_input -m tcp -p tcp -d $IP --dport 8649 -j ACCEPT iptables -A nova_input -m udp -p udp -d $IP --dport 8649 -j ACCEPT fi -if [ "$CMD" == "dashboard" ]; then +if [ "$CMD" == "dashboard" ] || [ "$CMD" == "all" ]; then # dashboard iptables -A nova_input -m tcp -p tcp -d $IP --dport 80 -j ACCEPT iptables -A nova_input -m tcp -p tcp -d $IP --dport 443 -j ACCEPT fi -if [ "$CMD" == "objectstore" ]; then +if [ "$CMD" == "objectstore" ] || [ "$CMD" == "all" ]; then iptables -A nova_input -m tcp -p tcp -d $IP --dport 3333 -j ACCEPT +fi + +if [ "$CMD" == "api" ] || [ "$CMD" == "all" ]; then iptables -A nova_input -m tcp -p tcp -d $IP --dport 8773 -j ACCEPT fi -if [ "$CMD" == "redis" ]; then +if [ "$CMD" == "redis" ] || [ "$CMD" == "all" ]; then iptables -A nova_input -m tcp -p tcp -d $IP --dport 6379 -j ACCEPT fi -if [ "$CMD" == "mysql" ]; then +if [ "$CMD" == "mysql" ] || [ "$CMD" == "all" ]; then iptables -A nova_input -m tcp -p tcp -d $IP --dport 3306 -j ACCEPT fi -if [ "$CMD" == "rabbitmq" ]; then +if [ "$CMD" == "rabbitmq" ] || [ "$CMD" == "all" ]; then iptables -A nova_input -m tcp -p tcp -d $IP --dport 4369 -j ACCEPT iptables -A nova_input -m tcp -p tcp -d $IP --dport 5672 -j ACCEPT iptables -A nova_input -m tcp -p tcp -d $IP --dport 53284 -j ACCEPT fi -if [ "$CMD" == "dnsmasq" ]; then +if [ "$CMD" == "dnsmasq" ] || [ "$CMD" == "all" ]; then # NOTE(vish): this could theoretically be setup per network # for each host, but it seems like overkill iptables -A nova_input -m tcp -p tcp -s $PRIVATE_RANGE --dport 53 -j ACCEPT iptables -A nova_input -m udp -p udp -s $PRIVATE_RANGE --dport 53 -j ACCEPT iptables -A nova_input -m udp -p udp --dport 67 -j ACCEPT +fi -if [ "$CMD" == "ldap" ]; then +if [ "$CMD" == "ldap" ] || [ "$CMD" == "all" ]; then iptables -A nova_input -m tcp -p tcp -d $IP --dport 389 -j ACCEPT fi -- cgit From 41a598f09baee94125608873f4d7118000fc55ea Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 24 Sep 2010 19:57:41 -0700 Subject: add a reset command --- tools/setup_iptables.sh | 74 +++++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/tools/setup_iptables.sh b/tools/setup_iptables.sh index fd32f6f82..7368fadf9 100755 --- a/tools/setup_iptables.sh +++ b/tools/setup_iptables.sh @@ -43,40 +43,48 @@ if [ -n "$4" ]; then else MGMT_IP="$IP" fi +if [ "$CMD" == "clear" ]; then + iptables -P INPUT ACCEPT + iptables -P FORWARD ACCEPT + iptables -P OUTPUT ACCEPT + iptables -F + iptables -X +fi -iptables -F -iptables -P INPUT DROP -iptables -A INPUT -m state --state INVALID -j DROP -iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT -iptables -A INPUT -m tcp -p tcp -d $MGMT_IP --dport 22 -j ACCEPT -iptables -A INPUT -m udp -p udp --dport 123 -j ACCEPT -iptables -N nova_input -iptables -A INPUT -j nova_input -iptables -A INPUT -p icmp -j ACCEPT -iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset -iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable - -iptables -P FORWARD DROP -iptables -A FORWARD -m state --state INVALID -j DROP -iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT -iptables -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu -iptables -N nova_forward -iptables -A FORWARD -j nova_forward - -iptables -P OUTPUT DROP -iptables -A OUTPUT -m state --state INVALID -j DROP -iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT -iptables -N nova_output -iptables -A OUTPUT -j nova_output - -iptables -t nat -N nova_prerouting -iptables -t nat -A PREROUTING -j nova_prerouting - -iptables -t nat -N nova_postrouting -iptables -t nat -A POSTROUTING -j nova_postrouting - -iptables -t nat -N nova_output -iptables -t nat -A OUTPUT -j nova_output +if [ "$CMD" == "base" ] || [ "$CMD" == "all" ]; then + iptables -P INPUT DROP + iptables -A INPUT -m state --state INVALID -j DROP + iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -A INPUT -m tcp -p tcp -d $MGMT_IP --dport 22 -j ACCEPT + iptables -A INPUT -m udp -p udp --dport 123 -j ACCEPT + iptables -N nova_input + iptables -A INPUT -j nova_input + iptables -A INPUT -p icmp -j ACCEPT + iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset + iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable + + iptables -P FORWARD DROP + iptables -A FORWARD -m state --state INVALID -j DROP + iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu + iptables -N nova_forward + iptables -A FORWARD -j nova_forward + + iptables -P OUTPUT DROP + iptables -A OUTPUT -m state --state INVALID -j DROP + iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -N nova_output + iptables -A OUTPUT -j nova_output + + iptables -t nat -N nova_prerouting + iptables -t nat -A PREROUTING -j nova_prerouting + + iptables -t nat -N nova_postrouting + iptables -t nat -A POSTROUTING -j nova_postrouting + + iptables -t nat -N nova_output + iptables -t nat -A OUTPUT -j nova_output +fi if [ "$CMD" == "ganglia" ] || [ "$CMD" == "all" ]; then iptables -A nova_input -m tcp -p tcp -d $IP --dport 8649 -j ACCEPT -- cgit From 73cc0e446297781182fab1b8e8447e7c6d100b08 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Fri, 24 Sep 2010 23:32:00 -0400 Subject: Update auth manager to have a update_user method and better tests. --- nova/auth/ldapdriver.py | 16 +- nova/auth/manager.py | 6 + nova/tests/auth_unittest.py | 408 +++++++++++++++++++++++++++++--------------- 3 files changed, 290 insertions(+), 140 deletions(-) diff --git a/nova/auth/ldapdriver.py b/nova/auth/ldapdriver.py index 021851ebf..640ea169e 100644 --- a/nova/auth/ldapdriver.py +++ b/nova/auth/ldapdriver.py @@ -256,8 +256,7 @@ class LdapDriver(object): if not self.__user_exists(uid): raise exception.NotFound("User %s doesn't exist" % uid) self.__remove_from_all(uid) - self.conn.delete_s('uid=%s,%s' % (uid, - FLAGS.ldap_user_subtree)) + self.conn.delete_s(self.__uid_to_dn(uid)) def delete_project(self, project_id): """Delete a project""" @@ -265,6 +264,19 @@ class LdapDriver(object): self.__delete_roles(project_dn) self.__delete_group(project_dn) + def modify_user(self, uid, access_key=None, secret_key=None, admin=None): + """Modify an existing project""" + if not access_key and not secret_key and admin is None: + return + attr = [] + if access_key: + attr.append((self.ldap.MOD_REPLACE, 'accessKey', access_key)) + if secret_key: + attr.append((self.ldap.MOD_REPLACE, 'secretKey', secret_key)) + if admin is not None: + attr.append((self.ldap.MOD_REPLACE, 'isAdmin', str(admin).upper())) + self.conn.modify_s(self.__uid_to_dn(uid), attr) + def __user_exists(self, uid): """Check if user exists""" return self.get_user(uid) != None diff --git a/nova/auth/manager.py b/nova/auth/manager.py index 55fbf42aa..0bc12c80f 100644 --- a/nova/auth/manager.py +++ b/nova/auth/manager.py @@ -630,6 +630,12 @@ class AuthManager(object): with self.driver() as drv: drv.delete_user(uid) + def modify_user(self, user, access_key=None, secret_key=None, admin=None): + """Modify credentials for a user""" + uid = User.safe_id(user) + with self.driver() as drv: + drv.modify_user(uid, access_key, secret_key, admin) + def get_credentials(self, user, project=None): """Get credential zip for user in project""" if not isinstance(user, User): diff --git a/nova/tests/auth_unittest.py b/nova/tests/auth_unittest.py index 3235dea39..7065105c3 100644 --- a/nova/tests/auth_unittest.py +++ b/nova/tests/auth_unittest.py @@ -28,25 +28,73 @@ from nova.api.ec2 import cloud FLAGS = flags.FLAGS - +class user_generator(object): + def __init__(self, manager, **user_state): + if 'name' not in user_state: + user_state['name'] = 'test1' + self.manager = manager + self.user = manager.create_user(**user_state) + + def __enter__(self): + return self.user + + def __exit__(self, value, type, trace): + self.manager.delete_user(self.user) + +class project_generator(object): + def __init__(self, manager, **project_state): + if 'name' not in project_state: + project_state['name'] = 'testproj' + if 'manager_user' not in project_state: + project_state['manager_user'] = 'test1' + self.manager = manager + self.project = manager.create_project(**project_state) + + def __enter__(self): + return self.project + + def __exit__(self, value, type, trace): + self.manager.delete_project(self.project) + +class user_and_project_generator(object): + def __init__(self, manager, user_state={}, project_state={}): + self.manager = manager + if 'name' not in user_state: + user_state['name'] = 'test1' + if 'name' not in project_state: + project_state['name'] = 'testproj' + if 'manager_user' not in project_state: + project_state['manager_user'] = 'test1' + self.user = manager.create_user(**user_state) + self.project = manager.create_project(**project_state) + + def __enter__(self): + return (self.user, self.project) + + def __exit__(self, value, type, trace): + self.manager.delete_user(self.user) + self.manager.delete_project(self.project) + +# TODO(todd): Have a test class that tests just the AuthManager functions +# and different test classes for User and Project wrappers class AuthTestCase(test.BaseTestCase): def setUp(self): super(AuthTestCase, self).setUp() self.flags(connection_type='fake') self.manager = manager.AuthManager() - def test_001_can_create_users(self): - self.manager.create_user('test1', 'access', 'secret') - self.manager.create_user('test2') - - def test_002_can_get_user(self): - user = self.manager.get_user('test1') + def test_create_and_find_user(self): + with user_generator(self.manager): + self.assert_(self.manager.get_user('test1')) - def test_003_can_retreive_properties(self): - user = self.manager.get_user('test1') - self.assertEqual('test1', user.id) - self.assertEqual('access', user.access) - self.assertEqual('secret', user.secret) + def test_create_and_find_with_properties(self): + with user_generator(self.manager, name="herbert", secret="classified", + access="private-party"): + u = self.manager.get_user('herbert') + self.assertEqual('herbert', u.id) + self.assertEqual('herbert', u.name) + self.assertEqual('classified', u.secret) + self.assertEqual('private-party', u.access) def test_004_signature_is_valid(self): #self.assertTrue(self.manager.authenticate( **boto.generate_url ... ? ? ? )) @@ -63,133 +111,217 @@ class AuthTestCase(test.BaseTestCase): 'export S3_URL="http://127.0.0.1:3333/"\n' + 'export EC2_USER_ID="test1"\n') - def test_010_can_list_users(self): - users = self.manager.get_users() - logging.warn(users) - self.assertTrue(filter(lambda u: u.id == 'test1', users)) - - def test_101_can_add_user_role(self): - self.assertFalse(self.manager.has_role('test1', 'itsec')) - self.manager.add_role('test1', 'itsec') - self.assertTrue(self.manager.has_role('test1', 'itsec')) - - def test_199_can_remove_user_role(self): - self.assertTrue(self.manager.has_role('test1', 'itsec')) - self.manager.remove_role('test1', 'itsec') - self.assertFalse(self.manager.has_role('test1', 'itsec')) - - def test_201_can_create_project(self): - project = self.manager.create_project('testproj', 'test1', 'A test project', ['test1']) - self.assertTrue(filter(lambda p: p.name == 'testproj', self.manager.get_projects())) - self.assertEqual(project.name, 'testproj') - self.assertEqual(project.description, 'A test project') - self.assertEqual(project.project_manager_id, 'test1') - self.assertTrue(project.has_member('test1')) - - def test_202_user1_is_project_member(self): - self.assertTrue(self.manager.get_user('test1').is_project_member('testproj')) - - def test_203_user2_is_not_project_member(self): - self.assertFalse(self.manager.get_user('test2').is_project_member('testproj')) - - def test_204_user1_is_project_manager(self): - self.assertTrue(self.manager.get_user('test1').is_project_manager('testproj')) - - def test_205_user2_is_not_project_manager(self): - self.assertFalse(self.manager.get_user('test2').is_project_manager('testproj')) - - def test_206_can_add_user_to_project(self): - self.manager.add_to_project('test2', 'testproj') - self.assertTrue(self.manager.get_project('testproj').has_member('test2')) - - def test_207_can_remove_user_from_project(self): - self.manager.remove_from_project('test2', 'testproj') - self.assertFalse(self.manager.get_project('testproj').has_member('test2')) - - def test_208_can_remove_add_user_with_role(self): - self.manager.add_to_project('test2', 'testproj') - self.manager.add_role('test2', 'developer', 'testproj') - self.manager.remove_from_project('test2', 'testproj') - self.assertFalse(self.manager.has_role('test2', 'developer', 'testproj')) - self.manager.add_to_project('test2', 'testproj') - self.manager.remove_from_project('test2', 'testproj') - - def test_209_can_generate_x509(self): - # MUST HAVE RUN CLOUD SETUP BY NOW - self.cloud = cloud.CloudController() - self.cloud.setup() - _key, cert_str = self.manager._generate_x509_cert('test1', 'testproj') - logging.debug(cert_str) - - # Need to verify that it's signed by the right intermediate CA - full_chain = crypto.fetch_ca(project_id='testproj', chain=True) - int_cert = crypto.fetch_ca(project_id='testproj', chain=False) - cloud_cert = crypto.fetch_ca() - logging.debug("CA chain:\n\n =====\n%s\n\n=====" % full_chain) - signed_cert = X509.load_cert_string(cert_str) - chain_cert = X509.load_cert_string(full_chain) - int_cert = X509.load_cert_string(int_cert) - cloud_cert = X509.load_cert_string(cloud_cert) - self.assertTrue(signed_cert.verify(chain_cert.get_pubkey())) - self.assertTrue(signed_cert.verify(int_cert.get_pubkey())) - - if not FLAGS.use_intermediate_ca: - self.assertTrue(signed_cert.verify(cloud_cert.get_pubkey())) - else: - self.assertFalse(signed_cert.verify(cloud_cert.get_pubkey())) - - def test_210_can_add_project_role(self): - project = self.manager.get_project('testproj') - self.assertFalse(project.has_role('test1', 'sysadmin')) - self.manager.add_role('test1', 'sysadmin') - self.assertFalse(project.has_role('test1', 'sysadmin')) - project.add_role('test1', 'sysadmin') - self.assertTrue(project.has_role('test1', 'sysadmin')) - - def test_211_can_list_project_roles(self): - project = self.manager.get_project('testproj') - user = self.manager.get_user('test1') - self.manager.add_role(user, 'netadmin', project) - roles = self.manager.get_user_roles(user) - self.assertTrue('sysadmin' in roles) - self.assertFalse('netadmin' in roles) - project_roles = self.manager.get_user_roles(user, project) - self.assertTrue('sysadmin' in project_roles) - self.assertTrue('netadmin' in project_roles) - # has role should be false because global role is missing - self.assertFalse(self.manager.has_role(user, 'netadmin', project)) - - - def test_212_can_remove_project_role(self): - project = self.manager.get_project('testproj') - self.assertTrue(project.has_role('test1', 'sysadmin')) - project.remove_role('test1', 'sysadmin') - self.assertFalse(project.has_role('test1', 'sysadmin')) - self.manager.remove_role('test1', 'sysadmin') - self.assertFalse(project.has_role('test1', 'sysadmin')) - - 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) - self.assertEqual(len(self.manager.get_projects('test2')), 1) - - def test_220_can_modify_project(self): - self.manager.modify_project('testproj', 'test2', 'new description') - project = self.manager.get_project('testproj') - self.assertEqual(project.project_manager_id, 'test2') - self.assertEqual(project.description, 'new description') - - def test_299_can_delete_project(self): - self.manager.delete_project('testproj') - self.assertFalse(filter(lambda p: p.name == 'testproj', self.manager.get_projects())) - self.manager.delete_project('testproj2') - - def test_999_can_delete_users(self): + def test_can_list_users(self): + with user_generator(self.manager): + with user_generator(self.manager, name="test2"): + users = self.manager.get_users() + self.assert_(filter(lambda u: u.id == 'test1', users)) + self.assert_(filter(lambda u: u.id == 'test2', users)) + self.assert_(not filter(lambda u: u.id == 'test3', users)) + + def test_can_add_and_remove_user_role(self): + with user_generator(self.manager): + self.assertFalse(self.manager.has_role('test1', 'itsec')) + self.manager.add_role('test1', 'itsec') + self.assertTrue(self.manager.has_role('test1', 'itsec')) + self.manager.remove_role('test1', 'itsec') + self.assertFalse(self.manager.has_role('test1', 'itsec')) + + def test_can_create_and_get_project(self): + with user_and_project_generator(self.manager) as (u,p): + self.assert_(self.manager.get_user('test1')) + self.assert_(self.manager.get_user('test1')) + self.assert_(self.manager.get_project('testproj')) + + def test_can_list_projects(self): + with user_and_project_generator(self.manager): + with project_generator(self.manager, name="testproj2"): + projects = self.manager.get_projects() + self.assert_(filter(lambda p: p.name == 'testproj', projects)) + self.assert_(filter(lambda p: p.name == 'testproj2', projects)) + self.assert_(not filter(lambda p: p.name == 'testproj3', + projects)) + + def test_can_create_and_get_project_with_attributes(self): + with user_generator(self.manager): + with project_generator(self.manager, description='A test project'): + project = self.manager.get_project('testproj') + self.assertEqual('A test project', project.description) + + def test_can_create_project_with_manager(self): + with user_and_project_generator(self.manager) as (user, project): + self.assertEqual('test1', project.project_manager_id) + self.assertTrue(user.is_project_manager('testproj')) + + def test_create_project_assigns_manager_to_members(self): + with user_and_project_generator(self.manager) as (user, project): + self.assertTrue(user.is_project_member('testproj')) + self.assertTrue(project.has_member('test1')) + + def test_no_extra_project_members(self): + with user_generator(self.manager, name='test2') as baduser: + with user_and_project_generator(self.manager) as (user, project): + self.assertFalse(baduser.is_project_member('testproj')) + self.assertFalse(project.has_member('test2')) + + def test_no_extra_project_managers(self): + with user_generator(self.manager, name='test2') as baduser: + with user_and_project_generator(self.manager) as (user, project): + self.assertFalse(baduser.is_project_manager('testproj')) + + def test_can_add_user_to_project(self): + with user_generator(self.manager, name='test2') as user: + with user_and_project_generator(self.manager): + self.manager.add_to_project('test2', 'testproj') + self.assertTrue(user.is_project_member('testproj')) + # NOTE(todd): have to reload after the add_to_project + project = self.manager.get_project('testproj') + self.assertTrue(project.has_member('test2')) + + def test_can_remove_user_from_project(self): + with user_generator(self.manager, name='test2') as user: + with user_and_project_generator(self.manager): + self.manager.add_to_project('test2', 'testproj') + self.assertTrue(user.is_project_member('testproj')) + self.manager.remove_from_project('test2', 'testproj') + self.assertFalse(user.is_project_member('testproj')) + # NOTE(todd): have to reload after the membership modifications + project = self.manager.get_project('testproj') + self.assertFalse(project.has_member('test2')) + + def test_can_add_remove_user_with_role(self): + with user_generator(self.manager, name='test2') as user: + with user_and_project_generator(self.manager): + self.manager.add_to_project('test2', 'testproj') + self.manager.add_role('test2', 'developer', 'testproj') + self.assertTrue(user.is_project_member('testproj')) + self.manager.remove_from_project('test2', 'testproj') + self.assertFalse(self.manager.has_role('test2', 'developer', + 'testproj')) + self.assertFalse(user.is_project_member('testproj')) + + def test_can_generate_x509(self): + # NOTE(todd): this doesn't assert against the auth manager + # so it probably belongs in crypto_unittest + # but I'm leaving it where I found it. + with user_and_project_generator(self.manager) as (user, project): + # NOTE(todd): Should mention why we must setup controller first + # (somebody please clue me in) + cloud_controller = cloud.CloudController() + cloud_controller.setup() + _key, cert_str = self.manager._generate_x509_cert('test1', + 'testproj') + logging.debug(cert_str) + + # Need to verify that it's signed by the right intermediate CA + full_chain = crypto.fetch_ca(project_id='testproj', chain=True) + int_cert = crypto.fetch_ca(project_id='testproj', chain=False) + cloud_cert = crypto.fetch_ca() + logging.debug("CA chain:\n\n =====\n%s\n\n=====" % full_chain) + signed_cert = X509.load_cert_string(cert_str) + chain_cert = X509.load_cert_string(full_chain) + int_cert = X509.load_cert_string(int_cert) + cloud_cert = X509.load_cert_string(cloud_cert) + self.assertTrue(signed_cert.verify(chain_cert.get_pubkey())) + self.assertTrue(signed_cert.verify(int_cert.get_pubkey())) + if not FLAGS.use_intermediate_ca: + self.assertTrue(signed_cert.verify(cloud_cert.get_pubkey())) + else: + self.assertFalse(signed_cert.verify(cloud_cert.get_pubkey())) + + def test_adding_role_to_project_is_ignored_unless_added_to_user(self): + with user_and_project_generator(self.manager) as (user, project): + self.assertFalse(project.has_role('test1', 'sysadmin')) + project.add_role('test1', 'sysadmin') + # NOTE(todd): it will still show up in get_user_roles(u, project) + self.assertFalse(project.has_role('test1', 'sysadmin')) + self.manager.add_role('test1', 'sysadmin') + self.assertTrue(project.has_role('test1', 'sysadmin')) + + def test_add_user_role_doesnt_infect_project_roles(self): + with user_and_project_generator(self.manager) as (user, project): + self.assertFalse(project.has_role('test1', 'sysadmin')) + self.manager.add_role('test1', 'sysadmin') + self.assertFalse(project.has_role('test1', 'sysadmin')) + + def test_can_list_user_roles(self): + with user_and_project_generator(self.manager) as (user, project): + self.manager.add_role('test1', 'sysadmin') + roles = self.manager.get_user_roles(user) + self.assertTrue('sysadmin' in roles) + self.assertFalse('netadmin' in roles) + + def test_can_list_project_roles(self): + with user_and_project_generator(self.manager) as (user, project): + self.manager.add_role('test1', 'sysadmin') + project.add_role('test1', 'sysadmin') + project.add_role('test1', 'netadmin') + project_roles = self.manager.get_user_roles(user, project) + self.assertTrue('sysadmin' in project_roles) + self.assertTrue('netadmin' in project_roles) + # has role should be false user-level role is missing + self.assertFalse(self.manager.has_role(user, 'netadmin', project)) + + def test_can_remove_user_roles(self): + with user_and_project_generator(self.manager) as (user, project): + self.manager.add_role('test1', 'sysadmin') + self.assertTrue(user.has_role('sysadmin')) + self.manager.remove_role('test1', 'sysadmin') + self.assertFalse(user.has_role('sysadmin')) + + def test_removing_user_role_hides_it_from_project(self): + with user_and_project_generator(self.manager) as (user, project): + self.manager.add_role('test1', 'sysadmin') + project.add_role('test1', 'sysadmin') + self.assertTrue(project.has_role('test1', 'sysadmin')) + self.manager.remove_role('test1', 'sysadmin') + self.assertFalse(project.has_role('test1', 'sysadmin')) + + def test_can_remove_project_role_but_keep_user_role(self): + with user_and_project_generator(self.manager) as (user, project): + self.manager.add_role('test1', 'sysadmin') + project.add_role('test1', 'sysadmin') + self.assertTrue(user.has_role('sysadmin')) + self.assertTrue(project.has_role('test1', 'sysadmin')) + project.remove_role('test1', 'sysadmin') + self.assertFalse(project.has_role('test1', 'sysadmin')) + self.assertTrue(user.has_role('sysadmin')) + + def test_can_retrieve_project_by_user(self): + with user_and_project_generator(self.manager) as (user, project): + self.assertEqual(1, len(self.manager.get_projects('test1'))) + + def test_can_modify_project(self): + with user_and_project_generator(self.manager): + with user_generator(self.manager, name='test2'): + self.manager.modify_project('testproj', 'test2', 'new desc') + project = self.manager.get_project('testproj') + self.assertEqual('test2', project.project_manager_id) + self.assertEqual('new desc', project.description) + + def test_can_delete_project(self): + with user_generator(self.manager): + self.manager.create_project('testproj', 'test1') + self.assert_(self.manager.get_project('testproj')) + self.manager.delete_project('testproj') + projectlist = self.manager.get_projects() + self.assert_(not filter(lambda p: p.name == 'testproj', + projectlist)) + + def test_can_delete_user(self): + self.manager.create_user('test1') + self.assert_(self.manager.get_user('test1')) self.manager.delete_user('test1') - users = self.manager.get_users() - self.assertFalse(filter(lambda u: u.id == 'test1', users)) - self.manager.delete_user('test2') - self.assertEqual(self.manager.get_user('test2'), None) + userlist = self.manager.get_users() + self.assert_(not filter(lambda u: u.id == 'test1', userlist)) + + def test_can_modify_users(self): + with user_generator(self.manager): + self.manager.modify_user('test1', 'access', 'secret', True) + user = self.manager.get_user('test1') + self.assertEqual('access', user.access) + self.assertEqual('secret', user.secret) + self.assertTrue(user.is_admin()) if __name__ == "__main__": -- cgit From 39e06d83d265036eb0104cd55d4a828271f62e96 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Sat, 25 Sep 2010 00:35:59 -0400 Subject: Test the AuthManager interface explicitly, in case the user/project wrappers fail or change at some point. Those interfaces should be tested on their own. --- nova/tests/auth_unittest.py | 113 +++++++++++++++++++++----------------------- 1 file changed, 55 insertions(+), 58 deletions(-) diff --git a/nova/tests/auth_unittest.py b/nova/tests/auth_unittest.py index 7065105c3..c3efc577f 100644 --- a/nova/tests/auth_unittest.py +++ b/nova/tests/auth_unittest.py @@ -75,11 +75,9 @@ class user_and_project_generator(object): self.manager.delete_user(self.user) self.manager.delete_project(self.project) -# TODO(todd): Have a test class that tests just the AuthManager functions -# and different test classes for User and Project wrappers -class AuthTestCase(test.BaseTestCase): +class AuthManagerTestCase(test.BaseTestCase): def setUp(self): - super(AuthTestCase, self).setUp() + super(AuthManagerTestCase, self).setUp() self.flags(connection_type='fake') self.manager = manager.AuthManager() @@ -151,54 +149,54 @@ class AuthTestCase(test.BaseTestCase): def test_can_create_project_with_manager(self): with user_and_project_generator(self.manager) as (user, project): self.assertEqual('test1', project.project_manager_id) - self.assertTrue(user.is_project_manager('testproj')) + self.assertTrue(self.manager.is_project_manager(user, project)) def test_create_project_assigns_manager_to_members(self): with user_and_project_generator(self.manager) as (user, project): - self.assertTrue(user.is_project_member('testproj')) - self.assertTrue(project.has_member('test1')) + self.assertTrue(self.manager.is_project_member(user, project)) def test_no_extra_project_members(self): with user_generator(self.manager, name='test2') as baduser: with user_and_project_generator(self.manager) as (user, project): - self.assertFalse(baduser.is_project_member('testproj')) - self.assertFalse(project.has_member('test2')) + self.assertFalse(self.manager.is_project_member(baduser, + project)) def test_no_extra_project_managers(self): with user_generator(self.manager, name='test2') as baduser: with user_and_project_generator(self.manager) as (user, project): - self.assertFalse(baduser.is_project_manager('testproj')) + self.assertFalse(self.manager.is_project_manager(baduser, + project)) def test_can_add_user_to_project(self): with user_generator(self.manager, name='test2') as user: - with user_and_project_generator(self.manager): - self.manager.add_to_project('test2', 'testproj') - self.assertTrue(user.is_project_member('testproj')) - # NOTE(todd): have to reload after the add_to_project + with user_and_project_generator(self.manager) as (_user, project): + self.manager.add_to_project(user, project) project = self.manager.get_project('testproj') - self.assertTrue(project.has_member('test2')) + self.assertTrue(self.manager.is_project_member(user, project)) def test_can_remove_user_from_project(self): with user_generator(self.manager, name='test2') as user: - with user_and_project_generator(self.manager): - self.manager.add_to_project('test2', 'testproj') - self.assertTrue(user.is_project_member('testproj')) - self.manager.remove_from_project('test2', 'testproj') - self.assertFalse(user.is_project_member('testproj')) - # NOTE(todd): have to reload after the membership modifications + with user_and_project_generator(self.manager) as (_user, project): + self.manager.add_to_project(user, project) project = self.manager.get_project('testproj') - self.assertFalse(project.has_member('test2')) + self.assertTrue(self.manager.is_project_member(user, project)) + self.manager.remove_from_project(user, project) + project = self.manager.get_project('testproj') + self.assertFalse(self.manager.is_project_member(user, project)) def test_can_add_remove_user_with_role(self): with user_generator(self.manager, name='test2') as user: - with user_and_project_generator(self.manager): - self.manager.add_to_project('test2', 'testproj') - self.manager.add_role('test2', 'developer', 'testproj') - self.assertTrue(user.is_project_member('testproj')) - self.manager.remove_from_project('test2', 'testproj') - self.assertFalse(self.manager.has_role('test2', 'developer', - 'testproj')) - self.assertFalse(user.is_project_member('testproj')) + with user_and_project_generator(self.manager) as (_user, project): + # NOTE(todd): after modifying users you must reload project + self.manager.add_to_project(user, project) + project = self.manager.get_project('testproj') + self.manager.add_role(user, 'developer', project) + self.assertTrue(self.manager.is_project_member(user, project)) + self.manager.remove_from_project(user, project) + project = self.manager.get_project('testproj') + self.assertFalse(self.manager.has_role(user, 'developer', + project)) + self.assertFalse(self.manager.is_project_member(user, project)) def test_can_generate_x509(self): # NOTE(todd): this doesn't assert against the auth manager @@ -231,31 +229,31 @@ class AuthTestCase(test.BaseTestCase): def test_adding_role_to_project_is_ignored_unless_added_to_user(self): with user_and_project_generator(self.manager) as (user, project): - self.assertFalse(project.has_role('test1', 'sysadmin')) - project.add_role('test1', 'sysadmin') + self.assertFalse(self.manager.has_role(user, 'sysadmin', project)) + self.manager.add_role(user, 'sysadmin', project) # NOTE(todd): it will still show up in get_user_roles(u, project) - self.assertFalse(project.has_role('test1', 'sysadmin')) - self.manager.add_role('test1', 'sysadmin') - self.assertTrue(project.has_role('test1', 'sysadmin')) + self.assertFalse(self.manager.has_role(user, 'sysadmin', project)) + self.manager.add_role(user, 'sysadmin') + self.assertTrue(self.manager.has_role(user, 'sysadmin', project)) def test_add_user_role_doesnt_infect_project_roles(self): with user_and_project_generator(self.manager) as (user, project): - self.assertFalse(project.has_role('test1', 'sysadmin')) - self.manager.add_role('test1', 'sysadmin') - self.assertFalse(project.has_role('test1', 'sysadmin')) + self.assertFalse(self.manager.has_role(user, 'sysadmin', project)) + self.manager.add_role(user, 'sysadmin') + self.assertFalse(self.manager.has_role(user, 'sysadmin', project)) def test_can_list_user_roles(self): with user_and_project_generator(self.manager) as (user, project): - self.manager.add_role('test1', 'sysadmin') + self.manager.add_role(user, 'sysadmin') roles = self.manager.get_user_roles(user) self.assertTrue('sysadmin' in roles) self.assertFalse('netadmin' in roles) def test_can_list_project_roles(self): with user_and_project_generator(self.manager) as (user, project): - self.manager.add_role('test1', 'sysadmin') - project.add_role('test1', 'sysadmin') - project.add_role('test1', 'netadmin') + self.manager.add_role(user, 'sysadmin') + self.manager.add_role(user, 'sysadmin', project) + self.manager.add_role(user, 'netadmin', project) project_roles = self.manager.get_user_roles(user, project) self.assertTrue('sysadmin' in project_roles) self.assertTrue('netadmin' in project_roles) @@ -264,28 +262,27 @@ class AuthTestCase(test.BaseTestCase): def test_can_remove_user_roles(self): with user_and_project_generator(self.manager) as (user, project): - self.manager.add_role('test1', 'sysadmin') - self.assertTrue(user.has_role('sysadmin')) - self.manager.remove_role('test1', 'sysadmin') - self.assertFalse(user.has_role('sysadmin')) + self.manager.add_role(user, 'sysadmin') + self.assertTrue(self.manager.has_role(user, 'sysadmin')) + self.manager.remove_role(user, 'sysadmin') + self.assertFalse(self.manager.has_role(user, 'sysadmin')) def test_removing_user_role_hides_it_from_project(self): with user_and_project_generator(self.manager) as (user, project): - self.manager.add_role('test1', 'sysadmin') - project.add_role('test1', 'sysadmin') - self.assertTrue(project.has_role('test1', 'sysadmin')) - self.manager.remove_role('test1', 'sysadmin') - self.assertFalse(project.has_role('test1', 'sysadmin')) + self.manager.add_role(user, 'sysadmin') + self.manager.add_role(user, 'sysadmin', project) + self.assertTrue(self.manager.has_role(user, 'sysadmin', project)) + self.manager.remove_role(user, 'sysadmin') + self.assertFalse(self.manager.has_role(user, 'sysadmin', project)) def test_can_remove_project_role_but_keep_user_role(self): with user_and_project_generator(self.manager) as (user, project): - self.manager.add_role('test1', 'sysadmin') - project.add_role('test1', 'sysadmin') - self.assertTrue(user.has_role('sysadmin')) - self.assertTrue(project.has_role('test1', 'sysadmin')) - project.remove_role('test1', 'sysadmin') - self.assertFalse(project.has_role('test1', 'sysadmin')) - self.assertTrue(user.has_role('sysadmin')) + self.manager.add_role(user, 'sysadmin') + self.manager.add_role(user, 'sysadmin', project) + self.assertTrue(self.manager.has_role(user, 'sysadmin')) + self.manager.remove_role(user, 'sysadmin', project) + self.assertFalse(self.manager.has_role(user, 'sysadmin', project)) + self.assertTrue(self.manager.has_role(user, 'sysadmin')) def test_can_retrieve_project_by_user(self): with user_and_project_generator(self.manager) as (user, project): -- cgit From c3fcb1b2176f4b7afbffb3555da55c0754bacaad Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sat, 25 Sep 2010 01:05:39 -0700 Subject: flush the nova chains --- tools/setup_iptables.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/setup_iptables.sh b/tools/setup_iptables.sh index 7368fadf9..d045b50cd 100755 --- a/tools/setup_iptables.sh +++ b/tools/setup_iptables.sh @@ -48,6 +48,9 @@ if [ "$CMD" == "clear" ]; then iptables -P FORWARD ACCEPT iptables -P OUTPUT ACCEPT iptables -F + iptables -F nova_input + iptables -F nova_output + iptables -F nova_forward iptables -X fi -- cgit From 125e69dd42f6f91f727258dc388d15ce63076d1f Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sat, 25 Sep 2010 02:51:50 -0700 Subject: allow mgmt ip access to api --- tools/setup_iptables.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/setup_iptables.sh b/tools/setup_iptables.sh index d045b50cd..b7e2f9a11 100755 --- a/tools/setup_iptables.sh +++ b/tools/setup_iptables.sh @@ -106,6 +106,9 @@ fi if [ "$CMD" == "api" ] || [ "$CMD" == "all" ]; then iptables -A nova_input -m tcp -p tcp -d $IP --dport 8773 -j ACCEPT + if [ "$IP" != "$MGMT_IP" ]; then + iptables -A nova_input -m tcp -p tcp -d $MGMT_IP --dport 8773 -j ACCEPT + fi fi if [ "$CMD" == "redis" ] || [ "$CMD" == "all" ]; then -- cgit From 7ce67ea60f6e7d20665c10318b29e2659fd91513 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sat, 25 Sep 2010 08:35:16 -0700 Subject: fix a few missed calls to _confirm_rule and 80 char issues --- nova/network/linux_net.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 38a616e83..8058c970d 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -60,8 +60,10 @@ def init_host(): "-j SNAT --to-source %s" % (FLAGS.private_range, FLAGS.routing_source_ip)) - _confirm_rule("POSTROUTING", "-t nat -s %s -j MASQUERADE" % FLAGS.private_range) - _confirm_rule("POSTROUTING", "-t nat -s %(range)s -d %(range)s -j ACCEPT" % {'range': FLAGS.private_range}) + _confirm_rule("POSTROUTING", "-t nat -s %s -j MASQUERADE" % + FLAGS.private_range) + _confirm_rule("POSTROUTING", "-t nat -s %(range)s -d %(range)s -j ACCEPT" % + {'range': FLAGS.private_range}) def bind_floating_ip(floating_ip): """Bind ip to public interface""" @@ -77,9 +79,10 @@ def unbind_floating_ip(floating_ip): def ensure_vlan_forward(public_ip, port, private_ip): """Sets up forwarding rules for vlan""" - _confirm_rule("FORWARD", "-d %s -p udp --dport 1194 -j ACCEPT" % private_ip) - _confirm_rule( - "PREROUTING -t nat -d %s -p udp --dport %s -j DNAT --to %s:1194" + _confirm_rule("FORWARD", "-d %s -p udp --dport 1194 -j ACCEPT" % + private_ip) + _confirm_rule("PREROUTING", + "-t nat -d %s -p udp --dport %s -j DNAT --to %s:1194" % (public_ip, port, private_ip)) @@ -93,8 +96,7 @@ def ensure_floating_forward(floating_ip, fixed_ip): _confirm_rule("FORWARD", "-d %s -p icmp -j ACCEPT" % (fixed_ip)) for (protocol, port) in DEFAULT_PORTS: - _confirm_rule( - "FORWARD -d %s -p %s --dport %s -j ACCEPT" + _confirm_rule("FORWARD","-d %s -p %s --dport %s -j ACCEPT" % (fixed_ip, protocol, port)) -- cgit From 888a99182ca3152f68b762dab4fc95d7d3f1cadb Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sat, 25 Sep 2010 09:28:05 -0700 Subject: add forwarding ACCEPT for outgoing packets on compute host --- nova/network/linux_net.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 8058c970d..78cc64cb0 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -148,6 +148,7 @@ def ensure_bridge(bridge, interface, net_attrs=None): _confirm_rule("FORWARD", "--in-interface %s -j ACCEPT" % bridge) else: _execute("sudo ifconfig %s up" % bridge) + _confirm_rule("FORWARD", "--out-interface %s -j ACCEPT" % bridge) def get_dhcp_hosts(context, network_id): -- cgit From 6a3cd55a9c933c329da1117179d676e9141c5b4d Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sat, 25 Sep 2010 10:47:51 -0700 Subject: disable output drop for the moment because it is too restrictive --- tools/setup_iptables.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/setup_iptables.sh b/tools/setup_iptables.sh index b7e2f9a11..dd91c76e0 100755 --- a/tools/setup_iptables.sh +++ b/tools/setup_iptables.sh @@ -52,6 +52,11 @@ if [ "$CMD" == "clear" ]; then iptables -F nova_output iptables -F nova_forward iptables -X + iptables -t nat -F + iptables -t nat -F nova_input + iptables -t nat -F nova_output + iptables -t nat -F nova_forward + iptables -t nat -X fi if [ "$CMD" == "base" ] || [ "$CMD" == "all" ]; then @@ -73,7 +78,7 @@ if [ "$CMD" == "base" ] || [ "$CMD" == "all" ]; then iptables -N nova_forward iptables -A FORWARD -j nova_forward - iptables -P OUTPUT DROP + # iptables -P OUTPUT DROP # too restrictive for the moment iptables -A OUTPUT -m state --state INVALID -j DROP iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT iptables -N nova_output -- cgit From 0d0884b2c1692d03e0994baecbb23ce24ef71e44 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sat, 25 Sep 2010 13:53:29 -0700 Subject: allow in and out for network and compute hosts --- nova/network/linux_net.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 78cc64cb0..6b47b6d97 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -145,10 +145,10 @@ def ensure_bridge(bridge, interface, net_attrs=None): net_attrs['gateway'], net_attrs['broadcast'], net_attrs['netmask'])) - _confirm_rule("FORWARD", "--in-interface %s -j ACCEPT" % bridge) else: _execute("sudo ifconfig %s up" % bridge) - _confirm_rule("FORWARD", "--out-interface %s -j ACCEPT" % bridge) + _confirm_rule("FORWARD", "--in-interface %s -j ACCEPT" % bridge) + _confirm_rule("FORWARD", "--out-interface %s -j ACCEPT" % bridge) def get_dhcp_hosts(context, network_id): -- cgit From 5d6ab2b2540743e0a53b01129df722610b3ae3b6 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sat, 25 Sep 2010 18:33:27 -0700 Subject: reorganize iptables clear and make sure use_nova_chains is a boolean --- nova/network/linux_net.py | 4 ++-- tools/setup_iptables.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 6b47b6d97..01a1d2ad0 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -41,8 +41,8 @@ flags.DEFINE_string('bridge_dev', 'eth0', 'network device for bridges') flags.DEFINE_string('routing_source_ip', utils.get_my_ip(), 'Public IP of network host') -flags.DEFINE_string('use_nova_chains', False, - 'use the nova_ routing chains instead of default') +flags.DEFINE_boo('use_nova_chains', False, + 'use the nova_ routing chains instead of default') DEFAULT_PORTS = [("tcp", 80), ("tcp", 22), ("udp", 1194), ("tcp", 443)] diff --git a/tools/setup_iptables.sh b/tools/setup_iptables.sh index dd91c76e0..b6b8414e3 100755 --- a/tools/setup_iptables.sh +++ b/tools/setup_iptables.sh @@ -48,15 +48,15 @@ if [ "$CMD" == "clear" ]; then iptables -P FORWARD ACCEPT iptables -P OUTPUT ACCEPT iptables -F + iptables -t nat -F iptables -F nova_input iptables -F nova_output iptables -F nova_forward - iptables -X - iptables -t nat -F iptables -t nat -F nova_input iptables -t nat -F nova_output iptables -t nat -F nova_forward iptables -t nat -X + iptables -X fi if [ "$CMD" == "base" ] || [ "$CMD" == "all" ]; then -- cgit From 4b26e60e3c84d4535fbd4ba7a3b2bf29f2121072 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 28 Sep 2010 14:09:53 -0400 Subject: Instance & Image renaming fixes. --- nova/endpoint/cloud.py | 24 ++++++++++-------------- nova/endpoint/images.py | 6 ++++-- nova/objectstore/handler.py | 18 +++++++++--------- nova/objectstore/image.py | 14 ++++++-------- nova/tests/cloud_unittest.py | 2 +- nova/tests/objectstore_unittest.py | 6 +++--- 6 files changed, 33 insertions(+), 37 deletions(-) diff --git a/nova/endpoint/cloud.py b/nova/endpoint/cloud.py index 2c174bb81..9c96ced9c 100644 --- a/nova/endpoint/cloud.py +++ b/nova/endpoint/cloud.py @@ -310,10 +310,11 @@ class CloudController(object): 'volume_id': volume['str_id']}] else: v['attachmentSet'] = [{}] - if 'display_name' in volume: - v['display_name'] = volume['display_name'] - if 'display_description' in volume: - v['display_description'] = volume['display_description'] + + # TODO(todd): check api version and only pass back to nova-aware + # clients + v['display_name'] = volume['display_name'] + v['display_description'] = volume['display_description'] return v @rbac.allow('projectmanager', 'sysadmin') @@ -746,7 +747,9 @@ class CloudController(object): if field in kwargs: changes[field] = kwargs[field] if changes: - db.instance_update(context, instance_id, kwargs) + db_context = {} + inst = db.instance_get_by_str(db_context, instance_id) + db.instance_update(db_context, inst['id'], kwargs) return defer.succeed(True) @rbac.allow('projectmanager', 'sysadmin') @@ -814,13 +817,6 @@ class CloudController(object): return defer.succeed(result) @rbac.allow('projectmanager', 'sysadmin') - def set_image_name(self, context, image_id, name): - result = images.update_user_editable_field(context, image_id, - 'displayName', name) - return defer.succeed(result) - - @rbac.allow('projectmanager', 'sysadmin') - def set_image_description(self, context, image_id, name): - result = images.update_user_editable_field(context, image_id, - 'displayDescription', name) + def update_image(self, context, image_id, **kwargs): + result = images.update(context, image_id, dict(kwargs)) return defer.succeed(result) diff --git a/nova/endpoint/images.py b/nova/endpoint/images.py index a63c059bb..cb54cdda2 100644 --- a/nova/endpoint/images.py +++ b/nova/endpoint/images.py @@ -43,11 +43,13 @@ def modify(context, image_id, operation): return True -def update_user_editable_field(context, image_id, field, value): +def update(context, image_id, attributes): + """update an image's attributes / info.json""" + attributes.update({"image_id": image_id}) conn(context).make_request( method='POST', bucket='_images', - query_args=qs({'image_id': image_id, 'field': field, 'value': value})) + query_args=qs(attributes)) return True def register(context, image_location): diff --git a/nova/objectstore/handler.py b/nova/objectstore/handler.py index f4596e982..daf633ac2 100644 --- a/nova/objectstore/handler.py +++ b/nova/objectstore/handler.py @@ -352,6 +352,8 @@ class ImagesResource(resource.Resource): m[u'imageType'] = m['type'] elif 'imageType' in m: m[u'type'] = m['imageType'] + if 'displayName' not in m: + m[u'displayName'] = u'' return m request.write(json.dumps([decorate(i.metadata) for i in images])) @@ -386,23 +388,21 @@ class ImagesResource(resource.Resource): image_id = get_argument(request, 'image_id', u'') image_object = image.Image(image_id) if not image_object.is_authorized(request.context): - logging.debug("not authorized for handle_POST in images") + logging.debug("not authorized for render_POST in images") raise exception.NotAuthorized operation = get_argument(request, 'operation', u'') - field = get_argument(request, 'field', u'') - value = get_argument(request, 'value', u'') if operation: # operation implies publicity toggle logging.debug("handling publicity toggle") image_object.set_public(operation=='add') - elif field: - # field implies user field editing (value can be blank) - logging.debug("update user field") - image_object.update_user_editable_field(field, value) else: - logging.debug("unknown action for handle_POST in images") - + # other attributes imply update + logging.debug("update user fields") + clean_args = {} + for arg in request.args.keys(): + clean_args[arg] = request.args[arg][0] + image_object.update_user_editable_fields(clean_args) return '' def render_DELETE(self, request): # pylint: disable-msg=R0201 diff --git a/nova/objectstore/image.py b/nova/objectstore/image.py index ad1745af6..def1b8167 100644 --- a/nova/objectstore/image.py +++ b/nova/objectstore/image.py @@ -82,15 +82,13 @@ class Image(object): with open(os.path.join(self.path, 'info.json'), 'w') as f: json.dump(md, f) - def update_user_editable_field(self, field, value): - fields = ['displayName', 'displayDescription'] - if field not in fields: - raise KeyError("Invalid field: %s" % field) + def update_user_editable_fields(self, args): + """args is from the request parameters, so requires extra cleaning""" + fields = {'display_name': 'displayName', 'description': 'description'} info = self.metadata - if value: - info[field] = value - elif field in info: - del info[field] + for field in fields.keys(): + if field in args: + info[fields[field]] = args[field] with open(os.path.join(self.path, 'info.json'), 'w') as f: json.dump(info, f) diff --git a/nova/tests/cloud_unittest.py b/nova/tests/cloud_unittest.py index cea2a67ce..f7340f12d 100644 --- a/nova/tests/cloud_unittest.py +++ b/nova/tests/cloud_unittest.py @@ -219,7 +219,7 @@ class CloudTestCase(test.BaseTestCase): def test_update_of_instance_display_fields(self): inst = db.instance_create({}, {}) - self.cloud.update_instance(self.context, inst['id'], + self.cloud.update_instance(self.context, inst['str_id'], display_name='c00l 1m4g3') inst = db.instance_get({}, inst['id']) self.assertEqual('c00l 1m4g3', inst['display_name']) diff --git a/nova/tests/objectstore_unittest.py b/nova/tests/objectstore_unittest.py index 82c6e2c49..4216cf190 100644 --- a/nova/tests/objectstore_unittest.py +++ b/nova/tests/objectstore_unittest.py @@ -165,10 +165,10 @@ class ObjectStoreTestCase(test.BaseTestCase): self.assertFalse(my_img.is_authorized(self.context)) # change user-editable fields - my_img.update_user_editable_field('displayName', 'my cool image') + my_img.update_user_editable_fields({'display_name': 'my cool image'}) self.assertEqual('my cool image', my_img.metadata['displayName']) - my_img.update_user_editable_field('displayName', '') - self.assert_(not 'displayName' in my_img.metadata) + my_img.update_user_editable_fields({'display_name': ''}) + self.assert_(not my_img.metadata['displayName']) class TestHTTPChannel(http.HTTPChannel): -- cgit From 08a6dc59add5d72c6d925d1ca43868557a4b3148 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 28 Sep 2010 15:54:05 -0400 Subject: Hook the AuthManger#modify_user method into nova-manage commands. --- bin/nova-manage | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/bin/nova-manage b/bin/nova-manage index baa1cb4db..da18e7d1b 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -228,6 +228,19 @@ class UserCommands(object): for user in self.manager.get_users(): print user.name + def modify(self, name, access_key, secret_key, is_admin): + """update a users keys & admin flag + arguments: accesskey secretkey admin + leave any field blank to ignore it, admin should be 'T', 'F', or blank + """ + if not is_admin: + is_admin = None + elif is_admin.upper()[0] == 'T': + is_admin = True + else: + is_admin = False + print "is_admin: %r" % is_admin + self.manager.modify_user(name, access_key, secret_key, is_admin) class ProjectCommands(object): """Class for managing projects.""" -- cgit From c1f7914f9d8c4f7687c67de37c5eda5a95245a0d Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 28 Sep 2010 16:41:39 -0400 Subject: Remove TODO, since apparently newer boto doesn't die on extra fields. --- nova/api/ec2/cloud.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 4defef87e..528380f0f 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -286,8 +286,6 @@ class CloudController(object): else: v['attachmentSet'] = [{}] - # TODO(todd): check api version and only pass back to nova-aware - # clients v['display_name'] = volume['display_name'] v['display_description'] = volume['display_description'] return v -- cgit From c80c0786baadf521c86ceff21288e3760aaea5bd Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 28 Sep 2010 16:47:29 -0400 Subject: Add authorization info for cloud endpoints. --- nova/api/ec2/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index f0aa57ee4..7a958f841 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -158,12 +158,14 @@ class Authorizer(wsgi.Middleware): 'RunInstances': ['projectmanager', 'sysadmin'], 'TerminateInstances': ['projectmanager', 'sysadmin'], 'RebootInstances': ['projectmanager', 'sysadmin'], + 'UpdateInstance': ['projectmanager', 'sysadmin'], 'DeleteVolume': ['projectmanager', 'sysadmin'], 'DescribeImages': ['all'], 'DeregisterImage': ['projectmanager', 'sysadmin'], 'RegisterImage': ['projectmanager', 'sysadmin'], 'DescribeImageAttribute': ['all'], 'ModifyImageAttribute': ['projectmanager', 'sysadmin'], + 'UpdateImage': ['projectmanager', 'sysadmin'], }, 'AdminController': { # All actions have the same permission: ['none'] (the default) -- cgit From c4df3d63c83c073664a9ed0abaefe2adbe4cd061 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 28 Sep 2010 14:59:32 -0700 Subject: fix test for editable image --- nova/tests/cloud_unittest.py | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/nova/tests/cloud_unittest.py b/nova/tests/cloud_unittest.py index 8db4aeb77..cce4a5568 100644 --- a/nova/tests/cloud_unittest.py +++ b/nova/tests/cloud_unittest.py @@ -202,18 +202,37 @@ class CloudTestCase(test.BaseTestCase): # data = self.cloud.get_metadata(instance(i)['private_dns_name']) # self.assert_(data['meta-data']['ami-id'] == 'ami-%s' % i) + @staticmethod + def _fake_set_image_description(ctxt, image_id, description): + from nova.objectstore import handler + class req: + pass + request = req() + request.context = ctxt + request.args = {'image_id': [image_id], + 'description': [description]} + + resource = handler.ImagesResource() + resource.render_POST(request) + def test_user_editable_image_endpoint(self): pathdir = os.path.join(FLAGS.images_path, 'ami-testing') os.mkdir(pathdir) - info = {} + info = {'isPublic': False} with open(os.path.join(pathdir, 'info.json'), 'w') as f: json.dump(info, f) - yield self.cloud.set_image_description(self.context, 'ami-testing', - 'Foo Img') img = image.Image('ami-testing') - self.assertEqual('Foo Img', img.metadata['displayDescription']) - self.cloud.set_image_description(self.context, 'ami-testing', '') - self.assert_(not 'displayDescription' in img.metadata) + # self.cloud.set_image_description(self.context, 'ami-testing', + # 'Foo Img') + # NOTE(vish): Above won't work unless we start objectstore or create + # a fake version of api/ec2/images.py conn that can + # call methods directly instead of going through boto. + # for now, just cheat and call the method directly + self._fake_set_image_description(self.context, 'ami-testing', + 'Foo Img') + self.assertEqual('Foo Img', img.metadata['description']) + self._fake_set_image_description(self.context, 'ami-testing', '') + self.assertEqual('', img.metadata['description']) def test_update_of_instance_display_fields(self): inst = db.instance_create({}, {}) -- cgit From bbc00d9eca3b874e240e50bfa9f397afc36d0bee Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 28 Sep 2010 16:34:21 -0700 Subject: removed extra code that slipped in from a test branch --- nova/tests/rpc_unittest.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/nova/tests/rpc_unittest.py b/nova/tests/rpc_unittest.py index f4d7b4b28..e12a28fbc 100644 --- a/nova/tests/rpc_unittest.py +++ b/nova/tests/rpc_unittest.py @@ -67,17 +67,6 @@ class RpcTestCase(test.BaseTestCase): except rpc.RemoteError as exc: self.assertEqual(int(exc.value), value) - def test_pass_object(self): - """Test that we can pass objects through rpc""" - class x(): - pass - obj = x() - x.foo = 'bar' - x.baz = 100 - - result = yield rpc.call('test', {"method": "echo", - "args": {"value": obj}}) - print result class TestReceiver(object): """Simple Proxy class so the consumer has methods to call -- cgit From 1e4bca12e7e06698d3a13d6a208be90647f27555 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 28 Sep 2010 17:15:59 -0700 Subject: typo s/boo/bool --- nova/network/linux_net.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 01a1d2ad0..6f1d594fa 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -41,8 +41,8 @@ flags.DEFINE_string('bridge_dev', 'eth0', 'network device for bridges') flags.DEFINE_string('routing_source_ip', utils.get_my_ip(), 'Public IP of network host') -flags.DEFINE_boo('use_nova_chains', False, - 'use the nova_ routing chains instead of default') +flags.DEFINE_bool('use_nova_chains', False, + 'use the nova_ routing chains instead of default') DEFAULT_PORTS = [("tcp", 80), ("tcp", 22), ("udp", 1194), ("tcp", 443)] -- cgit From 533f72379931aa7bf67a0e7d1d7664ca151afda0 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 28 Sep 2010 17:24:25 -0700 Subject: fix flag defaults --- nova/flags.py | 2 +- nova/network/linux_net.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/flags.py b/nova/flags.py index 5ed0f92ee..92f6766cf 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -190,7 +190,7 @@ DEFINE_string('rabbit_virtual_host', '/', 'rabbit virtual host') DEFINE_string('control_exchange', 'nova', 'the main exchange to connect to') DEFINE_string('cc_host', '127.0.0.1', 'ip of api server') DEFINE_integer('cc_port', 8773, 'cloud controller port') -DEFINE_string('ec2_url', 'http://%s:%s/services/Cloud' % (FLAGS.cc_host, FLAGS.cc_port), +DEFINE_string('ec2_url', 'http://127.0.0.1:8773/services/Cloud' 'Url to ec2 api server') DEFINE_string('default_image', 'ami-11111', diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 6f1d594fa..fa77c5ba8 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -39,7 +39,7 @@ flags.DEFINE_string('public_interface', 'vlan1', 'Interface for public IP addresses') flags.DEFINE_string('bridge_dev', 'eth0', 'network device for bridges') -flags.DEFINE_string('routing_source_ip', utils.get_my_ip(), +flags.DEFINE_string('routing_source_ip', '127.0.0.1', 'Public IP of network host') flags.DEFINE_bool('use_nova_chains', False, 'use the nova_ routing chains instead of default') -- cgit From 05e3e188e03624884ed019fe9cd8f216c9262f98 Mon Sep 17 00:00:00 2001 From: Michael Gundlach Date: Tue, 28 Sep 2010 20:36:50 -0400 Subject: Fault support --- nova/api/rackspace/__init__.py | 9 +++-- nova/api/rackspace/auth.py | 7 ++-- nova/api/rackspace/faults.py | 61 ++++++++++++++++++++++++++++++++++ nova/api/rackspace/flavors.py | 3 +- nova/api/rackspace/images.py | 7 ++-- nova/tests/api/rackspace/testfaults.py | 30 +++++++++++++++++ 6 files changed, 107 insertions(+), 10 deletions(-) create mode 100644 nova/api/rackspace/faults.py create mode 100644 nova/tests/api/rackspace/testfaults.py diff --git a/nova/api/rackspace/__init__.py b/nova/api/rackspace/__init__.py index c24d08585..447037020 100644 --- a/nova/api/rackspace/__init__.py +++ b/nova/api/rackspace/__init__.py @@ -31,6 +31,7 @@ import webob from nova import flags from nova import utils from nova import wsgi +from nova.api.rackspace import faults from nova.api.rackspace import flavors from nova.api.rackspace import images from nova.api.rackspace import ratelimiting @@ -66,7 +67,7 @@ class AuthMiddleware(wsgi.Middleware): user = self.auth_driver.authorize_token(req.headers["X-Auth-Token"]) if not user: - return webob.exc.HTTPUnauthorized() + return faults.Fault(webob.exc.HTTPUnauthorized()) context = {'user': user} req.environ['nova.context'] = context return self.application @@ -109,8 +110,10 @@ class RateLimitingMiddleware(wsgi.Middleware): delay = self.get_delay(action_name, username) if delay: # TODO(gundlach): Get the retry-after format correct. - raise webob.exc.HTTPRequestEntityTooLarge(headers={ - 'Retry-After': time.time() + delay}) + exc = webob.exc.HTTPRequestEntityTooLarge( + explanation='Too many requests.', + headers={'Retry-After': time.time() + delay}) + raise faults.Fault(exc) return self.application def get_delay(self, action_name, username): diff --git a/nova/api/rackspace/auth.py b/nova/api/rackspace/auth.py index ce5a967eb..519263367 100644 --- a/nova/api/rackspace/auth.py +++ b/nova/api/rackspace/auth.py @@ -9,6 +9,7 @@ from nova import auth from nova import manager from nova import db from nova import utils +from nova.api.rackspace import faults FLAGS = flags.FLAGS @@ -34,13 +35,13 @@ class BasicApiAuthManager(object): # honor it path_info = req.path_info if len(path_info) > 1: - return webob.exc.HTTPUnauthorized() + return faults.Fault(webob.exc.HTTPUnauthorized()) try: username, key = req.headers['X-Auth-User'], \ req.headers['X-Auth-Key'] except KeyError: - return webob.exc.HTTPUnauthorized() + return faults.Fault(webob.exc.HTTPUnauthorized()) username, key = req.headers['X-Auth-User'], req.headers['X-Auth-Key'] token, user = self._authorize_user(username, key) @@ -55,7 +56,7 @@ class BasicApiAuthManager(object): res.status = '204' return res else: - return webob.exc.HTTPUnauthorized() + return faults.Fault(webob.exc.HTTPUnauthorized()) def authorize_token(self, token_hash): """ retrieves user information from the datastore given a token diff --git a/nova/api/rackspace/faults.py b/nova/api/rackspace/faults.py new file mode 100644 index 000000000..fd6bc3623 --- /dev/null +++ b/nova/api/rackspace/faults.py @@ -0,0 +1,61 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 OpenStack LLC. +# 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. + + +import webob.dec + +from nova import wsgi + + +class Fault(wsgi.Application): + + """An RS API fault response.""" + + _fault_names = { + 400: "badRequest", + 401: "unauthorized", + 403: "resizeNotAllowed", + 404: "itemNotFound", + 405: "badMethod", + 409: "inProgress", + 413: "overLimit", + 415: "badMediaType", + 501: "notImplemented", + 503: "serviceUnavailable"} + + def __init__(self, exception): + """Create a Fault for the given webob.exc.exception.""" + self.exception = exception + + @webob.dec.wsgify + def __call__(self, req): + """Generate a WSGI response based on self.exception.""" + # Replace the body with fault details. + code = self.exception.status_int + fault_name = self._fault_names.get(code, "cloudServersFault") + fault_data = { + fault_name: { + 'code': code, + 'message': self.exception.explanation}} + if code == 413: + retry = self.exception.headers['Retry-After'] + fault_data[fault_name]['retryAfter'] = retry + # 'code' is an attribute on the fault tag itself + metadata = {'application/xml': {'attributes': {fault_name: 'code'}}} + serializer = wsgi.Serializer(req.environ, metadata) + self.exception.body = serializer.to_content_type(fault_data) + return self.exception diff --git a/nova/api/rackspace/flavors.py b/nova/api/rackspace/flavors.py index 60b35c939..6cc57be33 100644 --- a/nova/api/rackspace/flavors.py +++ b/nova/api/rackspace/flavors.py @@ -16,6 +16,7 @@ # under the License. from nova.api.rackspace import base +from nova.api.rackspace import faults from nova.compute import instance_types from webob import exc @@ -47,7 +48,7 @@ class Controller(base.Controller): item = dict(ram=val['memory_mb'], disk=val['local_gb'], id=val['flavorid'], name=name) return dict(flavor=item) - raise exc.HTTPNotFound() + raise faults.Fault(exc.HTTPNotFound()) def _all_ids(self): """Return the list of all flavorids.""" diff --git a/nova/api/rackspace/images.py b/nova/api/rackspace/images.py index 2f3e928b9..1c50d0bec 100644 --- a/nova/api/rackspace/images.py +++ b/nova/api/rackspace/images.py @@ -18,6 +18,7 @@ import nova.image.service from nova.api.rackspace import base from nova.api.rackspace import _id_translator +from nova.api.rackspace import faults from webob import exc class Controller(base.Controller): @@ -57,14 +58,14 @@ class Controller(base.Controller): def delete(self, req, id): # Only public images are supported for now. - raise exc.HTTPNotFound() + raise faults.Fault(exc.HTTPNotFound()) def create(self, req): # Only public images are supported for now, so a request to # make a backup of a server cannot be supproted. - raise exc.HTTPNotFound() + raise faults.Fault(exc.HTTPNotFound()) def update(self, req, id): # Users may not modify public images, and that's all that # we support for now. - raise exc.HTTPNotFound() + raise faults.Fault(exc.HTTPNotFound()) diff --git a/nova/tests/api/rackspace/testfaults.py b/nova/tests/api/rackspace/testfaults.py new file mode 100644 index 000000000..74caffd30 --- /dev/null +++ b/nova/tests/api/rackspace/testfaults.py @@ -0,0 +1,30 @@ +import unittest +import webob +import webob.exc + +from nova.api.rackspace import faults + +class TestFaults(unittest.TestCase): + + def test_fault_parts(self): + req = webob.Request.blank('/.xml') + f = faults.Fault(webob.exc.HTTPBadRequest(explanation='scram')) + resp = req.get_response(f) + + first_two_words = resp.body.strip().split()[:2] + self.assertEqual(first_two_words, ['']) + body_without_spaces = ''.join(resp.body.split()) + self.assertTrue('scram' in body_without_spaces) + + def test_retry_header(self): + req = webob.Request.blank('/.xml') + exc = webob.exc.HTTPRequestEntityTooLarge(explanation='sorry', + headers={'Retry-After': 4}) + f = faults.Fault(exc) + resp = req.get_response(f) + first_two_words = resp.body.strip().split()[:2] + self.assertEqual(first_two_words, ['']) + body_sans_spaces = ''.join(resp.body.split()) + self.assertTrue('sorry' in body_sans_spaces) + self.assertTrue('4' in body_sans_spaces) + self.assertEqual(resp.headers['Retry-After'], 4) -- cgit From d1c454ba4331794931e94cc2864f4e1a6ef5bf22 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 28 Sep 2010 17:41:57 -0700 Subject: improved commenting --- tools/setup_iptables.sh | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/tools/setup_iptables.sh b/tools/setup_iptables.sh index b6b8414e3..673353eb4 100755 --- a/tools/setup_iptables.sh +++ b/tools/setup_iptables.sh @@ -17,6 +17,13 @@ # License for the specific language governing permissions and limitations # under the License. +# NOTE(vish): This script sets up some reasonable defaults for iptables and +# creates nova-specific chains. If you use this script you should +# run nova-network and nova-compute with --use_nova_chains=True + +# NOTE(vish): If you run nova-api on a different port, make sure to change +# the port here +API_PORT=${API_PORT:-"8773"} if [ -n "$1" ]; then CMD=$1 else @@ -26,8 +33,9 @@ fi if [ -n "$2" ]; then IP=$2 else - # NOTE(vish): this will just get the first ip in the list, so if you - # have more than one eth device set up, this will fail + # NOTE(vish): This will just get the first ip in the list, so if you + # have more than one eth device set up, this will fail, and + # you should explicitly pass in the ip of the instance IP=`ifconfig | grep -m 1 'inet addr:'| cut -d: -f2 | awk '{print $1}'` fi @@ -39,6 +47,8 @@ fi if [ -n "$4" ]; then + # NOTE(vish): Management IP is the ip over which to allow ssh traffic. It + # will also allow traffic to nova-api MGMT_IP=$4 else MGMT_IP="$IP" @@ -78,7 +88,9 @@ if [ "$CMD" == "base" ] || [ "$CMD" == "all" ]; then iptables -N nova_forward iptables -A FORWARD -j nova_forward - # iptables -P OUTPUT DROP # too restrictive for the moment + # NOTE(vish): DROP on output is too restrictive for now. We need to add + # in a bunch of more specific output rules to use it. + # iptables -P OUTPUT DROP iptables -A OUTPUT -m state --state INVALID -j DROP iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT iptables -N nova_output @@ -99,8 +111,9 @@ if [ "$CMD" == "ganglia" ] || [ "$CMD" == "all" ]; then iptables -A nova_input -m udp -p udp -d $IP --dport 8649 -j ACCEPT fi -if [ "$CMD" == "dashboard" ] || [ "$CMD" == "all" ]; then - # dashboard +if [ "$CMD" == "web" ] || [ "$CMD" == "all" ]; then + # NOTE(vish): This opens up ports for web access, allowing web-based + # dashboards to work. iptables -A nova_input -m tcp -p tcp -d $IP --dport 80 -j ACCEPT iptables -A nova_input -m tcp -p tcp -d $IP --dport 443 -j ACCEPT fi @@ -110,9 +123,9 @@ if [ "$CMD" == "objectstore" ] || [ "$CMD" == "all" ]; then fi if [ "$CMD" == "api" ] || [ "$CMD" == "all" ]; then - iptables -A nova_input -m tcp -p tcp -d $IP --dport 8773 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $IP --dport $API_PORT -j ACCEPT if [ "$IP" != "$MGMT_IP" ]; then - iptables -A nova_input -m tcp -p tcp -d $MGMT_IP --dport 8773 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $MGMT_IP --dport $API_PORT -j ACCEPT fi fi -- cgit From bc88c73a4e986289be7835b95ec97ffb7a50f7d7 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 28 Sep 2010 17:53:27 -0700 Subject: missed a comma --- nova/flags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/flags.py b/nova/flags.py index 92f6766cf..c32cdd7a4 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -190,7 +190,7 @@ DEFINE_string('rabbit_virtual_host', '/', 'rabbit virtual host') DEFINE_string('control_exchange', 'nova', 'the main exchange to connect to') DEFINE_string('cc_host', '127.0.0.1', 'ip of api server') DEFINE_integer('cc_port', 8773, 'cloud controller port') -DEFINE_string('ec2_url', 'http://127.0.0.1:8773/services/Cloud' +DEFINE_string('ec2_url', 'http://127.0.0.1:8773/services/Cloud', 'Url to ec2 api server') DEFINE_string('default_image', 'ami-11111', -- cgit From 4c1aa3d96f0c44d3e01864ca3128e9b052d1d7fd Mon Sep 17 00:00:00 2001 From: Michael Gundlach Date: Wed, 29 Sep 2010 10:17:10 -0400 Subject: After update from trunk, a few more exceptions that need to be converted to Faults --- nova/api/rackspace/backup_schedules.py | 7 ++++--- nova/api/rackspace/servers.py | 17 +++++++++-------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/nova/api/rackspace/backup_schedules.py b/nova/api/rackspace/backup_schedules.py index 46da778ee..cb83023bc 100644 --- a/nova/api/rackspace/backup_schedules.py +++ b/nova/api/rackspace/backup_schedules.py @@ -20,6 +20,7 @@ from webob import exc from nova import wsgi from nova.api.rackspace import _id_translator +from nova.api.rackspace import faults import nova.image.service class Controller(wsgi.Controller): @@ -27,12 +28,12 @@ class Controller(wsgi.Controller): pass def index(self, req, server_id): - return exc.HTTPNotFound() + return faults.Fault(exc.HTTPNotFound()) def create(self, req, server_id): """ No actual update method required, since the existing API allows both create and update through a POST """ - return exc.HTTPNotFound() + return faults.Fault(exc.HTTPNotFound()) def delete(self, req, server_id): - return exc.HTTPNotFound() + return faults.Fault(exc.HTTPNotFound()) diff --git a/nova/api/rackspace/servers.py b/nova/api/rackspace/servers.py index 4ab04bde7..888d67542 100644 --- a/nova/api/rackspace/servers.py +++ b/nova/api/rackspace/servers.py @@ -24,6 +24,7 @@ from nova import rpc from nova import utils from nova import wsgi from nova.api.rackspace import _id_translator +from nova.api.rackspace import faults from nova.compute import power_state import nova.image.service @@ -120,7 +121,7 @@ class Controller(wsgi.Controller): if inst: if inst.user_id == user_id: return _entity_detail(inst) - raise exc.HTTPNotFound() + raise faults.Fault(exc.HTTPNotFound()) def delete(self, req, id): """ Destroys a server """ @@ -128,13 +129,13 @@ class Controller(wsgi.Controller): instance = self.db_driver.instance_get(None, id) if instance and instance['user_id'] == user_id: self.db_driver.instance_destroy(None, id) - return exc.HTTPAccepted() - return exc.HTTPNotFound() + return faults.Fault(exc.HTTPAccepted()) + return faults.Fault(exc.HTTPNotFound()) def create(self, req): """ Creates a new server for a given user """ if not req.environ.has_key('inst_dict'): - return exc.HTTPUnprocessableEntity() + return faults.Fault(exc.HTTPUnprocessableEntity()) inst = self._build_server_instance(req) @@ -147,22 +148,22 @@ class Controller(wsgi.Controller): def update(self, req, id): """ Updates the server name or password """ if not req.environ.has_key('inst_dict'): - return exc.HTTPUnprocessableEntity() + return faults.Fault(exc.HTTPUnprocessableEntity()) instance = self.db_driver.instance_get(None, id) if not instance: - return exc.HTTPNotFound() + return faults.Fault(exc.HTTPNotFound()) attrs = req.environ['nova.context'].get('model_attributes', None) if attrs: self.db_driver.instance_update(None, id, _filter_params(attrs)) - return exc.HTTPNoContent() + return faults.Fault(exc.HTTPNoContent()) def action(self, req, id): """ multi-purpose method used to reboot, rebuild, and resize a server """ if not req.environ.has_key('inst_dict'): - return exc.HTTPUnprocessableEntity() + return faults.Fault(exc.HTTPUnprocessableEntity()) def _build_server_instance(self, req): """Build instance data structure and save it to the data store.""" -- cgit From 29eca7e7992fc5c073d70f7c8ca5e5bc03f62af7 Mon Sep 17 00:00:00 2001 From: Michael Gundlach Date: Wed, 29 Sep 2010 11:37:26 -0400 Subject: Limit entity lists by &offset and &limit --- nova/api/rackspace/__init__.py | 20 ++++++++++++++++++++ nova/api/rackspace/flavors.py | 5 ++++- nova/api/rackspace/images.py | 2 ++ nova/api/rackspace/servers.py | 18 ++++++++++++------ nova/tests/api/rackspace/__init__.py | 29 +++++++++++++++++++++++++++++ 5 files changed, 67 insertions(+), 7 deletions(-) diff --git a/nova/api/rackspace/__init__.py b/nova/api/rackspace/__init__.py index 98802663f..48104f6df 100644 --- a/nova/api/rackspace/__init__.py +++ b/nova/api/rackspace/__init__.py @@ -165,3 +165,23 @@ class APIRouter(wsgi.Router): controller=sharedipgroups.Controller()) super(APIRouter, self).__init__(mapper) + + +def limited(items, req): + """Return a slice of items according to requested offset and limit. + + items - a sliceable + req - wobob.Request possibly containing offset and limit GET variables. + offset is where to start in the list, and limit is the maximum number + of items to return. + + If limit is not specified, 0, or > 1000, defaults to 1000. + """ + offset = int(req.GET.get('offset', 0)) + limit = int(req.GET.get('limit', 0)) + if not limit: + limit = 1000 + limit = min(1000, limit) + range_end = offset + limit + return items[offset:range_end] + diff --git a/nova/api/rackspace/flavors.py b/nova/api/rackspace/flavors.py index 3bcf170e5..ba7aa937c 100644 --- a/nova/api/rackspace/flavors.py +++ b/nova/api/rackspace/flavors.py @@ -15,9 +15,11 @@ # License for the specific language governing permissions and limitations # under the License. +from webob import exc + from nova.compute import instance_types from nova import wsgi -from webob import exc +import nova.api.rackspace class Controller(wsgi.Controller): """Flavor controller for the Rackspace API.""" @@ -38,6 +40,7 @@ class Controller(wsgi.Controller): def detail(self, req): """Return all flavors in detail.""" items = [self.show(req, id)['flavor'] for id in self._all_ids()] + items = nova.api.rackspace.limited(items, req) return dict(flavors=items) def show(self, req, id): diff --git a/nova/api/rackspace/images.py b/nova/api/rackspace/images.py index 11b058dec..7da17e6a7 100644 --- a/nova/api/rackspace/images.py +++ b/nova/api/rackspace/images.py @@ -19,6 +19,7 @@ from webob import exc from nova import wsgi from nova.api.rackspace import _id_translator +import nova.api.rackspace import nova.image.service class Controller(wsgi.Controller): @@ -45,6 +46,7 @@ class Controller(wsgi.Controller): def detail(self, req): """Return all public images in detail.""" data = self._service.index() + data = nova.api.rackspace.limited(data, req) for img in data: img['id'] = self._id_translator.to_rs_id(img['id']) return dict(images=data) diff --git a/nova/api/rackspace/servers.py b/nova/api/rackspace/servers.py index 4ab04bde7..958fc86a3 100644 --- a/nova/api/rackspace/servers.py +++ b/nova/api/rackspace/servers.py @@ -25,6 +25,7 @@ from nova import utils from nova import wsgi from nova.api.rackspace import _id_translator from nova.compute import power_state +import nova.api.rackspace import nova.image.service FLAGS = flags.FLAGS @@ -101,16 +102,21 @@ class Controller(wsgi.Controller): def index(self, req): """ Returns a list of server names and ids for a given user """ - user_id = req.environ['nova.context']['user']['id'] - instance_list = self.db_driver.instance_get_all_by_user(None, user_id) - res = [_entity_inst(inst)['server'] for inst in instance_list] - return _entity_list(res) + return self._items(req, entity_maker=_entity_inst) def detail(self, req): """ Returns a list of server details for a given user """ + return self._items(req, entity_maker=_entity_detail) + + def _items(self, req, entity_maker): + """Returns a list of servers for a given user. + + entity_maker - either _entity_detail or _entity_inst + """ user_id = req.environ['nova.context']['user']['id'] - res = [_entity_detail(inst)['server'] for inst in - self.db_driver.instance_get_all_by_user(None, user_id)] + instance_list = self.db_driver.instance_get_all_by_user(None, user_id) + limited_list = nova.api.rackspace.limited(instance_list, req) + res = [entity_maker(inst)['server'] for inst in limited_list] return _entity_list(res) def show(self, req, id): diff --git a/nova/tests/api/rackspace/__init__.py b/nova/tests/api/rackspace/__init__.py index 622cb4335..bfd0f87a7 100644 --- a/nova/tests/api/rackspace/__init__.py +++ b/nova/tests/api/rackspace/__init__.py @@ -17,6 +17,7 @@ import unittest +from nova.api.rackspace import limited from nova.api.rackspace import RateLimitingMiddleware from nova.tests.api.test_helper import * from webob import Request @@ -77,3 +78,31 @@ class RateLimitingMiddlewareTest(unittest.TestCase): self.assertEqual(middleware.limiter.__class__.__name__, "Limiter") middleware = RateLimitingMiddleware(APIStub(), service_host='foobar') self.assertEqual(middleware.limiter.__class__.__name__, "WSGIAppProxy") + + +class LimiterTest(unittest.TestCase): + + def testLimiter(self): + items = range(2000) + req = Request.blank('/') + self.assertEqual(limited(items, req), items[ :1000]) + req = Request.blank('/?offset=0') + self.assertEqual(limited(items, req), items[ :1000]) + req = Request.blank('/?offset=3') + self.assertEqual(limited(items, req), items[3:1003]) + req = Request.blank('/?offset=2005') + self.assertEqual(limited(items, req), []) + req = Request.blank('/?limit=10') + self.assertEqual(limited(items, req), items[ :10]) + req = Request.blank('/?limit=0') + self.assertEqual(limited(items, req), items[ :1000]) + req = Request.blank('/?limit=3000') + self.assertEqual(limited(items, req), items[ :1000]) + req = Request.blank('/?offset=1&limit=3') + self.assertEqual(limited(items, req), items[1:4]) + req = Request.blank('/?offset=3&limit=0') + self.assertEqual(limited(items, req), items[3:1003]) + req = Request.blank('/?offset=3&limit=1500') + self.assertEqual(limited(items, req), items[3:1003]) + req = Request.blank('/?offset=3000&limit=10') + self.assertEqual(limited(items, req), []) -- cgit From 724c15b6583b47baabd2f01090580cb248ad1244 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Wed, 29 Sep 2010 14:12:02 -0400 Subject: Remove debuggish print statement. --- bin/nova-manage | 1 - 1 file changed, 1 deletion(-) diff --git a/bin/nova-manage b/bin/nova-manage index 7a9a4c3d1..bf3c67612 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -277,7 +277,6 @@ class UserCommands(object): is_admin = True else: is_admin = False - print "is_admin: %r" % is_admin self.manager.modify_user(name, access_key, secret_key, is_admin) class ProjectCommands(object): -- cgit From 2136f12d29cef9acc7dc6ee0a5901fa3878160f8 Mon Sep 17 00:00:00 2001 From: Michael Gundlach Date: Wed, 29 Sep 2010 15:09:39 -0400 Subject: Make Fault raiseable by inheriting from webob.exc.HTTPException. Change from using self.exception which is reserved by HTTPException to self.wrapped_exc. --- nova/api/rackspace/faults.py | 17 +++++++++-------- nova/tests/api/rackspace/testfaults.py | 10 ++++++++++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/nova/api/rackspace/faults.py b/nova/api/rackspace/faults.py index fd6bc3623..32e5c866f 100644 --- a/nova/api/rackspace/faults.py +++ b/nova/api/rackspace/faults.py @@ -17,11 +17,12 @@ import webob.dec +import webob.exc from nova import wsgi -class Fault(wsgi.Application): +class Fault(webob.exc.HTTPException): """An RS API fault response.""" @@ -39,23 +40,23 @@ class Fault(wsgi.Application): def __init__(self, exception): """Create a Fault for the given webob.exc.exception.""" - self.exception = exception + self.wrapped_exc = exception @webob.dec.wsgify def __call__(self, req): - """Generate a WSGI response based on self.exception.""" + """Generate a WSGI response based on the exception passed to ctor.""" # Replace the body with fault details. - code = self.exception.status_int + code = self.wrapped_exc.status_int fault_name = self._fault_names.get(code, "cloudServersFault") fault_data = { fault_name: { 'code': code, - 'message': self.exception.explanation}} + 'message': self.wrapped_exc.explanation}} if code == 413: - retry = self.exception.headers['Retry-After'] + retry = self.wrapped_exc.headers['Retry-After'] fault_data[fault_name]['retryAfter'] = retry # 'code' is an attribute on the fault tag itself metadata = {'application/xml': {'attributes': {fault_name: 'code'}}} serializer = wsgi.Serializer(req.environ, metadata) - self.exception.body = serializer.to_content_type(fault_data) - return self.exception + self.wrapped_exc.body = serializer.to_content_type(fault_data) + return self.wrapped_exc diff --git a/nova/tests/api/rackspace/testfaults.py b/nova/tests/api/rackspace/testfaults.py index 74caffd30..b2931bc98 100644 --- a/nova/tests/api/rackspace/testfaults.py +++ b/nova/tests/api/rackspace/testfaults.py @@ -1,5 +1,6 @@ import unittest import webob +import webob.dec import webob.exc from nova.api.rackspace import faults @@ -28,3 +29,12 @@ class TestFaults(unittest.TestCase): self.assertTrue('sorry' in body_sans_spaces) self.assertTrue('4' in body_sans_spaces) self.assertEqual(resp.headers['Retry-After'], 4) + + def test_raise(self): + @webob.dec.wsgify + def raiser(req): + raise faults.Fault(webob.exc.HTTPNotFound(explanation='whut?')) + req = webob.Request.blank('/.xml') + resp = req.get_response(raiser) + self.assertEqual(resp.status_int, 404) + self.assertTrue('whut?' in resp.body) -- cgit