User updatable name & description for images.
This commit is contained in:
@@ -752,3 +752,15 @@ class CloudController(object):
|
|||||||
raise exception.ApiError('operation_type must be add or remove')
|
raise exception.ApiError('operation_type must be add or remove')
|
||||||
result = images.modify(context, image_id, operation_type)
|
result = images.modify(context, image_id, operation_type)
|
||||||
return defer.succeed(result)
|
return defer.succeed(result)
|
||||||
|
|
||||||
|
@rbac.allow('projectmanager', 'sysadmin')
|
||||||
|
def set_image_name(self, context, image_id, name):
|
||||||
|
result = images.update_user_editable_field(context, image_id,
|
||||||
|
'displayName', name)
|
||||||
|
return defer.succeed(result)
|
||||||
|
|
||||||
|
@rbac.allow('projectmanager', 'sysadmin')
|
||||||
|
def set_image_description(self, context, image_id, name):
|
||||||
|
result = images.update_user_editable_field(context, image_id,
|
||||||
|
'displayDescription', name)
|
||||||
|
return defer.succeed(result)
|
||||||
|
|||||||
@@ -43,6 +43,12 @@ def modify(context, image_id, operation):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def update_user_editable_field(context, image_id, field, value):
|
||||||
|
conn(context).make_request(
|
||||||
|
method='POST',
|
||||||
|
bucket='_images',
|
||||||
|
query_args=qs({'image_id': image_id, 'field': field, 'value': value}))
|
||||||
|
return True
|
||||||
|
|
||||||
def register(context, image_location):
|
def register(context, image_location):
|
||||||
""" rpc call to register a new image based from a manifest """
|
""" rpc call to register a new image based from a manifest """
|
||||||
|
|||||||
@@ -16,9 +16,13 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import StringIO
|
import StringIO
|
||||||
|
import tempfile
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from tornado import ioloop
|
from tornado import ioloop
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
import unittest
|
import unittest
|
||||||
@@ -32,15 +36,22 @@ from nova.auth import manager
|
|||||||
from nova.compute import power_state
|
from nova.compute import power_state
|
||||||
from nova.endpoint import api
|
from nova.endpoint import api
|
||||||
from nova.endpoint import cloud
|
from nova.endpoint import cloud
|
||||||
|
from nova.objectstore import image
|
||||||
|
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
FLAGS = flags.FLAGS
|
||||||
|
|
||||||
|
|
||||||
|
# Temp dirs for working with image attributes through the cloud controller
|
||||||
|
# (stole this from objectstore_unittest.py)
|
||||||
|
OSS_TEMPDIR = tempfile.mkdtemp(prefix='test_oss-')
|
||||||
|
IMAGES_PATH = os.path.join(OSS_TEMPDIR, 'images')
|
||||||
|
os.makedirs(IMAGES_PATH)
|
||||||
|
|
||||||
class CloudTestCase(test.BaseTestCase):
|
class CloudTestCase(test.BaseTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(CloudTestCase, self).setUp()
|
super(CloudTestCase, self).setUp()
|
||||||
self.flags(connection_type='fake')
|
self.flags(connection_type='fake', images_path=IMAGES_PATH)
|
||||||
|
|
||||||
self.conn = rpc.Connection.instance()
|
self.conn = rpc.Connection.instance()
|
||||||
logging.getLogger().setLevel(logging.DEBUG)
|
logging.getLogger().setLevel(logging.DEBUG)
|
||||||
@@ -156,3 +167,16 @@ class CloudTestCase(test.BaseTestCase):
|
|||||||
#for i in xrange(4):
|
#for i in xrange(4):
|
||||||
# data = self.cloud.get_metadata(instance(i)['private_dns_name'])
|
# data = self.cloud.get_metadata(instance(i)['private_dns_name'])
|
||||||
# self.assert_(data['meta-data']['ami-id'] == 'ami-%s' % i)
|
# self.assert_(data['meta-data']['ami-id'] == 'ami-%s' % i)
|
||||||
|
|
||||||
|
def test_user_editable_endpoint(self):
|
||||||
|
pathdir = os.path.join(FLAGS.images_path, 'i-testing')
|
||||||
|
os.mkdir(pathdir)
|
||||||
|
info = {}
|
||||||
|
with open(os.path.join(pathdir, 'info.json'), 'w') as f:
|
||||||
|
json.dump(info, f)
|
||||||
|
yield self.cloud.set_image_description(self.context, 'i-testing',
|
||||||
|
'Foo Img')
|
||||||
|
img = image.Image('i-testing')
|
||||||
|
self.assertEqual('Foo Img', img.metadata['displayDescription'])
|
||||||
|
self.cloud.set_image_description(self.context, 'i-testing', '')
|
||||||
|
self.assert_(not 'displayDescription' in img.metadata)
|
||||||
|
|||||||
@@ -164,6 +164,12 @@ class ObjectStoreTestCase(test.BaseTestCase):
|
|||||||
self.context.project = self.auth_manager.get_project('proj2')
|
self.context.project = self.auth_manager.get_project('proj2')
|
||||||
self.assertFalse(my_img.is_authorized(self.context))
|
self.assertFalse(my_img.is_authorized(self.context))
|
||||||
|
|
||||||
|
# change user-editable fields
|
||||||
|
my_img.update_user_editable_field('displayName', 'my cool image')
|
||||||
|
self.assertEqual('my cool image', my_img.metadata['displayName'])
|
||||||
|
my_img.update_user_editable_field('displayName', '')
|
||||||
|
self.assert_(not 'displayName' in my_img.metadata)
|
||||||
|
|
||||||
|
|
||||||
class TestHTTPChannel(http.HTTPChannel):
|
class TestHTTPChannel(http.HTTPChannel):
|
||||||
"""Dummy site required for twisted.web"""
|
"""Dummy site required for twisted.web"""
|
||||||
|
|||||||
Reference in New Issue
Block a user