From b760e7cf6c84233bba1bcf336f630cbbbe54f672 Mon Sep 17 00:00:00 2001 From: Ricardo Carrillo Cruz Date: Wed, 19 Jan 2011 21:15:23 +0100 Subject: Exception string lacking 'G' for gigabytes unit --- nova/volume/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/volume/api.py b/nova/volume/api.py index ce4831cc3..cab9e4b05 100644 --- a/nova/volume/api.py +++ b/nova/volume/api.py @@ -44,7 +44,7 @@ class API(base.Base): LOG.warn(_("Quota exceeeded for %s, tried to create %sG volume"), context.project_id, size) raise quota.QuotaError(_("Volume quota exceeded. You cannot " - "create a volume of size %s") % size) + "create a volume of size %sG") % size) options = { 'size': size, -- cgit From 1a44e3cd5e366b0862c505e8d93581184c3162f1 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 27 Jan 2011 13:48:19 -0800 Subject: remove all floating addresses on terminate instance --- nova/compute/manager.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'nova') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 0f9bf301f..84de590d7 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -231,22 +231,25 @@ class ComputeManager(manager.Manager): instance_ref = self.db.instance_get(context, instance_id) LOG.audit(_("Terminating instance %s"), instance_id, context=context) - if not FLAGS.stub_network: - address = self.db.instance_get_floating_address(context, - instance_ref['id']) - if address: - LOG.debug(_("Disassociating address %s"), address, + fixed_ip = instance_ref.get('fixed_ip', None) + if not FLAGS.stub_network and fixed_ip: + floating_ips = fixed_ip.get('floating_ips') or [] + for floating_ip in floating_ips: + address = floating_ip['address'] + LOG.debug("Disassociating address %s", address, context=context) # NOTE(vish): Right now we don't really care if the ip is # disassociated. We may need to worry about # checking this later. + network_topic = self.db.queue_get_for(context, + FLAGS.network_topic, + floating_ip['host']) rpc.cast(context, - self.get_network_topic(context), + network_topic, {"method": "disassociate_floating_ip", "args": {"floating_address": address}}) - address = self.db.instance_get_fixed_address(context, - instance_ref['id']) + address = fixed_ip['address'] if address: LOG.debug(_("Deallocating address %s"), address, context=context) @@ -256,7 +259,7 @@ class ComputeManager(manager.Manager): self.network_manager.deallocate_fixed_ip(context.elevated(), address) - volumes = instance_ref.get('volumes', []) or [] + volumes = instance_ref.get('volumes') or [] for volume in volumes: self.detach_volume(context, instance_id, volume['id']) if instance_ref['state'] == power_state.SHUTOFF: -- cgit From c5a691be561615073507b61dca5a9f8f768a48b1 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 27 Jan 2011 14:14:10 -0800 Subject: makes sure that : is in the availability zone before it attempts to use it to send instances to a particular host --- nova/scheduler/simple.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/scheduler/simple.py b/nova/scheduler/simple.py index baf4966d4..0191ceb3d 100644 --- a/nova/scheduler/simple.py +++ b/nova/scheduler/simple.py @@ -43,7 +43,9 @@ class SimpleScheduler(chance.ChanceScheduler): def schedule_run_instance(self, context, instance_id, *_args, **_kwargs): """Picks a host that is up and has the fewest running instances.""" instance_ref = db.instance_get(context, instance_id) - if instance_ref['availability_zone'] and context.is_admin: + if (instance_ref['availability_zone'] + and ':' in instance_ref['availability_zone'] + and context.is_admin): zone, _x, host = instance_ref['availability_zone'].partition(':') service = db.service_get_by_args(context.elevated(), host, 'nova-compute') @@ -75,7 +77,9 @@ class SimpleScheduler(chance.ChanceScheduler): def schedule_create_volume(self, context, volume_id, *_args, **_kwargs): """Picks a host that is up and has the fewest volumes.""" volume_ref = db.volume_get(context, volume_id) - if (':' in volume_ref['availability_zone']) and context.is_admin: + if (volume_ref['availability_zone'] + and ':' in volume_ref['availability_zone'] + and context.is_admin): zone, _x, host = volume_ref['availability_zone'].partition(':') service = db.service_get_by_args(context.elevated(), host, 'nova-volume') -- cgit From 98cc358d4cc04b61fc19ce77f5db58cf88c6e908 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 27 Jan 2011 16:26:15 -0800 Subject: simplify get and remove extra reference to import logging. --- nova/compute/manager.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 84de590d7..f4418af26 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -37,7 +37,6 @@ terminating it. import datetime import random import string -import logging import socket import functools @@ -231,7 +230,7 @@ class ComputeManager(manager.Manager): instance_ref = self.db.instance_get(context, instance_id) LOG.audit(_("Terminating instance %s"), instance_id, context=context) - fixed_ip = instance_ref.get('fixed_ip', None) + fixed_ip = instance_ref.get('fixed_ip') if not FLAGS.stub_network and fixed_ip: floating_ips = fixed_ip.get('floating_ips') or [] for floating_ip in floating_ips: -- cgit From 7b34f59ef8d2f6a752dcd94be3f5d14f0f93d3b2 Mon Sep 17 00:00:00 2001 From: Anthony Young Date: Thu, 27 Jan 2011 22:51:36 -0800 Subject: fixes for bug #709057 --- nova/db/sqlalchemy/api.py | 6 +++++ nova/db/sqlalchemy/models.py | 6 ++++- nova/tests/test_compute.py | 58 ++++++++++++++++++++++++++++++++++++++------ 3 files changed, 61 insertions(+), 9 deletions(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 895e7eabe..50c4b2189 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -672,6 +672,9 @@ def instance_destroy(context, instance_id): with session.begin(): instance_ref = instance_get(context, instance_id, session=session) instance_ref.delete(session=session) + session.execute('update security_group_instance_association' + ' set deleted=1 where instance_id=:id', + {'id': instance_id}) @require_context @@ -1583,6 +1586,9 @@ def security_group_destroy(context, security_group_id): # TODO(vish): do we have to use sql here? session.execute('update security_groups set deleted=1 where id=:id', {'id': security_group_id}) + session.execute('update security_group_instance_association' + ' set deleted=1 where security_group_id=:id', + {'id': security_group_id}) session.execute('update security_group_rules set deleted=1 ' 'where group_id=:id', {'id': security_group_id}) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index c54ebe3ba..1399e29ad 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -311,10 +311,14 @@ class SecurityGroup(BASE, NovaBase): secondary="security_group_instance_association", primaryjoin='and_(' 'SecurityGroup.id == ' - 'SecurityGroupInstanceAssociation.security_group_id,' + 'SecurityGroupInstanceAssociation.security_group_id,' + 'SecurityGroupInstanceAssociation.deleted == False,' 'SecurityGroup.deleted == False)', secondaryjoin='and_(' 'SecurityGroupInstanceAssociation.instance_id == Instance.id,' + # (anthony) the condition below shouldn't be necessary now that the + # association is being marked as deleted. However, removing this + # may cause existing deployments to choke, so I'm leaving it 'Instance.deleted == False)', backref='security_groups') diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 09f6ee94a..ad2ac8375 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -49,7 +49,7 @@ class ComputeTestCase(test.TestCase): self.manager = manager.AuthManager() self.user = self.manager.create_user('fake', 'fake', 'fake') self.project = self.manager.create_project('fake', 'fake', 'fake') - self.context = context.get_admin_context() + self.context = context.RequestContext('fake', 'fake', False) def tearDown(self): self.manager.delete_user(self.user) @@ -69,6 +69,13 @@ class ComputeTestCase(test.TestCase): inst['ami_launch_index'] = 0 return db.instance_create(self.context, inst)['id'] + def _create_group(self): + values = {'name': 'testgroup', + 'description': 'testgroup', + 'user_id': self.user.id, + 'project_id': self.project.id} + return db.security_group_create(self.context, values) + def test_create_instance_defaults_display_name(self): """Verify that an instance cannot be created without a display_name.""" cases = [dict(), dict(display_name=None)] @@ -82,23 +89,58 @@ class ComputeTestCase(test.TestCase): def test_create_instance_associates_security_groups(self): """Make sure create associates security groups""" - values = {'name': 'default', - 'description': 'default', - 'user_id': self.user.id, - 'project_id': self.project.id} - group = db.security_group_create(self.context, values) + group = self._create_group() ref = self.compute_api.create( self.context, instance_type=FLAGS.default_instance_type, image_id=None, - security_group=['default']) + security_group=['testgroup']) try: self.assertEqual(len(db.security_group_get_by_instance( - self.context, ref[0]['id'])), 1) + self.context, ref[0]['id'])), 1) + group = db.security_group_get(self.context, group['id']) + self.assert_(len(group.instances) == 1) finally: db.security_group_destroy(self.context, group['id']) db.instance_destroy(self.context, ref[0]['id']) + def test_destroy_instance_disassociates_security_groups(self): + """Make sure destroying disassociates security groups""" + group = self._create_group() + + ref = self.compute_api.create( + self.context, + instance_type=FLAGS.default_instance_type, + image_id=None, + security_group=['testgroup']) + try: + db.instance_destroy(self.context, ref[0]['id']) + instance = db.instance_get(context.get_admin_context( + read_deleted=True), ref[0]['id']) + + group = db.security_group_get(self.context, group['id']) + self.assert_(len(group.instances) == 0) + finally: + db.security_group_destroy(self.context, group['id']) + + def test_destroy_security_group_disassociates_instances(self): + """Make sure destroying security groups disassociates instances""" + group = self._create_group() + + ref = self.compute_api.create( + self.context, + instance_type=FLAGS.default_instance_type, + image_id=None, + security_group=['testgroup']) + + try: + db.security_group_destroy(self.context, group['id']) + group = db.security_group_get(context.get_admin_context( + read_deleted=True), group['id']) + self.assert_(len(group.instances) == 0) + finally: + db.instance_destroy(self.context, ref[0]['id']) + def test_run_terminate(self): """Make sure it is possible to run and terminate instance""" instance_id = self._create_instance() -- cgit From af343a09b66ecded610051a443cb24f6b63e48ec Mon Sep 17 00:00:00 2001 From: Ilya Alekseyev Date: Fri, 28 Jan 2011 20:10:03 +0300 Subject: Changed default handler for uncaughted exceptions. Logging with level critical instead of print to stderr --- nova/log.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'nova') diff --git a/nova/log.py b/nova/log.py index e1c9f46f4..70719e95b 100644 --- a/nova/log.py +++ b/nova/log.py @@ -31,6 +31,7 @@ import cStringIO import json import logging import logging.handlers +import sys import traceback from nova import flags @@ -191,6 +192,10 @@ class NovaLogger(logging.Logger): kwargs.pop('exc_info') self.error(message, **kwargs) +def handle_exception(type, value, tb): + logging.root.critical(str(value), exc_info=(type, value, tb)) + +sys.excepthook = handle_exception logging.setLoggerClass(NovaLogger) -- cgit From 5850b1505cbd9e63418d9edaf003d3bd426279a2 Mon Sep 17 00:00:00 2001 From: Ilya Alekseyev Date: Fri, 28 Jan 2011 20:46:46 +0300 Subject: Fixed pep8 errors --- nova/log.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'nova') diff --git a/nova/log.py b/nova/log.py index 70719e95b..b541488bd 100644 --- a/nova/log.py +++ b/nova/log.py @@ -192,9 +192,11 @@ class NovaLogger(logging.Logger): kwargs.pop('exc_info') self.error(message, **kwargs) + def handle_exception(type, value, tb): logging.root.critical(str(value), exc_info=(type, value, tb)) + sys.excepthook = handle_exception logging.setLoggerClass(NovaLogger) -- cgit From 29931605602e0eba562f870cd14cb6f16d3a215d Mon Sep 17 00:00:00 2001 From: Anthony Young Date: Fri, 28 Jan 2011 09:55:58 -0800 Subject: remove extraneous line --- nova/tests/test_compute.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index ad2ac8375..2aa0690e7 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -115,9 +115,6 @@ class ComputeTestCase(test.TestCase): security_group=['testgroup']) try: db.instance_destroy(self.context, ref[0]['id']) - instance = db.instance_get(context.get_admin_context( - read_deleted=True), ref[0]['id']) - group = db.security_group_get(self.context, group['id']) self.assert_(len(group.instances) == 0) finally: -- cgit From 3f3f169d6b9cdf0b0d4dca308dbded38bf9a87b9 Mon Sep 17 00:00:00 2001 From: Devin Carlen Date: Fri, 28 Jan 2011 12:32:21 -0800 Subject: Made adminclient get_user return None instead of throwing EC2Exception if requested user not available --- nova/adminclient.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'nova') diff --git a/nova/adminclient.py b/nova/adminclient.py index 3cdd8347f..6b7549d26 100644 --- a/nova/adminclient.py +++ b/nova/adminclient.py @@ -21,6 +21,7 @@ Nova User API client library. import base64 import boto +import boto.exception import httplib from boto.ec2.regioninfo import RegionInfo @@ -287,11 +288,13 @@ class NovaAdminClient(object): return self.apiconn.get_list('DescribeUsers', {}, [('item', UserInfo)]) def get_user(self, name): - """Grab a single user by name.""" - user = self.apiconn.get_object('DescribeUser', {'Name': name}, - UserInfo) - if user.username != None: - return user + """ grab a single user by name """ + try: + return self.apiconn.get_object('DescribeUser', {'Name': name}, UserInfo) + except boto.exception.BotoServerError, e: + if e.status == 400 and e.error_code == 'NotFound': + return None + raise def has_user(self, username): """Determine if user exists.""" -- cgit From a1a1ee16992c0292de18828cd9bfc93d9bc6c1cc Mon Sep 17 00:00:00 2001 From: Devin Carlen Date: Fri, 28 Jan 2011 12:37:19 -0800 Subject: Fixed whitespace --- nova/adminclient.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/adminclient.py b/nova/adminclient.py index 6b7549d26..dc949dd95 100644 --- a/nova/adminclient.py +++ b/nova/adminclient.py @@ -288,7 +288,7 @@ class NovaAdminClient(object): return self.apiconn.get_list('DescribeUsers', {}, [('item', UserInfo)]) def get_user(self, name): - """ grab a single user by name """ + """Grab a single user by name.""" try: return self.apiconn.get_object('DescribeUser', {'Name': name}, UserInfo) except boto.exception.BotoServerError, e: -- cgit From 5becf2ad3dbbb7074202406fdd6f7f05dfef53cc Mon Sep 17 00:00:00 2001 From: Anthony Young Date: Fri, 28 Jan 2011 15:31:23 -0800 Subject: incorporate feedback from devin - use sql consistently in instance_destroy also, set deleted_at --- nova/db/sqlalchemy/api.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 50c4b2189..85250d56e 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -19,6 +19,7 @@ Implementation of SQLAlchemy backend. """ +import datetime import warnings from nova import db @@ -670,11 +671,14 @@ def instance_data_get_for_project(context, project_id): def instance_destroy(context, instance_id): session = get_session() with session.begin(): - instance_ref = instance_get(context, instance_id, session=session) - instance_ref.delete(session=session) - session.execute('update security_group_instance_association' - ' set deleted=1 where instance_id=:id', - {'id': instance_id}) + session.execute('update instances set deleted=1,' + 'deleted_at=:at where id=:id', + {'id': instance_id, + 'at': datetime.datetime.utcnow()}) + session.execute('update security_group_instance_association ' + 'set deleted=1,deleted_at=:at where instance_id=:id', + {'id': instance_id, + 'at': datetime.datetime.utcnow()}) @require_context @@ -1586,9 +1590,11 @@ def security_group_destroy(context, security_group_id): # TODO(vish): do we have to use sql here? session.execute('update security_groups set deleted=1 where id=:id', {'id': security_group_id}) - session.execute('update security_group_instance_association' - ' set deleted=1 where security_group_id=:id', - {'id': security_group_id}) + session.execute('update security_group_instance_association ' + 'set deleted=1,deleted_at=:at ' + 'where security_group_id=:id', + {'id': security_group_id, + 'at': datetime.datetime.utcnow()}) session.execute('update security_group_rules set deleted=1 ' 'where group_id=:id', {'id': security_group_id}) -- cgit From 54779a3db8af199f4b72043aa7c1bed208fefd88 Mon Sep 17 00:00:00 2001 From: Devin Carlen Date: Fri, 28 Jan 2011 22:40:41 -0800 Subject: Added modify project to ec2 admin api --- nova/adminclient.py | 7 +++++++ nova/api/ec2/admin.py | 11 +++++++++++ 2 files changed, 18 insertions(+) (limited to 'nova') diff --git a/nova/adminclient.py b/nova/adminclient.py index 3cdd8347f..64ebdb4d9 100644 --- a/nova/adminclient.py +++ b/nova/adminclient.py @@ -376,6 +376,13 @@ class NovaAdminClient(object): 'MemberUsers': member_users} return self.apiconn.get_object('RegisterProject', params, ProjectInfo) + def modify_project(self, projectname, manager_user=None, description=None): + """Modifies an existing project.""" + params = {'Name': projectname, + 'ManagerUser': manager_user, + 'Description': description} + return self.apiconn.get_status('ModifyProject', params) + def delete_project(self, projectname): """Permanently deletes the specified project.""" return self.apiconn.get_object('DeregisterProject', diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index d7e899d12..735951082 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -184,6 +184,17 @@ class AdminController(object): description=None, member_users=None)) + def modify_project(self, context, name, manager_user, description=None, + **kwargs): + """Modifies a project""" + msg = _("Modify project: %(name)s managed by" + " %(manager_user)s") % locals() + LOG.audit(msg, context=context) + manager.AuthManager().modify_project(name, + manager_user=manager_user, + description=description) + return True + def deregister_project(self, context, name): """Permanently deletes a project.""" LOG.audit(_("Delete project: %s"), name, context=context) -- cgit From 6ed93f6116ed092e64ceef9a255b46167099bfc3 Mon Sep 17 00:00:00 2001 From: Devin Carlen Date: Sun, 30 Jan 2011 15:45:17 -0800 Subject: pep8 --- nova/adminclient.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/adminclient.py b/nova/adminclient.py index dc949dd95..4106f0f47 100644 --- a/nova/adminclient.py +++ b/nova/adminclient.py @@ -290,7 +290,9 @@ class NovaAdminClient(object): def get_user(self, name): """Grab a single user by name.""" try: - return self.apiconn.get_object('DescribeUser', {'Name': name}, UserInfo) + return self.apiconn.get_object('DescribeUser', + {'Name': name}, + UserInfo) except boto.exception.BotoServerError, e: if e.status == 400 and e.error_code == 'NotFound': return None -- cgit From 07698fa5826ad65553d0c86594098ad5b980dc8a Mon Sep 17 00:00:00 2001 From: termie Date: Sun, 30 Jan 2011 17:01:49 -0800 Subject: fix austin->bexar db migration --- .../sqlalchemy/migrate_repo/versions/001_austin.py | 24 +++++---------------- .../sqlalchemy/migrate_repo/versions/002_bexar.py | 24 ++++++++++++++++++++- nova/db/sqlalchemy/migration.py | 11 ++++++---- nova/tests/db/nova.austin.sqlite | Bin 0 -> 44032 bytes 4 files changed, 35 insertions(+), 24 deletions(-) create mode 100644 nova/tests/db/nova.austin.sqlite (limited to 'nova') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/001_austin.py b/nova/db/sqlalchemy/migrate_repo/versions/001_austin.py index a312a7190..366944591 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/001_austin.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/001_austin.py @@ -134,6 +134,9 @@ instances = Table('instances', meta, Column('ramdisk_id', String(length=255, convert_unicode=False, assert_unicode=None, unicode_error=None, _warn_on_bytestring=False)), + Column('server_name', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), Column('launch_index', Integer()), Column('key_name', String(length=255, convert_unicode=False, assert_unicode=None, @@ -178,23 +181,6 @@ instances = Table('instances', meta, ) -iscsi_targets = Table('iscsi_targets', meta, - Column('created_at', DateTime(timezone=False)), - Column('updated_at', DateTime(timezone=False)), - Column('deleted_at', DateTime(timezone=False)), - Column('deleted', Boolean(create_constraint=True, name=None)), - Column('id', Integer(), primary_key=True, nullable=False), - Column('target_num', Integer()), - Column('host', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)), - Column('volume_id', - Integer(), - ForeignKey('volumes.id'), - nullable=True), - ) - - key_pairs = Table('key_pairs', meta, Column('created_at', DateTime(timezone=False)), Column('updated_at', DateTime(timezone=False)), @@ -523,7 +509,7 @@ def upgrade(migrate_engine): meta.bind = migrate_engine for table in (auth_tokens, export_devices, fixed_ips, floating_ips, - instances, iscsi_targets, key_pairs, networks, + instances, key_pairs, networks, projects, quotas, security_groups, security_group_inst_assoc, security_group_rules, services, users, user_project_association, user_project_role_association, @@ -539,7 +525,7 @@ def upgrade(migrate_engine): def downgrade(migrate_engine): # Operations to reverse the above upgrade go here. for table in (auth_tokens, export_devices, fixed_ips, floating_ips, - instances, iscsi_targets, key_pairs, networks, + instances, key_pairs, networks, projects, quotas, security_groups, security_group_inst_assoc, security_group_rules, services, users, user_project_association, user_project_role_association, diff --git a/nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py b/nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py index bd3a3e6f8..699b837f8 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py @@ -41,6 +41,10 @@ networks = Table('networks', meta, Column('id', Integer(), primary_key=True, nullable=False), ) +volumes = Table('volumes', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + # # New Tables @@ -131,6 +135,23 @@ instance_actions = Table('instance_actions', meta, ) +iscsi_targets = Table('iscsi_targets', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('id', Integer(), primary_key=True, nullable=False), + Column('target_num', Integer()), + Column('host', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('volume_id', + Integer(), + ForeignKey('volumes.id'), + nullable=True), + ) + + # # Tables to alter # @@ -188,7 +209,8 @@ def upgrade(migrate_engine): # Upgrade operations go here. Don't create your own engine; # bind migrate_engine to your metadata meta.bind = migrate_engine - for table in (certificates, consoles, console_pools, instance_actions): + for table in (certificates, consoles, console_pools, instance_actions, + iscsi_targets): try: table.create() except Exception: diff --git a/nova/db/sqlalchemy/migration.py b/nova/db/sqlalchemy/migration.py index 33d14827b..2a13c5466 100644 --- a/nova/db/sqlalchemy/migration.py +++ b/nova/db/sqlalchemy/migration.py @@ -46,12 +46,15 @@ def db_version(): meta.reflect(bind=engine) try: for table in ('auth_tokens', 'export_devices', 'fixed_ips', - 'floating_ips', 'instances', 'iscsi_targets', + 'floating_ips', 'instances', 'key_pairs', 'networks', 'projects', 'quotas', - 'security_group_rules', - 'security_group_instance_association', 'services', + 'security_group_instance_association', + 'security_group_rules', 'security_groups', + 'services', 'users', 'user_project_association', - 'user_project_role_association', 'volumes'): + 'user_project_role_association', + 'user_role_association', + 'volumes'): assert table in meta.tables return db_version_control(1) except AssertionError: diff --git a/nova/tests/db/nova.austin.sqlite b/nova/tests/db/nova.austin.sqlite new file mode 100644 index 000000000..ad1326bce Binary files /dev/null and b/nova/tests/db/nova.austin.sqlite differ -- cgit From b1caafa03d0fb36c9df5502282c4267974d1b889 Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Mon, 31 Jan 2011 14:36:40 -0800 Subject: Fix for LP Bug #709510 --- nova/api/ec2/__init__.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index e25943a13..ddcdc673c 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -171,7 +171,7 @@ class Authenticate(wsgi.Middleware): req.path) # Be explicit for what exceptions are 403, the rest bubble as 500 except (exception.NotFound, exception.NotAuthorized) as ex: - LOG.audit(_("Authentication Failure: %s"), ex.args[0]) + LOG.audit(_("Authentication Failure: %s"), unicode(ex)) raise webob.exc.HTTPForbidden() # Authenticated! @@ -316,30 +316,31 @@ class Executor(wsgi.Application): try: result = api_request.invoke(context) except exception.InstanceNotFound as ex: - LOG.info(_('InstanceNotFound raised: %s'), ex.args[0], + LOG.info(_('InstanceNotFound raised: %s'), unicode(ex), context=context) ec2_id = cloud.id_to_ec2_id(ex.instance_id) message = _('Instance %s not found') % ec2_id return self._error(req, context, type(ex).__name__, message) except exception.VolumeNotFound as ex: - LOG.info(_('VolumeNotFound raised: %s'), ex.args[0], + LOG.info(_('VolumeNotFound raised: %s'), unicode(ex), context=context) ec2_id = cloud.id_to_ec2_id(ex.volume_id, 'vol-%08x') message = _('Volume %s not found') % ec2_id return self._error(req, context, type(ex).__name__, message) except exception.NotFound as ex: - LOG.info(_('NotFound raised: %s'), ex.args[0], context=context) - return self._error(req, context, type(ex).__name__, ex.args[0]) + LOG.info(_('NotFound raised: %s'), unicode(ex), context=context) + return self._error(req, context, type(ex).__name__, unicode(ex)) except exception.ApiError as ex: - LOG.exception(_('ApiError raised: %s'), ex.args[0], + LOG.exception(_('ApiError raised: %s'), unicode(ex), context=context) if ex.code: - return self._error(req, context, ex.code, ex.args[0]) + return self._error(req, context, ex.code, unicode(ex)) else: - return self._error(req, context, type(ex).__name__, ex.args[0]) + return self._error(req, context, type(ex).__name__, + unicode(ex)) except Exception as ex: extra = {'environment': req.environ} - LOG.exception(_('Unexpected error raised: %s'), ex.args[0], + LOG.exception(_('Unexpected error raised: %s'), unicode(ex), extra=extra, context=context) return self._error(req, context, -- cgit From 85fe11e11a6332f95a9dac300994e77e7e48dfec Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 31 Jan 2011 14:38:45 -0800 Subject: change ensure_bridge so it doesn't overwrite existing ips --- nova/network/linux_net.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'nova') diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index cdd1f666a..87eefa8b2 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -191,18 +191,19 @@ def ensure_bridge(bridge, interface, net_attrs=None): _execute("sudo brctl stp %s off" % bridge) if interface: _execute("sudo brctl addif %s %s" % (bridge, interface)) + _execute("sudo ifconfig %s up" % bridge) if net_attrs: - _execute("sudo ifconfig %s %s broadcast %s netmask %s up" % \ - (bridge, - net_attrs['gateway'], + # NOTE(vish): use ip addr add so it doesn't overwrite + # manual addresses on the bridge. + suffix = net_attrs['cidr'].rpartition('/')[0] + _execute("sudo ip addr add %s/%s brd %s dev %s" % + (net_attrs['gateway'], + suffix, net_attrs['broadcast'], - net_attrs['netmask'])) + bridge)) if(FLAGS.use_ipv6): _execute("sudo ip -f inet6 addr change %s dev %s" % (net_attrs['cidr_v6'], bridge)) - _execute("sudo ifconfig %s up" % bridge) - else: - _execute("sudo ifconfig %s up" % bridge) if FLAGS.use_nova_chains: (out, err) = _execute("sudo iptables -N nova_forward", check_exit_code=False) -- cgit From cf5e4de7019091ee931ea911d69732c25a2cc1dd Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Mon, 31 Jan 2011 14:43:03 -0800 Subject: Fix for LP Bug #709510 --- nova/api/openstack/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index c70bb39ed..056c7dd27 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -51,8 +51,8 @@ class FaultWrapper(wsgi.Middleware): try: return req.get_response(self.application) except Exception as ex: - LOG.exception(_("Caught error: %s"), str(ex)) - exc = webob.exc.HTTPInternalServerError(explanation=str(ex)) + LOG.exception(_("Caught error: %s"), unicode(ex)) + exc = webob.exc.HTTPInternalServerError(explanation=unicode(ex)) return faults.Fault(exc) -- cgit From e4356ceab8b2627dda0b02c7ebbba6d033129360 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 31 Jan 2011 14:43:33 -0800 Subject: rpartition sticks the rhs in [2] --- nova/network/linux_net.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 87eefa8b2..db3ecc7f9 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -195,7 +195,7 @@ def ensure_bridge(bridge, interface, net_attrs=None): if net_attrs: # NOTE(vish): use ip addr add so it doesn't overwrite # manual addresses on the bridge. - suffix = net_attrs['cidr'].rpartition('/')[0] + suffix = net_attrs['cidr'].rpartition('/')[2] _execute("sudo ip addr add %s/%s brd %s dev %s" % (net_attrs['gateway'], suffix, -- cgit From 199e511e17af5e1a0659cc9ca65e9d55a5296947 Mon Sep 17 00:00:00 2001 From: Anthony Young Date: Tue, 1 Feb 2011 08:09:34 -0800 Subject: fix pep8 error :/ --- nova/db/sqlalchemy/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 1399e29ad..7efb36c0e 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -316,7 +316,7 @@ class SecurityGroup(BASE, NovaBase): 'SecurityGroup.deleted == False)', secondaryjoin='and_(' 'SecurityGroupInstanceAssociation.instance_id == Instance.id,' - # (anthony) the condition below shouldn't be necessary now that the + # (anthony) the condition below shouldn't be necessary now that the # association is being marked as deleted. However, removing this # may cause existing deployments to choke, so I'm leaving it 'Instance.deleted == False)', -- cgit From 48a0e252be8b001705db98de8143e1c1ad6294ad Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 1 Feb 2011 17:55:14 -0800 Subject: better setup for flatdhcp --- nova/network/linux_net.py | 40 +++++++++++++++++++++++++++++++++------- nova/network/manager.py | 10 ++++++---- 2 files changed, 39 insertions(+), 11 deletions(-) (limited to 'nova') diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index db3ecc7f9..1a63b759f 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -20,6 +20,7 @@ Implements vlans, bridges, and iptables rules using linux utilities. import os from nova import db +from nova import exception from nova import flags from nova import log as logging from nova import utils @@ -164,7 +165,7 @@ def remove_floating_forward(floating_ip, fixed_ip): % (fixed_ip, floating_ip)) -def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None): +def ensure_vlan_bridge(vlan_num, bridge, net_attrs, set_ip=False): """Create a vlan and bridge unless they already exist""" interface = ensure_vlan(vlan_num) ensure_bridge(bridge, interface, net_attrs) @@ -181,7 +182,7 @@ def ensure_vlan(vlan_num): return interface -def ensure_bridge(bridge, interface, net_attrs=None): +def ensure_bridge(bridge, interface, net_attrs, set_ip=False): """Create a bridge unless it already exists""" if not _device_exists(bridge): LOG.debug(_("Starting Bridge interface for %s"), interface) @@ -189,12 +190,10 @@ def ensure_bridge(bridge, interface, net_attrs=None): _execute("sudo brctl setfd %s 0" % bridge) # _execute("sudo brctl setageing %s 10" % bridge) _execute("sudo brctl stp %s off" % bridge) - if interface: - _execute("sudo brctl addif %s %s" % (bridge, interface)) _execute("sudo ifconfig %s up" % bridge) - if net_attrs: - # NOTE(vish): use ip addr add so it doesn't overwrite - # manual addresses on the bridge. + if set_ip: + # NOTE(vish): The ip for dnsmasq has to be the first address on the + # bridge for it to respond to reqests properly suffix = net_attrs['cidr'].rpartition('/')[2] _execute("sudo ip addr add %s/%s brd %s dev %s" % (net_attrs['gateway'], @@ -204,6 +203,33 @@ def ensure_bridge(bridge, interface, net_attrs=None): if(FLAGS.use_ipv6): _execute("sudo ip -f inet6 addr change %s dev %s" % (net_attrs['cidr_v6'], bridge)) + else: + # NOTE(vish): if we don't give an ip to the bridge, we set up a route + # for the guests + out, err = _execute("sudo route add -net %s dev %s" % + (net_attrs['cidr'], bridge), + check_exit_code=False) + if err and err != "SIOCADDRT: File exists\n": + raise exception.Error("Failed to add route: %s" % err) + if interface: + # NOTE(vish): This will break if there is already an ip on the + # interface, so we move any ips to the bridge + out, err = _execute("sudo ip addr show dev %s scope global" % + interface) + for line in out.split("\n"): + fields = line.split() + if fields and fields[0] == "inet": + params = ' '.join(fields[1:-2]) + _execute("sudo ip addr del %s dev %s" % (params, fields[-1])) + _execute("sudo ip addr add %s dev %s" % (params, bridge)) + out, err = _execute("sudo brctl addif %s %s" % + (bridge, interface), + check_exit_code=False) + + if (err and err != "device %s is already a member of a bridge; can't " + "enslave it to bridge %s.\n" % (interface, bridge)): + raise exception.Error("Failed to add interface: %s" % err) + if FLAGS.use_nova_chains: (out, err) = _execute("sudo iptables -N nova_forward", check_exit_code=False) diff --git a/nova/network/manager.py b/nova/network/manager.py index fbcbea131..735327230 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -402,7 +402,8 @@ class FlatDHCPManager(FlatManager): """Sets up matching network for compute hosts.""" network_ref = db.network_get_by_instance(context, instance_id) self.driver.ensure_bridge(network_ref['bridge'], - FLAGS.flat_interface) + FLAGS.flat_interface, + network_ref) def allocate_fixed_ip(self, context, instance_id, *args, **kwargs): """Setup dhcp for this network.""" @@ -427,7 +428,7 @@ class FlatDHCPManager(FlatManager): network_ref = db.network_get(context, network_id) self.driver.ensure_bridge(network_ref['bridge'], FLAGS.flat_interface, - network_ref) + network_ref, True) if not FLAGS.fake_network: self.driver.update_dhcp(context, network_id) if(FLAGS.use_ipv6): @@ -498,7 +499,8 @@ class VlanManager(NetworkManager): """Sets up matching network for compute hosts.""" network_ref = db.network_get_by_instance(context, instance_id) self.driver.ensure_vlan_bridge(network_ref['vlan'], - network_ref['bridge']) + network_ref['bridge'], + network_ref) def create_networks(self, context, cidr, num_networks, network_size, cidr_v6, vlan_start, vpn_start): @@ -565,7 +567,7 @@ class VlanManager(NetworkManager): address = network_ref['vpn_public_address'] self.driver.ensure_vlan_bridge(network_ref['vlan'], network_ref['bridge'], - network_ref) + network_ref, True) # NOTE(vish): only ensure this forward if the address hasn't been set # manually. -- cgit From 08794dba04c3919f9abbbfea1615b651394e5ee8 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 1 Feb 2011 18:42:12 -0800 Subject: don't fail on ip add exists and recreate default route on ip move if needed --- nova/network/linux_net.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'nova') diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 1a63b759f..efeed5bc7 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -195,11 +195,14 @@ def ensure_bridge(bridge, interface, net_attrs, set_ip=False): # NOTE(vish): The ip for dnsmasq has to be the first address on the # bridge for it to respond to reqests properly suffix = net_attrs['cidr'].rpartition('/')[2] - _execute("sudo ip addr add %s/%s brd %s dev %s" % - (net_attrs['gateway'], - suffix, - net_attrs['broadcast'], - bridge)) + out, err = _execute("sudo ip addr add %s/%s brd %s dev %s" % + (net_attrs['gateway'], + suffix, + net_attrs['broadcast'], + bridge), + check_exit_code=False) + if err and err != "RTNETLINK answers: File exists\n": + raise exception.Error("Failed to add ip: %s" % err) if(FLAGS.use_ipv6): _execute("sudo ip -f inet6 addr change %s dev %s" % (net_attrs['cidr_v6'], bridge)) @@ -214,14 +217,22 @@ def ensure_bridge(bridge, interface, net_attrs, set_ip=False): if interface: # NOTE(vish): This will break if there is already an ip on the # interface, so we move any ips to the bridge + gateway = None + out, err = _execute("sudo route") + for line in out.split("\n"): + fields = line.split() + if fields and fields[0] == "default" and fields[-1] == interface: + gateway = fields[1] out, err = _execute("sudo ip addr show dev %s scope global" % interface) for line in out.split("\n"): fields = line.split() if fields and fields[0] == "inet": - params = ' '.join(fields[1:-2]) + params = ' '.join(fields[1:-1]) _execute("sudo ip addr del %s dev %s" % (params, fields[-1])) _execute("sudo ip addr add %s dev %s" % (params, bridge)) + if gateway: + _execute("sudo route add default gw %s" % gateway) out, err = _execute("sudo brctl addif %s %s" % (bridge, interface), check_exit_code=False) -- cgit From d647a067acb884ff2c324afa5a962a8dae71c6c6 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 1 Feb 2011 19:31:43 -0800 Subject: pass the set_ip from ensure_vlan_bridge --- nova/network/linux_net.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index efeed5bc7..c5cf761f2 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -168,7 +168,7 @@ def remove_floating_forward(floating_ip, fixed_ip): def ensure_vlan_bridge(vlan_num, bridge, net_attrs, set_ip=False): """Create a vlan and bridge unless they already exist""" interface = ensure_vlan(vlan_num) - ensure_bridge(bridge, interface, net_attrs) + ensure_bridge(bridge, interface, net_attrs, set_ip) def ensure_vlan(vlan_num): -- cgit From c98a298c3aefb1f465530537ee4773bd04673fe4 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Wed, 2 Feb 2011 11:16:00 +0100 Subject: Set FINAL = True in version.py. --- nova/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/version.py b/nova/version.py index 7b27acb6a..48ac84a03 100644 --- a/nova/version.py +++ b/nova/version.py @@ -24,7 +24,7 @@ except ImportError: NOVA_VERSION = ['2011', '1'] YEAR, COUNT = NOVA_VERSION -FINAL = False # This becomes true at Release Candidate time +FINAL = True # This becomes true at Release Candidate time def canonical_version_string(): -- cgit From 10657adef2028926319fa5c0402f434187213263 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Wed, 2 Feb 2011 11:23:08 +0100 Subject: Open Cactus development. --- nova/version.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/version.py b/nova/version.py index 48ac84a03..c3ecc2245 100644 --- a/nova/version.py +++ b/nova/version.py @@ -21,10 +21,10 @@ except ImportError: 'revision_id': 'LOCALREVISION', 'revno': 0} -NOVA_VERSION = ['2011', '1'] +NOVA_VERSION = ['2011', '2'] YEAR, COUNT = NOVA_VERSION -FINAL = True # This becomes true at Release Candidate time +FINAL = False # This becomes true at Release Candidate time def canonical_version_string(): -- cgit From ef9217548b1ae74d3e4f7282d26e0d9fee5470ce Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 2 Feb 2011 08:15:57 -0800 Subject: moves driver.init_host into the base class so it happens before floating forwards and sets up proper iptables chains --- nova/network/manager.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/network/manager.py b/nova/network/manager.py index fbcbea131..8eb9f041b 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -118,6 +118,10 @@ class NetworkManager(manager.Manager): super(NetworkManager, self).__init__(*args, **kwargs) def init_host(self): + """Do any initialization that needs to be run if this is a + standalone service. + """ + self.driver.init_host() # Set up networking for the projects for which we're already # the designated network host. ctxt = context.get_admin_context() @@ -395,7 +399,6 @@ class FlatDHCPManager(FlatManager): standalone service. """ super(FlatDHCPManager, self).init_host() - self.driver.init_host() self.driver.metadata_forward() def setup_compute_network(self, context, instance_id): @@ -465,7 +468,6 @@ class VlanManager(NetworkManager): standalone service. """ super(VlanManager, self).init_host() - self.driver.init_host() self.driver.metadata_forward() def allocate_fixed_ip(self, context, instance_id, *args, **kwargs): -- cgit From 321f581660aad3fc9da5f88276bfdf11f6960d97 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 3 Feb 2011 13:10:19 -0800 Subject: Don't need a route for guests. Turns out the issue with routing from the guests was due to duplicate macs --- nova/network/linux_net.py | 8 -------- 1 file changed, 8 deletions(-) (limited to 'nova') diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index c5cf761f2..3a8bd9435 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -206,14 +206,6 @@ def ensure_bridge(bridge, interface, net_attrs, set_ip=False): if(FLAGS.use_ipv6): _execute("sudo ip -f inet6 addr change %s dev %s" % (net_attrs['cidr_v6'], bridge)) - else: - # NOTE(vish): if we don't give an ip to the bridge, we set up a route - # for the guests - out, err = _execute("sudo route add -net %s dev %s" % - (net_attrs['cidr'], bridge), - check_exit_code=False) - if err and err != "SIOCADDRT: File exists\n": - raise exception.Error("Failed to add route: %s" % err) if interface: # NOTE(vish): This will break if there is already an ip on the # interface, so we move any ips to the bridge -- cgit From 62d2a1a101dc97e80cb0a582197a6b7468abf593 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Fri, 4 Feb 2011 11:04:55 -0600 Subject: Implementation of 'SAN' volumes A SAN volume is 'special' because the volume service probably won't run on the iSCSI target. Initial support is for Solaris with COMSTAR (Solaris 11) --- nova/volume/driver.py | 10 +- nova/volume/manager.py | 2 +- nova/volume/san.py | 373 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 379 insertions(+), 6 deletions(-) create mode 100644 nova/volume/san.py (limited to 'nova') diff --git a/nova/volume/driver.py b/nova/volume/driver.py index da7307733..82f4c2f54 100644 --- a/nova/volume/driver.py +++ b/nova/volume/driver.py @@ -294,8 +294,10 @@ class ISCSIDriver(VolumeDriver): self._execute("sudo ietadm --op delete --tid=%s" % iscsi_target) - def _get_name_and_portal(self, volume_name, host): + def _get_name_and_portal(self, volume): """Gets iscsi name and portal from volume name and host.""" + volume_name = volume['name'] + host = volume['host'] (out, _err) = self._execute("sudo iscsiadm -m discovery -t " "sendtargets -p %s" % host) for target in out.splitlines(): @@ -307,8 +309,7 @@ class ISCSIDriver(VolumeDriver): def discover_volume(self, volume): """Discover volume on a remote host.""" - iscsi_name, iscsi_portal = self._get_name_and_portal(volume['name'], - volume['host']) + iscsi_name, iscsi_portal = self._get_name_and_portal(volume) self._execute("sudo iscsiadm -m node -T %s -p %s --login" % (iscsi_name, iscsi_portal)) self._execute("sudo iscsiadm -m node -T %s -p %s --op update " @@ -319,8 +320,7 @@ class ISCSIDriver(VolumeDriver): def undiscover_volume(self, volume): """Undiscover volume on a remote host.""" - iscsi_name, iscsi_portal = self._get_name_and_portal(volume['name'], - volume['host']) + iscsi_name, iscsi_portal = self._get_name_and_portal(volume) self._execute("sudo iscsiadm -m node -T %s -p %s --op update " "-n node.startup -v manual" % (iscsi_name, iscsi_portal)) diff --git a/nova/volume/manager.py b/nova/volume/manager.py index 6f8e25e19..6e70ec881 100644 --- a/nova/volume/manager.py +++ b/nova/volume/manager.py @@ -87,7 +87,7 @@ class VolumeManager(manager.Manager): if volume['status'] in ['available', 'in-use']: self.driver.ensure_export(ctxt, volume) else: - LOG.info(_("volume %s: skipping export"), volume_ref['name']) + LOG.info(_("volume %s: skipping export"), volume['name']) def create_volume(self, context, volume_id): """Creates and exports the volume.""" diff --git a/nova/volume/san.py b/nova/volume/san.py new file mode 100644 index 000000000..22a0da13f --- /dev/null +++ b/nova/volume/san.py @@ -0,0 +1,373 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Justin Santa Barbara +# 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. +""" +Drivers for san-stored volumes. +The unique thing about a SAN is that we don't expect that we can run the volume + controller on the SAN hardware. We expect to access it over SSH or some API. +""" + +import os +import paramiko + +from nova import exception +from nova import flags +from nova import log as logging +from nova.volume.driver import ISCSIDriver + +LOG = logging.getLogger("nova.volume.driver") +FLAGS = flags.FLAGS +flags.DEFINE_boolean('san_thin_provision', 'true', + 'Use thin provisioning for SAN volumes?') +flags.DEFINE_string('san_ip', '', + 'IP address of SAN controller') +flags.DEFINE_string('san_login', 'admin', + 'Username for SAN controller') +flags.DEFINE_string('san_password', '', + 'Password for SAN controller') +flags.DEFINE_string('san_cluster_name', '', + 'Cluster name for creating SAN volumes') +flags.DEFINE_string('san_privatekey', '', + 'Filename of private key to use for SSH authentication') + +class SanISCSIDriver(ISCSIDriver): + """ Base class for SAN-style storage volumes + (storage providers we access over SSH)""" + #Override because SAN ip != host ip + def _get_name_and_portal(self, volume): + """Gets iscsi name and portal from volume name and host.""" + volume_name = volume['name'] + + # TODO(justinsb): store in volume, remerge with generic iSCSI code + host = FLAGS.san_ip + + (out, _err) = self._execute("sudo iscsiadm -m discovery -t " + "sendtargets -p %s" % host) + + location = None + find_iscsi_name = self._build_iscsi_target_name(volume) + for target in out.splitlines(): + if find_iscsi_name in target: + (location, _sep, iscsi_name) = target.partition(" ") + break + if not location: + raise exception.Error(_("Could not find iSCSI export " + " for volume %s") % + volume_name) + + iscsi_portal = location.split(",")[0] + LOG.debug("iscsi_name=%s, iscsi_portal=%s" % (iscsi_name, iscsi_portal)) + return (iscsi_name, iscsi_portal) + + def _build_iscsi_target_name(self, volume): + return "%s%s" % (FLAGS.iscsi_target_prefix, volume['name']) + + # discover_volume is still OK + # undiscover_volume is still OK + + def _connect_to_ssh(self): + ssh = paramiko.SSHClient() + #TODO(justinsb): We need a better SSH key policy + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + if FLAGS.san_password: + ssh.connect(FLAGS.san_ip, + username=FLAGS.san_login, + password=FLAGS.san_password) + elif FLAGS.san_privatekey: + privatekeyfile = os.path.expanduser(FLAGS.san_privatekey) + # It sucks that paramiko doesn't support DSA keys + privatekey = paramiko.RSAKey.from_private_key_file(privatekeyfile) + ssh.connect(FLAGS.san_ip, username=FLAGS.san_login, pkey=privatekey) + else: + raise exception.Error("Specify san_password or san_privatekey") + return ssh + + def _run_ssh(self, command, check_exit_code=True): + #TODO(justinsb): SSH connection caching (?) + ssh = self._connect_to_ssh() + + #TODO(justinsb): Reintroduce the retry hack + ret = ssh_execute(ssh, command, check_exit_code=check_exit_code) + + ssh.close() + + return ret + + def ensure_export(self, context, volume): + """Synchronously recreates an export for a logical volume.""" + pass + + def create_export(self, context, volume): + """Exports the volume.""" + pass + + def remove_export(self, context, volume): + """Removes an export for a logical volume.""" + pass + + def check_for_setup_error(self): + """Returns an error if prerequisites aren't met""" + if not (FLAGS.san_password or FLAGS.san_privatekey): + raise exception.Error("Specify san_password or san_privatekey") + + if not (FLAGS.san_ip): + raise exception.Error("san_ip must be set") + +def _collect_lines(data): + """ Split lines from data into an array, trimming them """ + matches = [] + for line in data.splitlines(): + match = line.strip() + matches.append(match) + + return matches + +def _get_prefixed_values(data, prefix): + """Collect lines which start with prefix; with trimming""" + matches = [] + for line in data.splitlines(): + line = line.strip() + if line.startswith(prefix): + match = line[len(prefix):] + match = match.strip() + matches.append(match) + + return matches + +class SolarisISCSIDriver(SanISCSIDriver): + """Executes commands relating to Solaris-hosted ISCSI volumes.""" + + #pkg install storage-server SUNWiscsit + #svcadm enable stmf + #zfs allow justinsb create,mount,destroy rpool + #usermod -P'File System Management' justinsb + # I don't know which perms are needed for sbdadm... + #usermod -P'Primary Administrator' justinsb + # svcadm enable -r svc:/network/iscsi/target:default + # pfexec itadm create-tpg e1000g0 10.1.184.98 + # pfexec itadm create-target -t e1000g0 + + def _view_exists(self, luid): + (out, _err) = self._run_ssh("pfexec /usr/sbin/stmfadm list-view -l %s" % + (luid), + check_exit_code=False) + if "no views found" in out: + return False + + if "View Entry:" in out: + return True + + raise exception.Error("Cannot parse list-view output: %s" % (out)) + + def _get_target_groups(self): + """Gets list of target groups from host.""" + (out, _err) = self._run_ssh("pfexec /usr/sbin/stmfadm list-tg") + matches = _get_prefixed_values(out, 'Target group: ') + LOG.debug("target_groups=%s" % matches) + return matches + + def _target_group_exists(self, target_group_name): + return target_group_name not in self._get_target_groups() + + def _get_target_group_members(self, target_group_name): + (out, _err) = self._run_ssh("pfexec /usr/sbin/stmfadm list-tg -v %s" % + (target_group_name)) + matches = _get_prefixed_values(out, 'Member: ') + LOG.debug("members of %s=%s" % (target_group_name, matches)) + return matches + + def _is_target_group_member(self, target_group_name, iscsi_target_name): + return iscsi_target_name in ( + self._get_target_group_members(target_group_name)) + + def _get_iscsi_targets(self): + cmd = ("pfexec /usr/sbin/itadm list-target | " + "awk '{print $1}' | grep -v ^TARGET") + (out, _err) = self._run_ssh(cmd) + matches = _collect_lines(out) + LOG.debug("_get_iscsi_targets=%s" % (matches)) + return matches + + def _iscsi_target_exists(self, iscsi_target_name): + return iscsi_target_name in self._get_iscsi_targets() + + def _build_zfs_poolname(self, volume): + #TODO(justinsb): rpool should be configurable + zfs_poolname = 'rpool/%s' % (volume['name']) + return zfs_poolname + + def create_volume(self, volume): + """Creates a volume.""" + if int(volume['size']) == 0: + sizestr = '100M' + else: + sizestr = '%sG' % volume['size'] + + zfs_poolname = self._build_zfs_poolname(volume) + + thin_provision_arg = '-s' if FLAGS.san_thin_provision else '' + # Create a zfs volume + self._run_ssh("pfexec /usr/sbin/zfs create %s -V %s %s" % + (thin_provision_arg, + sizestr, + zfs_poolname)) + + return { # 'target_ip': FLAGS.san_ip + } + + def _get_luid(self, volume): + zfs_poolname = self._build_zfs_poolname(volume) + + cmd = ("pfexec /usr/sbin/sbdadm list-lu | " + "grep -w %s | awk '{print $1}'" % + (zfs_poolname)) + + (stdout, _stderr) = self._run_ssh(cmd) + + luid = stdout.strip() + return luid + + def _is_lu_created(self, volume): + luid = self._get_luid(volume) + return luid + + def delete_volume(self, volume): + """Deletes a volume.""" + zfs_poolname = self._build_zfs_poolname(volume) + self._run_ssh("pfexec /usr/sbin/zfs destroy %s" % + (zfs_poolname)) + + def local_path(self, volume): + # TODO(justinsb): Is this needed here? + escaped_group = FLAGS.volume_group.replace('-', '--') + escaped_name = volume['name'].replace('-', '--') + return "/dev/mapper/%s-%s" % (escaped_group, escaped_name) + + def ensure_export(self, context, volume): + """Synchronously recreates an export for a logical volume.""" + #TODO(justinsb): On bootup, this is called for every volume. + # It then runs ~5 SSH commands for each volume, + # most of which fetch the same info each time + # This makes initial start stupid-slow + self._do_export(volume, force_create=False) + + def create_export(self, context, volume): + self._do_export(volume, force_create=True) + + def _do_export(self, volume, force_create): + # Create a Logical Unit (LU) backed by the zfs volume + zfs_poolname = self._build_zfs_poolname(volume) + + if force_create or not self._is_lu_created(volume): + cmd = ("pfexec /usr/sbin/sbdadm create-lu /dev/zvol/rdsk/%s" % + (zfs_poolname)) + self._run_ssh(cmd) + + luid = self._get_luid(volume) + iscsi_name = self._build_iscsi_target_name(volume) + target_group_name = 'tg-%s' % volume['name'] + + # The sequence of commands looks like this: + #pfexec stmfadm create-tg tg-vol2 + # Yes, we add it before we create it! + #pfexec stmfadm add-tg-member -g tg-vol2 iqn.2010-10.org.openstack:vol2 + #pfexec itadm create-target -n iqn.2010-10.org.openstack:vol2 + #pfexec stmfadm add-view -t tg-vol2 600144F051E2440000004D4A0F730004 + + # Create a iSCSI target, mapped to just this volume + if force_create or not self._target_group_exists(target_group_name): + self._run_ssh("pfexec /usr/sbin/stmfadm create-tg %s" % + (target_group_name)) + + # Yes, we add the initiatior before we create it! + if force_create or not self._is_target_group_member(target_group_name, + iscsi_name): + self._run_ssh("pfexec /usr/sbin/stmfadm add-tg-member -g %s %s" % + (target_group_name, iscsi_name)) + if force_create or not self._iscsi_target_exists(iscsi_name): + self._run_ssh("pfexec /usr/sbin/itadm create-target -n %s" % + (iscsi_name)) + if force_create or not self._view_exists(luid): + self._run_ssh("pfexec /usr/sbin/stmfadm add-view -t %s %s" % + (target_group_name, luid)) + + def remove_export(self, context, volume): + """Removes an export for a logical volume.""" + + # This is the reverse of _do_export + luid = self._get_luid(volume) + iscsi_name = self._build_iscsi_target_name(volume) + target_group_name = 'tg-%s' % volume['name'] + + if self._view_exists(luid): + self._run_ssh("pfexec /usr/sbin/stmfadm remove-view -l %s -a" % + (luid)) + + if self._iscsi_target_exists(iscsi_name): + self._run_ssh("pfexec /usr/sbin/stmfadm offline-target %s" % + (iscsi_name)) + self._run_ssh("pfexec /usr/sbin/itadm delete-target %s" % + (iscsi_name)) + + # We don't delete the tg-member; we delete the whole tg! + + if self._target_group_exists(target_group_name): + self._run_ssh("pfexec /usr/sbin/stmfadm delete-tg %s" % + (target_group_name)) + + if self._is_lu_created(volume): + self._run_ssh("pfexec /usr/sbin/sbdadm delete-lu %s" % + (luid)) + + +def ssh_execute(ssh, cmd, process_input=None, + addl_env=None, check_exit_code=True): + LOG.debug(_("Running cmd (subprocess): %s"), cmd) + if addl_env: + raise exception.Error("Environment not supported over SSH") + + if process_input: + # This is (probably) fixable if we need it... + raise exception.Error("process_input not supported over SSH") + + stdin_stream, stdout_stream, stderr_stream = ssh.exec_command(cmd) + channel = stdout_stream.channel + + #stdin.write('process_input would go here') + #stdin.flush() + + # I'm suspicious of this... + # ...other SSH clients have buffering issues with this approach + stdout = stdout_stream.read() + stderr = stderr_stream.read() + stdin_stream.close() + + exit_status = channel.recv_exit_status() + + # exit_status == -1 if no exit code was returned + if exit_status != -1: + LOG.debug(_("Result was %s") % exit_status) + if check_exit_code and exit_status != 0: + raise exception.ProcessExecutionError(exit_code=exit_status, + stdout=stdout, + stderr=stderr, + cmd=cmd) + + # Removed, although this is workaround is needed in utils.execute: + # (no reason to believe it's needed here!) + # greenthread.sleep(0) + + return (stdout, stderr) -- cgit From 5315d5068c6250ccf71c1aa67e7e0109a0718f2a Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Fri, 4 Feb 2011 13:07:17 -0600 Subject: Fixes for Vish & Devin's feedback --- nova/utils.py | 35 +++++++++++++++++++++++++ nova/volume/san.py | 77 +++++++++++++----------------------------------------- 2 files changed, 53 insertions(+), 59 deletions(-) (limited to 'nova') diff --git a/nova/utils.py b/nova/utils.py index 5f5225289..100e26f70 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -152,6 +152,41 @@ def execute(cmd, process_input=None, addl_env=None, check_exit_code=True): return result +def ssh_execute(ssh, cmd, process_input=None, + addl_env=None, check_exit_code=True): + LOG.debug(_("Running cmd (SSH): %s"), cmd) + if addl_env: + raise exception.Error("Environment not supported over SSH") + + if process_input: + # This is (probably) fixable if we need it... + raise exception.Error("process_input not supported over SSH") + + stdin_stream, stdout_stream, stderr_stream = ssh.exec_command(cmd) + channel = stdout_stream.channel + + #stdin.write('process_input would go here') + #stdin.flush() + + # NOTE(justinsb): This seems suspicious... + # ...other SSH clients have buffering issues with this approach + stdout = stdout_stream.read() + stderr = stderr_stream.read() + stdin_stream.close() + + exit_status = channel.recv_exit_status() + + # exit_status == -1 if no exit code was returned + if exit_status != -1: + LOG.debug(_("Result was %s") % exit_status) + if check_exit_code and exit_status != 0: + raise exception.ProcessExecutionError(exit_code=exit_status, + stdout=stdout, + stderr=stderr, + cmd=cmd) + + return (stdout, stderr) + def abspath(s): return os.path.join(os.path.dirname(__file__), s) diff --git a/nova/volume/san.py b/nova/volume/san.py index 22a0da13f..1f3417ae5 100644 --- a/nova/volume/san.py +++ b/nova/volume/san.py @@ -26,6 +26,7 @@ import paramiko from nova import exception from nova import flags from nova import log as logging +from nova.utils import ssh_execute from nova.volume.driver import ISCSIDriver LOG = logging.getLogger("nova.volume.driver") @@ -38,8 +39,6 @@ flags.DEFINE_string('san_login', 'admin', 'Username for SAN controller') flags.DEFINE_string('san_password', '', 'Password for SAN controller') -flags.DEFINE_string('san_cluster_name', '', - 'Cluster name for creating SAN volumes') flags.DEFINE_string('san_privatekey', '', 'Filename of private key to use for SSH authentication') @@ -148,17 +147,22 @@ def _get_prefixed_values(data, prefix): return matches class SolarisISCSIDriver(SanISCSIDriver): - """Executes commands relating to Solaris-hosted ISCSI volumes.""" - - #pkg install storage-server SUNWiscsit - #svcadm enable stmf - #zfs allow justinsb create,mount,destroy rpool - #usermod -P'File System Management' justinsb - # I don't know which perms are needed for sbdadm... - #usermod -P'Primary Administrator' justinsb - # svcadm enable -r svc:/network/iscsi/target:default - # pfexec itadm create-tpg e1000g0 10.1.184.98 - # pfexec itadm create-target -t e1000g0 + """Executes commands relating to Solaris-hosted ISCSI volumes. + Basic setup for a Solaris iSCSI server: + pkg install storage-server SUNWiscsit + svcadm enable stmf + svcadm enable -r svc:/network/iscsi/target:default + pfexec itadm create-tpg e1000g0 ${MYIP} + pfexec itadm create-target -t e1000g0 + + Then grant the user that will be logging on lots of permissions. + I'm not sure exactly which though: + zfs allow justinsb create,mount,destroy rpool + usermod -P'File System Management' justinsb + usermod -P'Primary Administrator' justinsb + + Also make sure you can login using san_login & san_password/san_privatekey + """ def _view_exists(self, luid): (out, _err) = self._run_ssh("pfexec /usr/sbin/stmfadm list-view -l %s" % @@ -280,19 +284,13 @@ class SolarisISCSIDriver(SanISCSIDriver): iscsi_name = self._build_iscsi_target_name(volume) target_group_name = 'tg-%s' % volume['name'] - # The sequence of commands looks like this: - #pfexec stmfadm create-tg tg-vol2 - # Yes, we add it before we create it! - #pfexec stmfadm add-tg-member -g tg-vol2 iqn.2010-10.org.openstack:vol2 - #pfexec itadm create-target -n iqn.2010-10.org.openstack:vol2 - #pfexec stmfadm add-view -t tg-vol2 600144F051E2440000004D4A0F730004 - # Create a iSCSI target, mapped to just this volume if force_create or not self._target_group_exists(target_group_name): self._run_ssh("pfexec /usr/sbin/stmfadm create-tg %s" % (target_group_name)) # Yes, we add the initiatior before we create it! + # Otherwise, it complains that the target is already active if force_create or not self._is_target_group_member(target_group_name, iscsi_name): self._run_ssh("pfexec /usr/sbin/stmfadm add-tg-member -g %s %s" % @@ -332,42 +330,3 @@ class SolarisISCSIDriver(SanISCSIDriver): self._run_ssh("pfexec /usr/sbin/sbdadm delete-lu %s" % (luid)) - -def ssh_execute(ssh, cmd, process_input=None, - addl_env=None, check_exit_code=True): - LOG.debug(_("Running cmd (subprocess): %s"), cmd) - if addl_env: - raise exception.Error("Environment not supported over SSH") - - if process_input: - # This is (probably) fixable if we need it... - raise exception.Error("process_input not supported over SSH") - - stdin_stream, stdout_stream, stderr_stream = ssh.exec_command(cmd) - channel = stdout_stream.channel - - #stdin.write('process_input would go here') - #stdin.flush() - - # I'm suspicious of this... - # ...other SSH clients have buffering issues with this approach - stdout = stdout_stream.read() - stderr = stderr_stream.read() - stdin_stream.close() - - exit_status = channel.recv_exit_status() - - # exit_status == -1 if no exit code was returned - if exit_status != -1: - LOG.debug(_("Result was %s") % exit_status) - if check_exit_code and exit_status != 0: - raise exception.ProcessExecutionError(exit_code=exit_status, - stdout=stdout, - stderr=stderr, - cmd=cmd) - - # Removed, although this is workaround is needed in utils.execute: - # (no reason to believe it's needed here!) - # greenthread.sleep(0) - - return (stdout, stderr) -- cgit From d86f1af6326d4276e9cbfb3274c211ff3f5629cb Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 4 Feb 2011 17:03:37 -0800 Subject: allow for bridge to be the public interface --- nova/network/linux_net.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'nova') diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 3a8bd9435..69389b333 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -206,6 +206,11 @@ def ensure_bridge(bridge, interface, net_attrs, set_ip=False): if(FLAGS.use_ipv6): _execute("sudo ip -f inet6 addr change %s dev %s" % (net_attrs['cidr_v6'], bridge)) + # NOTE(vish): If the public interface is the same as the + # bridge, then the bridge has to be in promiscuous + # to forward packets properly. + if(FLAGS.public_interface == bridge): + _execute("sudo ifconfig %s promisc" % bridge) if interface: # NOTE(vish): This will break if there is already an ip on the # interface, so we move any ips to the bridge -- cgit From 1833ed67ffde756ac1bf1aadbc164a22f7a9b005 Mon Sep 17 00:00:00 2001 From: Christian Berendt Date: Mon, 7 Feb 2011 10:51:43 +0100 Subject: applied http://launchpadlibrarian.net/63698868/713434.patch --- nova/network/linux_net.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index cdd1f666a..6ed42caae 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -37,6 +37,9 @@ FLAGS = flags.FLAGS flags.DEFINE_string('dhcpbridge_flagfile', '/etc/nova/nova-dhcpbridge.conf', 'location of flagfile for dhcpbridge') +flags.DEFINE_string('dhcp_domain', + 'novalocal', + 'domain to use for building the hostnames') flags.DEFINE_string('networks_path', '$state_path/networks', 'Location to keep network config files') @@ -313,8 +316,9 @@ interface %s def _host_dhcp(fixed_ip_ref): """Return a host string for an address""" instance_ref = fixed_ip_ref['instance'] - return "%s,%s.novalocal,%s" % (instance_ref['mac_address'], + return "%s,%s.%s,%s" % (instance_ref['mac_address'], instance_ref['hostname'], + FLAGS.dhcp_domain, fixed_ip_ref['address']) @@ -359,6 +363,7 @@ def _dnsmasq_cmd(net): ' --strict-order', ' --bind-interfaces', ' --conf-file=', + ' --domain=%s' % FLAGS.dhcp_domain, ' --pid-file=%s' % _dhcp_file(net['bridge'], 'pid'), ' --listen-address=%s' % net['gateway'], ' --except-interface=lo', -- cgit From e12069f79cbf35215eeba5257b2394e9ebde5855 Mon Sep 17 00:00:00 2001 From: Christian Berendt Date: Mon, 7 Feb 2011 14:29:11 +0100 Subject: replaced all calls to ifconfig with calls to ip --- nova/network/linux_net.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'nova') diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index cdd1f666a..6b8108b14 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -177,7 +177,7 @@ def ensure_vlan(vlan_num): LOG.debug(_("Starting VLAN inteface %s"), interface) _execute("sudo vconfig set_name_type VLAN_PLUS_VID_NO_PAD") _execute("sudo vconfig add %s %s" % (FLAGS.vlan_interface, vlan_num)) - _execute("sudo ifconfig %s up" % interface) + _execute("sudo ip link set %s up" % interface) return interface @@ -192,17 +192,17 @@ def ensure_bridge(bridge, interface, net_attrs=None): if interface: _execute("sudo brctl addif %s %s" % (bridge, interface)) if net_attrs: - _execute("sudo ifconfig %s %s broadcast %s netmask %s up" % \ - (bridge, - net_attrs['gateway'], - net_attrs['broadcast'], - net_attrs['netmask'])) + _execute("sudo ip addr add %s/%s dev %s broadcast %s" % \ + (net_attrs['gateway'], + net_attrs['netmask'], + bridge, + net_attrs['broadcast'])) if(FLAGS.use_ipv6): _execute("sudo ip -f inet6 addr change %s dev %s" % (net_attrs['cidr_v6'], bridge)) - _execute("sudo ifconfig %s up" % bridge) + _execute("sudo ip link set %s up" % bridge) else: - _execute("sudo ifconfig %s up" % bridge) + _execute("sudo ip link set %s up" % bridge) if FLAGS.use_nova_chains: (out, err) = _execute("sudo iptables -N nova_forward", check_exit_code=False) @@ -329,7 +329,8 @@ def _execute(cmd, *args, **kwargs): def _device_exists(device): """Check if ethernet device exists""" - (_out, err) = _execute("ifconfig %s" % device, check_exit_code=False) + (_out, err) = _execute("ip link show dev %s" % device, + check_exit_code=False) return not err -- cgit From e62665a12e0b02ef73562a5d579782972332cbe1 Mon Sep 17 00:00:00 2001 From: Christian Berendt Date: Mon, 7 Feb 2011 15:20:16 +0100 Subject: fixed format according to PEP8 --- nova/network/linux_net.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 6ed42caae..de0e488ae 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -318,7 +318,7 @@ def _host_dhcp(fixed_ip_ref): instance_ref = fixed_ip_ref['instance'] return "%s,%s.%s,%s" % (instance_ref['mac_address'], instance_ref['hostname'], - FLAGS.dhcp_domain, + FLAGS.dhcp_domain, fixed_ip_ref['address']) @@ -363,7 +363,7 @@ def _dnsmasq_cmd(net): ' --strict-order', ' --bind-interfaces', ' --conf-file=', - ' --domain=%s' % FLAGS.dhcp_domain, + ' --domain=%s' % FLAGS.dhcp_domain, ' --pid-file=%s' % _dhcp_file(net['bridge'], 'pid'), ' --listen-address=%s' % net['gateway'], ' --except-interface=lo', -- cgit From 15321719332a5b782ba5ac66d85db0eccc98ccba Mon Sep 17 00:00:00 2001 From: Ryan Lane Date: Mon, 7 Feb 2011 23:01:57 +0000 Subject: Checking whether the instance id is a list or not before assignment. This is to fix a bug relating to nova/boto. The AWK-SDK libraries pass in a string, not a list. the euca tools pass in a list. --- nova/api/ec2/cloud.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 00d044e95..c80e1168a 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -512,8 +512,11 @@ class CloudController(object): def get_console_output(self, context, instance_id, **kwargs): LOG.audit(_("Get console output for instance %s"), instance_id, context=context) - # instance_id is passed in as a list of instances - ec2_id = instance_id[0] + # instance_id may be passed in as a list of instances + if type(instance_id) == list: + ec2_id = instance_id[0] + else: + ec2_id = instance_id instance_id = ec2_id_to_id(ec2_id) output = self.compute_api.get_console_output( context, instance_id=instance_id) -- cgit From a51d187128def4f44ad06cd0880a3e3b4518e073 Mon Sep 17 00:00:00 2001 From: Ryan Lane Date: Tue, 8 Feb 2011 01:00:00 +0000 Subject: Catching all socket errors in _get_my_ip, since any socket error is likely enough to cause a failure in detection. --- nova/flags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/flags.py b/nova/flags.py index 43bc174d2..1d8eba94f 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -208,7 +208,7 @@ def _get_my_ip(): (addr, port) = csock.getsockname() csock.close() return addr - except socket.gaierror as ex: + except socket.error as ex: return "127.0.0.1" -- cgit From 976420e608140e449db5748e57cb18fab74b6d43 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 7 Feb 2011 22:05:45 -0800 Subject: use route -n instead of route to avoid chopped names --- nova/network/linux_net.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 69389b333..b66ca1973 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -215,10 +215,10 @@ def ensure_bridge(bridge, interface, net_attrs, set_ip=False): # NOTE(vish): This will break if there is already an ip on the # interface, so we move any ips to the bridge gateway = None - out, err = _execute("sudo route") + out, err = _execute("sudo route -n") for line in out.split("\n"): fields = line.split() - if fields and fields[0] == "default" and fields[-1] == interface: + if fields and fields[0] == "0.0.0.0" and fields[-1] == interface: gateway = fields[1] out, err = _execute("sudo ip addr show dev %s scope global" % interface) @@ -229,7 +229,7 @@ def ensure_bridge(bridge, interface, net_attrs, set_ip=False): _execute("sudo ip addr del %s dev %s" % (params, fields[-1])) _execute("sudo ip addr add %s dev %s" % (params, bridge)) if gateway: - _execute("sudo route add default gw %s" % gateway) + _execute("sudo route add 0.0.0.0 gw %s" % gateway) out, err = _execute("sudo brctl addif %s %s" % (bridge, interface), check_exit_code=False) -- cgit From 6f30cff7374e91c2920759e13971c0149d96d821 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 8 Feb 2011 10:54:29 -0800 Subject: add docstring and revert set_ip changes as they are unnecessary --- nova/network/linux_net.py | 21 ++++++++++++++++----- nova/network/manager.py | 10 ++++------ 2 files changed, 20 insertions(+), 11 deletions(-) (limited to 'nova') diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 37993d34a..df54606db 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -168,10 +168,10 @@ def remove_floating_forward(floating_ip, fixed_ip): % (fixed_ip, floating_ip)) -def ensure_vlan_bridge(vlan_num, bridge, net_attrs, set_ip=False): +def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None): """Create a vlan and bridge unless they already exist""" interface = ensure_vlan(vlan_num) - ensure_bridge(bridge, interface, net_attrs, set_ip) + ensure_bridge(bridge, interface, net_attrs) def ensure_vlan(vlan_num): @@ -185,8 +185,19 @@ def ensure_vlan(vlan_num): return interface -def ensure_bridge(bridge, interface, net_attrs, set_ip=False): - """Create a bridge unless it already exists""" +def ensure_bridge(bridge, interface, net_attrs=None): + """Create a bridge unless it already exists. + + :param interface: the interface to create the bridge on. + :param net_attrs: dictionary with attributes used to create the bridge. + + If net_attrs is set, it will add the net_attrs['gateway'] to the bridge + using net_attrs['broadcast'] and net_attrs['cidr']. It will also add + the ip_v6 address specified in net_attrs['cidr_v6'] if use_ipv6 is set. + + The code will attempt to move any ips that already exist on the interface + onto the bridge and reset the default gateway if necessary. + """ if not _device_exists(bridge): LOG.debug(_("Starting Bridge interface for %s"), interface) _execute("sudo brctl addbr %s" % bridge) @@ -194,7 +205,7 @@ def ensure_bridge(bridge, interface, net_attrs, set_ip=False): # _execute("sudo brctl setageing %s 10" % bridge) _execute("sudo brctl stp %s off" % bridge) _execute("sudo ip link set %s up" % bridge) - if set_ip: + if net_attrs: # NOTE(vish): The ip for dnsmasq has to be the first address on the # bridge for it to respond to reqests properly suffix = net_attrs['cidr'].rpartition('/')[2] diff --git a/nova/network/manager.py b/nova/network/manager.py index 735327230..fbcbea131 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -402,8 +402,7 @@ class FlatDHCPManager(FlatManager): """Sets up matching network for compute hosts.""" network_ref = db.network_get_by_instance(context, instance_id) self.driver.ensure_bridge(network_ref['bridge'], - FLAGS.flat_interface, - network_ref) + FLAGS.flat_interface) def allocate_fixed_ip(self, context, instance_id, *args, **kwargs): """Setup dhcp for this network.""" @@ -428,7 +427,7 @@ class FlatDHCPManager(FlatManager): network_ref = db.network_get(context, network_id) self.driver.ensure_bridge(network_ref['bridge'], FLAGS.flat_interface, - network_ref, True) + network_ref) if not FLAGS.fake_network: self.driver.update_dhcp(context, network_id) if(FLAGS.use_ipv6): @@ -499,8 +498,7 @@ class VlanManager(NetworkManager): """Sets up matching network for compute hosts.""" network_ref = db.network_get_by_instance(context, instance_id) self.driver.ensure_vlan_bridge(network_ref['vlan'], - network_ref['bridge'], - network_ref) + network_ref['bridge']) def create_networks(self, context, cidr, num_networks, network_size, cidr_v6, vlan_start, vpn_start): @@ -567,7 +565,7 @@ class VlanManager(NetworkManager): address = network_ref['vpn_public_address'] self.driver.ensure_vlan_bridge(network_ref['vlan'], network_ref['bridge'], - network_ref, True) + network_ref) # NOTE(vish): only ensure this forward if the address hasn't been set # manually. -- cgit From d5501234cfc434fbb4b959b63822598a1a68b88b Mon Sep 17 00:00:00 2001 From: Bilal Akhtar Date: Wed, 9 Feb 2011 11:34:23 +0300 Subject: Give a better error message if the instance type specified is invalid. --- nova/compute/instance_types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 196d6a8df..510bfd99e 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -38,8 +38,8 @@ def get_by_type(instance_type): if instance_type is None: return FLAGS.default_instance_type if instance_type not in INSTANCE_TYPES: - raise exception.ApiError(_("Unknown instance type: %s"), - instance_type) + raise exception.ApiError(_("Unknown instance type: %s") % (instance_type), + "Invalid") return instance_type -- cgit From fef2b4edaaf77109457dc2bf80e7f845a65a129e Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Wed, 9 Feb 2011 13:22:10 +0100 Subject: Don't convert datetime objects to a string using .isoformat(). Leave it to sqlalchmeny (or pysqlite or whatever it is that does the magic) to work it out. --- nova/db/sqlalchemy/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 85250d56e..80c844635 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -579,7 +579,7 @@ def fixed_ip_disassociate_all_by_timeout(_context, host, time): 'AND instance_id IS NOT NULL ' 'AND allocated = 0', {'host': host, - 'time': time.isoformat()}) + 'time': time}) return result.rowcount -- cgit From 96fbbcde9c84a72036a74ba07318f31a471eb402 Mon Sep 17 00:00:00 2001 From: Bilal Akhtar Date: Wed, 9 Feb 2011 17:24:36 +0300 Subject: Add my name to AUTHORS, remove parentheses from the substitution made in the previous commit --- nova/compute/instance_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 510bfd99e..a41f5fa79 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -38,7 +38,7 @@ def get_by_type(instance_type): if instance_type is None: return FLAGS.default_instance_type if instance_type not in INSTANCE_TYPES: - raise exception.ApiError(_("Unknown instance type: %s") % (instance_type), + raise exception.ApiError(_("Unknown instance type: %s") % instance_type, "Invalid") return instance_type -- cgit From d4b4fa93e7603d73eb99c8edb0e648ff047cda30 Mon Sep 17 00:00:00 2001 From: SuperStack Date: Wed, 9 Feb 2011 11:18:48 -0800 Subject: Fix PEP8 violations --- nova/utils.py | 1 + nova/volume/san.py | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) (limited to 'nova') diff --git a/nova/utils.py b/nova/utils.py index 100e26f70..8d7ff1f64 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -187,6 +187,7 @@ def ssh_execute(ssh, cmd, process_input=None, return (stdout, stderr) + def abspath(s): return os.path.join(os.path.dirname(__file__), s) diff --git a/nova/volume/san.py b/nova/volume/san.py index 1f3417ae5..e7d559a2a 100644 --- a/nova/volume/san.py +++ b/nova/volume/san.py @@ -42,6 +42,7 @@ flags.DEFINE_string('san_password', '', flags.DEFINE_string('san_privatekey', '', 'Filename of private key to use for SSH authentication') + class SanISCSIDriver(ISCSIDriver): """ Base class for SAN-style storage volumes (storage providers we access over SSH)""" @@ -68,7 +69,8 @@ class SanISCSIDriver(ISCSIDriver): volume_name) iscsi_portal = location.split(",")[0] - LOG.debug("iscsi_name=%s, iscsi_portal=%s" % (iscsi_name, iscsi_portal)) + LOG.debug("iscsi_name=%s, iscsi_portal=%s" % + (iscsi_name, iscsi_portal)) return (iscsi_name, iscsi_portal) def _build_iscsi_target_name(self, volume): @@ -89,7 +91,9 @@ class SanISCSIDriver(ISCSIDriver): privatekeyfile = os.path.expanduser(FLAGS.san_privatekey) # It sucks that paramiko doesn't support DSA keys privatekey = paramiko.RSAKey.from_private_key_file(privatekeyfile) - ssh.connect(FLAGS.san_ip, username=FLAGS.san_login, pkey=privatekey) + ssh.connect(FLAGS.san_ip, + username=FLAGS.san_login, + pkey=privatekey) else: raise exception.Error("Specify san_password or san_privatekey") return ssh @@ -125,6 +129,7 @@ class SanISCSIDriver(ISCSIDriver): if not (FLAGS.san_ip): raise exception.Error("san_ip must be set") + def _collect_lines(data): """ Split lines from data into an array, trimming them """ matches = [] @@ -134,6 +139,7 @@ def _collect_lines(data): return matches + def _get_prefixed_values(data, prefix): """Collect lines which start with prefix; with trimming""" matches = [] @@ -146,6 +152,7 @@ def _get_prefixed_values(data, prefix): return matches + class SolarisISCSIDriver(SanISCSIDriver): """Executes commands relating to Solaris-hosted ISCSI volumes. Basic setup for a Solaris iSCSI server: @@ -165,8 +172,8 @@ class SolarisISCSIDriver(SanISCSIDriver): """ def _view_exists(self, luid): - (out, _err) = self._run_ssh("pfexec /usr/sbin/stmfadm list-view -l %s" % - (luid), + cmd = "pfexec /usr/sbin/stmfadm list-view -l %s" % (luid) + (out, _err) = self._run_ssh(cmd, check_exit_code=False) if "no views found" in out: return False @@ -229,9 +236,6 @@ class SolarisISCSIDriver(SanISCSIDriver): sizestr, zfs_poolname)) - return { # 'target_ip': FLAGS.san_ip - } - def _get_luid(self, volume): zfs_poolname = self._build_zfs_poolname(volume) @@ -329,4 +333,3 @@ class SolarisISCSIDriver(SanISCSIDriver): if self._is_lu_created(volume): self._run_ssh("pfexec /usr/sbin/sbdadm delete-lu %s" % (luid)) - -- cgit From b1fbb1a4a087722ce6dc7fd7a1b9fe01cb6ec766 Mon Sep 17 00:00:00 2001 From: SuperStack Date: Wed, 9 Feb 2011 11:25:18 -0800 Subject: Indent args to ssh_connect correctly --- nova/volume/san.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/volume/san.py b/nova/volume/san.py index e7d559a2a..26d6125e7 100644 --- a/nova/volume/san.py +++ b/nova/volume/san.py @@ -92,8 +92,8 @@ class SanISCSIDriver(ISCSIDriver): # It sucks that paramiko doesn't support DSA keys privatekey = paramiko.RSAKey.from_private_key_file(privatekeyfile) ssh.connect(FLAGS.san_ip, - username=FLAGS.san_login, - pkey=privatekey) + username=FLAGS.san_login, + pkey=privatekey) else: raise exception.Error("Specify san_password or san_privatekey") return ssh -- cgit From 216822bdda290f964020134599749ebbadc76566 Mon Sep 17 00:00:00 2001 From: Bilal Akhtar Date: Thu, 10 Feb 2011 13:08:25 +0300 Subject: Wrap line to under 79 characters --- nova/compute/instance_types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index a41f5fa79..309313fd0 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -38,8 +38,8 @@ def get_by_type(instance_type): if instance_type is None: return FLAGS.default_instance_type if instance_type not in INSTANCE_TYPES: - raise exception.ApiError(_("Unknown instance type: %s") % instance_type, - "Invalid") + raise exception.ApiError(_("Unknown instance type: %s") % \ + instance_type, "Invalid") return instance_type -- cgit