diff --git a/ironic/common/exception.py b/ironic/common/exception.py index f9da690d18..8615048da5 100644 --- a/ironic/common/exception.py +++ b/ironic/common/exception.py @@ -249,6 +249,10 @@ class PortNotFound(NotFound): message = _("Port %(port)s could not be found.") +class ChassisNotFound(NotFound): + message = _("Chassis %(chassis)s could not be found.") + + class PowerStateFailure(IronicException): message = _("Failed to set node power state to %(pstate)s.") diff --git a/ironic/db/api.py b/ironic/db/api.py index 87b04018b2..e41237db52 100644 --- a/ironic/db/api.py +++ b/ironic/db/api.py @@ -192,3 +192,34 @@ class Connection(object): :param port: The id or MAC of a port. """ + + @abc.abstractmethod + def create_chassis(self, values): + """Create a new chassis. + + :param values: Dict of values. + """ + + @abc.abstractmethod + def get_chassis(self, chassis): + """Return a chassis representation. + + :param chassis: The id or the UUID of a chassis. + :returns: A chassis. + """ + + @abc.abstractmethod + def update_chassis(self, chassis, values): + """Update properties of an chassis. + + :param chassis: The id or the uuid of a chassis. + :param values: Dict of values to update. + :returns: A chassis. + """ + + @abc.abstractmethod + def destroy_chassis(self, chassis): + """Destroy a chassis. + + :param chassis: The id or the uuid of a chassis. + """ diff --git a/ironic/db/sqlalchemy/api.py b/ironic/db/sqlalchemy/api.py index d3c85cbef6..aad067413b 100644 --- a/ironic/db/sqlalchemy/api.py +++ b/ironic/db/sqlalchemy/api.py @@ -299,3 +299,40 @@ class Connection(api.Connection): count = query.delete() if count != 1: raise exception.PortNotFound(port=port) + + def get_chassis(self, chassis): + query = model_query(models.Chassis) + query = add_identity_filter(query, chassis) + + try: + return query.one() + except NoResultFound: + raise exception.ChassisNotFound(chassis=chassis) + + def create_chassis(self, values): + chassis = models.Chassis() + chassis.update(values) + chassis.save() + return chassis + + def update_chassis(self, chassis, values): + session = get_session() + with session.begin(): + query = model_query(models.Chassis, session=session) + query = add_identity_filter(query, chassis) + + count = query.update(values) + if count != 1: + raise exception.ChassisNotFound(chassis=chassis) + ref = query.one() + return ref + + def destroy_chassis(self, chassis): + session = get_session() + with session.begin(): + query = model_query(models.Chassis, session=session) + query = add_identity_filter(query, chassis) + + count = query.delete() + if count != 1: + raise exception.ChassisNotFound(chassis=chassis) diff --git a/ironic/db/sqlalchemy/migrate_repo/versions/007_add_extra_created_updated_to_chassis.py b/ironic/db/sqlalchemy/migrate_repo/versions/007_add_extra_created_updated_to_chassis.py new file mode 100644 index 0000000000..50e385c86e --- /dev/null +++ b/ironic/db/sqlalchemy/migrate_repo/versions/007_add_extra_created_updated_to_chassis.py @@ -0,0 +1,31 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# -*- encoding: 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 sqlalchemy import Table, Column, Text, DateTime, MetaData + + +def upgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + chassis = Table('chassis', meta, autoload=True) + + chassis.create_column(Column('extra', Text)) + chassis.create_column(Column('created_at', DateTime)) + chassis.create_column(Column('updated_at', DateTime)) + + +def downgrade(migrate_engine): + raise NotImplementedError('Downgrade from version 007 is unsupported.') diff --git a/ironic/db/sqlalchemy/models.py b/ironic/db/sqlalchemy/models.py index 368eb9e0cd..acc7df1dc4 100644 --- a/ironic/db/sqlalchemy/models.py +++ b/ironic/db/sqlalchemy/models.py @@ -85,6 +85,7 @@ class Chassis(Base): __tablename__ = 'chassis' id = Column(Integer, primary_key=True) uuid = Column(String(36), unique=True) + extra = Column(JSONEncodedDict) class Node(Base): diff --git a/ironic/tests/db/test_chassis.py b/ironic/tests/db/test_chassis.py new file mode 100644 index 0000000000..a49f919cb6 --- /dev/null +++ b/ironic/tests/db/test_chassis.py @@ -0,0 +1,71 @@ +# 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. + +"""Tests for manipulating Chassis via the DB API""" + +from ironic.common import exception +from ironic.db import api as dbapi +from ironic.openstack.common import uuidutils +from ironic.tests.db import base +from ironic.tests.db import utils + + +class DbChassisTestCase(base.DbTestCase): + + def setUp(self): + super(DbChassisTestCase, self).setUp() + self.dbapi = dbapi.get_instance() + self.ch = utils.get_test_chassis() + self.ch = self.dbapi.create_chassis(self.ch) + + def test_get_chassis_by_id(self): + chassis = self.dbapi.get_chassis('42') + self.assertEqual(chassis['id'], self.ch['id']) + self.assertEqual(chassis['uuid'], self.ch['uuid']) + + def test_get_chassis_by_uuid(self): + chassis = self.dbapi.get_chassis('42') + self.assertEqual(chassis['id'], self.ch['id']) + self.assertEqual(chassis['uuid'], self.ch['uuid']) + + def test_get_chassis_that_does_not_exist(self): + self.assertRaises(exception.ChassisNotFound, + self.dbapi.get_chassis, 666) + + def test_update_chassis(self): + new_uuid = uuidutils.generate_uuid() + + self.ch['uuid'] = new_uuid + res = self.dbapi.update_chassis(self.ch['id'], {'uuid': new_uuid}) + + self.assertEqual(res['uuid'], new_uuid) + + def test_update_chassis_that_does_not_exist(self): + new_uuid = uuidutils.generate_uuid() + + self.assertRaises(exception.ChassisNotFound, + self.dbapi.update_chassis, 666, {'uuid': new_uuid}) + + def test_destroy_chassis(self): + self.dbapi.destroy_chassis(self.ch['id']) + + self.assertRaises(exception.ChassisNotFound, + self.dbapi.get_chassis, self.ch['id']) + + def test_destroy_chassis_that_does_not_exist(self): + self.assertRaises(exception.ChassisNotFound, + self.dbapi.destroy_chassis, 666) diff --git a/ironic/tests/db/utils.py b/ironic/tests/db/utils.py index 7d1f9c5929..dabc657521 100644 --- a/ironic/tests/db/utils.py +++ b/ironic/tests/db/utils.py @@ -98,3 +98,15 @@ def get_test_port(**kw): } return port + + +def get_test_chassis(**kw): + chassis = { + 'id': kw.get('id', 42), + 'uuid': kw.get('uuid', 'e74c40e0-d825-11e2-a28f-0800200c9a66'), + 'extra': kw.get('extra', '{}'), + 'created_at': kw.get('created_at'), + 'updated_at': kw.get('updated_at'), + } + + return chassis