Create Port object.
1. Added the model of a Port. 2. Defined __all__ for ironic.objects to simplify accessing the models. 3. Made @objectify universal for all objects. Partially implements blueprint: ironic-object-model Change-Id: Iec88310811d0e17903b5182d08e293dd4d0967d0
This commit is contained in:
@@ -26,7 +26,7 @@ from ironic.common import exception
|
||||
from ironic.common import utils
|
||||
from ironic.db import api
|
||||
from ironic.db.sqlalchemy import models
|
||||
from ironic.objects import node
|
||||
from ironic import objects
|
||||
from ironic.openstack.common.db.sqlalchemy import session as db_session
|
||||
from ironic.openstack.common import log
|
||||
from ironic.openstack.common import uuidutils
|
||||
@@ -84,19 +84,19 @@ class Connection(api.Connection):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@node.objectify
|
||||
@objects.objectify(objects.Node)
|
||||
def get_nodes(self, columns):
|
||||
pass
|
||||
|
||||
@node.objectify
|
||||
@objects.objectify(objects.Node)
|
||||
def get_associated_nodes(self):
|
||||
pass
|
||||
|
||||
@node.objectify
|
||||
@objects.objectify(objects.Node)
|
||||
def get_unassociated_nodes(self):
|
||||
pass
|
||||
|
||||
@node.objectify
|
||||
@objects.objectify(objects.Node)
|
||||
def reserve_nodes(self, tag, nodes):
|
||||
# Ensure consistent sort order so we don't run into deadlocks.
|
||||
nodes.sort()
|
||||
@@ -150,14 +150,14 @@ class Connection(api.Connection):
|
||||
if ref['reservation'] is not None:
|
||||
raise exception.NodeLocked(node=node)
|
||||
|
||||
@node.objectify
|
||||
@objects.objectify(objects.Node)
|
||||
def create_node(self, values):
|
||||
node = models.Node()
|
||||
node.update(values)
|
||||
node.save()
|
||||
return node
|
||||
|
||||
@node.objectify
|
||||
@objects.objectify(objects.Node)
|
||||
def get_node(self, node):
|
||||
query = model_query(models.Node)
|
||||
query = add_uuid_filter(query, node)
|
||||
@@ -169,7 +169,7 @@ class Connection(api.Connection):
|
||||
|
||||
return result
|
||||
|
||||
@node.objectify
|
||||
@objects.objectify(objects.Node)
|
||||
def get_node_by_instance(self, instance):
|
||||
query = model_query(models.Node)
|
||||
if uuidutils.is_uuid_like(instance):
|
||||
@@ -194,7 +194,7 @@ class Connection(api.Connection):
|
||||
if count != 1:
|
||||
raise exception.NodeNotFound(node=node)
|
||||
|
||||
@node.objectify
|
||||
@objects.objectify(objects.Node)
|
||||
def update_node(self, node, values):
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
@@ -209,6 +209,7 @@ class Connection(api.Connection):
|
||||
ref = query.one()
|
||||
return ref
|
||||
|
||||
@objects.objectify(objects.Port)
|
||||
def get_port(self, port):
|
||||
query = model_query(models.Port)
|
||||
query = add_port_filter(query, port)
|
||||
@@ -220,9 +221,11 @@ class Connection(api.Connection):
|
||||
|
||||
return result
|
||||
|
||||
@objects.objectify(objects.Port)
|
||||
def get_port_by_vif(self, vif):
|
||||
pass
|
||||
|
||||
@objects.objectify(objects.Port)
|
||||
def get_ports_by_node(self, node):
|
||||
session = get_session()
|
||||
|
||||
@@ -238,12 +241,14 @@ class Connection(api.Connection):
|
||||
|
||||
return result
|
||||
|
||||
@objects.objectify(objects.Port)
|
||||
def create_port(self, values):
|
||||
port = models.Port()
|
||||
port.update(values)
|
||||
port.save()
|
||||
return port
|
||||
|
||||
@objects.objectify(objects.Port)
|
||||
def update_port(self, port, values):
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
|
||||
@@ -11,3 +11,28 @@
|
||||
# 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 ironic.objects import node
|
||||
from ironic.objects import port
|
||||
|
||||
|
||||
def objectify(klass):
|
||||
"""Decorator to convert database results into specified objects."""
|
||||
def the_decorator(fn):
|
||||
def wrapper(*args, **kwargs):
|
||||
result = fn(*args, **kwargs)
|
||||
try:
|
||||
return klass._from_db_object(klass(), result)
|
||||
except TypeError:
|
||||
# TODO(deva): handle lists of objects better
|
||||
# once support for those lands and is imported.
|
||||
return [klass._from_db_object(klass(), obj) for obj in result]
|
||||
return wrapper
|
||||
return the_decorator
|
||||
|
||||
Node = node.Node
|
||||
Port = port.Port
|
||||
|
||||
__all__ = (Node,
|
||||
Port,
|
||||
objectify)
|
||||
|
||||
@@ -19,19 +19,6 @@ from ironic.objects import base
|
||||
from ironic.objects import utils
|
||||
|
||||
|
||||
def objectify(fn):
|
||||
"""Decorator to convert database results into Node objects."""
|
||||
def wrapper(*args, **kwargs):
|
||||
result = fn(*args, **kwargs)
|
||||
try:
|
||||
return Node._from_db_object(Node(), result)
|
||||
except TypeError:
|
||||
# TODO(deva): handle lists of objects better
|
||||
# once support for those lands and is imported.
|
||||
return [Node._from_db_object(Node(), obj) for obj in result]
|
||||
return wrapper
|
||||
|
||||
|
||||
class Node(base.IronicObject):
|
||||
|
||||
dbapi = db_api.get_instance()
|
||||
|
||||
85
ironic/objects/port.py
Normal file
85
ironic/objects/port.py
Normal file
@@ -0,0 +1,85 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
# coding=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 ironic.db import api as dbapi
|
||||
from ironic.objects import base
|
||||
from ironic.objects import utils
|
||||
|
||||
|
||||
class Port(base.IronicObject):
|
||||
dbapi = dbapi.get_instance()
|
||||
|
||||
fields = {
|
||||
'id': int,
|
||||
'uuid': utils.str_or_none,
|
||||
'node_id': utils.int_or_none,
|
||||
'address': utils.str_or_none,
|
||||
'extra': utils.str_or_none,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _from_db_object(port, db_port):
|
||||
"""Converts a database entity to a formal object."""
|
||||
for field in port.fields:
|
||||
port[field] = db_port[field]
|
||||
|
||||
port.obj_reset_changes()
|
||||
return port
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_uuid(cls, context, uuid=None):
|
||||
"""Find a port based on uuid and return a Port object.
|
||||
|
||||
:param uuid: the uuid of a port.
|
||||
:returns: a :class:`Port` object.
|
||||
"""
|
||||
db_port = cls.dbapi.get_port(uuid)
|
||||
return Port._from_db_object(cls(), db_port)
|
||||
|
||||
@base.remotable
|
||||
def save(self, context):
|
||||
"""Save updates to this Port.
|
||||
|
||||
Updates will be made column by column based on the result
|
||||
of self.what_changed(). If expected_task_state is provided,
|
||||
it will be checked against the in-database copy of the port
|
||||
before updates are made.
|
||||
|
||||
:param context: Security context
|
||||
"""
|
||||
updates = {}
|
||||
changes = self.obj_what_changed()
|
||||
for field in changes:
|
||||
updates[field] = self[field]
|
||||
self.dbapi.update_port(self.uuid, updates)
|
||||
|
||||
self.obj_reset_changes()
|
||||
|
||||
@base.remotable
|
||||
def refresh(self, context):
|
||||
"""Loads updates for this Port.
|
||||
|
||||
Loads a port with the same uuid from the database and
|
||||
checks for updated attributes. Updates are applied from
|
||||
the loaded port column by column, if there are any updates.
|
||||
|
||||
:param context: Security context
|
||||
"""
|
||||
current = self.__class__.get_by_uuid(context, uuid=self.uuid)
|
||||
for field in self.fields:
|
||||
if (hasattr(self, base.get_attrname(field)) and
|
||||
self[field] != current[field]):
|
||||
self[field] = current[field]
|
||||
@@ -16,7 +16,6 @@
|
||||
# under the License.
|
||||
"""Ironic test utilities."""
|
||||
|
||||
from ironic.db.sqlalchemy import models
|
||||
from ironic.openstack.common import jsonutils as json
|
||||
|
||||
|
||||
@@ -88,10 +87,14 @@ def get_test_node(**kw):
|
||||
|
||||
|
||||
def get_test_port(**kw):
|
||||
port = models.Port()
|
||||
port.id = kw.get('id', 987)
|
||||
port.uuid = kw.get('uuid', '4fc26c0b-03f2-4d2e-ae87-c02d7f33c234')
|
||||
port.node_id = kw.get('node_id', 123)
|
||||
port.address = kw.get('address', '52:54:00:cf:2d:31')
|
||||
port = {
|
||||
'id': kw.get('id', 987),
|
||||
'uuid': kw.get('uuid', '1be26c0b-03f2-4d2e-ae87-c02d7f33c781'),
|
||||
'node_id': kw.get('node_id', 123),
|
||||
'address': kw.get('address', '52:54:00:cf:2d:31'),
|
||||
'extra': kw.get('extra', '{}'),
|
||||
'created_at': kw.get('created_at'),
|
||||
'updated_at': kw.get('updated_at'),
|
||||
}
|
||||
|
||||
return port
|
||||
|
||||
@@ -378,8 +378,8 @@ class SSHDriverTestCase(db_base.DbTestCase):
|
||||
id=7,
|
||||
address='dd:ee:ff',
|
||||
uuid='4fc26c0b-03f2-4d2e-ae87-c02d7f33c234')]
|
||||
self.dbapi.create_port(ports[0])
|
||||
self.dbapi.create_port(ports[1])
|
||||
ports[0] = self.dbapi.create_port(ports[0])
|
||||
ports[1] = self.dbapi.create_port(ports[1])
|
||||
|
||||
self.mox.StubOutWithMock(self.dbapi, 'get_ports_by_node')
|
||||
self.dbapi.get_ports_by_node(self.node.get('id')).\
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
from ironic.common import context
|
||||
from ironic.db import api as db_api
|
||||
from ironic.db.sqlalchemy import models
|
||||
from ironic.objects import node
|
||||
from ironic import objects
|
||||
from ironic.tests.db import base
|
||||
from ironic.tests.db import utils
|
||||
|
||||
@@ -37,7 +37,7 @@ class TestNodeObject(base.DbTestCase):
|
||||
self.dbapi.get_node(uuid).AndReturn(self.fake_node)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
node.Node.get_by_uuid(ctxt, uuid)
|
||||
objects.Node.get_by_uuid(ctxt, uuid)
|
||||
self.mox.VerifyAll()
|
||||
# TODO(deva): add tests for load-on-demand info, eg. ports,
|
||||
# once Port objects are created
|
||||
@@ -52,7 +52,7 @@ class TestNodeObject(base.DbTestCase):
|
||||
self.dbapi.update_node(uuid, {'properties': "new property"})
|
||||
self.mox.ReplayAll()
|
||||
|
||||
n = node.Node.get_by_uuid(ctxt, uuid)
|
||||
n = objects.Node.get_by_uuid(ctxt, uuid)
|
||||
n.properties = "new property"
|
||||
n.save()
|
||||
self.mox.VerifyAll()
|
||||
@@ -68,7 +68,7 @@ class TestNodeObject(base.DbTestCase):
|
||||
dict(self.fake_node, properties="second"))
|
||||
self.mox.ReplayAll()
|
||||
|
||||
n = node.Node.get_by_uuid(ctxt, uuid)
|
||||
n = objects.Node.get_by_uuid(ctxt, uuid)
|
||||
self.assertEqual(n.properties, "first")
|
||||
n.refresh()
|
||||
self.assertEqual(n.properties, "second")
|
||||
@@ -80,12 +80,12 @@ class TestNodeObject(base.DbTestCase):
|
||||
n.update(self.fake_node)
|
||||
return n
|
||||
|
||||
@node.objectify
|
||||
@objects.objectify(objects.Node)
|
||||
def _convert_db_node():
|
||||
return _get_db_node()
|
||||
|
||||
self.assertIsInstance(_get_db_node(), models.Node)
|
||||
self.assertIsInstance(_convert_db_node(), node.Node)
|
||||
self.assertIsInstance(_convert_db_node(), objects.Node)
|
||||
|
||||
def test_objectify_many(self):
|
||||
def _get_db_nodes():
|
||||
@@ -96,11 +96,11 @@ class TestNodeObject(base.DbTestCase):
|
||||
nodes.append(n)
|
||||
return nodes
|
||||
|
||||
@node.objectify
|
||||
@objects.objectify(objects.Node)
|
||||
def _convert_db_nodes():
|
||||
return _get_db_nodes()
|
||||
|
||||
for n in _get_db_nodes():
|
||||
self.assertIsInstance(n, models.Node)
|
||||
for n in _convert_db_nodes():
|
||||
self.assertIsInstance(n, node.Node)
|
||||
self.assertIsInstance(n, objects.Node)
|
||||
|
||||
106
ironic/tests/objects/test_port.py
Normal file
106
ironic/tests/objects/test_port.py
Normal file
@@ -0,0 +1,106 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
# coding=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 ironic.common import context
|
||||
from ironic.db import api as db_api
|
||||
from ironic.db.sqlalchemy import models
|
||||
from ironic import objects
|
||||
from ironic.tests.db import base
|
||||
from ironic.tests.db import utils
|
||||
|
||||
|
||||
class TestPortObject(base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestPortObject, self).setUp()
|
||||
self.fake_port = utils.get_test_port()
|
||||
self.dbapi = db_api.get_instance()
|
||||
|
||||
def test_load(self):
|
||||
ctxt = context.get_admin_context()
|
||||
uuid = self.fake_port['uuid']
|
||||
self.mox.StubOutWithMock(self.dbapi, 'get_port')
|
||||
|
||||
self.dbapi.get_port(uuid).AndReturn(self.fake_port)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
objects.Port.get_by_uuid(ctxt, uuid)
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_save(self):
|
||||
ctxt = context.get_admin_context()
|
||||
uuid = self.fake_port['uuid']
|
||||
self.mox.StubOutWithMock(self.dbapi, 'get_port')
|
||||
self.mox.StubOutWithMock(self.dbapi, 'update_port')
|
||||
|
||||
self.dbapi.get_port(uuid).AndReturn(self.fake_port)
|
||||
self.dbapi.update_port(uuid, {'address': "b2:54:00:cf:2d:40"})
|
||||
self.mox.ReplayAll()
|
||||
|
||||
p = objects.Port.get_by_uuid(ctxt, uuid)
|
||||
p.address = "b2:54:00:cf:2d:40"
|
||||
p.save()
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_refresh(self):
|
||||
ctxt = context.get_admin_context()
|
||||
uuid = self.fake_port['uuid']
|
||||
self.mox.StubOutWithMock(self.dbapi, 'get_port')
|
||||
|
||||
self.dbapi.get_port(uuid).AndReturn(self.fake_port)
|
||||
self.dbapi.get_port(uuid).AndReturn(
|
||||
utils.get_test_port(address="c3:54:00:cf:2d:40"))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
p = objects.Port.get_by_uuid(ctxt, uuid)
|
||||
self.assertEqual(p.address, "52:54:00:cf:2d:31")
|
||||
|
||||
p.refresh()
|
||||
|
||||
self.assertEqual(p.address, "c3:54:00:cf:2d:40")
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_objectify(self):
|
||||
def _get_db_port():
|
||||
p = models.Port()
|
||||
p.update(self.fake_port)
|
||||
return p
|
||||
|
||||
@objects.objectify(objects.Port)
|
||||
def _convert_db_port():
|
||||
return _get_db_port()
|
||||
|
||||
self.assertIsInstance(_get_db_port(), models.Port)
|
||||
self.assertIsInstance(_convert_db_port(), objects.Port)
|
||||
|
||||
def test_objectify_many(self):
|
||||
def _get_db_ports():
|
||||
nodes = []
|
||||
for i in xrange(5):
|
||||
n = models.Port()
|
||||
n.update(self.fake_port)
|
||||
nodes.append(n)
|
||||
return nodes
|
||||
|
||||
@objects.objectify(objects.Port)
|
||||
def _convert_db_nodes():
|
||||
return _get_db_ports()
|
||||
|
||||
for p in _get_db_ports():
|
||||
self.assertIsInstance(p, models.Port)
|
||||
for p in _convert_db_nodes():
|
||||
self.assertIsInstance(p, objects.Port)
|
||||
Reference in New Issue
Block a user