From 9e242f8d2d1dded3a3a9b58a16d0b7d50f0ed831 Mon Sep 17 00:00:00 2001 From: Devananda van der Veen Date: Fri, 10 May 2013 11:48:08 -0700 Subject: [PATCH] Implement skeleton for a new DB backend. --- ironic/api/app.py | 6 +- ironic/api/controllers/v1.py | 24 +-- ironic/api/hooks.py | 12 +- ironic/cmd/dbsync.py | 38 ++++ ironic/db/api.py | 122 +++++++++++ ironic/db/models.py | 69 +++++++ ironic/db/sqlalchemy/__init__.py | 0 ironic/db/sqlalchemy/api.py | 195 ++++++++++++++++++ ironic/db/sqlalchemy/migrate_repo/__init__.py | 0 ironic/db/sqlalchemy/migrate_repo/manage.py | 22 ++ ironic/db/sqlalchemy/migrate_repo/migrate.cfg | 20 ++ .../migrate_repo/versions/001_init.py | 81 ++++++++ .../migrate_repo/versions/__init__.py | 0 ironic/db/sqlalchemy/migration.py | 9 +- ironic/db/sqlalchemy/models.py | 102 +++++++++ setup.cfg | 1 + setup.py | 21 +- 17 files changed, 689 insertions(+), 33 deletions(-) create mode 100644 ironic/cmd/dbsync.py create mode 100644 ironic/db/api.py create mode 100644 ironic/db/models.py create mode 100644 ironic/db/sqlalchemy/__init__.py create mode 100644 ironic/db/sqlalchemy/api.py create mode 100644 ironic/db/sqlalchemy/migrate_repo/__init__.py create mode 100644 ironic/db/sqlalchemy/migrate_repo/manage.py create mode 100644 ironic/db/sqlalchemy/migrate_repo/migrate.cfg create mode 100644 ironic/db/sqlalchemy/migrate_repo/versions/001_init.py create mode 100644 ironic/db/sqlalchemy/migrate_repo/versions/__init__.py create mode 100644 ironic/db/sqlalchemy/models.py diff --git a/ironic/api/app.py b/ironic/api/app.py index 12f5b8d1b7..8539defec3 100644 --- a/ironic/api/app.py +++ b/ironic/api/app.py @@ -39,9 +39,8 @@ def get_pecan_config(): def setup_app(pecan_config=None, extra_hooks=None): - # FIXME: Replace DBHook with a hooks.TransactionHook - app_hooks = [hooks.ConfigHook()] -# hooks.DBHook()] + app_hooks = [hooks.ConfigHook(), + hooks.DBHook()] if extra_hooks: app_hooks.extend(extra_hooks) @@ -62,6 +61,7 @@ def setup_app(pecan_config=None, extra_hooks=None): force_canonical=getattr(pecan_config.app, 'force_canonical', True), hooks=app_hooks, ) +# TODO: add this back in # wrap_app=middleware.ParsableErrorMiddleware, if pecan_config.app.enable_acl: diff --git a/ironic/api/controllers/v1.py b/ironic/api/controllers/v1.py index b7f83c9058..9e87e68ab8 100644 --- a/ironic/api/controllers/v1.py +++ b/ironic/api/controllers/v1.py @@ -61,9 +61,10 @@ class Interface(Base): node_id = int address = wtypes.text - def __init__(self, node_id=None, address=None): - self.node_id = node_id - self.address = address + def __init__(self, **kwargs): + self.fields = list(kwargs) + for k, v in kwargs.iteritems(): + setattr(self, k, v) @classmethod def sample(cls): @@ -108,19 +109,15 @@ class Node(Base): """A representation of a bare metal node""" uuid = wtypes.text - cpus = int - memory_mb = int - def __init__(self, uuid=None, cpus=None, memory_mb=None): - self.uuid = uuid - self.cpus = cpus - self.memory_mb = memory_mb + def __init__(self, **kwargs): + self.fields = list(kwargs) + for k, v in kwargs.iteritems(): + setattr(self, k, v) @classmethod def sample(cls): return cls(uuid='1be26c0b-03f2-4d2e-ae87-c02d7f33c123', - cpus=2, - memory_mb=1024, ) @@ -141,9 +138,8 @@ class NodesController(rest.RestController): @wsme_pecan.wsexpose(Node, unicode) def get_one(self, node_id): """Retrieve information about the given node.""" - one = Node.sample() - one.uuid = node_id - return one + r = pecan.request.dbapi.get_node_by_id(node_id) + return r @wsme_pecan.wsexpose() def delete(self, node_id): diff --git a/ironic/api/hooks.py b/ironic/api/hooks.py index 6b066571ee..b26cea34ec 100644 --- a/ironic/api/hooks.py +++ b/ironic/api/hooks.py @@ -19,7 +19,7 @@ from oslo.config import cfg from pecan import hooks -from ironic import db +from ironic.db import api as dbapi class ConfigHook(hooks.PecanHook): @@ -34,12 +34,4 @@ class ConfigHook(hooks.PecanHook): class DBHook(hooks.PecanHook): def before(self, state): - # FIXME - storage_engine = storage.get_engine(state.request.cfg) - state.request.storage_engine = storage_engine - state.request.storage_conn = storage_engine.get_connection( - state.request.cfg) - - # def after(self, state): - # print 'method:', state.request.method - # print 'response:', state.response.status + state.request.dbapi = dbapi.get_instance() diff --git a/ironic/cmd/dbsync.py b/ironic/cmd/dbsync.py new file mode 100644 index 0000000000..439bf3c9d2 --- /dev/null +++ b/ironic/cmd/dbsync.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +# +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright 2013 Hewlett-Packard Development Company, L.P. +# 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. + +""" +Run storage database migration. +""" + +import sys + +from oslo.config import cfg + +from ironic.common import service +from ironic.db import migration + +CONF = cfg.CONF +CONF.import_opt('db_backend', + 'ironic.openstack.common.db.api') + +def main(): + service.prepare_service(sys.argv) + migration.db_sync() diff --git a/ironic/db/api.py b/ironic/db/api.py new file mode 100644 index 0000000000..3791deec43 --- /dev/null +++ b/ironic/db/api.py @@ -0,0 +1,122 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# -*- encoding: utf-8 -*- +# +# Copyright 2013 Hewlett-Packard Development Company, L.P. +# +# 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. +""" +Base classes for storage engines +""" + +import abc + +from ironic.openstack.common.db import api as db_api + +_BACKEND_MAPPING = {'sqlalchemy': 'ironic.db.sqlalchemy.api'} + + +def get_instance(): + """Return a DB API instance.""" + IMPL = db_api.DBAPI(backend_mapping=_BACKEND_MAPPING) + return IMPL + + +class Connection(object): + """Base class for storage system connections.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __init__(self): + """Constructor.""" + + @abc.abstractmethod + def get_nodes(self, columns): + """Return a list of dicts of all nodes. + + :param columns: List of columns to return. + """ + + @abc.abstractmethod + def get_associated_nodes(self): + """Return a list of ids of all associated nodes.""" + + @abc.abstractmethod + def get_unassociated_nodes(self): + """Return a list of ids of all unassociated nodes.""" + + @abc.abstractmethod + def reserve_node(self, *args, **kwargs): + """Find a free node and associate it. + + TBD + """ + + @abc.abstractmethod + def create_node(self, *args, **kwargs): + """Create a new node.""" + + @abc.abstractmethod + def get_node_by_id(self, node_id): + """Return a node. + + :param node_id: The id or uuid of a node. + """ + + @abc.abstractmethod + def get_node_by_instance_id(self, instance_id): + """Return a node. + + :param instance_id: The instance id or uuid of a node. + """ + + @abc.abstractmethod + def destroy_node(self, node_id): + """Destroy a node. + + :param node_id: The id or uuid of a node. + """ + + @abc.abstractmethod + def update_node(self, node_id, *args, **kwargs): + """Update properties of a node. + + :param node_id: The id or uuid of a node. + TBD + """ + + @abc.abstractmethod + def get_iface(self, iface_id): + """Return an interface. + + :param iface_id: The id or MAC of an interface. + """ + + @abc.abstractmethod + def create_iface(self, *args, **kwargs): + """Create a new iface.""" + + @abc.abstractmethod + def update_iface(self, iface_id, *args, **kwargs): + """Update properties of an iface. + + :param iface_id: The id or MAC of an interface. + TBD + """ + + @abc.abstractmethod + def destroy_iface(self, iface_id): + """Destroy an iface. + + :param iface_id: The id or MAC of an interface. + """ diff --git a/ironic/db/models.py b/ironic/db/models.py new file mode 100644 index 0000000000..22083800be --- /dev/null +++ b/ironic/db/models.py @@ -0,0 +1,69 @@ +# -*- encoding: utf-8 -*- +# +# Copyright © 2013 New Dream Network, LLC (DreamHost) +# +# Author: Doug Hellmann +# +# 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. +""" +Model classes for use in the storage API. +""" + + +class Model(object): + """Base class for storage API models. + """ + + def __init__(self, **kwds): + self.fields = list(kwds) + for k, v in kwds.iteritems(): + setattr(self, k, v) + + def as_dict(self): + d = {} + for f in self.fields: + v = getattr(self, f) + if isinstance(v, Model): + v = v.as_dict() + elif isinstance(v, list) and v and isinstance(v[0], Model): + v = [sub.as_dict() for sub in v] + d[f] = v + return d + + def __eq__(self, other): + return self.as_dict() == other.as_dict() + + +class Node(Model): + """Representation of a bare metal node.""" + + def __init__(self, uuid, power_info, task_state, image_path, + instance_uuid, instance_name, extra): + Model.__init__(uuid=uuid, + power_info=power_info, + task_state=task_state, + image_path=image_path, + instance_uuid=instance_uuid, + instance_name=instance_name, + extra=extra, + ) + + +class Iface(Model): + """Representation of a NIC.""" + + def __init__(self, mac, node_id, extra): + Model.__init__(mac=mac, + node_id=node_id, + extra=extra, + ) diff --git a/ironic/db/sqlalchemy/__init__.py b/ironic/db/sqlalchemy/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ironic/db/sqlalchemy/api.py b/ironic/db/sqlalchemy/api.py new file mode 100644 index 0000000000..c615b485cd --- /dev/null +++ b/ironic/db/sqlalchemy/api.py @@ -0,0 +1,195 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# -*- encoding: utf-8 -*- +# +# Copyright 2013 Hewlett-Packard Development Company, L.P. +# +# 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. + +"""SQLAlchemy storage backend.""" + +import sys +import uuid + +from oslo.config import cfg + +from sqlalchemy.orm.exc import NoResultFound +from sqlalchemy.orm.exc import MultipleResultsFound + +from ironic.common import exception +from ironic.db import api +from ironic.db.sqlalchemy.models import Node, Iface +from ironic.openstack.common.db.sqlalchemy import session as db_session +from ironic.openstack.common import log +from ironic.openstack.common import uuidutils + +CONF = cfg.CONF +CONF.import_opt('sql_connection', + 'ironic.openstack.common.db.sqlalchemy.session') + +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. + + :param session: if present, the session to use + """ + + session = kwargs.get('session') or get_session() + query = session.query(model, *args) + return query + + +class Connection(api.Connection): + """SqlAlchemy connection.""" + + def __init__(self): + 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. + + TBD + """ + pass + + def create_node(self, *args, **kwargs): + """Create a new node.""" + node = Node() + + def get_node_by_id(self, node_id): + """Return a node. + + :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) + + try: + result = query.one() + except NoResultFound: + raise + except MultipleResultsFound: + raise + return result + + + def get_node_by_instance_id(self, instance_id): + """Return a node. + + :param instance_id: The instance id or uuid of a node. + """ + pass + + def destroy_node(self, node_id): + """Destroy a node. + + :param node_id: The id or uuid of a node. + """ + pass + + def update_node(self, node_id, *args, **kwargs): + """Update properties of a node. + + :param node_id: The id or uuid of a node. + TBD + """ + pass + + def get_iface(self, iface_id): + """Return an interface. + + :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 diff --git a/ironic/db/sqlalchemy/migrate_repo/__init__.py b/ironic/db/sqlalchemy/migrate_repo/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ironic/db/sqlalchemy/migrate_repo/manage.py b/ironic/db/sqlalchemy/migrate_repo/manage.py new file mode 100644 index 0000000000..a5502298b9 --- /dev/null +++ b/ironic/db/sqlalchemy/migrate_repo/manage.py @@ -0,0 +1,22 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# -*- encoding: utf-8 -*- +# +# Copyright 2013 Hewlett-Packard Development Company, L.P. +# +# 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 migrate.versioning.shell import main + + +if __name__ == '__main__': + main(debug='False', repository='.') diff --git a/ironic/db/sqlalchemy/migrate_repo/migrate.cfg b/ironic/db/sqlalchemy/migrate_repo/migrate.cfg new file mode 100644 index 0000000000..bf8401966e --- /dev/null +++ b/ironic/db/sqlalchemy/migrate_repo/migrate.cfg @@ -0,0 +1,20 @@ +[db_settings] +# Used to identify which repository this database is versioned under. +# You can use the name of your project. +repository_id=ironic + +# The name of the database table used to track the schema version. +# This name shouldn't already be used by your project. +# If this is changed once a database is under version control, you'll need to +# change the table name in each database too. +version_table=migrate_version + +# When committing a change script, Migrate will attempt to generate the +# sql for all supported databases; normally, if one of them fails - probably +# because you don't have that database installed - it is ignored and the +# commit continues, perhaps ending successfully. +# Databases in this list MUST compile successfully during a commit, or the +# entire commit will fail. List the databases your application will actually +# be using to ensure your updates to that database work properly. +# This must be a list; example: ['postgres','sqlite'] +required_dbs=[] diff --git a/ironic/db/sqlalchemy/migrate_repo/versions/001_init.py b/ironic/db/sqlalchemy/migrate_repo/versions/001_init.py new file mode 100644 index 0000000000..a0a79ca1de --- /dev/null +++ b/ironic/db/sqlalchemy/migrate_repo/versions/001_init.py @@ -0,0 +1,81 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# -*- encoding: utf-8 -*- +# +# Copyright 2013 Hewlett-Packard Development Company, L.P. +# +# 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 migrate.changeset import UniqueConstraint +from sqlalchemy import Table, Column, Index, ForeignKey, MetaData +from sqlalchemy import Boolean, DateTime, Float, Integer, String, Text + +from ironic.openstack.common import log as logging + +LOG = logging.getLogger(__name__) + +ENGINE='InnoDB' +CHARSET='utf8' + + +def upgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + nodes = Table('nodes', meta, + Column('id', Integer, primary_key=True, nullable=False), + Column('uuid', String(length=26)), + Column('power_info', Text), + Column('task_state', String(length=255)), + Column('image_path', String(length=255), nullable=True), + Column('instance_uuid', String(length=255), nullable=True), + Column('instance_name', String(length=255), nullable=True), + Column('extra', Text), + Column('created_at', DateTime), + Column('updated_at', DateTime), + mysql_engine=ENGINE, + mysql_charset=CHARSET, + ) + + ifaces = Table('ifaces', meta, + Column('id', Integer, primary_key=True, nullable=False), + Column('uuid', String(length=26)), + Column('node_id', Integer, ForeignKey('nodes.id'), + nullable=True), + Column('extra', Text), + Column('created_at', DateTime), + Column('updated_at', DateTime), + mysql_engine=ENGINE, + mysql_charset=CHARSET, + ) + + tables = [nodes, ifaces] + for table in tables: + try: + table.create() + except Exception: + LOG.info(repr(table)) + LOG.Exception(_('Exception while creating table.')) + raise + + indexes = [ + Index('uuid', nodes.c.uuid), + Index('uuid', ifaces.c.uuid), + ] + + if migrate_engine.name == 'mysql' or migrate_engine.name == 'postgresql': + for index in indexes: + index.create(migrate_engine) + +def downgrade(migrate_engine): + raise NotImplementedError('Downgrade from Folsom is unsupported.') diff --git a/ironic/db/sqlalchemy/migrate_repo/versions/__init__.py b/ironic/db/sqlalchemy/migrate_repo/versions/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ironic/db/sqlalchemy/migration.py b/ironic/db/sqlalchemy/migration.py index d4b972ab68..f6999e4159 100644 --- a/ironic/db/sqlalchemy/migration.py +++ b/ironic/db/sqlalchemy/migration.py @@ -19,15 +19,14 @@ import distutils.version as dist_version import os -from ironic.common import exception -from ironic.db import migration -from ironic.openstack.common.db.sqlalchemy import session as db_session - - import migrate from migrate.versioning import util as migrate_util import sqlalchemy +from ironic.common import exception +from ironic.db import migration +from ironic.openstack.common.db.sqlalchemy import session as db_session + @migrate_util.decorator def patched_with_engine(f, *a, **kw): diff --git a/ironic/db/sqlalchemy/models.py b/ironic/db/sqlalchemy/models.py new file mode 100644 index 0000000000..547ddb5ff4 --- /dev/null +++ b/ironic/db/sqlalchemy/models.py @@ -0,0 +1,102 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# -*- encoding: utf-8 -*- +# +# Copyright 2013 Hewlett-Packard Development Company, L.P. +# +# 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. + +""" +SQLAlchemy models for baremetal data. +""" + +import urlparse + +from oslo.config import cfg + +from sqlalchemy import Table, Column, Index, ForeignKey +from sqlalchemy import Boolean, DateTime, Float, Integer, String, Text +from sqlalchemy.ext.declarative import declarative_base + +from ironic.openstack.common.db.sqlalchemy import models + +sql_opts = [ + cfg.StrOpt('mysql_engine', + default='InnoDB', + help='MySQL engine') +] + +cfg.CONF.register_opts(sql_opts) + + +def table_args(): + engine_name = urlparse.urlparse(cfg.CONF.database_connection).scheme + if engine_name == 'mysql': + return {'mysql_engine': cfg.CONF.mysql_engine, + 'mysql_charset': "utf8"} + return None + +class IronicBase(models.TimestampMixin, + models.ModelBase): + metadata = None + + +Base = declarative_base(cls=IronicBase) + + +class Node(Base): + """Represents a bare metal node.""" + + __tablename__ = 'nodes' + id = Column(Integer, primary_key=True) + uuid = Column(String(36)) + power_info = Column(Text) + task_state = Column(String(255)) + image_path = Column(String(255), nullable=True) + instance_uuid = Column(String(36), nullable=True) + instance_name = Column(String(255), nullable=True) + extra = Column(Text) + + +class Iface(Base): + """Represents a NIC in a bare metal node.""" + + __tablename__ = 'ifaces' + id = Column(Integer, primary_key=True) + mac = Column(String(255), 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) diff --git a/setup.cfg b/setup.cfg index c636393106..b7a9167545 100644 --- a/setup.cfg +++ b/setup.cfg @@ -29,6 +29,7 @@ packages = [entry_points] console_scripts = ironic-api = ironic.cmd.api:main + ironic-dbsync = ironic.cmd.dbsync:main ironic-manager = ironic.cmd.manager:main [build_sphinx] diff --git a/setup.py b/setup.py index 1e9882df0e..bad41c2049 100644 --- a/setup.py +++ b/setup.py @@ -16,6 +16,25 @@ import setuptools +from ironic.openstack.common import setup as common_setup + +project = 'ironic' + setuptools.setup( + name=project, + version=common_setup.get_version(project, '2013.1'), + description='Bare Metal controller', + classifiers=[ + 'Environment :: OpenStack', + 'Intended Audience :: Information Technology', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + ], + include_package_data=True, setup_requires=['d2to1>=0.2.10,<0.3', 'pbr>=0.5,<0.6'], - d2to1=True) + d2to1=True, +)