port baremetal_nodes API into v3 part1

This changeset only copies the v2 files into the appropriate v3
directories unchanged. The copy as-is will not be loaded by either
the v2 or v3 extension loaders. The second changeset will then
make the changes required for it to work as a v3 extension.

This is being tried in order to make reviewing of extension
porting easier as gerrit will display only what is actually
changed for v3 rather than entirely new files.

Change-Id: I180bbf7306e7d3bffa4d6093fedfd52e4baf5215
This commit is contained in:
ivan-zhu 2013-07-15 17:01:27 +08:00
parent d657b317a1
commit 5de5ae1b5d
2 changed files with 437 additions and 0 deletions

View File

@ -0,0 +1,218 @@
# Copyright (c) 2013 NTT DOCOMO, 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.
"""The bare-metal admin extension."""
import webob
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova import exception
from nova.virt.baremetal import db
authorize = extensions.extension_authorizer('compute', 'baremetal_nodes')
node_fields = ['id', 'cpus', 'local_gb', 'memory_mb', 'pm_address',
'pm_user',
'service_host', 'terminal_port', 'instance_uuid',
]
interface_fields = ['id', 'address', 'datapath_id', 'port_no']
def _node_dict(node_ref):
d = {}
for f in node_fields:
d[f] = node_ref.get(f)
return d
def _interface_dict(interface_ref):
d = {}
for f in interface_fields:
d[f] = interface_ref.get(f)
return d
def _make_node_elem(elem):
for f in node_fields:
elem.set(f)
def _make_interface_elem(elem):
for f in interface_fields:
elem.set(f)
class NodeTemplate(xmlutil.TemplateBuilder):
def construct(self):
node_elem = xmlutil.TemplateElement('node', selector='node')
_make_node_elem(node_elem)
ifs_elem = xmlutil.TemplateElement('interfaces')
if_elem = xmlutil.SubTemplateElement(ifs_elem, 'interface',
selector='interfaces')
_make_interface_elem(if_elem)
node_elem.append(ifs_elem)
return xmlutil.MasterTemplate(node_elem, 1)
class NodesTemplate(xmlutil.TemplateBuilder):
def construct(self):
root = xmlutil.TemplateElement('nodes')
node_elem = xmlutil.SubTemplateElement(root, 'node', selector='nodes')
_make_node_elem(node_elem)
ifs_elem = xmlutil.TemplateElement('interfaces')
if_elem = xmlutil.SubTemplateElement(ifs_elem, 'interface',
selector='interfaces')
_make_interface_elem(if_elem)
node_elem.append(ifs_elem)
return xmlutil.MasterTemplate(root, 1)
class InterfaceTemplate(xmlutil.TemplateBuilder):
def construct(self):
root = xmlutil.TemplateElement('interface', selector='interface')
_make_interface_elem(root)
return xmlutil.MasterTemplate(root, 1)
class BareMetalNodeController(wsgi.Controller):
"""The Bare-Metal Node API controller for the OpenStack API."""
@wsgi.serializers(xml=NodesTemplate)
def index(self, req):
context = req.environ['nova.context']
authorize(context)
nodes_from_db = db.bm_node_get_all(context)
nodes = []
for node_from_db in nodes_from_db:
try:
ifs = db.bm_interface_get_all_by_bm_node_id(
context, node_from_db['id'])
except exception.NodeNotFound:
ifs = []
node = _node_dict(node_from_db)
node['interfaces'] = [_interface_dict(i) for i in ifs]
nodes.append(node)
return {'nodes': nodes}
@wsgi.serializers(xml=NodeTemplate)
def show(self, req, id):
context = req.environ['nova.context']
authorize(context)
try:
node = db.bm_node_get(context, id)
except exception.NodeNotFound:
raise webob.exc.HTTPNotFound()
try:
ifs = db.bm_interface_get_all_by_bm_node_id(context, id)
except exception.NodeNotFound:
ifs = []
node = _node_dict(node)
node['interfaces'] = [_interface_dict(i) for i in ifs]
return {'node': node}
@wsgi.serializers(xml=NodeTemplate)
def create(self, req, body):
context = req.environ['nova.context']
authorize(context)
values = body['node'].copy()
prov_mac_address = values.pop('prov_mac_address', None)
node = db.bm_node_create(context, values)
node = _node_dict(node)
if prov_mac_address:
if_id = db.bm_interface_create(context,
bm_node_id=node['id'],
address=prov_mac_address,
datapath_id=None,
port_no=None)
if_ref = db.bm_interface_get(context, if_id)
node['interfaces'] = [_interface_dict(if_ref)]
else:
node['interfaces'] = []
return {'node': node}
def delete(self, req, id):
context = req.environ['nova.context']
authorize(context)
try:
db.bm_node_destroy(context, id)
except exception.NodeNotFound:
raise webob.exc.HTTPNotFound()
return webob.Response(status_int=202)
def _check_node_exists(self, context, node_id):
try:
db.bm_node_get(context, node_id)
except exception.NodeNotFound:
raise webob.exc.HTTPNotFound()
@wsgi.serializers(xml=InterfaceTemplate)
@wsgi.action('add_interface')
def _add_interface(self, req, id, body):
context = req.environ['nova.context']
authorize(context)
self._check_node_exists(context, id)
body = body['add_interface']
address = body['address']
datapath_id = body.get('datapath_id')
port_no = body.get('port_no')
if_id = db.bm_interface_create(context,
bm_node_id=id,
address=address,
datapath_id=datapath_id,
port_no=port_no)
if_ref = db.bm_interface_get(context, if_id)
return {'interface': _interface_dict(if_ref)}
@wsgi.response(202)
@wsgi.action('remove_interface')
def _remove_interface(self, req, id, body):
context = req.environ['nova.context']
authorize(context)
self._check_node_exists(context, id)
body = body['remove_interface']
if_id = body.get('id')
address = body.get('address')
if not if_id and not address:
raise webob.exc.HTTPBadRequest(
explanation=_("Must specify id or address"))
ifs = db.bm_interface_get_all_by_bm_node_id(context, id)
for i in ifs:
if if_id and if_id != i['id']:
continue
if address and address != i['address']:
continue
db.bm_interface_destroy(context, i['id'])
return webob.Response(status_int=202)
raise webob.exc.HTTPNotFound()
class Baremetal_nodes(extensions.ExtensionDescriptor):
"""Admin-only bare-metal node administration."""
name = "BareMetalNodes"
alias = "os-baremetal-nodes"
namespace = "http://docs.openstack.org/compute/ext/baremetal_nodes/api/v2"
updated = "2013-01-04T00:00:00+00:00"
def get_resources(self):
resources = []
res = extensions.ResourceExtension('os-baremetal-nodes',
BareMetalNodeController(),
member_actions={"action": "POST", })
resources.append(res)
return resources

View File

@ -0,0 +1,219 @@
# Copyright (c) 2013 NTT DOCOMO, 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.
from webob import exc
from nova.api.openstack.compute.contrib import baremetal_nodes
from nova import context
from nova import exception
from nova import test
from nova.virt.baremetal import db
class FakeRequest(object):
def __init__(self, context):
self.environ = {"nova.context": context}
class BareMetalNodesTest(test.TestCase):
def setUp(self):
super(BareMetalNodesTest, self).setUp()
self.context = context.get_admin_context()
self.controller = baremetal_nodes.BareMetalNodeController()
self.request = FakeRequest(self.context)
def test_create(self):
node = {
'service_host': "host",
'cpus': 8,
'memory_mb': 8192,
'local_gb': 128,
'pm_address': "10.1.2.3",
'pm_user': "pm_user",
'pm_password': "pm_pass",
'terminal_port': 8000,
'interfaces': [],
}
response = node.copy()
response['id'] = 100
del response['pm_password']
response['instance_uuid'] = None
self.mox.StubOutWithMock(db, 'bm_node_create')
db.bm_node_create(self.context, node).AndReturn(response)
self.mox.ReplayAll()
res_dict = self.controller.create(self.request, {'node': node})
self.assertEqual({'node': response}, res_dict)
def test_delete(self):
self.mox.StubOutWithMock(db, 'bm_node_destroy')
db.bm_node_destroy(self.context, 1)
self.mox.ReplayAll()
self.controller.delete(self.request, 1)
def test_delete_node_not_found(self):
self.mox.StubOutWithMock(db, 'bm_node_destroy')
db.bm_node_destroy(self.context, 1).\
AndRaise(exception.NodeNotFound(node_id=1))
self.mox.ReplayAll()
self.assertRaises(
exc.HTTPNotFound,
self.controller.delete,
self.request,
1)
def test_index(self):
nodes = [{'id': 1},
{'id': 2},
]
interfaces = [{'id': 1, 'address': '11:11:11:11:11:11'},
{'id': 2, 'address': '22:22:22:22:22:22'},
]
self.mox.StubOutWithMock(db, 'bm_node_get_all')
self.mox.StubOutWithMock(db, 'bm_interface_get_all_by_bm_node_id')
db.bm_node_get_all(self.context).AndReturn(nodes)
db.bm_interface_get_all_by_bm_node_id(self.context, 1).\
AndRaise(exception.NodeNotFound(node_id=1))
db.bm_interface_get_all_by_bm_node_id(self.context, 2).\
AndReturn(interfaces)
self.mox.ReplayAll()
res_dict = self.controller.index(self.request)
self.assertEqual(2, len(res_dict['nodes']))
self.assertEqual([], res_dict['nodes'][0]['interfaces'])
self.assertEqual(2, len(res_dict['nodes'][1]['interfaces']))
def test_show(self):
node_id = 1
node = {'id': node_id}
interfaces = [{'id': 1, 'address': '11:11:11:11:11:11'},
{'id': 2, 'address': '22:22:22:22:22:22'},
]
self.mox.StubOutWithMock(db, 'bm_node_get')
self.mox.StubOutWithMock(db, 'bm_interface_get_all_by_bm_node_id')
db.bm_node_get(self.context, node_id).AndReturn(node)
db.bm_interface_get_all_by_bm_node_id(self.context, node_id).\
AndReturn(interfaces)
self.mox.ReplayAll()
res_dict = self.controller.show(self.request, node_id)
self.assertEqual(node_id, res_dict['node']['id'])
self.assertEqual(2, len(res_dict['node']['interfaces']))
def test_show_no_interfaces(self):
node_id = 1
node = {'id': node_id}
self.mox.StubOutWithMock(db, 'bm_node_get')
self.mox.StubOutWithMock(db, 'bm_interface_get_all_by_bm_node_id')
db.bm_node_get(self.context, node_id).AndReturn(node)
db.bm_interface_get_all_by_bm_node_id(self.context, node_id).\
AndRaise(exception.NodeNotFound(node_id=node_id))
self.mox.ReplayAll()
res_dict = self.controller.show(self.request, node_id)
self.assertEqual(node_id, res_dict['node']['id'])
self.assertEqual(0, len(res_dict['node']['interfaces']))
def test_add_interface(self):
node_id = 1
address = '11:22:33:44:55:66'
body = {'add_interface': {'address': address}}
self.mox.StubOutWithMock(db, 'bm_node_get')
self.mox.StubOutWithMock(db, 'bm_interface_create')
self.mox.StubOutWithMock(db, 'bm_interface_get')
db.bm_node_get(self.context, node_id)
db.bm_interface_create(self.context,
bm_node_id=node_id,
address=address,
datapath_id=None,
port_no=None).\
AndReturn(12345)
db.bm_interface_get(self.context, 12345).\
AndReturn({'id': 12345, 'address': address})
self.mox.ReplayAll()
res_dict = self.controller._add_interface(self.request, node_id, body)
self.assertEqual(12345, res_dict['interface']['id'])
self.assertEqual(address, res_dict['interface']['address'])
def test_remove_interface(self):
node_id = 1
interfaces = [{'id': 1},
{'id': 2},
{'id': 3},
]
body = {'remove_interface': {'id': 2}}
self.mox.StubOutWithMock(db, 'bm_node_get')
self.mox.StubOutWithMock(db, 'bm_interface_get_all_by_bm_node_id')
self.mox.StubOutWithMock(db, 'bm_interface_destroy')
db.bm_node_get(self.context, node_id)
db.bm_interface_get_all_by_bm_node_id(self.context, node_id).\
AndReturn(interfaces)
db.bm_interface_destroy(self.context, 2)
self.mox.ReplayAll()
self.controller._remove_interface(self.request, node_id, body)
def test_remove_interface_by_address(self):
node_id = 1
interfaces = [{'id': 1, 'address': '11:11:11:11:11:11'},
{'id': 2, 'address': '22:22:22:22:22:22'},
{'id': 3, 'address': '33:33:33:33:33:33'},
]
self.mox.StubOutWithMock(db, 'bm_node_get')
self.mox.StubOutWithMock(db, 'bm_interface_get_all_by_bm_node_id')
self.mox.StubOutWithMock(db, 'bm_interface_destroy')
db.bm_node_get(self.context, node_id)
db.bm_interface_get_all_by_bm_node_id(self.context, node_id).\
AndReturn(interfaces)
db.bm_interface_destroy(self.context, 2)
self.mox.ReplayAll()
body = {'remove_interface': {'address': '22:22:22:22:22:22'}}
self.controller._remove_interface(self.request, node_id, body)
def test_remove_interface_no_id_no_address(self):
node_id = 1
self.mox.StubOutWithMock(db, 'bm_node_get')
db.bm_node_get(self.context, node_id)
self.mox.ReplayAll()
body = {'remove_interface': {}}
self.assertRaises(exc.HTTPBadRequest,
self.controller._remove_interface,
self.request,
node_id,
body)
def test_add_interface_node_not_found(self):
node_id = 1
self.mox.StubOutWithMock(db, 'bm_node_get')
db.bm_node_get(self.context, node_id).\
AndRaise(exception.NodeNotFound(node_id=node_id))
self.mox.ReplayAll()
body = {'add_interface': {'address': '11:11:11:11:11:11'}}
self.assertRaises(exc.HTTPNotFound,
self.controller._add_interface,
self.request,
node_id,
body)
def test_remove_interface_node_not_found(self):
node_id = 1
self.mox.StubOutWithMock(db, 'bm_node_get')
db.bm_node_get(self.context, node_id).\
AndRaise(exception.NodeNotFound(node_id=node_id))
self.mox.ReplayAll()
body = {'remove_interface': {'address': '11:11:11:11:11:11'}}
self.assertRaises(exc.HTTPNotFound,
self.controller._remove_interface,
self.request,
node_id,
body)