Update db models and API.
This commit is contained in:
parent
aee8142fb4
commit
0bd40b7c82
@ -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):
|
||||
|
@ -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:
|
||||
|
@ -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.
|
||||
"""
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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.')
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user