move and copy
- Rename file/server-meta commands to file/server-metadata
- Rename image-[add|del]member commands to members-[add|delete]
-- Remove update option from imagre-register
+- Remove update option from image-register
- In image-compute split properties to properties-list and properties-get
- Add optional output to methods[#3756, #3732]:
- file:
- image: members, member
- image compute: properties
- server: firewall, metadata
+- Add a _format_image_headers method and use it in image_register and get_meta
+ for uniform image meta output [#3797]
+
Features:
- A logger module container a set of basic loging method for kamaki [#3668]
from kamaki.clients.utils import path4url, filter_in
+def _format_image_headers(headers):
+ reply = dict(properties=dict())
+ meta_prefix = 'x-image-meta-'
+ property_prefix = 'x-image-meta-property-'
+
+ for key, val in headers.items():
+ key = key.lower()
+ if key.startswith(property_prefix):
+ key = key[len(property_prefix):].upper().replace('-', '_')
+ reply['properties'][key] = val
+ elif key.startswith(meta_prefix):
+ key = key[len(meta_prefix):]
+ reply[key] = val
+ return reply
+
+
class ImageClient(Client):
"""Synnefo Plankton API client"""
path = path4url('images', image_id)
r = self.head(path, success=200)
- reply = {}
- properties = {}
- meta_prefix = 'x-image-meta-'
- property_prefix = 'x-image-meta-property-'
-
- for key, val in r.headers.items():
- key = key.lower()
- if key.startswith(property_prefix):
- key = key[len(property_prefix):]
- properties[key] = val
- elif key.startswith(meta_prefix):
- key = key[len(meta_prefix):]
- reply[key] = val
-
- if properties:
- reply['properties'] = properties
- return reply
+ return _format_image_headers(r.headers)
def register(self, name, location, params={}, properties={}):
"""Register an image that is uploaded at location
:param properties: (dict) image properties (X-Image-Meta-Property)
- :returns: (dict) details of the created image
+ :returns: (dict) metadata of the created image
"""
path = path4url('images') + '/'
self.set_header('X-Image-Meta-Name', name)
async_headers['x-image-meta-property-%s' % key] = val
r = self.post(path, success=200, async_headers=async_headers)
- return filter_in(r.headers, 'X-Image-')
+
+ return _format_image_headers(r.headers)
def unregister(self, image_id):
"""Unregister an image
params=params, properties=props)
expectedict = dict(example_image_headers)
expectedict.pop('extraheaders')
- self.assert_dicts_are_equal(expectedict, r)
+ from kamaki.clients.image import _format_image_headers
+ self.assert_dicts_are_equal(_format_image_headers(expectedict), r)
self.assertEqual(
post.mock_calls[-1],
call('/images/', async_headers=async_headers, success=200))
self.location,
params=dict(is_public=True))
self._imglist[self.imgname] = dict(
- name=r['x-image-meta-name'], id=r['x-image-meta-id'])
+ name=r['name'], id=r['id'])
self._imgdetails[self.imgname] = r
def tearDown(self):
'properties',
'size'):
self.assertTrue(term in img)
- if img['properties']:
+ if len(img['properties']):
for interm in ('osfamily', 'users', 'root_partition'):
self.assertTrue(interm in img['properties'])
size_max = 1000000000
'container-format'):
self.assertTrue(term in r)
for interm in (
- 'kernel',
- 'osfamily',
- 'users',
- 'gui', 'sortorder',
- 'root-partition',
- 'os',
- 'description'):
+ 'KERNEL',
+ 'OSFAMILY',
+ 'USERS',
+ 'GUI',
+ 'SORTORDER',
+ 'ROOT_PARTITION',
+ 'OS',
+ 'DESCRIPTION'):
self.assertTrue(interm in r['properties'])
def test_register(self):
for img in self._imglist.values():
self.assertTrue(img is not None)
r = set(self._imgdetails[img['name']].keys())
- self.assertTrue(
- r.issubset(['x-image-meta-%s' % k for k in IMGMETA]))
+ self.assertTrue(r.issubset(IMGMETA.union(['properties'])))
def test_unregister(self):
"""Test unregister"""