Implement chassis api actions

Partially implements blueprint chassis-api-actions:
* adds 'description' field to chassis
* api to retrieve list of chassis
* api to retrieve details of a single chassis
* api to create/update/delete a chassis

Change-Id: I02bee4e10dcee233209e57025c18e5ae5cea86f8
This commit is contained in:
Lucas Alvares Gomes 2013-07-10 11:04:44 +01:00
parent 1769be51ad
commit d497895767
7 changed files with 143 additions and 0 deletions

View File

@ -0,0 +1,105 @@
#!/usr/bin/env python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2013 Red Hat, Inc.
# 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.
import pecan
from pecan import rest
import wsme
from wsme import types as wtypes
import wsmeext.pecan as wsme_pecan
from ironic.api.controllers.v1 import base
from ironic import objects
from ironic.openstack.common import log
LOG = log.getLogger(__name__)
class Chassis(base.APIBase):
"""API representation of a chassis.
This class enforces type checking and value constraints, and converts
between the internal object model and the API representation of
a chassis.
"""
# NOTE: translate 'id' publicly to 'uuid' internally
uuid = wtypes.text
"The UUID of the chassis"
description = wtypes.text
"The description of the chassis"
extra = {wtypes.text: wtypes.text}
"The metadata of the chassis"
def __init__(self, **kwargs):
self.fields = objects.Chassis.fields.keys()
for k in self.fields:
setattr(self, k, kwargs.get(k))
class ChassisController(rest.RestController):
"""REST controller for Chassis."""
@wsme_pecan.wsexpose([unicode])
def get_all(self):
"""Retrieve a list of chassis."""
return pecan.request.dbapi.get_chassis_list()
@wsme_pecan.wsexpose(Chassis, unicode)
def get_one(self, uuid):
"""Retrieve information about the given chassis."""
chassis = objects.Chassis.get_by_uuid(pecan.request.context, uuid)
return chassis
@wsme.validate(Chassis)
@wsme_pecan.wsexpose(Chassis, body=Chassis)
def post(self, chassis):
"""Create a new chassis."""
try:
new_chassis = pecan.request.dbapi.create_chassis(chassis.as_dict())
except Exception as e:
LOG.exception(e)
raise wsme.exc.ClientSideError(_("Invalid data"))
return new_chassis
@wsme.validate(Chassis)
@wsme_pecan.wsexpose(Chassis, unicode, body=Chassis)
def patch(self, uuid, delta_chassis):
"""Update an existing chassis."""
chassis = objects.Chassis.get_by_uuid(pecan.request.context, uuid)
nn_delta_ch = delta_chassis.as_terse_dict()
# Ensure immutable keys are not present
# TODO(lucasagomes): Not sure if 'id' will ever be present here
# the translation should occur before it reaches this point
if any(v for v in nn_delta_ch if v in ("id", "uuid")):
raise wsme.exc.ClientSideError(_("'uuid' is immutable"))
for k in nn_delta_ch:
chassis[k] = nn_delta_ch[k]
chassis.save()
return chassis
@wsme_pecan.wsexpose(None, unicode, status_code=204)
def delete(self, uuid):
"""Delete a chassis."""
# TODO(lucasagomes): be more cautious when deleting a chassis
# which has nodes
pecan.request.dbapi.destroy_chassis(uuid)

View File

@ -14,6 +14,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from ironic.api.controllers.v1 import chassis
from ironic.api.controllers.v1 import node
from ironic.api.controllers.v1 import port
@ -23,3 +24,4 @@ class Controller(object):
nodes = node.NodesController()
ports = port.PortsController()
chassis = chassis.ChassisController()

View File

@ -354,6 +354,10 @@ class Connection(api.Connection):
@objects.objectify(objects.Chassis)
def create_chassis(self, values):
if not values.get('uuid'):
values['uuid'] = uuidutils.generate_uuid()
if not values.get('extra'):
values['extra'] = '{}'
chassis = models.Chassis()
chassis.update(values)
chassis.save()

View File

@ -0,0 +1,29 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# -*- encoding: utf-8 -*-
#
# 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 sqlalchemy import Table, Column, MetaData, String
def upgrade(migrate_engine):
meta = MetaData()
meta.bind = migrate_engine
chassis = Table('chassis', meta, autoload=True)
chassis.create_column(Column('description', String(255), nullable=True))
def downgrade(migrate_engine):
raise NotImplementedError('Downgrade from version 008 is unsupported.')

View File

@ -86,6 +86,7 @@ class Chassis(Base):
id = Column(Integer, primary_key=True)
uuid = Column(String(36), unique=True)
extra = Column(JSONEncodedDict)
description = Column(String(255), nullable=True)
class Node(Base):

View File

@ -26,6 +26,7 @@ class Chassis(base.IronicObject):
'id': int,
'uuid': utils.str_or_none,
'extra': utils.dict_or_none,
'description': utils.str_or_none,
}
@staticmethod

View File

@ -105,6 +105,7 @@ def get_test_chassis(**kw):
'id': kw.get('id', 42),
'uuid': kw.get('uuid', 'e74c40e0-d825-11e2-a28f-0800200c9a66'),
'extra': kw.get('extra', {}),
'description': kw.get('description', 'data-center-1-chassis'),
'created_at': kw.get('created_at'),
'updated_at': kw.get('updated_at'),
}