Implement port api action

Jobs have done in this patch
1. API to retrieve list of ports: Done
2. API to retrieve details of a single port: Done
3. API to create (insert) a new port: Done
4. API to update an existing port: Done
5. API and RPC to delete an existing port: Done
6. DB API of get_port_list and tests

Change-Id: Idd8c4ab9bc9e1eac2c0c93dfa699c81070f5fbe8
This commit is contained in:
jiangwt100 2013-07-07 17:34:17 +08:00
parent 34823e35c8
commit 68bbcb7255
5 changed files with 136 additions and 2 deletions

View File

@ -15,9 +15,11 @@
# under the License.
from ironic.api.controllers.v1 import node
from ironic.api.controllers.v1 import port
class Controller(object):
"""Version 1 API controller root."""
nodes = node.NodesController()
ports = port.PortsController()

View File

@ -0,0 +1,95 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2013 UnitedStack 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 Port(base.APIBase):
"""API representation of a port.
This class enforces type checking and value constraints, and converts
between the internal object model and the API representation of a port.
"""
# NOTE: translate 'id' publicly to 'uuid' internally
uuid = wtypes.text
address = wtypes.text
# NOTE: translate 'extra' internally to 'meta_data' externally
extra = {wtypes.text: wtypes.text}
node_id = int
def __init__(self, **kwargs):
self.fields = objects.Port.fields.keys()
for k in self.fields:
setattr(self, k, kwargs.get(k))
class PortsController(rest.RestController):
"""REST controller for Ports."""
@wsme_pecan.wsexpose([unicode])
def get_all(self):
"""Retrieve a list of ports."""
return pecan.request.dbapi.get_port_list()
@wsme_pecan.wsexpose(Port, unicode)
def get_one(self, uuid):
"""Retrieve information about the given port."""
port = objects.Port.get_by_uuid(pecan.request.context, uuid)
return port
@wsme.validate(Port)
@wsme_pecan.wsexpose(Port, body=Port)
def post(self, port):
"""Ceate a new port."""
try:
new_port = pecan.request.dbapi.create_port(port.as_dict())
except Exception as e:
LOG.exception(e)
raise wsme.exc.ClientSideError(_("Invalid data"))
return new_port
@wsme.validate(Port)
@wsme_pecan.wsexpose(Port, unicode, body=Port)
def patch(self, uuid, port_data):
"""Update an existing port."""
# TODO(wentian): add rpc handle,
# eg. if update fails because node is already locked
port = objects.Port.get_by_uuid(pecan.request.context, uuid)
nn_delta_p = port_data.as_terse_dict()
for k in nn_delta_p:
port[k] = nn_delta_p[k]
port.save()
return port
@wsme_pecan.wsexpose(None, unicode, status_code=204)
def delete(self, port_id):
"""Delete a port."""
pecan.request.dbapi.destroy_port(port_id)

View File

@ -170,6 +170,13 @@ class Connection(object):
:returns: A port.
"""
@abc.abstractmethod
def get_port_list(self):
"""Return a lists of port
:return: Port list.
"""
@abc.abstractmethod
def get_ports_by_node(self, node):
"""List all the ports for a given node.

View File

@ -293,6 +293,10 @@ class Connection(api.Connection):
def get_port_by_vif(self, vif):
pass
def get_port_list(self):
query = model_query(models.Port.uuid)
return [i[0] for i in query.all()]
@objects.objectify(objects.Port)
def get_ports_by_node(self, node):
query = model_query(models.Port)
@ -302,6 +306,10 @@ class Connection(api.Connection):
@objects.objectify(objects.Port)
def create_port(self, values):
if not values.get('uuid'):
values['uuid'] = uuidutils.generate_uuid()
if not values.get('extra'):
values['extra'] = '{}'
port = models.Port()
port.update(values)
port.save()

View File

@ -19,6 +19,7 @@
from ironic.common import exception
from ironic.db import api as dbapi
from ironic.openstack.common import uuidutils
from ironic.tests.db import base
from ironic.tests.db import utils
@ -33,18 +34,31 @@ class DbPortTestCase(base.DbTestCase):
self.n = utils.get_test_node()
self.p = utils.get_test_port()
self.dbapi.create_node(self.n)
self.dbapi.create_port(self.p)
def test_get_port_by_id(self):
self.dbapi.create_port(self.p)
res = self.dbapi.get_port(self.p['id'])
self.assertEqual(self.p['address'], res['address'])
def test_get_port_by_uuid(self):
self.dbapi.create_port(self.p)
res = self.dbapi.get_port(self.p['uuid'])
self.assertEqual(self.p['id'], res['id'])
def test_get_port_list(self):
uuids = []
for i in xrange(1, 6):
n = utils.get_test_port(id=i, uuid=uuidutils.generate_uuid())
self.dbapi.create_port(n)
uuids.append(unicode(n['uuid']))
res = self.dbapi.get_port_list()
uuids.sort()
res.sort()
self.assertEqual(uuids, res)
def test_get_port_by_address(self):
self.dbapi.create_port(self.p)
res = self.dbapi.get_port(self.p['address'])
self.assertEqual(self.p['id'], res['id'])
@ -56,14 +70,20 @@ class DbPortTestCase(base.DbTestCase):
self.dbapi.get_port, 'not-a-mac')
def test_get_ports_by_node_id(self):
self.dbapi.create_node(self.n)
self.dbapi.create_port(self.p)
res = self.dbapi.get_ports_by_node(self.n['id'])
self.assertEqual(self.p['address'], res[0]['address'])
def test_get_ports_by_node_uuid(self):
self.dbapi.create_node(self.n)
self.dbapi.create_port(self.p)
res = self.dbapi.get_ports_by_node(self.n['uuid'])
self.assertEqual(self.p['address'], res[0]['address'])
def test_get_ports_by_node_that_does_not_exist(self):
self.dbapi.create_node(self.n)
self.dbapi.create_port(self.p)
res = self.dbapi.get_ports_by_node(99)
self.assertEqual(0, len(res))
@ -72,11 +92,13 @@ class DbPortTestCase(base.DbTestCase):
self.assertEqual(0, len(res))
def test_destroy_port(self):
self.dbapi.create_port(self.p)
self.dbapi.destroy_port(self.p['id'])
self.assertRaises(exception.PortNotFound,
self.dbapi.destroy_port, self.p['id'])
def test_update_port(self):
self.dbapi.create_port(self.p)
old_address = self.p['address']
new_address = 'ff.ee.dd.cc.bb.aa'