Tanvir Talukder ad2b7fab31 Fix pep8 and docstring violations
Fix issues in py files in the directories
valet/valet/api/common
valet/api
valet/cli
valet/engine/optimizer/
valet/tests/
valet/tests/tempest
valet_plugins
2017-05-01 19:24:57 -05:00

292 lines
10 KiB
Python

#
# Copyright 2014-2017 AT&T Intellectual Property
#
# 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.
"""Plans."""
import logging
from notario import decorators
from notario.validators import types
from pecan import expose, request, response
from pecan_notario import validate
from valet.api.common.i18n import _
from valet.api.common.ostro_helper import Ostro
from valet.api.db.models import Placement, Plan
from valet.api.v1.controllers import error
from valet.api.v1.controllers import set_placements
from valet.api.v1.controllers import update_placements
from valet.api.v1.controllers import valid_plan_update_action
LOG = logging.getLogger(__name__)
CREATE_SCHEMA = (
('plan_name', types.string),
('resources', types.dictionary),
('stack_id', types.string),
(decorators.optional('timeout'), types.string)
)
UPDATE_SCHEMA = (
('action', valid_plan_update_action),
(decorators.optional('excluded_hosts'), types.array),
(decorators.optional('plan_name'), types.string),
# FIXME: resources needs to work against valid_plan_resources
('resources', types.array),
(decorators.optional('timeout'), types.string)
)
# pylint: disable=R0201
class PlansItemController(object):
"""Plan Item Controller /v1/plans/{plan_id}."""
def __init__(self, uuid4):
"""Initializer."""
self.uuid = uuid4
self.plan = Plan.query.filter_by(id=self.uuid).first()
# pylint: disable=E1101
if not self.plan:
self.plan = Plan.query.filter_by(stack_id=self.uuid).first()
# pylint: disable=E1101
if not self.plan:
error('/errors/not_found', _('Plan not found'))
request.context['plan_id'] = self.plan.id
@classmethod
def allow(cls):
"""Allowed methods."""
return 'GET,PUT,DELETE'
@expose(generic=True, template='json')
def index(self):
"""Catchall for unallowed methods."""
message = _('The %s method is not allowed.') % request.method
kwargs = {'allow': self.allow()}
error('/errors/not_allowed', message, **kwargs)
@index.when(method='OPTIONS', template='json')
def index_options(self):
"""Index Options."""
response.headers['Allow'] = self.allow()
response.status = 204
@index.when(method='GET', template='json')
def index_get(self):
"""Get plan."""
return {"plan": self.plan}
@index.when(method='PUT', template='json')
@validate(UPDATE_SCHEMA, '/errors/schema')
def index_put(self, **kwargs):
"""Update a Plan."""
action = kwargs.get('action')
if action == 'migrate':
# Replan the placement of an existing resource.
excluded_hosts = kwargs.get('excluded_hosts', [])
resources = kwargs.get('resources', [])
# TODO(JD): Support replan of more than one existing resource
if not isinstance(resources, list) or len(resources) != 1:
error('/errors/invalid',
_('resources must be a list of length 1.'))
# We either got a resource or orchestration id.
the_id = resources[0]
placement = Placement.query.filter_by(resource_id=the_id).first()
# pylint: disable=E1101
if not placement:
placement = Placement.query.filter_by(
orchestration_id=the_id).first() # pylint: disable=E1101
if not placement:
error('/errors/invalid', _('Unknown resource or '
'orchestration id: %s') % the_id)
LOG.info(_('Migration request for resource id {0}, '
'orchestration id {1}.').format(
placement.resource_id, placement.orchestration_id))
args = {
"stack_id": self.plan.stack_id,
"excluded_hosts": excluded_hosts,
"orchestration_id": placement.orchestration_id,
}
ostro_kwargs = {
"args": args,
}
ostro = Ostro()
ostro.migrate(**ostro_kwargs)
ostro.send()
status_type = ostro.response['status']['type']
if status_type != 'ok':
message = ostro.response['status']['message']
error(ostro.error_uri, _('Ostro error: %s') % message)
placements = ostro.response['resources']
update_placements(placements, unlock_all=True)
response.status = 201
# Flush so that the DB is current.
self.plan.flush()
self.plan = Plan.query.filter_by(
stack_id=self.plan.stack_id).first() # pylint: disable=E1101
LOG.info(_('Plan with stack id %s updated.'), self.plan.stack_id)
return {"plan": self.plan}
# TODO(JD): Throw unimplemented error?
# pylint: disable=W0612
'''
# FIXME: This is broken. Save for Valet 1.1
# New placements are not being seen in the response, so
# set_placements is currently failing as a result.
ostro = Ostro()
args = request.json
kwargs = {
'tenant_id': request.context['tenant_id'],
'args': args
}
# Prepare the request. If request prep fails,
# an error message will be in the response.
# Though the Ostro helper reports the error,
# we cite it as a Valet error.
if not ostro.build_request(**kwargs):
message = ostro.response['status']['message']
error(ostro.error_uri, _('Valet error: %s') % message)
ostro.send()
status_type = ostro.response['status']['type']
if status_type != 'ok':
message = ostro.response['status']['message']
error(ostro.error_uri, _('Ostro error: %s') % message)
# TODO(JD): Keep. See if we will eventually need these for Ostro.
#plan_name = args['plan_name']
#stack_id = args['stack_id']
resources = ostro.request['resources_update']
placements = ostro.response['resources']
set_placements(self.plan, resources, placements)
response.status = 201
# Flush so that the DB is current.
self.plan.flush()
return self.plan
'''
# pylint: enable=W0612
@index.when(method='DELETE', template='json')
def index_delete(self):
"""Delete a Plan."""
for placement in self.plan.placements():
placement.delete()
stack_id = self.plan.stack_id
self.plan.delete()
LOG.info(_('Plan with stack id %s deleted.'), stack_id)
response.status = 204
class PlansController(object):
"""Plans Controller /v1/plans."""
@classmethod
def allow(cls):
"""Allowed methods."""
return 'GET,POST'
@expose(generic=True, template='json')
def index(self):
"""Catchall for unallowed methods."""
message = _('The %s method is not allowed.') % request.method
kwargs = {'allow': self.allow()}
error('/errors/not_allowed', message, **kwargs)
@index.when(method='OPTIONS', template='json')
def index_options(self):
"""Index Options."""
response.headers['Allow'] = self.allow()
response.status = 204
@index.when(method='GET', template='json')
def index_get(self):
"""Get all the plans."""
plans_array = []
for plan in Plan.query.all(): # pylint: disable=E1101
plans_array.append(plan)
return {"plans": plans_array}
@index.when(method='POST', template='json')
@validate(CREATE_SCHEMA, '/errors/schema')
def index_post(self):
"""Create a Plan."""
ostro = Ostro()
args = request.json
kwargs = {
'tenant_id': request.context['tenant_id'],
'args': args
}
# Prepare the request. If request prep fails,
# an error message will be in the response.
# Though the Ostro helper reports the error,
# we cite it as a Valet error.
if not ostro.build_request(**kwargs):
message = ostro.response['status']['message']
error(ostro.error_uri, _('Valet error: %s') % message)
# If there are no serviceable resources, bail. Not an error.
# Treat it as if an "empty plan" was created.
# FIXME: Ostro should likely handle this and not error out.
if not ostro.is_request_serviceable():
LOG.info(_('Plan has no serviceable resources. Skipping.'))
response.status = 201
return {"plan": {}}
ostro.send()
status_type = ostro.response['status']['type']
if status_type != 'ok':
message = ostro.response['status']['message']
error(ostro.error_uri, _('Ostro error: %s') % message)
plan_name = args['plan_name']
stack_id = args['stack_id']
resources = ostro.request['resources']
placements = ostro.response['resources']
plan = Plan(plan_name, stack_id)
if plan:
set_placements(plan, resources, placements)
response.status = 201
# Flush so that the DB is current.
plan.flush()
LOG.info(_('Plan with stack id %s created.'), plan.stack_id)
return {"plan": plan}
else:
error('/errors/server_error', _('Unable to create Plan.'))
@expose()
def _lookup(self, uuid4, *remainder):
"""Pecan subcontroller routing callback."""
return PlansItemController(uuid4), remainder