OpenStack Image Management (Glance)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

__init__.py 37KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967
  1. # Copyright 2010 United States Government as represented by the
  2. # Administrator of the National Aeronautics and Space Administration.
  3. # Copyright 2010-2012 OpenStack Foundation
  4. # Copyright 2013 IBM Corp.
  5. # Copyright 2015 Mirantis, Inc.
  6. # All Rights Reserved.
  7. #
  8. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  9. # not use this file except in compliance with the License. You may obtain
  10. # a copy of the License at
  11. #
  12. # http://www.apache.org/licenses/LICENSE-2.0
  13. #
  14. # Unless required by applicable law or agreed to in writing, software
  15. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  16. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  17. # License for the specific language governing permissions and limitations
  18. # under the License.
  19. from oslo_config import cfg
  20. from oslo_utils import importutils
  21. from wsme.rest import json
  22. from glance.api.v2.model.metadef_property_type import PropertyType
  23. from glance.common import crypt
  24. from glance.common import exception
  25. from glance.common.glare import serialization
  26. from glance.common import location_strategy
  27. import glance.domain
  28. import glance.domain.proxy
  29. from glance import glare as ga
  30. from glance.i18n import _
  31. CONF = cfg.CONF
  32. CONF.import_opt('image_size_cap', 'glance.common.config')
  33. CONF.import_opt('metadata_encryption_key', 'glance.common.config')
  34. def get_api():
  35. api = importutils.import_module(CONF.data_api)
  36. if hasattr(api, 'configure'):
  37. api.configure()
  38. return api
  39. def unwrap(db_api):
  40. return db_api
  41. # attributes common to all models
  42. BASE_MODEL_ATTRS = set(['id', 'created_at', 'updated_at', 'deleted_at',
  43. 'deleted'])
  44. IMAGE_ATTRS = BASE_MODEL_ATTRS | set(['name', 'status', 'size', 'virtual_size',
  45. 'disk_format', 'container_format',
  46. 'min_disk', 'min_ram', 'is_public',
  47. 'locations', 'checksum', 'owner',
  48. 'protected'])
  49. class ArtifactRepo(object):
  50. fields = ['id', 'name', 'version', 'type_name', 'type_version',
  51. 'visibility', 'state', 'owner', 'scope', 'created_at',
  52. 'updated_at', 'tags', 'dependencies', 'blobs', 'properties']
  53. def __init__(self, context, db_api, plugins):
  54. self.context = context
  55. self.db_api = db_api
  56. self.plugins = plugins
  57. def get(self, artifact_id, type_name=None, type_version=None,
  58. show_level=None, include_deleted=False):
  59. if show_level is None:
  60. show_level = ga.Showlevel.BASIC
  61. try:
  62. db_api_artifact = self.db_api.artifact_get(self.context,
  63. artifact_id,
  64. type_name,
  65. type_version,
  66. show_level)
  67. if db_api_artifact["state"] == 'deleted' and not include_deleted:
  68. raise exception.ArtifactNotFound(artifact_id)
  69. except (exception.ArtifactNotFound, exception.ArtifactForbidden):
  70. msg = _("No artifact found with ID %s") % artifact_id
  71. raise exception.ArtifactNotFound(msg)
  72. return serialization.deserialize_from_db(db_api_artifact, self.plugins)
  73. def list(self, marker=None, limit=None,
  74. sort_keys=None, sort_dirs=None, filters=None,
  75. show_level=None):
  76. sort_keys = ['created_at'] if sort_keys is None else sort_keys
  77. sort_dirs = ['desc'] if sort_dirs is None else sort_dirs
  78. if show_level is None:
  79. show_level = ga.Showlevel.NONE
  80. db_api_artifacts = self.db_api.artifact_get_all(
  81. self.context, filters=filters, marker=marker, limit=limit,
  82. sort_keys=sort_keys, sort_dirs=sort_dirs, show_level=show_level)
  83. artifacts = []
  84. for db_api_artifact in db_api_artifacts:
  85. artifact = serialization.deserialize_from_db(db_api_artifact,
  86. self.plugins)
  87. artifacts.append(artifact)
  88. return artifacts
  89. def _format_artifact_from_db(self, db_artifact):
  90. kwargs = {k: db_artifact.get(k, None) for k in self.fields}
  91. return glance.domain.Artifact(**kwargs)
  92. def add(self, artifact):
  93. artifact_values = serialization.serialize_for_db(artifact)
  94. artifact_values['updated_at'] = artifact.updated_at
  95. self.db_api.artifact_create(self.context, artifact_values,
  96. artifact.type_name, artifact.type_version)
  97. def save(self, artifact):
  98. artifact_values = serialization.serialize_for_db(artifact)
  99. try:
  100. db_api_artifact = self.db_api.artifact_update(
  101. self.context,
  102. artifact_values,
  103. artifact.id,
  104. artifact.type_name,
  105. artifact.type_version)
  106. except (exception.ArtifactNotFound,
  107. exception.ArtifactForbidden):
  108. msg = _("No artifact found with ID %s") % artifact.id
  109. raise exception.ArtifactNotFound(msg)
  110. return serialization.deserialize_from_db(db_api_artifact, self.plugins)
  111. def remove(self, artifact):
  112. try:
  113. self.db_api.artifact_delete(self.context, artifact.id,
  114. artifact.type_name,
  115. artifact.type_version)
  116. except (exception.NotFound, exception.Forbidden):
  117. msg = _("No artifact found with ID %s") % artifact.id
  118. raise exception.ArtifactNotFound(msg)
  119. def publish(self, artifact):
  120. try:
  121. artifact_changed = (
  122. self.db_api.artifact_publish(
  123. self.context,
  124. artifact.id,
  125. artifact.type_name,
  126. artifact.type_version))
  127. return serialization.deserialize_from_db(artifact_changed,
  128. self.plugins)
  129. except (exception.NotFound, exception.Forbidden):
  130. msg = _("No artifact found with ID %s") % artifact.id
  131. raise exception.ArtifactNotFound(msg)
  132. class ImageRepo(object):
  133. def __init__(self, context, db_api):
  134. self.context = context
  135. self.db_api = db_api
  136. def get(self, image_id):
  137. try:
  138. db_api_image = dict(self.db_api.image_get(self.context, image_id))
  139. if db_api_image['deleted']:
  140. raise exception.ImageNotFound()
  141. except (exception.ImageNotFound, exception.Forbidden):
  142. msg = _("No image found with ID %s") % image_id
  143. raise exception.ImageNotFound(msg)
  144. tags = self.db_api.image_tag_get_all(self.context, image_id)
  145. image = self._format_image_from_db(db_api_image, tags)
  146. return ImageProxy(image, self.context, self.db_api)
  147. def list(self, marker=None, limit=None, sort_key=None,
  148. sort_dir=None, filters=None, member_status='accepted'):
  149. sort_key = ['created_at'] if not sort_key else sort_key
  150. sort_dir = ['desc'] if not sort_dir else sort_dir
  151. db_api_images = self.db_api.image_get_all(
  152. self.context, filters=filters, marker=marker, limit=limit,
  153. sort_key=sort_key, sort_dir=sort_dir,
  154. member_status=member_status, return_tag=True)
  155. images = []
  156. for db_api_image in db_api_images:
  157. db_image = dict(db_api_image)
  158. image = self._format_image_from_db(db_image, db_image['tags'])
  159. images.append(image)
  160. return images
  161. def _format_image_from_db(self, db_image, db_tags):
  162. properties = {}
  163. for prop in db_image.pop('properties'):
  164. # NOTE(markwash) db api requires us to filter deleted
  165. if not prop['deleted']:
  166. properties[prop['name']] = prop['value']
  167. locations = [loc for loc in db_image['locations']
  168. if loc['status'] == 'active']
  169. if CONF.metadata_encryption_key:
  170. key = CONF.metadata_encryption_key
  171. for l in locations:
  172. l['url'] = crypt.urlsafe_decrypt(key, l['url'])
  173. return glance.domain.Image(
  174. image_id=db_image['id'],
  175. name=db_image['name'],
  176. status=db_image['status'],
  177. created_at=db_image['created_at'],
  178. updated_at=db_image['updated_at'],
  179. visibility=db_image['visibility'],
  180. min_disk=db_image['min_disk'],
  181. min_ram=db_image['min_ram'],
  182. protected=db_image['protected'],
  183. locations=location_strategy.get_ordered_locations(locations),
  184. checksum=db_image['checksum'],
  185. owner=db_image['owner'],
  186. disk_format=db_image['disk_format'],
  187. container_format=db_image['container_format'],
  188. size=db_image['size'],
  189. virtual_size=db_image['virtual_size'],
  190. extra_properties=properties,
  191. tags=db_tags
  192. )
  193. def _format_image_to_db(self, image):
  194. locations = image.locations
  195. if CONF.metadata_encryption_key:
  196. key = CONF.metadata_encryption_key
  197. ld = []
  198. for loc in locations:
  199. url = crypt.urlsafe_encrypt(key, loc['url'])
  200. ld.append({'url': url, 'metadata': loc['metadata'],
  201. 'status': loc['status'],
  202. # NOTE(zhiyan): New location has no ID field.
  203. 'id': loc.get('id')})
  204. locations = ld
  205. return {
  206. 'id': image.image_id,
  207. 'name': image.name,
  208. 'status': image.status,
  209. 'created_at': image.created_at,
  210. 'min_disk': image.min_disk,
  211. 'min_ram': image.min_ram,
  212. 'protected': image.protected,
  213. 'locations': locations,
  214. 'checksum': image.checksum,
  215. 'owner': image.owner,
  216. 'disk_format': image.disk_format,
  217. 'container_format': image.container_format,
  218. 'size': image.size,
  219. 'virtual_size': image.virtual_size,
  220. 'visibility': image.visibility,
  221. 'properties': dict(image.extra_properties),
  222. }
  223. def add(self, image):
  224. image_values = self._format_image_to_db(image)
  225. if (image_values['size'] is not None
  226. and image_values['size'] > CONF.image_size_cap):
  227. raise exception.ImageSizeLimitExceeded
  228. # the updated_at value is not set in the _format_image_to_db
  229. # function since it is specific to image create
  230. image_values['updated_at'] = image.updated_at
  231. new_values = self.db_api.image_create(self.context, image_values)
  232. self.db_api.image_tag_set_all(self.context,
  233. image.image_id, image.tags)
  234. image.created_at = new_values['created_at']
  235. image.updated_at = new_values['updated_at']
  236. def save(self, image, from_state=None):
  237. image_values = self._format_image_to_db(image)
  238. if (image_values['size'] is not None
  239. and image_values['size'] > CONF.image_size_cap):
  240. raise exception.ImageSizeLimitExceeded
  241. try:
  242. new_values = self.db_api.image_update(self.context,
  243. image.image_id,
  244. image_values,
  245. purge_props=True,
  246. from_state=from_state)
  247. except (exception.ImageNotFound, exception.Forbidden):
  248. msg = _("No image found with ID %s") % image.image_id
  249. raise exception.ImageNotFound(msg)
  250. self.db_api.image_tag_set_all(self.context, image.image_id,
  251. image.tags)
  252. image.updated_at = new_values['updated_at']
  253. def remove(self, image):
  254. try:
  255. self.db_api.image_update(self.context, image.image_id,
  256. {'status': image.status},
  257. purge_props=True)
  258. except (exception.ImageNotFound, exception.Forbidden):
  259. msg = _("No image found with ID %s") % image.image_id
  260. raise exception.ImageNotFound(msg)
  261. # NOTE(markwash): don't update tags?
  262. new_values = self.db_api.image_destroy(self.context, image.image_id)
  263. image.updated_at = new_values['updated_at']
  264. class ImageProxy(glance.domain.proxy.Image):
  265. def __init__(self, image, context, db_api):
  266. self.context = context
  267. self.db_api = db_api
  268. self.image = image
  269. super(ImageProxy, self).__init__(image)
  270. class ImageMemberRepo(object):
  271. def __init__(self, context, db_api, image):
  272. self.context = context
  273. self.db_api = db_api
  274. self.image = image
  275. def _format_image_member_from_db(self, db_image_member):
  276. return glance.domain.ImageMembership(
  277. id=db_image_member['id'],
  278. image_id=db_image_member['image_id'],
  279. member_id=db_image_member['member'],
  280. status=db_image_member['status'],
  281. created_at=db_image_member['created_at'],
  282. updated_at=db_image_member['updated_at']
  283. )
  284. def _format_image_member_to_db(self, image_member):
  285. image_member = {'image_id': self.image.image_id,
  286. 'member': image_member.member_id,
  287. 'status': image_member.status,
  288. 'created_at': image_member.created_at}
  289. return image_member
  290. def list(self):
  291. db_members = self.db_api.image_member_find(
  292. self.context, image_id=self.image.image_id)
  293. image_members = []
  294. for db_member in db_members:
  295. image_members.append(self._format_image_member_from_db(db_member))
  296. return image_members
  297. def add(self, image_member):
  298. try:
  299. self.get(image_member.member_id)
  300. except exception.NotFound:
  301. pass
  302. else:
  303. msg = _('The target member %(member_id)s is already '
  304. 'associated with image %(image_id)s.') % {
  305. 'member_id': image_member.member_id,
  306. 'image_id': self.image.image_id}
  307. raise exception.Duplicate(msg)
  308. image_member_values = self._format_image_member_to_db(image_member)
  309. # Note(shalq): find the image member including the member marked with
  310. # deleted. We will use only one record to represent membership between
  311. # the same image and member. The record of the deleted image member
  312. # will be reused, if it exists, update its properties instead of
  313. # creating a new one.
  314. members = self.db_api.image_member_find(self.context,
  315. image_id=self.image.image_id,
  316. member=image_member.member_id,
  317. include_deleted=True)
  318. if members:
  319. new_values = self.db_api.image_member_update(self.context,
  320. members[0]['id'],
  321. image_member_values)
  322. else:
  323. new_values = self.db_api.image_member_create(self.context,
  324. image_member_values)
  325. image_member.created_at = new_values['created_at']
  326. image_member.updated_at = new_values['updated_at']
  327. image_member.id = new_values['id']
  328. def remove(self, image_member):
  329. try:
  330. self.db_api.image_member_delete(self.context, image_member.id)
  331. except (exception.NotFound, exception.Forbidden):
  332. msg = _("The specified member %s could not be found")
  333. raise exception.NotFound(msg % image_member.id)
  334. def save(self, image_member, from_state=None):
  335. image_member_values = self._format_image_member_to_db(image_member)
  336. try:
  337. new_values = self.db_api.image_member_update(self.context,
  338. image_member.id,
  339. image_member_values)
  340. except (exception.NotFound, exception.Forbidden):
  341. raise exception.NotFound()
  342. image_member.updated_at = new_values['updated_at']
  343. def get(self, member_id):
  344. try:
  345. db_api_image_member = self.db_api.image_member_find(
  346. self.context,
  347. self.image.image_id,
  348. member_id)
  349. if not db_api_image_member:
  350. raise exception.NotFound()
  351. except (exception.NotFound, exception.Forbidden):
  352. raise exception.NotFound()
  353. image_member = self._format_image_member_from_db(
  354. db_api_image_member[0])
  355. return image_member
  356. class TaskRepo(object):
  357. def __init__(self, context, db_api):
  358. self.context = context
  359. self.db_api = db_api
  360. def _format_task_from_db(self, db_task):
  361. return glance.domain.Task(
  362. task_id=db_task['id'],
  363. task_type=db_task['type'],
  364. status=db_task['status'],
  365. owner=db_task['owner'],
  366. expires_at=db_task['expires_at'],
  367. created_at=db_task['created_at'],
  368. updated_at=db_task['updated_at'],
  369. task_input=db_task['input'],
  370. result=db_task['result'],
  371. message=db_task['message'],
  372. )
  373. def _format_task_stub_from_db(self, db_task):
  374. return glance.domain.TaskStub(
  375. task_id=db_task['id'],
  376. task_type=db_task['type'],
  377. status=db_task['status'],
  378. owner=db_task['owner'],
  379. expires_at=db_task['expires_at'],
  380. created_at=db_task['created_at'],
  381. updated_at=db_task['updated_at'],
  382. )
  383. def _format_task_to_db(self, task):
  384. task = {'id': task.task_id,
  385. 'type': task.type,
  386. 'status': task.status,
  387. 'input': task.task_input,
  388. 'result': task.result,
  389. 'owner': task.owner,
  390. 'message': task.message,
  391. 'expires_at': task.expires_at,
  392. 'created_at': task.created_at,
  393. 'updated_at': task.updated_at,
  394. }
  395. return task
  396. def get(self, task_id):
  397. try:
  398. db_api_task = self.db_api.task_get(self.context, task_id)
  399. except (exception.NotFound, exception.Forbidden):
  400. msg = _('Could not find task %s') % task_id
  401. raise exception.NotFound(msg)
  402. return self._format_task_from_db(db_api_task)
  403. def list(self, marker=None, limit=None, sort_key='created_at',
  404. sort_dir='desc', filters=None):
  405. db_api_tasks = self.db_api.task_get_all(self.context,
  406. filters=filters,
  407. marker=marker,
  408. limit=limit,
  409. sort_key=sort_key,
  410. sort_dir=sort_dir)
  411. return [self._format_task_stub_from_db(task) for task in db_api_tasks]
  412. def save(self, task):
  413. task_values = self._format_task_to_db(task)
  414. try:
  415. updated_values = self.db_api.task_update(self.context,
  416. task.task_id,
  417. task_values)
  418. except (exception.NotFound, exception.Forbidden):
  419. msg = _('Could not find task %s') % task.task_id
  420. raise exception.NotFound(msg)
  421. task.updated_at = updated_values['updated_at']
  422. def add(self, task):
  423. task_values = self._format_task_to_db(task)
  424. updated_values = self.db_api.task_create(self.context, task_values)
  425. task.created_at = updated_values['created_at']
  426. task.updated_at = updated_values['updated_at']
  427. def remove(self, task):
  428. task_values = self._format_task_to_db(task)
  429. try:
  430. self.db_api.task_update(self.context, task.task_id, task_values)
  431. updated_values = self.db_api.task_delete(self.context,
  432. task.task_id)
  433. except (exception.NotFound, exception.Forbidden):
  434. msg = _('Could not find task %s') % task.task_id
  435. raise exception.NotFound(msg)
  436. task.updated_at = updated_values['updated_at']
  437. task.deleted_at = updated_values['deleted_at']
  438. class MetadefNamespaceRepo(object):
  439. def __init__(self, context, db_api):
  440. self.context = context
  441. self.db_api = db_api
  442. def _format_namespace_from_db(self, namespace_obj):
  443. return glance.domain.MetadefNamespace(
  444. namespace_id=namespace_obj['id'],
  445. namespace=namespace_obj['namespace'],
  446. display_name=namespace_obj['display_name'],
  447. description=namespace_obj['description'],
  448. owner=namespace_obj['owner'],
  449. visibility=namespace_obj['visibility'],
  450. protected=namespace_obj['protected'],
  451. created_at=namespace_obj['created_at'],
  452. updated_at=namespace_obj['updated_at']
  453. )
  454. def _format_namespace_to_db(self, namespace_obj):
  455. namespace = {
  456. 'namespace': namespace_obj.namespace,
  457. 'display_name': namespace_obj.display_name,
  458. 'description': namespace_obj.description,
  459. 'visibility': namespace_obj.visibility,
  460. 'protected': namespace_obj.protected,
  461. 'owner': namespace_obj.owner
  462. }
  463. return namespace
  464. def add(self, namespace):
  465. self.db_api.metadef_namespace_create(
  466. self.context,
  467. self._format_namespace_to_db(namespace)
  468. )
  469. def get(self, namespace):
  470. try:
  471. db_api_namespace = self.db_api.metadef_namespace_get(
  472. self.context, namespace)
  473. except (exception.NotFound, exception.Forbidden):
  474. msg = _('Could not find namespace %s') % namespace
  475. raise exception.NotFound(msg)
  476. return self._format_namespace_from_db(db_api_namespace)
  477. def list(self, marker=None, limit=None, sort_key='created_at',
  478. sort_dir='desc', filters=None):
  479. db_namespaces = self.db_api.metadef_namespace_get_all(
  480. self.context,
  481. marker=marker,
  482. limit=limit,
  483. sort_key=sort_key,
  484. sort_dir=sort_dir,
  485. filters=filters
  486. )
  487. return [self._format_namespace_from_db(namespace_obj)
  488. for namespace_obj in db_namespaces]
  489. def remove(self, namespace):
  490. try:
  491. self.db_api.metadef_namespace_delete(self.context,
  492. namespace.namespace)
  493. except (exception.NotFound, exception.Forbidden):
  494. msg = _("The specified namespace %s could not be found")
  495. raise exception.NotFound(msg % namespace.namespace)
  496. def remove_objects(self, namespace):
  497. try:
  498. self.db_api.metadef_object_delete_namespace_content(
  499. self.context,
  500. namespace.namespace
  501. )
  502. except (exception.NotFound, exception.Forbidden):
  503. msg = _("The specified namespace %s could not be found")
  504. raise exception.NotFound(msg % namespace.namespace)
  505. def remove_properties(self, namespace):
  506. try:
  507. self.db_api.metadef_property_delete_namespace_content(
  508. self.context,
  509. namespace.namespace
  510. )
  511. except (exception.NotFound, exception.Forbidden):
  512. msg = _("The specified namespace %s could not be found")
  513. raise exception.NotFound(msg % namespace.namespace)
  514. def remove_tags(self, namespace):
  515. try:
  516. self.db_api.metadef_tag_delete_namespace_content(
  517. self.context,
  518. namespace.namespace
  519. )
  520. except (exception.NotFound, exception.Forbidden):
  521. msg = _("The specified namespace %s could not be found")
  522. raise exception.NotFound(msg % namespace.namespace)
  523. def object_count(self, namespace_name):
  524. return self.db_api.metadef_object_count(
  525. self.context,
  526. namespace_name
  527. )
  528. def property_count(self, namespace_name):
  529. return self.db_api.metadef_property_count(
  530. self.context,
  531. namespace_name
  532. )
  533. def save(self, namespace):
  534. try:
  535. self.db_api.metadef_namespace_update(
  536. self.context, namespace.namespace_id,
  537. self._format_namespace_to_db(namespace)
  538. )
  539. except exception.NotFound as e:
  540. raise exception.NotFound(explanation=e.msg)
  541. return namespace
  542. class MetadefObjectRepo(object):
  543. def __init__(self, context, db_api):
  544. self.context = context
  545. self.db_api = db_api
  546. self.meta_namespace_repo = MetadefNamespaceRepo(context, db_api)
  547. def _format_metadef_object_from_db(self, metadata_object,
  548. namespace_entity):
  549. required_str = metadata_object['required']
  550. required_list = required_str.split(",") if required_str else []
  551. # Convert the persisted json schema to a dict of PropertyTypes
  552. property_types = {}
  553. json_props = metadata_object['json_schema']
  554. for id in json_props:
  555. property_types[id] = json.fromjson(PropertyType, json_props[id])
  556. return glance.domain.MetadefObject(
  557. namespace=namespace_entity,
  558. object_id=metadata_object['id'],
  559. name=metadata_object['name'],
  560. required=required_list,
  561. description=metadata_object['description'],
  562. properties=property_types,
  563. created_at=metadata_object['created_at'],
  564. updated_at=metadata_object['updated_at']
  565. )
  566. def _format_metadef_object_to_db(self, metadata_object):
  567. required_str = (",".join(metadata_object.required) if
  568. metadata_object.required else None)
  569. # Convert the model PropertyTypes dict to a JSON string
  570. properties = metadata_object.properties
  571. db_schema = {}
  572. if properties:
  573. for k, v in properties.items():
  574. json_data = json.tojson(PropertyType, v)
  575. db_schema[k] = json_data
  576. db_metadata_object = {
  577. 'name': metadata_object.name,
  578. 'required': required_str,
  579. 'description': metadata_object.description,
  580. 'json_schema': db_schema
  581. }
  582. return db_metadata_object
  583. def add(self, metadata_object):
  584. self.db_api.metadef_object_create(
  585. self.context,
  586. metadata_object.namespace,
  587. self._format_metadef_object_to_db(metadata_object)
  588. )
  589. def get(self, namespace, object_name):
  590. try:
  591. namespace_entity = self.meta_namespace_repo.get(namespace)
  592. db_metadata_object = self.db_api.metadef_object_get(
  593. self.context,
  594. namespace,
  595. object_name)
  596. except (exception.NotFound, exception.Forbidden):
  597. msg = _('Could not find metadata object %s') % object_name
  598. raise exception.NotFound(msg)
  599. return self._format_metadef_object_from_db(db_metadata_object,
  600. namespace_entity)
  601. def list(self, marker=None, limit=None, sort_key='created_at',
  602. sort_dir='desc', filters=None):
  603. namespace = filters['namespace']
  604. namespace_entity = self.meta_namespace_repo.get(namespace)
  605. db_metadata_objects = self.db_api.metadef_object_get_all(
  606. self.context, namespace)
  607. return [self._format_metadef_object_from_db(metadata_object,
  608. namespace_entity)
  609. for metadata_object in db_metadata_objects]
  610. def remove(self, metadata_object):
  611. try:
  612. self.db_api.metadef_object_delete(
  613. self.context,
  614. metadata_object.namespace.namespace,
  615. metadata_object.name
  616. )
  617. except (exception.NotFound, exception.Forbidden):
  618. msg = _("The specified metadata object %s could not be found")
  619. raise exception.NotFound(msg % metadata_object.name)
  620. def save(self, metadata_object):
  621. try:
  622. self.db_api.metadef_object_update(
  623. self.context, metadata_object.namespace.namespace,
  624. metadata_object.object_id,
  625. self._format_metadef_object_to_db(metadata_object))
  626. except exception.NotFound as e:
  627. raise exception.NotFound(explanation=e.msg)
  628. return metadata_object
  629. class MetadefResourceTypeRepo(object):
  630. def __init__(self, context, db_api):
  631. self.context = context
  632. self.db_api = db_api
  633. self.meta_namespace_repo = MetadefNamespaceRepo(context, db_api)
  634. def _format_resource_type_from_db(self, resource_type, namespace):
  635. return glance.domain.MetadefResourceType(
  636. namespace=namespace,
  637. name=resource_type['name'],
  638. prefix=resource_type['prefix'],
  639. properties_target=resource_type['properties_target'],
  640. created_at=resource_type['created_at'],
  641. updated_at=resource_type['updated_at']
  642. )
  643. def _format_resource_type_to_db(self, resource_type):
  644. db_resource_type = {
  645. 'name': resource_type.name,
  646. 'prefix': resource_type.prefix,
  647. 'properties_target': resource_type.properties_target
  648. }
  649. return db_resource_type
  650. def add(self, resource_type):
  651. self.db_api.metadef_resource_type_association_create(
  652. self.context, resource_type.namespace,
  653. self._format_resource_type_to_db(resource_type)
  654. )
  655. def get(self, resource_type, namespace):
  656. namespace_entity = self.meta_namespace_repo.get(namespace)
  657. db_resource_type = (
  658. self.db_api.
  659. metadef_resource_type_association_get(
  660. self.context,
  661. namespace,
  662. resource_type
  663. )
  664. )
  665. return self._format_resource_type_from_db(db_resource_type,
  666. namespace_entity)
  667. def list(self, filters=None):
  668. namespace = filters['namespace']
  669. if namespace:
  670. namespace_entity = self.meta_namespace_repo.get(namespace)
  671. db_resource_types = (
  672. self.db_api.
  673. metadef_resource_type_association_get_all_by_namespace(
  674. self.context,
  675. namespace
  676. )
  677. )
  678. return [self._format_resource_type_from_db(resource_type,
  679. namespace_entity)
  680. for resource_type in db_resource_types]
  681. else:
  682. db_resource_types = (
  683. self.db_api.
  684. metadef_resource_type_get_all(self.context)
  685. )
  686. return [glance.domain.MetadefResourceType(
  687. namespace=None,
  688. name=resource_type['name'],
  689. prefix=None,
  690. properties_target=None,
  691. created_at=resource_type['created_at'],
  692. updated_at=resource_type['updated_at']
  693. ) for resource_type in db_resource_types]
  694. def remove(self, resource_type):
  695. try:
  696. self.db_api.metadef_resource_type_association_delete(
  697. self.context, resource_type.namespace.namespace,
  698. resource_type.name)
  699. except (exception.NotFound, exception.Forbidden):
  700. msg = _("The specified resource type %s could not be found ")
  701. raise exception.NotFound(msg % resource_type.name)
  702. class MetadefPropertyRepo(object):
  703. def __init__(self, context, db_api):
  704. self.context = context
  705. self.db_api = db_api
  706. self.meta_namespace_repo = MetadefNamespaceRepo(context, db_api)
  707. def _format_metadef_property_from_db(
  708. self,
  709. property,
  710. namespace_entity):
  711. return glance.domain.MetadefProperty(
  712. namespace=namespace_entity,
  713. property_id=property['id'],
  714. name=property['name'],
  715. schema=property['json_schema']
  716. )
  717. def _format_metadef_property_to_db(self, property):
  718. db_metadata_object = {
  719. 'name': property.name,
  720. 'json_schema': property.schema
  721. }
  722. return db_metadata_object
  723. def add(self, property):
  724. self.db_api.metadef_property_create(
  725. self.context,
  726. property.namespace,
  727. self._format_metadef_property_to_db(property)
  728. )
  729. def get(self, namespace, property_name):
  730. try:
  731. namespace_entity = self.meta_namespace_repo.get(namespace)
  732. db_property_type = self.db_api.metadef_property_get(
  733. self.context,
  734. namespace,
  735. property_name
  736. )
  737. except (exception.NotFound, exception.Forbidden):
  738. msg = _('Could not find property %s') % property_name
  739. raise exception.NotFound(msg)
  740. return self._format_metadef_property_from_db(
  741. db_property_type, namespace_entity)
  742. def list(self, marker=None, limit=None, sort_key='created_at',
  743. sort_dir='desc', filters=None):
  744. namespace = filters['namespace']
  745. namespace_entity = self.meta_namespace_repo.get(namespace)
  746. db_properties = self.db_api.metadef_property_get_all(
  747. self.context, namespace)
  748. return (
  749. [self._format_metadef_property_from_db(
  750. property, namespace_entity) for property in db_properties]
  751. )
  752. def remove(self, property):
  753. try:
  754. self.db_api.metadef_property_delete(
  755. self.context, property.namespace.namespace, property.name)
  756. except (exception.NotFound, exception.Forbidden):
  757. msg = _("The specified property %s could not be found")
  758. raise exception.NotFound(msg % property.name)
  759. def save(self, property):
  760. try:
  761. self.db_api.metadef_property_update(
  762. self.context, property.namespace.namespace,
  763. property.property_id,
  764. self._format_metadef_property_to_db(property)
  765. )
  766. except exception.NotFound as e:
  767. raise exception.NotFound(explanation=e.msg)
  768. return property
  769. class MetadefTagRepo(object):
  770. def __init__(self, context, db_api):
  771. self.context = context
  772. self.db_api = db_api
  773. self.meta_namespace_repo = MetadefNamespaceRepo(context, db_api)
  774. def _format_metadef_tag_from_db(self, metadata_tag,
  775. namespace_entity):
  776. return glance.domain.MetadefTag(
  777. namespace=namespace_entity,
  778. tag_id=metadata_tag['id'],
  779. name=metadata_tag['name'],
  780. created_at=metadata_tag['created_at'],
  781. updated_at=metadata_tag['updated_at']
  782. )
  783. def _format_metadef_tag_to_db(self, metadata_tag):
  784. db_metadata_tag = {
  785. 'name': metadata_tag.name
  786. }
  787. return db_metadata_tag
  788. def add(self, metadata_tag):
  789. self.db_api.metadef_tag_create(
  790. self.context,
  791. metadata_tag.namespace,
  792. self._format_metadef_tag_to_db(metadata_tag)
  793. )
  794. def add_tags(self, metadata_tags):
  795. tag_list = []
  796. namespace = None
  797. for metadata_tag in metadata_tags:
  798. tag_list.append(self._format_metadef_tag_to_db(metadata_tag))
  799. if namespace is None:
  800. namespace = metadata_tag.namespace
  801. self.db_api.metadef_tag_create_tags(
  802. self.context, namespace, tag_list)
  803. def get(self, namespace, name):
  804. try:
  805. namespace_entity = self.meta_namespace_repo.get(namespace)
  806. db_metadata_tag = self.db_api.metadef_tag_get(
  807. self.context,
  808. namespace,
  809. name)
  810. except (exception.NotFound, exception.Forbidden):
  811. msg = _('Could not find metadata tag %s') % name
  812. raise exception.NotFound(msg)
  813. return self._format_metadef_tag_from_db(db_metadata_tag,
  814. namespace_entity)
  815. def list(self, marker=None, limit=None, sort_key='created_at',
  816. sort_dir='desc', filters=None):
  817. namespace = filters['namespace']
  818. namespace_entity = self.meta_namespace_repo.get(namespace)
  819. db_metadata_tag = self.db_api.metadef_tag_get_all(
  820. self.context, namespace, filters, marker, limit, sort_key,
  821. sort_dir)
  822. return [self._format_metadef_tag_from_db(metadata_tag,
  823. namespace_entity)
  824. for metadata_tag in db_metadata_tag]
  825. def remove(self, metadata_tag):
  826. try:
  827. self.db_api.metadef_tag_delete(
  828. self.context,
  829. metadata_tag.namespace.namespace,
  830. metadata_tag.name
  831. )
  832. except (exception.NotFound, exception.Forbidden):
  833. msg = _("The specified metadata tag %s could not be found")
  834. raise exception.NotFound(msg % metadata_tag.name)
  835. def save(self, metadata_tag):
  836. try:
  837. self.db_api.metadef_tag_update(
  838. self.context, metadata_tag.namespace.namespace,
  839. metadata_tag.tag_id,
  840. self._format_metadef_tag_to_db(metadata_tag))
  841. except exception.NotFound as e:
  842. raise exception.NotFound(explanation=e.msg)
  843. return metadata_tag