Add osc command "schema"

Change-Id: I02f3b79b81843da8342489f87663629d7112cc07
This commit is contained in:
Darja Malyavkina 2016-10-03 22:27:29 +03:00
parent 5b30efba6f
commit b1bf7d47e9
7 changed files with 508 additions and 2 deletions

View File

@ -374,3 +374,44 @@ class TypeList(command.Lister):
column_headers = [c.capitalize() for c in columns]
return (column_headers,
data)
class TypeSchema(command.Lister):
"""Schema of type name."""
def get_parser(self, prog_name):
parser = super(TypeSchema, self).get_parser(prog_name)
parser.add_argument(
'type_name',
metavar='<TYPE_NAME>',
action=TypeMapperAction,
help='Name of artifact type.',
)
return parser
def take_action(self, parsed_args):
LOG.debug('take_action({0})'.format(parsed_args))
client = self.app.client_manager.artifact
data = client.artifacts.get_type_schema(
type_name=parsed_args.type_name)['properties']
columns = ('name', 'glare_type', 'mutable', 'required',
'sortable', 'filters', 'available_values')
column_headers = [c.capitalize() for c in columns]
table = []
for name, values in six.iteritems(data):
row = (
name,
values.get('glareType'),
values.get('mutable', False),
values.get('required_on_activate', True),
values.get('sortable', False),
values.get('filter_ops'),
values.get('enum', '')
)
table.append(row)
return (column_headers,
table)

View File

@ -15,11 +15,12 @@
import sys
from glareclient.common import utils as g_utils
import mock
from osc_lib.tests import utils
from glareclient.common import utils as g_utils
from glareclient.tests.unit.osc.v1 import fakes_schemas
blob_fixture = {
"status": "active",
"url": "fake_url",
@ -70,6 +71,10 @@ def mock_g_servs(*args, **kwargs):
'status': 'active'}
def mock_g_schema(*args, **kwargs):
return fakes_schemas.FIXTURE_SCHEMA
def mock_get_data_file(*args, **kwargs):
return 'data'
@ -90,6 +95,8 @@ class TestArtifacts(utils.TestCommand):
self.app.client_manager.artifact.artifacts.publish = mock_g_servs
self.app.client_manager.artifact.blobs.upload_blob = mock_g_servs
self.app.client_manager.artifact.blobs.download_blob = mock_g_servs
self.app.client_manager.artifact.artifacts.get_type_schema = \
mock_g_schema
g_utils.get_data_file = mock.MagicMock()
g_utils.get_data_file = mock_get_data_file
g_utils.save_blob = mock.MagicMock()

View File

@ -0,0 +1,372 @@
# Copyright 2016 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
FIXTURE_SCHEMA = {
u'name': u'sample_artifact',
u'properties': {
u'activated_at': {
u'description': u'Datetime when artifact has became active.',
u'filter_ops': [u'eq',
u'neq',
u'in',
u'gt',
u'gte',
u'lt',
u'lte'],
u'format': u'date-time',
u'glareType': u'DateTime',
u'readOnly': True,
u'required_on_activate': False,
u'sortable': True,
u'type': [u'string',
u'null']},
u'created_at': {
u'description': u'Datetime when artifact has been created.',
u'filter_ops': [u'eq',
u'neq',
u'in',
u'gt',
u'gte',
u'lt',
u'lte'],
u'format': u'date-time',
u'glareType': u'DateTime',
u'readOnly': True,
u'sortable': True,
u'type': u'string'},
u'description': {u'default': u'',
u'description': u'Artifact description.',
u'filter_ops': [u'eq',
u'neq',
u'in'],
u'glareType': u'String',
u'maxLength': 4096,
u'mutable': True,
u'required_on_activate': False,
u'type': [u'string',
u'null']},
u'icon': {u'additionalProperties': False,
u'description': u'Artifact icon.',
u'filter_ops': [],
u'glareType': u'Blob',
u'properties': {u'md5': {u'type': [u'string', u'null']},
u'sha1': {u'type': [u'string', u'null']},
u'sha256': {u'type': [u'string', u'null']},
u'content_type': {u'type': u'string'},
u'external': {u'type': u'boolean'},
u'size': {u'type': [u'number',
u'null']},
u'status': {u'enum': [u'saving',
u'active',
u'pending_delete'],
u'type': u'string'}},
u'required': [u'size',
u'md5', u'sha1', u'sha256',
u'external',
u'status',
u'content_type'],
u'required_on_activate': False,
u'type': [u'object',
u'null']},
u'id': {u'description': u'Artifact UUID.',
u'filter_ops': [u'eq',
u'neq',
u'in'],
u'glareType': u'String',
u'maxLength': 255,
u'pattern': u'^([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-'
u'fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}$',
u'readOnly': True,
u'sortable': True,
u'type': u'string'},
u'license': {u'description': u'Artifact license type.',
u'filter_ops': [u'eq',
u'neq',
u'in'],
u'glareType': u'String',
u'maxLength': 255,
u'required_on_activate': False,
u'type': [u'string',
u'null']},
u'license_url': {u'description': u'URL to artifact license.',
u'filter_ops': [u'eq',
u'neq',
u'in'],
u'glareType': u'String',
u'maxLength': 255,
u'required_on_activate': False,
u'type': [u'string',
u'null']},
u'metadata': {u'additionalProperties': {u'type': u'string'},
u'default': {},
u'description': u'Key-value dict with useful information'
u'about an artifact.',
u'filter_ops': [u'eq',
u'neq'],
u'glareType': u'StringDict',
u'maxProperties': 255,
u'required_on_activate': False,
u'type': [u'object',
u'null']},
u'name': {u'description': u'Artifact Name.',
u'filter_ops': [u'eq',
u'neq',
u'in'],
u'glareType': u'String',
u'maxLength': 255,
u'required_on_activate': False,
u'sortable': True,
u'type': u'string'},
u'owner': {u'description': u'ID of user/tenant who uploaded artifact.',
u'filter_ops': [u'eq',
u'neq',
u'in'],
u'glareType': u'String',
u'maxLength': 255,
u'readOnly': True,
u'required_on_activate': False,
u'sortable': True,
u'type': u'string'},
u'provided_by': {u'additionalProperties': False,
u'description': u'Info about artifact authors.',
u'filter_ops': [u'eq',
u'neq',
u'in'],
u'glareType': u'StringDict',
u'maxProperties': 255,
u'properties': {u'company': {u'type': u'string'},
u'href': {u'type': u'string'},
u'name': {u'type': u'string'}},
u'required_on_activate': False,
u'type': [u'object',
u'null']},
u'release': {u'default': [],
u'description': u'Target OpenStack release for artifact. '
u'It is usually the same when artifact '
u'was uploaded.',
u'filter_ops': [u'eq',
u'neq',
u'in'],
u'glareType': u'StringList',
u'items': {u'type': u'string'},
u'maxItems': 255,
u'required_on_activate': False,
u'type': [u'array',
u'null'],
u'unique': True},
u'status': {u'default': u'drafted',
u'description': u'Artifact status.',
u'enum': [u'drafted',
u'active',
u'deactivated',
u'deleted'],
u'filter_ops': [u'eq',
u'neq',
u'in'],
u'glareType': u'String',
u'sortable': True,
u'type': u'string'},
u'supported_by': {u'additionalProperties': {u'type': u'string'},
u'description': u'Info about persons who '
u'responsible for artifact support',
u'filter_ops': [u'eq',
u'neq',
u'in'],
u'glareType': u'StringDict',
u'maxProperties': 255,
u'required': [u'name'],
u'required_on_activate': False,
u'type': [u'object',
u'null']},
u'tags': {u'default': [],
u'description': u'List of tags added to Artifact.',
u'filter_ops': [u'eq',
u'neq',
u'in'],
u'glareType': u'StringList',
u'items': {u'type': u'string'},
u'maxItems': 255,
u'mutable': True,
u'required_on_activate': False,
u'type': [u'array',
u'null']},
u'updated_at': {
u'description': u'Datetime when artifact has been updated '
u'last time.',
u'filter_ops': [u'eq',
u'neq',
u'in',
u'gt',
u'gte',
u'lt',
u'lte'],
u'format': u'date-time',
u'glareType': u'DateTime',
u'readOnly': True,
u'sortable': True,
u'type': u'string'},
u'version': {u'default': u'0.0.0',
u'description': u'Artifact version(semver).',
u'filter_ops': [u'eq',
u'neq',
u'in',
u'gt',
u'gte',
u'lt',
u'lte'],
u'glareType': u'String',
u'pattern': u'/^([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-'
u'([0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?'
u'(?:\\+[0-9A-Za-z-]+)?$/',
u'required_on_activate': False,
u'sortable': True,
u'type': u'string'},
u'visibility': {u'default': u'private',
u'description': u'Artifact visibility that defines if '
u'artifact can be available to other '
u'users.',
u'filter_ops': [u'eq'],
u'glareType': u'String',
u'maxLength': 255,
u'sortable': True,
u'type': u'string'},
u'image': {u'additionalProperties': False,
u'description': u'Image binary.',
u'filter_ops': [],
u'glareType': u'Blob',
u'properties': {
u'md5': {u'type': [u'string', u'null']},
u'sha1': {u'type': [u'string', u'null']},
u'sha256': {u'type': [u'string', u'null']},
u'content_type': {u'type': u'string'},
u'external': {u'type': u'boolean'},
u'size': {u'type': [u'number',
u'null']},
u'status': {u'enum': [u'saving',
u'active',
u'pending_delete'],
u'type': u'string'}},
u'required': [u'size',
u'md5', u'sha1', u'sha256',
u'external',
u'status',
u'content_type'],
u'required_on_activate': False,
u'type': [u'object', u'null']},
u'package': {
u'additionalProperties': False,
u'description': u'Murano Package binary.',
u'filter_ops': [],
u'glareType': u'Blob',
u'properties': {u'md5': {u'type': [u'string', u'null']},
u'sha1': {u'type': [u'string', u'null']},
u'sha256': {u'type': [u'string', u'null']},
u'content_type': {u'type': u'string'},
u'external': {u'type': u'boolean'},
u'size': {u'type': [u'number',
u'null']},
u'status': {u'enum': [u'saving',
u'active',
u'pending_delete'],
u'type': u'string'}},
u'required': [u'size',
u'md5', u'sha1', u'sha256',
u'external',
u'status',
u'content_type'],
u'required_on_activate': False,
u'type': [u'object',
u'null']},
u'environment': {
u'additionalProperties': False,
u'description': u'Heat Environment text body.',
u'filter_ops': [],
u'glareType': u'Blob',
u'properties': {u'md5': {u'type': [u'string', u'null']},
u'sha1': {u'type': [u'string', u'null']},
u'sha256': {u'type': [u'string', u'null']},
u'content_type': {u'type': u'string'},
u'external': {u'type': u'boolean'},
u'size': {u'type': [u'number',
u'null']},
u'status': {u'enum': [u'saving',
u'active',
u'pending_delete'],
u'type': u'string'}},
u'required': [u'size',
u'md5', u'sha1', u'sha256',
u'external',
u'status',
u'content_type'],
u'type': [u'object',
u'null']},
u'blob': {
u'additionalProperties': False,
u'description': u'I am Blob',
u'filter_ops': [],
u'glareType': u'Blob',
u'mutable': True,
u'properties': {
u'md5': {u'type': [u'string', u'null']},
u'sha1': {u'type': [u'string', u'null']},
u'sha256': {u'type': [u'string', u'null']},
u'content_type': {
u'type': u'string'},
u'external': {
u'type': u'boolean'},
u'size': {u'type': [
u'number',
u'null']},
u'status': {
u'enum': [
u'saving',
u'active',
u'pending_delete'],
u'type': u'string'}},
u'required': [u'size',
u'md5', u'sha1', u'sha256',
u'external',
u'status',
u'content_type'],
u'required_on_activate': False,
u'type': [u'object',
u'null']},
u'template': {
u'additionalProperties': False,
u'description': u'TOSCA template body.',
u'filter_ops': [],
u'glareType': u'Blob',
u'properties': {
u'md5': {u'type': [u'string', u'null']},
u'sha1': {u'type': [u'string', u'null']},
u'sha256': {u'type': [u'string', u'null']},
u'content_type': {
u'type': u'string'},
u'external': {u'type': u'boolean'},
u'size': {u'type': [u'number',
u'null']},
u'status': {u'enum': [u'saving',
u'active',
u'pending_delete'],
u'type': u'string'}},
u'required': [u'size',
u'md5', u'sha1', u'sha256',
u'external',
u'status',
u'content_type'],
u'type': [u'object',
u'null']},
}
}

View File

@ -341,3 +341,69 @@ class TestPublishArtifacts(TestArtifacts):
name_fields = set([column[0] for column in data])
# Check that columns are correct
self.assertEqual(self.COLUMNS, name_fields)
class TypeSchema(TestArtifacts):
def setUp(self):
super(TypeSchema, self).setUp()
self.artifact_mock.call.return_value = \
api_art.Controller(self.http, type_name='sample_artifact')
# Command to test
self.cmd = osc_art.TypeSchema(self.app, None)
def test_get_schema(self):
arglist = ['sample_artifact']
verify = [('type_name', 'sample_artifact')]
parsed_args = self.check_parser(self.cmd, arglist, verify)
columns, data = self.cmd.take_action(parsed_args)
exp_columns = ['Name', 'Glare_type', 'Mutable', 'Required',
'Sortable', 'Filters', 'Available_values']
exp_data = [
(u'image', u'Blob', False, False, False, [], ''),
(u'updated_at', u'DateTime', False, True, True,
[u'eq', u'neq', u'in', u'gt', u'gte', u'lt', u'lte'], ''),
(u'owner', u'String', False, False, True,
[u'eq', u'neq', u'in'], ''),
(u'provided_by', u'StringDict', False, False, False,
[u'eq', u'neq', u'in'], ''),
(u'id', u'String', False, True, True, [u'eq', u'neq', u'in'], ''),
(u'environment', u'Blob', False, True, False, [], ''),
(u'version', u'String', False, False, True,
[u'eq', u'neq', u'in', u'gt', u'gte', u'lt', u'lte'], ''),
(u'blob', u'Blob', True, False, False, [], ''),
(u'template', u'Blob', False, True, False, [], ''),
(u'metadata', u'StringDict', False, False, False,
[u'eq', u'neq'], ''),
(u'status', u'String', False, True, True, [u'eq', u'neq', u'in'],
[u'drafted', u'active', u'deactivated', u'deleted']),
(u'description', u'String', True, False, False,
[u'eq', u'neq', u'in'], ''),
(u'tags', u'StringList', True, False, False,
[u'eq', u'neq', u'in'], ''),
(u'activated_at', u'DateTime', False, False, True,
[u'eq', u'neq', u'in', u'gt', u'gte', u'lt', u'lte'], ''),
(u'supported_by', u'StringDict', False, False, False,
[u'eq', u'neq', u'in'], ''),
(u'visibility', u'String', False, True, True, [u'eq'], ''),
(u'icon', u'Blob', False, False, False, [], ''),
(u'name', u'String', False, False, True,
[u'eq', u'neq', u'in'], ''),
(u'license', u'String', False, False, False,
[u'eq', u'neq', u'in'], ''),
(u'package', u'Blob', False, False, False, [], ''),
(u'created_at', u'DateTime', False, True, True,
[u'eq', u'neq', u'in', u'gt', u'gte', u'lt', u'lte'], ''),
(u'license_url', u'String', False, False, False,
[u'eq', u'neq', u'in'], ''),
(u'release', u'StringList', False, False, False,
[u'eq', u'neq', u'in'], '')]
data.sort(key=lambda x: x[0])
exp_data.sort(key=lambda x: x[0])
# Check that columns are correct
self.assertEqual(exp_columns, columns)
self.assertEqual(exp_data, data)

View File

@ -198,5 +198,16 @@ data_fixtures = {
'heat_environments': {'name': 'heat_environments',
'version': '1.0'}}}
)
},
'/schemas/images': {
'GET': (
{},
{'schemas': {
'images': {'name': 'images',
'version': '1.0',
'properties': {'foo': 'bar'}
}}}
)
}
}

View File

@ -374,3 +374,11 @@ class TestController(testtools.TestCase):
expect_call = [('GET', '/schemas', {}, None)]
self.assertEqual(expect_call, self.api.calls)
self.assertEqual(expect_data, data)
def test_get_schema(self):
data = self.controller.get_type_schema(type_name='images')
expect_data = {'name': 'images', 'version': '1.0',
'properties': {'foo': 'bar'}}
expect_call = [('GET', '/schemas/images', {}, None)]
self.assertEqual(expect_call, self.api.calls)
self.assertEqual(expect_data, data)

View File

@ -47,6 +47,7 @@ openstack.artifact.v1 =
artifact_upload = glareclient.osc.v1.blobs:UploadBlob
artifact_download = glareclient.osc.v1.blobs:DownloadBlob
artifact_type-list = glareclient.osc.v1.artifacts:TypeList
artifact_schema = glareclient.osc.v1.artifacts:TypeSchema
[build_sphinx]
source-dir = doc/source