Add support for release network_metadata serialziation

Patch adds support for release network metadata serialization,
by adding additional handler on release/<release_id>/networks for PUT/GET
requestss
Proper validation for release networking data

Why not to add another field in release handler:
1. networks_metadata it is ours internal naming
2. url release/<release_id>/networks makes more sense for client
  applications

Change-Id: I205ec786f1a9d772905ff22e426c1ce8a6d4d6e7
Closes-Bug: 1381557
This commit is contained in:
Dima Shulyak 2014-11-05 13:06:24 +02:00
parent 35946b1f22
commit 4815936beb
8 changed files with 274 additions and 36 deletions

View File

@ -21,9 +21,8 @@ Handlers dealing with releases
from nailgun.api.v1.handlers.base import CollectionHandler from nailgun.api.v1.handlers.base import CollectionHandler
from nailgun.api.v1.handlers.base import content_json from nailgun.api.v1.handlers.base import content_json
from nailgun.api.v1.handlers.base import SingleHandler from nailgun.api.v1.handlers.base import SingleHandler
from nailgun.api.v1.validators.release import ReleaseNetworksValidator
from nailgun.api.v1.validators.release import ReleaseValidator from nailgun.api.v1.validators.release import ReleaseValidator
from nailgun.objects import Release from nailgun.objects import Release
from nailgun.objects import ReleaseCollection from nailgun.objects import ReleaseCollection
@ -50,3 +49,51 @@ class ReleaseCollectionHandler(CollectionHandler):
""" """
q = sorted(self.collection.all(), reverse=True) q = sorted(self.collection.all(), reverse=True)
return self.collection.to_json(q) return self.collection.to_json(q)
class ReleaseNetworksHandler(SingleHandler):
"""Release Handler for network metadata
"""
single = Release
validator = ReleaseNetworksValidator
@content_json
def GET(self, obj_id):
"""Read release networks metadata
:returns: Release networks metadata
:http: * 201 (object successfully created)
* 400 (invalid object data specified)
* 404 (release object not found)
"""
obj = self.get_object_or_404(self.single, obj_id)
return obj['networks_metadata']
@content_json
def PUT(self, obj_id):
"""Updates release networks metadata
:returns: Release networks metadata
:http: * 201 (object successfully created)
* 400 (invalid object data specified)
* 404 (release object not found)
"""
obj = self.get_object_or_404(self.single, obj_id)
data = self.checked_data()
self.single.update(obj, {'networks_metadata': data})
return obj['networks_metadata']
def POST(self, obj_id):
"""Creation of metadata disallowed
:http: * 405 (method not supported)
"""
raise self.http(405, 'Create not supported for this entity')
def DELETE(self, obj_id):
"""Deletion of metadata disallowed
:http: * 405 (method not supported)
"""
raise self.http(405, 'Delete not supported for this entity')

View File

@ -77,10 +77,10 @@ from nailgun.api.v1.handlers.orchestrator import DeploySelectedNodes
from nailgun.api.v1.handlers.orchestrator import ProvisioningInfo from nailgun.api.v1.handlers.orchestrator import ProvisioningInfo
from nailgun.api.v1.handlers.orchestrator import ProvisionSelectedNodes from nailgun.api.v1.handlers.orchestrator import ProvisionSelectedNodes
from nailgun.api.v1.handlers.registration import FuelKeyHandler from nailgun.api.v1.handlers.registration import FuelKeyHandler
from nailgun.api.v1.handlers.release import ReleaseCollectionHandler from nailgun.api.v1.handlers.release import ReleaseCollectionHandler
from nailgun.api.v1.handlers.release import ReleaseHandler from nailgun.api.v1.handlers.release import ReleaseHandler
from nailgun.api.v1.handlers.release import ReleaseNetworksHandler
from nailgun.api.v1.handlers.tasks import TaskCollectionHandler from nailgun.api.v1.handlers.tasks import TaskCollectionHandler
from nailgun.api.v1.handlers.tasks import TaskHandler from nailgun.api.v1.handlers.tasks import TaskHandler
@ -98,6 +98,8 @@ urls = (
ReleaseCollectionHandler, ReleaseCollectionHandler,
r'/releases/(?P<obj_id>\d+)/?$', r'/releases/(?P<obj_id>\d+)/?$',
ReleaseHandler, ReleaseHandler,
r'/releases/(?P<obj_id>\d+)/networks/?$',
ReleaseNetworksHandler,
r'/clusters/?$', r'/clusters/?$',
ClusterCollectionHandler, ClusterCollectionHandler,

View File

@ -0,0 +1,63 @@
# Copyright 2014 Mirantis, Inc.
#
# 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.
# Non-complete JSON Schema for validating IP addresses.
# Use it for better readability in the main schema.
_IP_ADDRESS_SCHEMA = {
'type': 'string',
'format': 'ipv4',
}
# Non-complete JSON schema for validating NET addresses.
# Use it for better readability in the main schema.
_NET_ADDRESS_SCHEMA = {
'type': 'string',
# check for valid ip address and route prefix
# e.g: 192.168.0.0/24
'pattern': '^(({octet}\.){{3}}{octet})({prefix})?$'.format(
octet='(2(5[0-5]|[0-4][0-9])|[01]?[0-9][0-9]?)',
prefix='/(3[012]|[12]?[0-9])'
),
}
# Non-complete JSON Schema for validating MAC addresses.
# Use it for better readability in the main schema.
_MAC_ADDRESS_SCHEMA = {
'type': 'string',
'pattern': '^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$',
}
NETWORK = {
'type': 'object',
'properties': {
'name': {'type': 'string'},
'cidr': _NET_ADDRESS_SCHEMA,
'gateway': _IP_ADDRESS_SCHEMA,
'ip_range': {'type': 'array'},
'vlan_start': {'type': ['null', 'number']},
'use_gateway': {'type': 'boolean'},
'notation': {'type': ['string', 'null']},
'render_type': {'type': ['null', 'string']},
'map_priority': {'type': 'number'},
'configurable': {'type': 'boolean'},
'floating_range_var': {'type': 'string'},
'ext_net_data': {'type': 'array'},
'assign_vip': {'type': 'boolean'}},
'required': ['name']
}

View File

@ -14,34 +14,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
# Non-complete JSON Schema for validating IP addresses. from nailgun.api.v1.validators.json_schema import networks
# Use it for better readability in the main schema.
_IP_ADDRESS_SCHEMA = {
'type': 'string',
'format': 'ipv4',
}
# Non-complete JSON schema for validating NET addresses.
# Use it for better readability in the main schema.
_NET_ADDRESS_SCHEMA = {
'type': 'string',
# check for valid ip address and route prefix
# e.g: 192.168.0.0/24
'pattern': '^(({octet}\.){{3}}{octet})({prefix})?$'.format(
octet='(2(5[0-5]|[0-4][0-9])|[01]?[0-9][0-9]?)',
prefix='/(3[012]|[12]?[0-9])'
),
}
# Non-complete JSON Schema for validating MAC addresses.
# Use it for better readability in the main schema.
_MAC_ADDRESS_SCHEMA = {
'type': 'string',
'pattern': '^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$',
}
# TODO(@ikalnitsky): add `required` properties to all needed objects # TODO(@ikalnitsky): add `required` properties to all needed objects
@ -51,8 +24,8 @@ node_format_schema = {
'description': 'Object with node description', 'description': 'Object with node description',
'type': 'object', 'type': 'object',
'properties': { 'properties': {
'mac': _MAC_ADDRESS_SCHEMA, 'mac': networks._MAC_ADDRESS_SCHEMA,
'ip': _IP_ADDRESS_SCHEMA, 'ip': networks._IP_ADDRESS_SCHEMA,
'meta': { 'meta': {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
@ -63,9 +36,9 @@ node_format_schema = {
'items': { 'items': {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
'ip': _IP_ADDRESS_SCHEMA, 'ip': networks._IP_ADDRESS_SCHEMA,
'netmask': _NET_ADDRESS_SCHEMA, 'netmask': networks._NET_ADDRESS_SCHEMA,
'mac': _MAC_ADDRESS_SCHEMA, 'mac': networks._MAC_ADDRESS_SCHEMA,
'state': {'type': 'string'}, 'state': {'type': 'string'},
'name': {'type': 'string'}, 'name': {'type': 'string'},
} }

View File

@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
# Copyright 2014 Mirantis, Inc.
#
# 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.
from nailgun.api.v1.validators.json_schema import networks
NOVA_NETWORK_SCHEMA = {
'type': 'object',
'properties': {
'networks': {
'type': 'array',
'items': networks.NETWORK},
'config': {'type': 'object'}},
'required': ['networks', 'config']
}
NEUTRON_SCHEMA = {
'type': 'object',
'properties': {
'networks': {
'type': 'array',
'items': networks.NETWORK},
'config': {'type': 'object'}},
'required': ['networks', 'config']
}
NETWORKS_SCHEMA = {
'type': 'object',
'properties': {
'nova_network': NOVA_NETWORK_SCHEMA,
'neutron': NEUTRON_SCHEMA,
},
'required': ['nova_network', 'neutron']
}

View File

@ -16,6 +16,7 @@
from sqlalchemy import not_ from sqlalchemy import not_
from nailgun.api.v1.validators.base import BasicValidator from nailgun.api.v1.validators.base import BasicValidator
from nailgun.api.v1.validators.json_schema import release
from nailgun.db import db from nailgun.db import db
from nailgun.db.sqlalchemy.models import Release from nailgun.db.sqlalchemy.models import Release
from nailgun.errors import errors from nailgun.errors import errors
@ -126,3 +127,17 @@ class ReleaseValidator(BasicValidator):
"Can't delete release with " "Can't delete release with "
"clusters assigned" "clusters assigned"
) )
class ReleaseNetworksValidator(BasicValidator):
@classmethod
def validate(cls, data):
parsed = super(ReleaseNetworksValidator, cls).validate(data)
cls.validate_schema(parsed)
return parsed
@classmethod
def validate_schema(cls, data):
return super(ReleaseNetworksValidator, cls).validate_schema(
data, release.NETWORKS_SCHEMA)

View File

@ -0,0 +1,62 @@
# Copyright 2014 Mirantis, Inc.
#
# 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.
from nailgun.openstack.common import jsonutils
from nailgun.test.base import BaseIntegrationTest
from nailgun.test.base import reverse
class TestReleaseNetworksHandlers(BaseIntegrationTest):
def setUp(self):
super(TestReleaseNetworksHandlers, self).setUp()
self.release = self.env.create_release()
def test_get(self):
resp = self.app.get(
reverse('ReleaseNetworksHandler',
kwargs={'obj_id': self.release.id}),
headers=self.default_headers
)
self.assertEqual(resp.status_code, 200)
self.assertEqual(self.release['networks_metadata'], resp.json)
def test_post(self):
resp = self.app.post(
reverse('ReleaseNetworksHandler',
kwargs={'obj_id': self.release.id}),
headers=self.default_headers,
expect_errors=True
)
self.assertEqual(resp.status_code, 405)
def test_delete(self):
resp = self.app.delete(
reverse('ReleaseNetworksHandler',
kwargs={'obj_id': self.release.id}),
headers=self.default_headers,
expect_errors=True
)
self.assertEqual(resp.status_code, 405)
def test_put(self):
data = jsonutils.dumps(self.release['networks_metadata'])
resp = self.app.put(
reverse('ReleaseNetworksHandler',
kwargs={'obj_id': self.release.id}),
data,
headers=self.default_headers
)
self.assertEqual(resp.status_code, 200)

View File

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# Copyright 2014 Mirantis, Inc.
#
# 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.
import jsonschema
from nailgun.api.v1.validators.json_schema import release
from nailgun.test import base
class TestRelaseNetworksSchema(base.BaseTestCase):
def test_networks_schema(self):
checker = jsonschema.FormatChecker()
release_data = self.env.create_release()
jsonschema.validate(
release_data['networks_metadata'],
release.NETWORKS_SCHEMA,
format_checker=checker)