예제 #1
0
    def test_baremetal_volume_target_delete_multiple_error(self):
        fake_volume_target_uuid2 = 'vvv-tttttt-tttt'
        arglist = [
            baremetal_fakes.baremetal_volume_target_uuid,
            fake_volume_target_uuid2
        ]
        verifylist = [('volume_targets', [
            baremetal_fakes.baremetal_volume_target_uuid,
            fake_volume_target_uuid2
        ])]

        self.baremetal_mock.volume_target.delete.side_effect = [
            None, exc.NotFound()
        ]

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
        self.assertRaises(exc.ClientException, self.cmd.take_action,
                          parsed_args)

        self.baremetal_mock.volume_target.delete.assert_has_calls([
            mock.call(baremetal_fakes.baremetal_volume_target_uuid),
            mock.call(fake_volume_target_uuid2)
        ])
        self.assertEqual(2,
                         self.baremetal_mock.volume_target.delete.call_count)
예제 #2
0
    def test_macs_for_instance_http_not_found(self, mock_get):
        mock_get.side_effect = ironic_exception.NotFound()

        instance = fake_instance.fake_instance_obj(
            self.ctx, node=uuidutils.generate_uuid())
        result = self.driver.macs_for_instance(instance)
        self.assertEqual([], result)
예제 #3
0
    def get_by_address(self, address, fields=None):
        """Get a port group with the specified MAC address.

        :param address: The MAC address of a portgroup.
        :param fields: Optional, a list with a specified set of fields
                       of the resource to be returned. Can not be used
                       when 'detail' is set.

        :returns: a :class:`Portgroup` object.

        """
        path = '?address=%s' % address
        if fields is not None:
            path += '&fields=' + ','.join(fields)
        else:
            path = 'detail' + path

        portgroups = self._list(self._path(path), 'portgroups')
        # get all the details of the portgroup assuming that
        # filtering by address returns a collection of one portgroup
        # if successful.
        if len(portgroups) == 1:
            return portgroups[0]
        else:
            raise exc.NotFound()
예제 #4
0
 def test_send_event_method_not_found(self, mock_client, mock_log):
     self.ironic_notifier.irclient = mock_client
     exception = ironic_exc.NotFound()
     mock_client.events.create.side_effect = exception
     self.ironic_notifier.send_events(['test', 'events'])
     self.assertEqual(1, mock_log.call_count)
     mock_log.assert_called_with('The ironic API appears to not support '
                                 'posting events. The API likely needs to '
                                 'be upgraded.')
예제 #5
0
 def get_by_address(self, address):
     path = "detail?address=%s" % address
     ports = self._list(self._path(path), 'ports')
     # get all the details of the port assuming that filtering by
     # address returns a collection of one port if successful.
     if len(ports) == 1:
         return ports[0]
     else:
         raise exc.NotFound()
예제 #6
0
 def test_validate_instance_and_node_failed(self, mock_gbiui):
     icli = cw.IronicClientWrapper()
     mock_gbiui.side_effect = ironic_exception.NotFound()
     instance_uuid = uuidutils.generate_uuid(),
     instance = fake_instance.fake_instance_obj(self.ctx,
                                                uuid=instance_uuid)
     self.assertRaises(exception.InstanceNotFound,
                       ironic_driver.validate_instance_and_node, icli,
                       instance)
예제 #7
0
 def get_by_instance_uuid(self, instance_uuid):
     path = "detail?instance_uuid=%s" % instance_uuid
     nodes = self._list(self._path(path), 'nodes')
     # get all the details of the node assuming that
     # filtering by instance_uuid returns a collection
     # of one node if successful.
     if len(nodes) == 1:
         return nodes[0]
     else:
         raise exc.NotFound()
예제 #8
0
    def test_baremetal_volume_target_delete_error(self):
        arglist = [baremetal_fakes.baremetal_volume_target_uuid]
        verifylist = [('volume_targets',
                       [baremetal_fakes.baremetal_volume_target_uuid])]

        self.baremetal_mock.volume_target.delete.side_effect = (exc.NotFound())

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
        self.assertRaises(exc.ClientException, self.cmd.take_action,
                          parsed_args)
        self.baremetal_mock.volume_target.delete.assert_called_with(
            baremetal_fakes.baremetal_volume_target_uuid)
예제 #9
0
    def test_get_info_http_not_found(self, mock_gbiu, mock_cli):
        mock_gbiu.side_effect = ironic_exception.NotFound()

        expected = {'state': nova_states.NOSTATE,
                    'max_mem': 0,
                    'mem': 0,
                    'num_cpu': 0,
                    'cpu_time': 0}
        instance = fake_instance.fake_instance_obj(
                                  self.ctx, uuid=uuidutils.generate_uuid())
        mock_cli.return_value = FAKE_CLIENT
        result = self.driver.get_info(instance)
        self.assertEqual(expected, result)
예제 #10
0
    def get_by_address(self, address, fields=None):
        path = '?address=%s' % address
        if fields is not None:
            path += '&fields=' + ','.join(fields)
        else:
            path = 'detail' + path

        ports = self._list(self._path(path), 'ports')
        # get all the details of the port assuming that filtering by
        # address returns a collection of one port if successful.
        if len(ports) == 1:
            return ports[0]
        else:
            raise exc.NotFound()
예제 #11
0
    def get_by_instance_uuid(self, instance_uuid, fields=None):
        path = '?instance_uuid=%s' % instance_uuid
        if fields is not None:
            path += '&fields=' + ','.join(fields)
        else:
            path = 'detail' + path

        nodes = self._list(self._path(path), 'nodes')
        # get all the details of the node assuming that
        # filtering by instance_uuid returns a collection
        # of one node if successful.
        if len(nodes) == 1:
            return nodes[0]
        else:
            raise exc.NotFound()
예제 #12
0
    def test_change_server_power_state_with_rollback_status(
            self, set_power_mock, get_power_mock, notify_mock):
        server = obj_utils.create_test_server(
            self.context, status=states.POWERING_OFF)
        exception = ironic_exc.NotFound("The bare metal node is not found")
        get_power_mock.return_value = states.POWER_ON
        set_power_mock.side_effect = exception

        self._start_service()
        self.assertRaises(ironic_exc.NotFound, self.service.set_power_state,
                          self.context, server, 'off')
        set_power_mock.assert_called_once_with(self.context, server, 'off')
        get_power_mock.assert_called_once_with(self.context, server.uuid)
        notify_mock.assert_called_once_with(
            self.context, server, 'test-host',
            action=fields.NotificationAction.POWER_OFF,
            phase=fields.NotificationPhase.ERROR, exception=exception)
        self.assertEqual(server.status, states.ACTIVE)
        self._stop_service()
예제 #13
0
class BareMetalNodesTestV21(test.NoDBTestCase):
    mod = b_nodes_v21

    def setUp(self):
        super(BareMetalNodesTestV21, self).setUp()

        self._setup()
        self.context = context.get_admin_context()
        self.request = FakeRequest(self.context)

    def _setup(self):
        self.controller = b_nodes_v21.BareMetalNodeController()

    @mock.patch.object(FAKE_IRONIC_CLIENT.node, 'list')
    def test_index_ironic(self, mock_list):
        properties = {'cpus': 2, 'memory_mb': 1024, 'local_gb': 20}
        node = ironic_utils.get_test_node(properties=properties)
        mock_list.return_value = [node]

        res_dict = self.controller.index(self.request)
        expected_output = {'nodes':
                            [{'memory_mb': properties['memory_mb'],
                             'host': 'IRONIC MANAGED',
                             'disk_gb': properties['local_gb'],
                             'interfaces': [],
                             'task_state': None,
                             'id': node.uuid,
                             'cpus': properties['cpus']}]}
        self.assertEqual(expected_output, res_dict)
        mock_list.assert_called_once_with(detail=True)

    @mock.patch.object(FAKE_IRONIC_CLIENT.node, 'list')
    def test_index_ironic_missing_properties(self, mock_list):
        properties = {'cpus': 2}
        node = ironic_utils.get_test_node(properties=properties)
        mock_list.return_value = [node]

        res_dict = self.controller.index(self.request)
        expected_output = {'nodes':
                            [{'memory_mb': 0,
                             'host': 'IRONIC MANAGED',
                             'disk_gb': 0,
                             'interfaces': [],
                             'task_state': None,
                             'id': node.uuid,
                             'cpus': properties['cpus']}]}
        self.assertEqual(expected_output, res_dict)
        mock_list.assert_called_once_with(detail=True)

    def test_index_ironic_not_implemented(self):
        with mock.patch.object(self.mod, 'ironic_client', None):
            self.assertRaises(exc.HTTPNotImplemented,
                              self.controller.index,
                              self.request)

    @mock.patch.object(FAKE_IRONIC_CLIENT.node, 'list_ports')
    @mock.patch.object(FAKE_IRONIC_CLIENT.node, 'get')
    def test_show_ironic(self, mock_get, mock_list_ports):
        properties = {'cpus': 1, 'memory_mb': 512, 'local_gb': 10}
        node = ironic_utils.get_test_node(properties=properties)
        port = ironic_utils.get_test_port()
        mock_get.return_value = node
        mock_list_ports.return_value = [port]

        res_dict = self.controller.show(self.request, node.uuid)
        expected_output = {'node':
                            {'memory_mb': properties['memory_mb'],
                             'instance_uuid': None,
                             'host': 'IRONIC MANAGED',
                             'disk_gb': properties['local_gb'],
                             'interfaces': [{'address': port.address}],
                             'task_state': None,
                             'id': node.uuid,
                             'cpus': properties['cpus']}}
        self.assertEqual(expected_output, res_dict)
        mock_get.assert_called_once_with(node.uuid)
        mock_list_ports.assert_called_once_with(node.uuid)

    @mock.patch.object(FAKE_IRONIC_CLIENT.node, 'list_ports')
    @mock.patch.object(FAKE_IRONIC_CLIENT.node, 'get')
    def test_show_ironic_no_properties(self, mock_get, mock_list_ports):
        properties = {}
        node = ironic_utils.get_test_node(properties=properties)
        port = ironic_utils.get_test_port()
        mock_get.return_value = node
        mock_list_ports.return_value = [port]

        res_dict = self.controller.show(self.request, node.uuid)
        expected_output = {'node':
                            {'memory_mb': 0,
                             'instance_uuid': None,
                             'host': 'IRONIC MANAGED',
                             'disk_gb': 0,
                             'interfaces': [{'address': port.address}],
                             'task_state': None,
                             'id': node.uuid,
                             'cpus': 0}}
        self.assertEqual(expected_output, res_dict)
        mock_get.assert_called_once_with(node.uuid)
        mock_list_ports.assert_called_once_with(node.uuid)

    @mock.patch.object(FAKE_IRONIC_CLIENT.node, 'list_ports')
    @mock.patch.object(FAKE_IRONIC_CLIENT.node, 'get')
    def test_show_ironic_no_interfaces(self, mock_get, mock_list_ports):
        properties = {'cpus': 1, 'memory_mb': 512, 'local_gb': 10}
        node = ironic_utils.get_test_node(properties=properties)
        mock_get.return_value = node
        mock_list_ports.return_value = []

        res_dict = self.controller.show(self.request, node.uuid)
        self.assertEqual([], res_dict['node']['interfaces'])
        mock_get.assert_called_once_with(node.uuid)
        mock_list_ports.assert_called_once_with(node.uuid)

    @mock.patch.object(FAKE_IRONIC_CLIENT.node, 'get',
                       side_effect=ironic_exc.NotFound())
    def test_show_ironic_node_not_found(self, mock_get):
        error = self.assertRaises(exc.HTTPNotFound, self.controller.show,
                                  self.request, 'fake-uuid')
        self.assertIn('fake-uuid', six.text_type(error))

    def test_show_ironic_not_implemented(self):
        with mock.patch.object(self.mod, 'ironic_client', None):
            properties = {'cpus': 1, 'memory_mb': 512, 'local_gb': 10}
            node = ironic_utils.get_test_node(properties=properties)
            self.assertRaises(exc.HTTPNotImplemented, self.controller.show,
                              self.request, node.uuid)

    def test_create_ironic_not_supported(self):
        self.assertRaises(exc.HTTPBadRequest,
                          self.controller.create,
                          self.request, {'node': object()})

    def test_delete_ironic_not_supported(self):
        self.assertRaises(exc.HTTPBadRequest,
                          self.controller.delete,
                          self.request, 'fake-id')

    def test_add_interface_ironic_not_supported(self):
        self.assertRaises(exc.HTTPBadRequest,
                          self.controller._add_interface,
                          self.request, 'fake-id', 'fake-body')

    def test_remove_interface_ironic_not_supported(self):
        self.assertRaises(exc.HTTPBadRequest,
                          self.controller._remove_interface,
                          self.request, 'fake-id', 'fake-body')
예제 #14
0
class BareMetalNodesTest(test.NoDBTestCase):
    def setUp(self):
        super(BareMetalNodesTest, self).setUp()

        self.ext_mgr = self.mox.CreateMock(extensions.ExtensionManager)
        self.context = context.get_admin_context()
        self.controller = baremetal_nodes.BareMetalNodeController(self.ext_mgr)
        self.request = FakeRequest(self.context)

    def _test_create(self, node, ext_status=False):
        response = node.copy()
        del response['pm_password']
        response['instance_uuid'] = None
        self.mox.StubOutWithMock(db, 'bm_node_create')
        db.bm_node_create(self.context, node).AndReturn(response)
        self.ext_mgr.is_loaded('os-baremetal-ext-status').AndReturn(ext_status)
        self.mox.ReplayAll()
        res_dict = self.controller.create(self.request, {'node': node})
        self.assertEqual({'node': response}, res_dict)

    def _test_show(self, node, ext_status=False):
        interfaces = [
            fake_interface(id=1, address='11:11:11:11:11:11'),
            fake_interface(id=2, address='22:22:22:22:22:22'),
        ]
        node.update(interfaces=interfaces)
        response = node.copy()
        del response['pm_password']
        self.mox.StubOutWithMock(db, 'bm_node_get')
        self.mox.StubOutWithMock(db, 'bm_interface_get_all_by_bm_node_id')
        db.bm_node_get(self.context, node['id']).AndReturn(node)
        db.bm_interface_get_all_by_bm_node_id(self.context, node['id']).\
                AndReturn(interfaces)
        self.ext_mgr.is_loaded('os-baremetal-ext-status').AndReturn(ext_status)
        self.mox.ReplayAll()
        res_dict = self.controller.show(self.request, node['id'])
        self.assertEqual({'node': response}, res_dict)
        self.assertEqual(2, len(res_dict['node']['interfaces']))

    def _test_show_no_interfaces(self, ext_status=False):
        node_id = 1
        node = {'id': node_id}
        self.mox.StubOutWithMock(db, 'bm_node_get')
        self.mox.StubOutWithMock(db, 'bm_interface_get_all_by_bm_node_id')
        db.bm_node_get(self.context, node_id).AndReturn(node)
        db.bm_interface_get_all_by_bm_node_id(self.context, node_id).\
                AndRaise(exception.NodeNotFound(node_id=node_id))
        self.ext_mgr.is_loaded('os-baremetal-ext-status').AndReturn(ext_status)
        self.mox.ReplayAll()
        res_dict = self.controller.show(self.request, node_id)
        self.assertEqual(node_id, res_dict['node']['id'])
        self.assertEqual(0, len(res_dict['node']['interfaces']))

    def _test_index(self, ext_status=False):
        nodes = [
            {
                'id': 1
            },
            {
                'id': 2
            },
        ]
        interfaces = [
            {
                'id': 1,
                'address': '11:11:11:11:11:11'
            },
            {
                'id': 2,
                'address': '22:22:22:22:22:22'
            },
        ]
        self.mox.StubOutWithMock(db, 'bm_node_get_all')
        self.mox.StubOutWithMock(db, 'bm_interface_get_all_by_bm_node_id')
        db.bm_node_get_all(self.context).AndReturn(nodes)
        db.bm_interface_get_all_by_bm_node_id(self.context, 1).\
                AndRaise(exception.NodeNotFound(node_id=1))
        for n in nodes:
            self.ext_mgr.is_loaded('os-baremetal-ext-status').\
                AndReturn(ext_status)
        db.bm_interface_get_all_by_bm_node_id(self.context, 2).\
                AndReturn(interfaces)
        self.mox.ReplayAll()
        res_dict = self.controller.index(self.request)
        self.assertEqual(2, len(res_dict['nodes']))
        self.assertEqual([], res_dict['nodes'][0]['interfaces'])
        self.assertEqual(2, len(res_dict['nodes'][1]['interfaces']))

    def test_create(self):
        node = fake_node(id=100)
        self._test_create(node)

    def test_create_ext_status(self):
        node = fake_node_ext_status(id=100)
        self._test_create(node, ext_status=True)

    def test_create_with_prov_mac_address(self):
        node = {
            'service_host': "host",
            'cpus': 8,
            'memory_mb': 8192,
            'local_gb': 128,
            'pm_address': "10.1.2.3",
            'pm_user': "******",
            'pm_password': "******",
            'terminal_port': 8000,
            'interfaces': [],
        }
        intf = {
            'address': '1a:B2:3C:4d:e5:6f',
            'datapath_id': None,
            'id': None,
            'port_no': None,
        }

        request = node.copy()
        request['prov_mac_address'] = intf['address']

        db_node = node.copy()
        db_node['id'] = 100

        response = node.copy()
        response.update(id=db_node['id'],
                        instance_uuid=None,
                        interfaces=[intf])
        del response['pm_password']

        self.mox.StubOutWithMock(db, 'bm_node_create')
        self.mox.StubOutWithMock(db, 'bm_interface_create')
        self.mox.StubOutWithMock(db, 'bm_interface_get')
        db.bm_node_create(self.context, node).AndReturn(db_node)
        self.ext_mgr.is_loaded('os-baremetal-ext-status').AndReturn(False)
        db.bm_interface_create(self.context,
                               bm_node_id=db_node['id'],
                               address=intf['address'],
                               datapath_id=intf['datapath_id'],
                               port_no=intf['port_no']).AndReturn(1000)
        db.bm_interface_get(self.context, 1000).AndReturn(intf)
        self.mox.ReplayAll()
        res_dict = self.controller.create(self.request, {'node': request})
        self.assertEqual({'node': response}, res_dict)

    def test_create_with_invalid_prov_mac_address(self):
        node = {
            'service_host': "host",
            'cpus': 8,
            'memory_mb': 8192,
            'local_gb': 128,
            'pm_address': "10.1.2.3",
            'pm_user': "******",
            'pm_password': "******",
            'terminal_port': 8000,
            'prov_mac_address': 'INVALID!!',
        }
        self.assertRaises(exc.HTTPBadRequest, self.controller.create,
                          self.request, {'node': node})

    def test_delete(self):
        self.mox.StubOutWithMock(db, 'bm_node_destroy')
        db.bm_node_destroy(self.context, 1)
        self.mox.ReplayAll()
        self.controller.delete(self.request, 1)

    def test_delete_node_not_found(self):
        self.mox.StubOutWithMock(db, 'bm_node_destroy')
        db.bm_node_destroy(self.context, 1).\
                AndRaise(exception.NodeNotFound(node_id=1))
        self.mox.ReplayAll()
        self.assertRaises(exc.HTTPNotFound, self.controller.delete,
                          self.request, 1)

    def test_index(self):
        self._test_index()

    def test_index_ext_status(self):
        self._test_index(ext_status=True)

    def test_show(self):
        node = fake_node(id=1)
        self._test_show(node)

    def test_show_ext_status(self):
        node = fake_node_ext_status(id=1)
        self._test_show(node, ext_status=True)

    def test_show_no_interfaces(self):
        self._test_show_no_interfaces()

    def test_show_no_interfaces_ext_status(self):
        self._test_show_no_interfaces(ext_status=True)

    @mock.patch.object(FAKE_IRONIC_CLIENT.node,
                       'get',
                       side_effect=ironic_exc.NotFound())
    def test_show_ironic_node_not_found(self, mock_get):
        CONF.set_override('compute_driver', 'nova.virt.ironic.driver')
        error = self.assertRaises(exc.HTTPNotFound, self.controller.show,
                                  self.request, 'fake-uuid')
        self.assertIn('fake-uuid', six.text_type(error))

    def test_add_interface(self):
        node_id = 1
        address = '11:22:33:ab:cd:ef'
        body = {'add_interface': {'address': address}}
        self.mox.StubOutWithMock(db, 'bm_node_get')
        self.mox.StubOutWithMock(db, 'bm_interface_create')
        self.mox.StubOutWithMock(db, 'bm_interface_get')
        db.bm_node_get(self.context, node_id)
        db.bm_interface_create(self.context,
                               bm_node_id=node_id,
                               address=address,
                               datapath_id=None,
                               port_no=None).\
                               AndReturn(12345)
        db.bm_interface_get(self.context, 12345).\
                AndReturn({'id': 12345, 'address': address})
        self.mox.ReplayAll()
        res_dict = self.controller._add_interface(self.request, node_id, body)
        self.assertEqual(12345, res_dict['interface']['id'])
        self.assertEqual(address, res_dict['interface']['address'])

    def test_add_interface_invalid_address(self):
        node_id = 1
        body = {'add_interface': {'address': ''}}
        self.mox.StubOutWithMock(db, 'bm_node_get')
        db.bm_node_get(self.context, node_id)
        self.mox.ReplayAll()
        self.assertRaises(exc.HTTPBadRequest, self.controller._add_interface,
                          self.request, node_id, body)

    def test_remove_interface(self):
        node_id = 1
        interfaces = [
            {
                'id': 1
            },
            {
                'id': 2
            },
            {
                'id': 3
            },
        ]
        body = {'remove_interface': {'id': 2}}
        self.mox.StubOutWithMock(db, 'bm_node_get')
        self.mox.StubOutWithMock(db, 'bm_interface_get_all_by_bm_node_id')
        self.mox.StubOutWithMock(db, 'bm_interface_destroy')
        db.bm_node_get(self.context, node_id)
        db.bm_interface_get_all_by_bm_node_id(self.context, node_id).\
                AndReturn(interfaces)
        db.bm_interface_destroy(self.context, 2)
        self.mox.ReplayAll()
        self.controller._remove_interface(self.request, node_id, body)

    def test_remove_interface_by_address(self):
        node_id = 1
        interfaces = [
            {
                'id': 1,
                'address': '11:11:11:11:11:11'
            },
            {
                'id': 2,
                'address': '22:22:22:22:22:22'
            },
            {
                'id': 3,
                'address': '33:33:33:33:33:33'
            },
        ]
        self.mox.StubOutWithMock(db, 'bm_node_get')
        self.mox.StubOutWithMock(db, 'bm_interface_get_all_by_bm_node_id')
        self.mox.StubOutWithMock(db, 'bm_interface_destroy')
        db.bm_node_get(self.context, node_id)
        db.bm_interface_get_all_by_bm_node_id(self.context, node_id).\
                AndReturn(interfaces)
        db.bm_interface_destroy(self.context, 2)
        self.mox.ReplayAll()
        body = {'remove_interface': {'address': '22:22:22:22:22:22'}}
        self.controller._remove_interface(self.request, node_id, body)

    def test_remove_interface_no_id_no_address(self):
        node_id = 1
        self.mox.StubOutWithMock(db, 'bm_node_get')
        db.bm_node_get(self.context, node_id)
        self.mox.ReplayAll()
        body = {'remove_interface': {}}
        self.assertRaises(exc.HTTPBadRequest,
                          self.controller._remove_interface, self.request,
                          node_id, body)

    def test_add_interface_node_not_found(self):
        node_id = 1
        self.mox.StubOutWithMock(db, 'bm_node_get')
        db.bm_node_get(self.context, node_id).\
                AndRaise(exception.NodeNotFound(node_id=node_id))
        self.mox.ReplayAll()
        body = {'add_interface': {'address': '11:11:11:11:11:11'}}
        self.assertRaises(exc.HTTPNotFound, self.controller._add_interface,
                          self.request, node_id, body)

    def test_remove_interface_node_not_found(self):
        node_id = 1
        self.mox.StubOutWithMock(db, 'bm_node_get')
        db.bm_node_get(self.context, node_id).\
                AndRaise(exception.NodeNotFound(node_id=node_id))
        self.mox.ReplayAll()
        body = {'remove_interface': {'address': '11:11:11:11:11:11'}}
        self.assertRaises(exc.HTTPNotFound, self.controller._remove_interface,
                          self.request, node_id, body)

    def test_is_valid_mac(self):
        self.assertFalse(baremetal_nodes.is_valid_mac(None))
        self.assertTrue(baremetal_nodes.is_valid_mac("52:54:00:cf:2d:31"))
        self.assertTrue(baremetal_nodes.is_valid_mac(u"52:54:00:cf:2d:31"))
        self.assertFalse(baremetal_nodes.is_valid_mac("127.0.0.1"))
        self.assertFalse(baremetal_nodes.is_valid_mac("not:a:mac:address"))
        self.assertFalse(baremetal_nodes.is_valid_mac("52-54-00-cf-2d-31"))
        self.assertFalse(baremetal_nodes.is_valid_mac("5254.00cf.2d31"))
        self.assertFalse(baremetal_nodes.is_valid_mac("52:54:0:cf:2d:31"))
        self.assertFalse(baremetal_nodes.is_valid_mac("aa bb cc dd ee ff"))
        self.assertTrue(baremetal_nodes.is_valid_mac("AA:BB:CC:DD:EE:FF"))
        self.assertFalse(baremetal_nodes.is_valid_mac("AA BB CC DD EE FF"))
        self.assertFalse(baremetal_nodes.is_valid_mac("AA-BB-CC-DD-EE-FF"))

    @mock.patch.object(FAKE_IRONIC_CLIENT.node, 'list')
    def test_index_ironic(self, mock_list):
        CONF.set_override('compute_driver', 'nova.virt.ironic.driver')

        properties = {'cpus': 2, 'memory_mb': 1024, 'local_gb': 20}
        node = ironic_utils.get_test_node(properties=properties)
        mock_list.return_value = [node]

        res_dict = self.controller.index(self.request)
        expected_output = {
            'nodes': [{
                'memory_mb': properties['memory_mb'],
                'host': 'IRONIC MANAGED',
                'disk_gb': properties['local_gb'],
                'interfaces': [],
                'task_state': None,
                'id': node.uuid,
                'cpus': properties['cpus']
            }]
        }
        self.assertEqual(expected_output, res_dict)
        mock_list.assert_called_once_with(detail=True)

    @mock.patch.object(FAKE_IRONIC_CLIENT.node, 'list')
    def test_index_ironic_missing_properties(self, mock_list):
        CONF.set_override('compute_driver', 'nova.virt.ironic.driver')

        properties = {'cpus': 2}
        node = ironic_utils.get_test_node(properties=properties)
        mock_list.return_value = [node]

        res_dict = self.controller.index(self.request)
        expected_output = {
            'nodes': [{
                'memory_mb': 0,
                'host': 'IRONIC MANAGED',
                'disk_gb': 0,
                'interfaces': [],
                'task_state': None,
                'id': node.uuid,
                'cpus': properties['cpus']
            }]
        }
        self.assertEqual(expected_output, res_dict)
        mock_list.assert_called_once_with(detail=True)

    @mock.patch.object(FAKE_IRONIC_CLIENT.node, 'list_ports')
    @mock.patch.object(FAKE_IRONIC_CLIENT.node, 'get')
    def test_show_ironic(self, mock_get, mock_list_ports):
        CONF.set_override('compute_driver', 'nova.virt.ironic.driver')

        properties = {'cpus': 1, 'memory_mb': 512, 'local_gb': 10}
        node = ironic_utils.get_test_node(properties=properties)
        port = ironic_utils.get_test_port()
        mock_get.return_value = node
        mock_list_ports.return_value = [port]

        res_dict = self.controller.show(self.request, node.uuid)
        expected_output = {
            'node': {
                'memory_mb': properties['memory_mb'],
                'instance_uuid': None,
                'host': 'IRONIC MANAGED',
                'disk_gb': properties['local_gb'],
                'interfaces': [{
                    'address': port.address
                }],
                'task_state': None,
                'id': node.uuid,
                'cpus': properties['cpus']
            }
        }
        self.assertEqual(expected_output, res_dict)
        mock_get.assert_called_once_with(node.uuid)
        mock_list_ports.assert_called_once_with(node.uuid)

    @mock.patch.object(FAKE_IRONIC_CLIENT.node, 'list_ports')
    @mock.patch.object(FAKE_IRONIC_CLIENT.node, 'get')
    def test_show_ironic_no_properties(self, mock_get, mock_list_ports):
        CONF.set_override('compute_driver', 'nova.virt.ironic.driver')

        properties = {}
        node = ironic_utils.get_test_node(properties=properties)
        port = ironic_utils.get_test_port()
        mock_get.return_value = node
        mock_list_ports.return_value = [port]

        res_dict = self.controller.show(self.request, node.uuid)
        expected_output = {
            'node': {
                'memory_mb': 0,
                'instance_uuid': None,
                'host': 'IRONIC MANAGED',
                'disk_gb': 0,
                'interfaces': [{
                    'address': port.address
                }],
                'task_state': None,
                'id': node.uuid,
                'cpus': 0
            }
        }
        self.assertEqual(expected_output, res_dict)
        mock_get.assert_called_once_with(node.uuid)
        mock_list_ports.assert_called_once_with(node.uuid)

    @mock.patch.object(FAKE_IRONIC_CLIENT.node, 'list_ports')
    @mock.patch.object(FAKE_IRONIC_CLIENT.node, 'get')
    def test_show_ironic_no_interfaces(self, mock_get, mock_list_ports):
        CONF.set_override('compute_driver', 'nova.virt.ironic.driver')

        properties = {'cpus': 1, 'memory_mb': 512, 'local_gb': 10}
        node = ironic_utils.get_test_node(properties=properties)
        mock_get.return_value = node
        mock_list_ports.return_value = []

        res_dict = self.controller.show(self.request, node.uuid)
        self.assertEqual([], res_dict['node']['interfaces'])
        mock_get.assert_called_once_with(node.uuid)
        mock_list_ports.assert_called_once_with(node.uuid)

    def test_create_ironic_not_supported(self):
        CONF.set_override('compute_driver', 'nova.virt.ironic.driver')
        self.assertRaises(exc.HTTPBadRequest, self.controller.create,
                          self.request, {'node': object()})

    def test_delete_ironic_not_supported(self):
        CONF.set_override('compute_driver', 'nova.virt.ironic.driver')
        self.assertRaises(exc.HTTPBadRequest, self.controller.delete,
                          self.request, 'fake-id')

    def test_add_interface_ironic_not_supported(self):
        CONF.set_override('compute_driver', 'nova.virt.ironic.driver')
        self.assertRaises(exc.HTTPBadRequest, self.controller._add_interface,
                          self.request, 'fake-id', 'fake-body')

    def test_remove_interface_ironic_not_supported(self):
        CONF.set_override('compute_driver', 'nova.virt.ironic.driver')
        self.assertRaises(exc.HTTPBadRequest,
                          self.controller._remove_interface, self.request,
                          'fake-id', 'fake-body')