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