Update db models and API.

This commit is contained in:
Devananda van der Veen 2013-05-11 07:09:40 -07:00
parent aee8142fb4
commit 0bd40b7c82
7 changed files with 254 additions and 167 deletions

View File

@ -109,6 +109,16 @@ class Node(Base):
"""A representation of a bare metal node"""
uuid = wtypes.text
cpu_arch = wtypes.text
cpu_num = int
memory = int
local_storage_max = int
task_state = wtypes.text
image_path = wtypes.text
instance_uuid = wtypes.text
instance_name = wtypes.text
power_info = wtypes.text
extra = wtypes.text
def __init__(self, **kwargs):
self.fields = list(kwargs)
@ -117,17 +127,35 @@ class Node(Base):
@classmethod
def sample(cls):
power_info = "{'driver': 'ipmi', 'user': 'fake', " \
+ "'password': 'password', 'address': '1.2.3.4'}"
return cls(uuid='1be26c0b-03f2-4d2e-ae87-c02d7f33c123',
cpu_arch='x86_64',
cpu_num=4,
memory=16384,
local_storage_max=1000,
task_state='NOSTATE',
image_path='/fake/image/path',
instance_uuid='8227348d-5f1d-4488-aad1-7c92b2d42504',
power_info=power_info,
extra='{}',
)
class NodesController(rest.RestController):
"""REST controller for Nodes"""
@wsme_pecan.wsexpose(Node, unicode)
def post(self, node):
@wsme_pecan.wsexpose(Node, body=Node)
def post(self, data):
"""Ceate a new node."""
return Node.sample()
try:
node = pecan.request.dbapi.create_node(
data.as_dict(db.models.Node))
except Exception as e:
LOG.exception(e)
raise wsme.exc.ClientSideError(_("Invalid data"))
return node
@wsme_pecan.wsexpose()
def get_all(self):
@ -139,7 +167,7 @@ class NodesController(rest.RestController):
def get_one(self, node_id):
"""Retrieve information about the given node."""
r = pecan.request.dbapi.get_node_by_id(node_id)
return r
return Node.from_db_model(r)
@wsme_pecan.wsexpose()
def delete(self, node_id):

View File

@ -305,6 +305,12 @@ def is_valid_boolstr(val):
return str(val).lower() in boolstrs
def is_valid_mac(address):
"""Verify the format of a MAC addres."""
if re.match("[0-9a-f]{2}([-:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", address.lower()):
return True
return False
def is_valid_ipv4(address):
"""Verify that address represents a valid IPv4 address."""
try:

View File

@ -56,67 +56,98 @@ class Connection(object):
"""Return a list of ids of all unassociated nodes."""
@abc.abstractmethod
def reserve_node(self, *args, **kwargs):
"""Find a free node and associate it.
def reserve_node(self, node, values):
"""Associate a node with an instance.
TBD
:param node: The id or uuid of a node.
:param values: Values to set while reserving the node.
Must include 'instance_uuid'.
:return: The reserved Node.
"""
@abc.abstractmethod
def create_node(self, *args, **kwargs):
"""Create a new node."""
def create_node(self, values):
"""Create a new node.
:param values: Values to instantiate the node with.
:returns: Node.
"""
@abc.abstractmethod
def get_node_by_id(self, node_id):
def get_node(self, node):
"""Return a node.
:param node_id: The id or uuid of a node.
:param node: The id or uuid of a node.
:return Node:
"""
@abc.abstractmethod
def get_node_by_instance_id(self, instance_id):
def get_node_by_instance(self, instance):
"""Return a node.
:param instance_id: The instance id or uuid of a node.
:param instance: The instance name or uuid to search for.
:returns: Node.
"""
@abc.abstractmethod
def destroy_node(self, node_id):
"""Destroy a node.
def destroy_node(self, node):
"""Destroy a node and all associated interfaces.
:param node_id: The id or uuid of a node.
:param node: The id or uuid of a node.
"""
@abc.abstractmethod
def update_node(self, node_id, *args, **kwargs):
def update_node(self, node, values):
"""Update properties of a node.
:param node_id: The id or uuid of a node.
TBD
:param node: The id or uuid of a node.
:param values: Dict of values to update.
:returns: Node.
"""
@abc.abstractmethod
def get_iface(self, iface_id):
def get_iface(self, iface):
"""Return an interface.
:param iface_id: The id or MAC of an interface.
:param iface: The id or MAC of an interface.
:returns: Iface.
"""
@abc.abstractmethod
def create_iface(self, *args, **kwargs):
"""Create a new iface."""
def get_iface_by_vif(self, vif):
"""Return the interface corresponding to this VIF.
:param vif: The uuid of the VIF.
:returns: Iface.
"""
@abc.abstractmethod
def update_iface(self, iface_id, *args, **kwargs):
def get_iface_by_node(self, node):
"""List all the interfaces for a given node.
:param node: The id or uuid of a node.
:returns: list of Iface.
"""
@abc.abstractmethod
def create_iface(self, values):
"""Create a new iface.
:param values: Dict of values.
"""
@abc.abstractmethod
def update_iface(self, iface, values):
"""Update properties of an iface.
:param iface_id: The id or MAC of an interface.
TBD
:param iface: The id or MAC of an interface.
:param values: Dict of values to update.
:returns: Iface.
"""
@abc.abstractmethod
def destroy_iface(self, iface_id):
def destroy_iface(self, iface):
"""Destroy an iface.
:param iface_id: The id or MAC of an interface.
:param iface: The id or MAC of an interface.
"""

View File

@ -16,13 +16,14 @@
# License for the specific language governing permissions and limitations
# under the License.
"""
Model classes for use in the storage API.
Model classes for use above the storage layer.
NOT YET IMPLEMENTED.
"""
class Model(object):
"""Base class for storage API models.
"""
"""Base class for API models."""
def __init__(self, **kwds):
self.fields = list(kwds)
@ -60,7 +61,7 @@ class Node(Model):
class Iface(Model):
"""Representation of a NIC."""
"""Representation of a network interface."""
def __init__(self, mac, node_id, extra):
Model.__init__(mac=mac,

View File

@ -26,8 +26,9 @@ from sqlalchemy.orm.exc import NoResultFound
from sqlalchemy.orm.exc import MultipleResultsFound
from ironic.common import exception
from ironic.common import utils
from ironic.db import api
from ironic.db.sqlalchemy.models import Node, Iface
from ironic.db.sqlalchemy import models
from ironic.openstack.common.db.sqlalchemy import session as db_session
from ironic.openstack.common import log
from ironic.openstack.common import uuidutils
@ -41,48 +42,12 @@ LOG = log.getLogger(__name__)
get_engine = db_session.get_engine
get_session = db_session.get_session
def get_backend():
"""The backend is this module itself."""
return Connection()
# - nodes
# - { id: AUTO_INC INTEGER
# uuid: node uuid
# power_info: JSON of power mgmt information
# task_state: current task
# image_path: URL of associated image
# instance_uuid: uuid of associated instance
# instance_name: name of associated instance
# hw_spec_id: hw specification id (->hw_specs.id)
# inst_spec_id: instance specification id (->inst_specs.id)
# extra: JSON blob of extra data
# }
# - ifaces
# - { id: AUTO_INC INTEGER
# mac: MAC address of this iface
# node_id: associated node (->nodes.id)
# ?datapath_id
# ?port_no
# ?model
# extra: JSON blob of extra data
# }
# - hw_specs
# - { id: AUTO_INC INTEGER
# cpu_arch:
# n_cpu:
# n_disk:
# ram_mb:
# storage_gb:
# }
# - inst_specs
# - { id: AUTO_INC INTEGER
# root_mb:
# swap_mb:
# image_path:
# }
def model_query(model, *args, **kwargs):
"""Query helper for simpler session usage.
@ -94,6 +59,24 @@ def model_query(model, *args, **kwargs):
return query
def add_uuid_filter(query, value):
if utils.is_int_like(value):
return query.filter_by(id=value)
elif uuidutils.is_uuid_like(value):
return query.filter_by(uuid=value)
else:
raise exception.InvalidUUID(uuid=value)
def add_mac_filter(query, value):
if utils.is_int_like(iface):
query.filter_by(id=iface)
elif utils.is_valid_mac(iface):
query.filter_by(address=iface)
else:
raise exception.InvalidMAC(mac=value)
class Connection(api.Connection):
"""SqlAlchemy connection."""
@ -101,95 +84,139 @@ class Connection(api.Connection):
pass
def get_nodes(self, columns):
"""Return a list of dicts of all nodes.
:param columns: List of columns to return.
"""
pass
def get_associated_nodes(self):
"""Return a list of ids of all associated nodes."""
pass
def get_unassociated_nodes(self):
"""Return a list of ids of all unassociated nodes."""
pass
def reserve_node(self, *args, **kwargs):
"""Find a free node and associate it.
def reserve_node(self, node, values):
if values.get('instance_uuid', None) is None:
raise exception.IronicException(_("Instance UUID not specified"))
TBD
"""
pass
session = get_session()
with session.begin():
query = model_query(models.Node, session=session)
query = add_uuid_filter(query, node)
def create_node(self, *args, **kwargs):
"""Create a new node."""
node = Node()
count = query.filter_by(instance_uuid=None).\
update(values)
if count != 1:
raise exception.IronicException(_(
"Failed to associate instance %(i)s to node %(n)s.") %
{'i': values['instance_uuid'], 'n': node})
ref = query.one()
def get_node_by_id(self, node_id):
"""Return a node.
return ref
:param node_id: The id or uuid of a node.
"""
query = model_query(Node)
if uuidutils.is_uuid_like(node_id):
query.filter_by(uuid=node_id)
else:
query.filter_by(id=node_id)
def create_node(self, values):
node = models.Node()
node.update(values)
node.save()
def get_node(self, node):
query = model_query(models.Node)
query = add_uuid_filter(query, node)
try:
result = query.one()
except NoResultFound:
raise
except MultipleResultsFound:
raise
raise exception.NodeNotFound(node=node)
return result
def get_node_by_instance(self, instance):
query = model_query(models.Node)
if uuidutils.is_uuid_like(instance):
query.filter_by(instance_uuid=instance)
else:
query.filter_by(instance_name=instance)
def get_node_by_instance_id(self, instance_id):
"""Return a node.
try:
result = query.one()
except NoResultFound:
raise exception.NodeNotFound(node=node)
:param instance_id: The instance id or uuid of a node.
"""
return result
def destroy_node(self, node):
session = get_session()
with session.begin():
query = model_query(models.Node, session=session)
query = add_uuid_filter(query, node)
count = query.delete()
if count != 1:
raise exception.NodeNotFound(node=node)
def update_node(self, node, values):
session = get_session()
with session.begin():
query = model_query(models.Node, session=session)
query = add_uuid_filter(query, node)
count = query.update(values)
if count != 1:
raise exception.NodeNotFound(node=node)
ref = query.one()
return ref
def get_iface(self, iface):
query = model_query(models.Iface)
query = add_mac_filter(query, iface)
try:
result = query.one()
except NoResultFound:
raise exception.InterfaceNotFound(iface=iface)
return result
def get_iface_by_vif(self, vif):
pass
def destroy_node(self, node_id):
"""Destroy a node.
def get_iface_by_node(self, node):
session = get_session()
:param node_id: The id or uuid of a node.
"""
pass
if is_int_like(node):
query = session.query(models.Iface).\
filter_by(node_id=node)
else:
query = session.query(models.Iface).\
join(models.Node,
models.Iface.node_id == models.Node.id).\
filter_by(models.Node.uuid == node)
result = query.all()
def update_node(self, node_id, *args, **kwargs):
"""Update properties of a node.
return result
:param node_id: The id or uuid of a node.
TBD
"""
pass
def create_iface(self, values):
iface = models.Iface()
iface.update(values)
iface.save()
def get_iface(self, iface_id):
"""Return an interface.
def update_iface(self, iface, values):
session = get_session()
with session.begin():
query = model_query(models.Iface, session=session)
query = add_mac_filter(query, iface)
count = query.update(values)
if count != 1:
raise exception.InterfaceNotFound(iface=iface)
ref = query.one()
return ref
:param iface_id: The id or MAC of an interface.
"""
pass
def create_iface(self, *args, **kwargs):
"""Create a new iface."""
pass
def update_iface(self, iface_id, *args, **kwargs):
"""Update properties of an iface.
:param iface_id: The id or MAC of an interface.
TBD
"""
pass
def destroy_iface(self, iface_id):
"""Destroy an iface.
:param iface_id: The id or MAC of an interface.
"""
pass
def destroy_iface(self, iface):
session = get_session()
with session.begin():
query = model_query(models.Iface, session=session)
query = add_mac_filter(query, iface)
count = query.update(values)
if count != 1:
raise exception.NodeNotFound(node=node)
ref = query.one()
return ref

View File

@ -34,8 +34,12 @@ def upgrade(migrate_engine):
nodes = Table('nodes', meta,
Column('id', Integer, primary_key=True, nullable=False),
Column('uuid', String(length=26)),
Column('uuid', String(length=36)),
Column('power_info', Text),
Column('cpu_arch', String(length=10)),
Column('cpu_num', Integer),
Column('memory', Integer),
Column('local_storage_max', Integer),
Column('task_state', String(length=255)),
Column('image_path', String(length=255), nullable=True),
Column('instance_uuid', String(length=255), nullable=True),
@ -49,7 +53,7 @@ def upgrade(migrate_engine):
ifaces = Table('ifaces', meta,
Column('id', Integer, primary_key=True, nullable=False),
Column('uuid', String(length=26)),
Column('address', String(length=18)),
Column('node_id', Integer, ForeignKey('nodes.id'),
nullable=True),
Column('extra', Text),
@ -69,13 +73,24 @@ def upgrade(migrate_engine):
raise
indexes = [
Index('uuid', nodes.c.uuid),
Index('uuid', ifaces.c.uuid),
Index('node_cpu_mem_disk', nodes.c.cpu_num,
nodes.c.memory, nodes.c.local_storage_max),
Index('node_instance_uuid', nodes.c.instance_uuid),
]
uniques = [
UniqueConstraint('uuid', table=nodes,
name='node_uuid_ux'),
UniqueConstraint('address', table=ifaces,
name='iface_address_ux'),
]
if migrate_engine.name == 'mysql' or migrate_engine.name == 'postgresql':
for index in indexes:
index.create(migrate_engine)
for index in uniques:
index.create(migrate_engine)
def downgrade(migrate_engine):
raise NotImplementedError('Downgrade from Folsom is unsupported.')

View File

@ -58,11 +58,15 @@ class Node(Base):
__tablename__ = 'nodes'
id = Column(Integer, primary_key=True)
uuid = Column(String(36))
uuid = Column(String(36), unique=True)
power_info = Column(Text)
cpu_arch = Column(String(10))
cpu_num = Column(Integer)
memory = Column(Integer)
local_storage_max = Column(Integer)
task_state = Column(String(255))
image_path = Column(String(255), nullable=True)
instance_uuid = Column(String(36), nullable=True)
instance_uuid = Column(String(36), nullable=True, unique=True)
instance_name = Column(String(255), nullable=True)
extra = Column(Text)
@ -72,31 +76,6 @@ class Iface(Base):
__tablename__ = 'ifaces'
id = Column(Integer, primary_key=True)
mac = Column(String(255), unique=True)
address = Column(String(18), unique=True)
node_id = Column(Integer, ForeignKey('nodes.id'), nullable=True)
extra = Column(Text)
class HwSpec(Base):
"""Represents a unique hardware class."""
__tablename__ = 'hw_specs'
id = Column(Integer, primary_key=True)
uuid = Column(String(36))
cpu_arch = Column(String(255))
n_cpu = Column(Integer)
ram_mb = Column(Integer)
storage_gb = Column(Integer)
name = Column(String(255), nullable=True)
n_disk = Column(Integer, nullable=True)
class InstSpec(Base):
"""Represents a unique instance class."""
__tablename__ = 'inst_specs'
id = Column(Integer, primary_key=True)
uuid = Column(String(36))
root_mb = Column(Integer)
swap_mb = Column(Integer)
name = Column(String(255), nullable=True)