summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTodd Willey <todd@ansolabs.com>2010-09-22 16:42:35 -0400
committerTodd Willey <todd@ansolabs.com>2010-09-22 16:42:35 -0400
commit71c41338e4aac98ec03eec1c90ee99e43d51bcb7 (patch)
tree389760f809a6a162aacccfc110e58a242063edf7
parentb68ab98d6718d5a7237f5620e8caffc770dfe822 (diff)
downloadnova-71c41338e4aac98ec03eec1c90ee99e43d51bcb7.tar.gz
nova-71c41338e4aac98ec03eec1c90ee99e43d51bcb7.tar.xz
nova-71c41338e4aac98ec03eec1c90ee99e43d51bcb7.zip
Add user display fields to instances & volumes.
-rw-r--r--nova/db/sqlalchemy/models.py9
-rw-r--r--nova/endpoint/cloud.py32
-rw-r--r--nova/tests/cloud_unittest.py43
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']
@@ -690,6 +711,17 @@ class CloudController(object):
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
volume_ref = db.volume_get_by_str(context, volume_id)
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'])