API schema validation
Story: 2007278 Task: #38717 Change-Id: I7a6fc62e8f2c0c3cc21560f9f889d0fe136ca33e Signed-off-by: Tomi Juvonen <tomi.juvonen@nokia.com>
This commit is contained in:
parent
e6b796f6b7
commit
e3004fec86
|
@ -47,7 +47,7 @@ Response codes
|
||||||
Update maintenance session (planned future functionality)
|
Update maintenance session (planned future functionality)
|
||||||
=========================================================
|
=========================================================
|
||||||
|
|
||||||
.. rest_method:: POST /v1/maintenance/{session_id}/
|
.. rest_method:: PUT /v1/maintenance/{session_id}/
|
||||||
|
|
||||||
Update existing maintenance session. This can be used to continue a failed
|
Update existing maintenance session. This can be used to continue a failed
|
||||||
session.
|
session.
|
||||||
|
|
|
@ -17,9 +17,8 @@ session_id:
|
||||||
description: |
|
description: |
|
||||||
Session ID
|
Session ID
|
||||||
in: path
|
in: path
|
||||||
required: false
|
required: true
|
||||||
type: string
|
type: string
|
||||||
min_version: \> 1
|
|
||||||
|
|
||||||
uuid-path:
|
uuid-path:
|
||||||
description: |
|
description: |
|
||||||
|
@ -68,7 +67,7 @@ action-plugins:
|
||||||
description: |
|
description: |
|
||||||
List of action plug-ins.
|
List of action plug-ins.
|
||||||
in: body
|
in: body
|
||||||
required: true
|
required: false
|
||||||
type: list of dictionaries
|
type: list of dictionaries
|
||||||
|
|
||||||
boolean:
|
boolean:
|
||||||
|
@ -90,7 +89,7 @@ hosts:
|
||||||
Hosts to be maintained. An empty list can indicate hosts are to be
|
Hosts to be maintained. An empty list can indicate hosts are to be
|
||||||
discovered.
|
discovered.
|
||||||
in: body
|
in: body
|
||||||
required: true
|
required: false
|
||||||
type: list of strings
|
type: list of strings
|
||||||
|
|
||||||
instance-action:
|
instance-action:
|
||||||
|
@ -102,7 +101,8 @@ instance-action:
|
||||||
|
|
||||||
instance-actions:
|
instance-actions:
|
||||||
description: |
|
description: |
|
||||||
instance ID : action string
|
instance ID : action string. This variable is not needed in reply to state
|
||||||
|
MAINTENANCE, SCALE_IN or MAINTENANCE_COMPLETE
|
||||||
in: body
|
in: body
|
||||||
required: true
|
required: true
|
||||||
type: dictionary
|
type: dictionary
|
||||||
|
@ -133,7 +133,10 @@ lead-time:
|
||||||
How long lead time VNF needs for 'migration_type' operation. VNF needs to
|
How long lead time VNF needs for 'migration_type' operation. VNF needs to
|
||||||
report back to Fenix as soon as it is ready, but at least within this
|
report back to Fenix as soon as it is ready, but at least within this
|
||||||
time. Reporting as fast as can is crucial for optimizing
|
time. Reporting as fast as can is crucial for optimizing
|
||||||
infrastructure upgrade/maintenance.
|
infrastructure upgrade/maintenance. Zero value means interaction with
|
||||||
|
VNFM is not used for this instance, but instance_group recovery_time
|
||||||
|
needs to be obeyed towards max_impacted_members.
|
||||||
|
|
||||||
in: body
|
in: body
|
||||||
required: true
|
required: true
|
||||||
type: integer
|
type: integer
|
||||||
|
|
|
@ -127,7 +127,7 @@ Request
|
||||||
- instance_id: uuid-path
|
- instance_id: uuid-path
|
||||||
- instance_id: uuid
|
- instance_id: uuid
|
||||||
- group_id: group-uuid
|
- group_id: group-uuid
|
||||||
- name: instance-name
|
- instance_name: instance-name
|
||||||
- migration_type: migration-type
|
- migration_type: migration-type
|
||||||
- max_interruption_time: max-interruption-time
|
- max_interruption_time: max-interruption-time
|
||||||
- resource_mitigation: resource-mitigation
|
- resource_mitigation: resource-mitigation
|
||||||
|
@ -160,7 +160,7 @@ Request
|
||||||
- instance_id: uuid-path
|
- instance_id: uuid-path
|
||||||
- instance_id: uuid
|
- instance_id: uuid
|
||||||
- group_id: group-uuid
|
- group_id: group-uuid
|
||||||
- name: instance-name
|
- instance_name: instance-name
|
||||||
- migration_type: migration-type
|
- migration_type: migration-type
|
||||||
- max_interruption_time: max-interruption-time
|
- max_interruption_time: max-interruption-time
|
||||||
- resource_mitigation: resource-mitigation
|
- resource_mitigation: resource-mitigation
|
||||||
|
@ -218,7 +218,7 @@ Request
|
||||||
- project_id: uuid
|
- project_id: uuid
|
||||||
- instance_id: uuid-path
|
- instance_id: uuid-path
|
||||||
- instance_id: uuid
|
- instance_id: uuid
|
||||||
- name: instance-name
|
- group_name: instance-group
|
||||||
- migration_type: migration-type
|
- migration_type: migration-type
|
||||||
- max_interruption_time: max-interruption-time
|
- max_interruption_time: max-interruption-time
|
||||||
- resource_mitigation: resource-mitigation
|
- resource_mitigation: resource-mitigation
|
||||||
|
@ -250,7 +250,7 @@ Request
|
||||||
- group_id: group-uuid-path
|
- group_id: group-uuid-path
|
||||||
- group_id: group-uuid
|
- group_id: group-uuid
|
||||||
- project_id: uuid
|
- project_id: uuid
|
||||||
- name: instance-group
|
- group_name: instance-group
|
||||||
- anti_affinity_group: boolean
|
- anti_affinity_group: boolean
|
||||||
- max_instances_per_host: max-instances-per-host
|
- max_instances_per_host: max-instances-per-host
|
||||||
- max_impacted_members: max-impacted-members
|
- max_impacted_members: max-impacted-members
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"project_id": "1ad1154137ac41799cefd5caebae379b",
|
"project_id": "1ad1154137ac41799cefd5caebae379b",
|
||||||
"group_id": "a01d192c-328e-4708-9b3c-9d716cd24a92",
|
"group_id": "a01d192c-328e-4708-9b3c-9d716cd24a92",
|
||||||
"name": "vm_ha_group",
|
"group_name": "vm_ha_group",
|
||||||
"anti_affinity_group": "True",
|
"anti_affinity_group": True,
|
||||||
"max_instances_per_host": 1,
|
"max_instances_per_host": 1,
|
||||||
"max_impacted_members": 1,
|
"max_impacted_members": 1,
|
||||||
"recovery_time": 15,
|
"recovery_time": 15,
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import jsonschema
|
||||||
from pecan import abort
|
from pecan import abort
|
||||||
from pecan import expose
|
from pecan import expose
|
||||||
from pecan import request
|
from pecan import request
|
||||||
|
@ -24,6 +25,7 @@ from oslo_log import log
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
|
|
||||||
from fenix.api.v1 import maintenance
|
from fenix.api.v1 import maintenance
|
||||||
|
from fenix.api.v1 import schema
|
||||||
from fenix import policy
|
from fenix import policy
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
@ -43,6 +45,12 @@ class ProjectController(rest.RestController):
|
||||||
if request.body:
|
if request.body:
|
||||||
LOG.error("Unexpected data")
|
LOG.error("Unexpected data")
|
||||||
abort(400)
|
abort(400)
|
||||||
|
try:
|
||||||
|
jsonschema.validate(session_id, schema.uid)
|
||||||
|
jsonschema.validate(project_id, schema.uid)
|
||||||
|
except jsonschema.exceptions.ValidationError as e:
|
||||||
|
LOG.error(str(e.message))
|
||||||
|
abort(422)
|
||||||
engine_data = self.engine_rpcapi.project_get_session(session_id,
|
engine_data = self.engine_rpcapi.project_get_session(session_id,
|
||||||
project_id)
|
project_id)
|
||||||
try:
|
try:
|
||||||
|
@ -55,6 +63,13 @@ class ProjectController(rest.RestController):
|
||||||
@expose(content_type='application/json')
|
@expose(content_type='application/json')
|
||||||
def put(self, session_id, project_id):
|
def put(self, session_id, project_id):
|
||||||
data = json.loads(request.body.decode('utf8'))
|
data = json.loads(request.body.decode('utf8'))
|
||||||
|
try:
|
||||||
|
jsonschema.validate(session_id, schema.uid)
|
||||||
|
jsonschema.validate(project_id, schema.uid)
|
||||||
|
jsonschema.validate(data, schema.maintenance_session_project_put)
|
||||||
|
except jsonschema.exceptions.ValidationError as e:
|
||||||
|
LOG.error(str(e.message))
|
||||||
|
abort(422)
|
||||||
engine_data = self.engine_rpcapi.project_update_session(session_id,
|
engine_data = self.engine_rpcapi.project_update_session(session_id,
|
||||||
project_id,
|
project_id,
|
||||||
data)
|
data)
|
||||||
|
@ -76,6 +91,16 @@ class ProjectInstanceController(rest.RestController):
|
||||||
@expose(content_type='application/json')
|
@expose(content_type='application/json')
|
||||||
def put(self, session_id, project_id, instance_id):
|
def put(self, session_id, project_id, instance_id):
|
||||||
data = json.loads(request.body.decode('utf8'))
|
data = json.loads(request.body.decode('utf8'))
|
||||||
|
try:
|
||||||
|
jsonschema.validate(session_id, schema.uid)
|
||||||
|
jsonschema.validate(project_id, schema.uid)
|
||||||
|
jsonschema.validate(instance_id, schema.uid)
|
||||||
|
jsonschema.validate(
|
||||||
|
data,
|
||||||
|
schema.maintenance_session_project_instance_put)
|
||||||
|
except jsonschema.exceptions.ValidationError as e:
|
||||||
|
LOG.error(str(e.message))
|
||||||
|
abort(422)
|
||||||
engine_data = (
|
engine_data = (
|
||||||
self.engine_rpcapi.project_update_session_instance(session_id,
|
self.engine_rpcapi.project_update_session_instance(session_id,
|
||||||
project_id,
|
project_id,
|
||||||
|
@ -98,13 +123,18 @@ class SessionController(rest.RestController):
|
||||||
@policy.authorize('maintenance:session', 'get')
|
@policy.authorize('maintenance:session', 'get')
|
||||||
@expose(content_type='application/json')
|
@expose(content_type='application/json')
|
||||||
def get(self, session_id):
|
def get(self, session_id):
|
||||||
|
try:
|
||||||
|
jsonschema.validate(session_id, schema.uid)
|
||||||
|
except jsonschema.exceptions.ValidationError as e:
|
||||||
|
LOG.error(str(e.message))
|
||||||
|
abort(422)
|
||||||
if request.body:
|
if request.body:
|
||||||
LOG.error("Unexpected data")
|
LOG.error("Unexpected data")
|
||||||
abort(400)
|
abort(400)
|
||||||
session = self.engine_rpcapi.admin_get_session(session_id)
|
session = self.engine_rpcapi.admin_get_session(session_id)
|
||||||
if session is None:
|
if session is None:
|
||||||
response.status = 404
|
LOG.error("Invalid session")
|
||||||
return {"error": "Invalid session"}
|
abort(404)
|
||||||
try:
|
try:
|
||||||
response.text = jsonutils.dumps(session)
|
response.text = jsonutils.dumps(session)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
|
@ -115,6 +145,13 @@ class SessionController(rest.RestController):
|
||||||
@expose(content_type='application/json')
|
@expose(content_type='application/json')
|
||||||
def put(self, session_id):
|
def put(self, session_id):
|
||||||
data = json.loads(request.body.decode('utf8'))
|
data = json.loads(request.body.decode('utf8'))
|
||||||
|
try:
|
||||||
|
jsonschema.validate(session_id, schema.uid)
|
||||||
|
# TBD implement this API
|
||||||
|
# jsonschema.validate(data, schema.maintenance_session_put)
|
||||||
|
except jsonschema.exceptions.ValidationError as e:
|
||||||
|
LOG.error(str(e.message))
|
||||||
|
abort(422)
|
||||||
engine_data = self.engine_rpcapi.admin_update_session(session_id, data)
|
engine_data = self.engine_rpcapi.admin_update_session(session_id, data)
|
||||||
try:
|
try:
|
||||||
response.text = jsonutils.dumps(engine_data)
|
response.text = jsonutils.dumps(engine_data)
|
||||||
|
@ -125,6 +162,11 @@ class SessionController(rest.RestController):
|
||||||
@policy.authorize('maintenance:session', 'delete')
|
@policy.authorize('maintenance:session', 'delete')
|
||||||
@expose(content_type='application/json')
|
@expose(content_type='application/json')
|
||||||
def delete(self, session_id):
|
def delete(self, session_id):
|
||||||
|
try:
|
||||||
|
jsonschema.validate(session_id, schema.uid)
|
||||||
|
except jsonschema.exceptions.ValidationError as e:
|
||||||
|
LOG.error(str(e.message))
|
||||||
|
abort(422)
|
||||||
if request.body:
|
if request.body:
|
||||||
LOG.error("Unexpected data")
|
LOG.error("Unexpected data")
|
||||||
abort(400)
|
abort(400)
|
||||||
|
@ -160,10 +202,15 @@ class MaintenanceController(rest.RestController):
|
||||||
@expose(content_type='application/json')
|
@expose(content_type='application/json')
|
||||||
def post(self):
|
def post(self):
|
||||||
data = json.loads(request.body.decode('utf8'))
|
data = json.loads(request.body.decode('utf8'))
|
||||||
|
try:
|
||||||
|
jsonschema.validate(data, schema.maintenance_post)
|
||||||
|
except jsonschema.exceptions.ValidationError as e:
|
||||||
|
LOG.error(str(e.message))
|
||||||
|
abort(422)
|
||||||
session = self.engine_rpcapi.admin_create_session(data)
|
session = self.engine_rpcapi.admin_create_session(data)
|
||||||
if session is None:
|
if session is None:
|
||||||
response.status = 509
|
LOG.error("Too many sessions")
|
||||||
return {"error": "Too many sessions"}
|
abort(509)
|
||||||
try:
|
try:
|
||||||
response.text = jsonutils.dumps(session)
|
response.text = jsonutils.dumps(session)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
|
@ -181,13 +228,18 @@ class InstanceController(rest.RestController):
|
||||||
@policy.authorize('instance', 'get')
|
@policy.authorize('instance', 'get')
|
||||||
@expose(content_type='application/json')
|
@expose(content_type='application/json')
|
||||||
def get(self, instance_id):
|
def get(self, instance_id):
|
||||||
|
try:
|
||||||
|
jsonschema.validate(instance_id, schema.uid)
|
||||||
|
except jsonschema.exceptions.ValidationError as e:
|
||||||
|
LOG.error(str(e.message))
|
||||||
|
abort(422)
|
||||||
if request.body:
|
if request.body:
|
||||||
LOG.error("Unexpected data")
|
LOG.error("Unexpected data")
|
||||||
abort(400)
|
abort(400)
|
||||||
session = self.engine_rpcapi.get_instance(instance_id)
|
session = self.engine_rpcapi.get_instance(instance_id)
|
||||||
if session is None:
|
if session is None:
|
||||||
response.status = 404
|
LOG.error("Invalid session")
|
||||||
return {"error": "Invalid session"}
|
abort(404)
|
||||||
try:
|
try:
|
||||||
response.text = jsonutils.dumps(session)
|
response.text = jsonutils.dumps(session)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
|
@ -198,6 +250,12 @@ class InstanceController(rest.RestController):
|
||||||
@expose(content_type='application/json')
|
@expose(content_type='application/json')
|
||||||
def put(self, instance_id):
|
def put(self, instance_id):
|
||||||
data = json.loads(request.body.decode('utf8'))
|
data = json.loads(request.body.decode('utf8'))
|
||||||
|
try:
|
||||||
|
jsonschema.validate(instance_id, schema.uid)
|
||||||
|
jsonschema.validate(data, schema.instance_put)
|
||||||
|
except jsonschema.exceptions.ValidationError as e:
|
||||||
|
LOG.error(str(e.message))
|
||||||
|
abort(422)
|
||||||
engine_data = self.engine_rpcapi.update_instance(instance_id,
|
engine_data = self.engine_rpcapi.update_instance(instance_id,
|
||||||
data)
|
data)
|
||||||
try:
|
try:
|
||||||
|
@ -209,6 +267,11 @@ class InstanceController(rest.RestController):
|
||||||
@policy.authorize('instance', 'delete')
|
@policy.authorize('instance', 'delete')
|
||||||
@expose(content_type='application/json')
|
@expose(content_type='application/json')
|
||||||
def delete(self, instance_id):
|
def delete(self, instance_id):
|
||||||
|
try:
|
||||||
|
jsonschema.validate(instance_id, schema.uid)
|
||||||
|
except jsonschema.exceptions.ValidationError as e:
|
||||||
|
LOG.error(str(e.message))
|
||||||
|
abort(422)
|
||||||
if request.body:
|
if request.body:
|
||||||
LOG.error("Unexpected data")
|
LOG.error("Unexpected data")
|
||||||
abort(400)
|
abort(400)
|
||||||
|
@ -230,13 +293,18 @@ class InstanceGroupController(rest.RestController):
|
||||||
@policy.authorize('instance_group', 'get')
|
@policy.authorize('instance_group', 'get')
|
||||||
@expose(content_type='application/json')
|
@expose(content_type='application/json')
|
||||||
def get(self, group_id):
|
def get(self, group_id):
|
||||||
|
try:
|
||||||
|
jsonschema.validate(group_id, schema.uid)
|
||||||
|
except jsonschema.exceptions.ValidationError as e:
|
||||||
|
LOG.error(str(e.message))
|
||||||
|
abort(422)
|
||||||
if request.body:
|
if request.body:
|
||||||
LOG.error("Unexpected data")
|
LOG.error("Unexpected data")
|
||||||
abort(400)
|
abort(400)
|
||||||
session = self.engine_rpcapi.get_instance_group(group_id)
|
session = self.engine_rpcapi.get_instance_group(group_id)
|
||||||
if session is None:
|
if session is None:
|
||||||
response.status = 404
|
LOG.error("Invalid session")
|
||||||
return {"error": "Invalid session"}
|
abort(404)
|
||||||
try:
|
try:
|
||||||
response.text = jsonutils.dumps(session)
|
response.text = jsonutils.dumps(session)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
|
@ -247,6 +315,12 @@ class InstanceGroupController(rest.RestController):
|
||||||
@expose(content_type='application/json')
|
@expose(content_type='application/json')
|
||||||
def put(self, group_id):
|
def put(self, group_id):
|
||||||
data = json.loads(request.body.decode('utf8'))
|
data = json.loads(request.body.decode('utf8'))
|
||||||
|
try:
|
||||||
|
jsonschema.validate(group_id, schema.uid)
|
||||||
|
jsonschema.validate(data, schema.instance_group_put)
|
||||||
|
except jsonschema.exceptions.ValidationError as e:
|
||||||
|
LOG.error(str(e.message))
|
||||||
|
abort(422)
|
||||||
engine_data = (
|
engine_data = (
|
||||||
self.engine_rpcapi.update_instance_group(group_id, data))
|
self.engine_rpcapi.update_instance_group(group_id, data))
|
||||||
try:
|
try:
|
||||||
|
@ -258,6 +332,11 @@ class InstanceGroupController(rest.RestController):
|
||||||
@policy.authorize('instance_group', 'delete')
|
@policy.authorize('instance_group', 'delete')
|
||||||
@expose(content_type='application/json')
|
@expose(content_type='application/json')
|
||||||
def delete(self, group_id):
|
def delete(self, group_id):
|
||||||
|
try:
|
||||||
|
jsonschema.validate(group_id, schema.uid)
|
||||||
|
except jsonschema.exceptions.ValidationError as e:
|
||||||
|
LOG.error(str(e.message))
|
||||||
|
abort(422)
|
||||||
if request.body:
|
if request.body:
|
||||||
LOG.error("Unexpected data")
|
LOG.error("Unexpected data")
|
||||||
abort(400)
|
abort(400)
|
||||||
|
|
|
@ -0,0 +1,202 @@
|
||||||
|
# Copyright 2020 OpenStack Foundation
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
uid = {
|
||||||
|
'type': 'string',
|
||||||
|
'minLength': 8,
|
||||||
|
'maxLength': 36,
|
||||||
|
}
|
||||||
|
|
||||||
|
states = ['MAINTENANCE',
|
||||||
|
'SCALE_IN',
|
||||||
|
'PREPARE_MAINTENANCE',
|
||||||
|
'START_MAINTENANCE',
|
||||||
|
'PLANNED_MAINTENANCE',
|
||||||
|
'MAINTENANCE_COMPLETE',
|
||||||
|
'MAINTENANCE_DONE',
|
||||||
|
'MAINTENANCE_FAILED']
|
||||||
|
|
||||||
|
reply_states = ['ACK_MAINTENANCE',
|
||||||
|
'ACK_SCALE_IN',
|
||||||
|
'ACK_PREPARE_MAINTENANCE',
|
||||||
|
'ACK_START_MAINTENANCE',
|
||||||
|
'ACK_PLANNED_MAINTENANCE',
|
||||||
|
'ACK_MAINTENANCE_COMPLETE',
|
||||||
|
'NACK_MAINTENANCE',
|
||||||
|
'NACK_SCALE_IN',
|
||||||
|
'NACK_PREPARE_MAINTENANCE',
|
||||||
|
'NACK_START_MAINTENANCE',
|
||||||
|
'NACK_PLANNED_MAINTENANCE',
|
||||||
|
'NACK_MAINTENANCE_COMPLETE']
|
||||||
|
|
||||||
|
allowed_actions = ['MIGRATE', 'LIVE_MIGRATE', 'OWN_ACTION']
|
||||||
|
|
||||||
|
maintenance_session_project_put = {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'instance_actions': {
|
||||||
|
'type': 'object'
|
||||||
|
},
|
||||||
|
'state': {
|
||||||
|
'type': 'string',
|
||||||
|
'enum': reply_states,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['state']
|
||||||
|
}
|
||||||
|
|
||||||
|
maintenance_session_project_instance_put = {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'instance_action': {
|
||||||
|
'type': 'string',
|
||||||
|
'enum': allowed_actions,
|
||||||
|
},
|
||||||
|
'state': {
|
||||||
|
'type': 'string',
|
||||||
|
'enum': reply_states,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'required': ['instance_action', 'state']
|
||||||
|
}
|
||||||
|
|
||||||
|
# TBD
|
||||||
|
# maintenance_session_put = {
|
||||||
|
#
|
||||||
|
# }
|
||||||
|
|
||||||
|
maintenance_post = {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'hosts': {
|
||||||
|
'type': 'array',
|
||||||
|
'minItems': 0,
|
||||||
|
'maxItems': 1000,
|
||||||
|
'items': {
|
||||||
|
'type': 'string',
|
||||||
|
'minLength': 2,
|
||||||
|
'maxLength': 255,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'state': {
|
||||||
|
'type': 'string',
|
||||||
|
'enum': states,
|
||||||
|
},
|
||||||
|
'maintenance_at': {
|
||||||
|
'type': 'string',
|
||||||
|
'format': 'date-time',
|
||||||
|
},
|
||||||
|
'metadata': {'type': 'object'},
|
||||||
|
'workflow': {
|
||||||
|
'type': 'string',
|
||||||
|
'minLength': 2,
|
||||||
|
'maxLength': 255,
|
||||||
|
},
|
||||||
|
'download': {
|
||||||
|
'type': 'array',
|
||||||
|
'minItems': 5,
|
||||||
|
'maxItems': 255,
|
||||||
|
'items': {
|
||||||
|
'type': 'string',
|
||||||
|
'minLength': 2,
|
||||||
|
'maxLength': 165,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'actions': {
|
||||||
|
'type': 'array',
|
||||||
|
'minItems': 0,
|
||||||
|
'maxItems': 255,
|
||||||
|
'items': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'plugin': {
|
||||||
|
'type': 'string',
|
||||||
|
'minLength': 2,
|
||||||
|
'maxLength': 255,
|
||||||
|
},
|
||||||
|
'type': {
|
||||||
|
'type': 'string',
|
||||||
|
'minLength': 2,
|
||||||
|
'maxLength': 32,
|
||||||
|
},
|
||||||
|
'metadata': {'type': 'object'},
|
||||||
|
},
|
||||||
|
'required': ['plugin', 'type']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'required': ['state', 'maintenance_at', 'workflow', 'metadata']
|
||||||
|
}
|
||||||
|
|
||||||
|
instance_put = {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'instance_id': uid,
|
||||||
|
'project_id': uid,
|
||||||
|
'group_id': uid,
|
||||||
|
'instance_name': {
|
||||||
|
'type': 'string',
|
||||||
|
'minLength': 1,
|
||||||
|
'maxLength': 255,
|
||||||
|
},
|
||||||
|
'max_interruption_time': {
|
||||||
|
'type': 'number',
|
||||||
|
'maximum': 21600
|
||||||
|
},
|
||||||
|
'migration_type': {
|
||||||
|
'type': 'string',
|
||||||
|
'enum': allowed_actions,
|
||||||
|
},
|
||||||
|
'resource_mitigation': {'type': 'boolean'},
|
||||||
|
'lead_time': {
|
||||||
|
'type': 'number',
|
||||||
|
'maximum': 21600
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['instance_id', 'project_id', 'group_id', 'instance_name',
|
||||||
|
'max_interruption_time', 'migration_type',
|
||||||
|
'resource_mitigation', 'lead_time']
|
||||||
|
}
|
||||||
|
|
||||||
|
instance_group_put = {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'project_id': uid,
|
||||||
|
'group_id': uid,
|
||||||
|
'group_name': {
|
||||||
|
'type': 'string',
|
||||||
|
'minLength': 1,
|
||||||
|
'maxLength': 255,
|
||||||
|
},
|
||||||
|
'anti_affinity_group': {'type': 'boolean'},
|
||||||
|
'max_instances_per_host': {
|
||||||
|
'type': 'number',
|
||||||
|
'maximum': 32000
|
||||||
|
},
|
||||||
|
'max_impacted_members': {
|
||||||
|
'type': 'number',
|
||||||
|
'minimum': 1,
|
||||||
|
'maximum': 32000
|
||||||
|
},
|
||||||
|
'recovery_time': {
|
||||||
|
'type': 'number',
|
||||||
|
'maximum': 21600
|
||||||
|
},
|
||||||
|
'resource_mitigation': {'type': 'boolean'},
|
||||||
|
},
|
||||||
|
'required': ['project_id', 'group_id', 'group_name',
|
||||||
|
'anti_affinity_group', 'max_instances_per_host',
|
||||||
|
'max_impacted_members', 'recovery_time',
|
||||||
|
'resource_mitigation']
|
||||||
|
}
|
8
tox.ini
8
tox.ini
|
@ -1,7 +1,7 @@
|
||||||
[tox]
|
[tox]
|
||||||
minversion = 2.0
|
minversion = 3.1.1
|
||||||
envlist = py36,py35,pep8,docs
|
envlist = py36,pep8,docs
|
||||||
skipsdist = True
|
ignore_basepython_conflict = True
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
usedevelop = True
|
usedevelop = True
|
||||||
|
@ -13,7 +13,7 @@ setenv =
|
||||||
OS_STDERR_CAPTURE=1
|
OS_STDERR_CAPTURE=1
|
||||||
OS_TEST_TIMEOUT=60
|
OS_TEST_TIMEOUT=60
|
||||||
deps =
|
deps =
|
||||||
-c{env:UPPER_CONSTRAINTS_FILE:https://opendev.org/openstack/requirements/raw/branch/master/upper-constraints.txt}
|
-c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
|
||||||
-r{toxinidir}/requirements.txt
|
-r{toxinidir}/requirements.txt
|
||||||
-r{toxinidir}/test-requirements.txt
|
-r{toxinidir}/test-requirements.txt
|
||||||
commands = stestr run {posargs}
|
commands = stestr run {posargs}
|
||||||
|
|
Loading…
Reference in New Issue