Beispiel #1
0
    def test_not_found_in_cache_active_introspection(self, mock_record):
        CONF.set_override('permit_active_introspection', True, 'processing')
        self.find_mock.side_effect = utils.NotFoundInCacheError('not found')
        self.cli.node.get.side_effect = exceptions.NotFound('boom')
        self.cache_fixture.mock.acquire_lock = mock.Mock()
        self.cache_fixture.mock.uuid = '1111'
        self.cache_fixture.mock.finished_at = None
        self.cache_fixture.mock.node = mock.Mock()
        mock_record.return_value = self.cache_fixture.mock
        res = process.process(self.data)

        self.assertEqual(self.fake_result_json, res)
        self.find_mock.assert_called_once_with(
            bmc_address=[self.bmc_address, self.bmc_v6address],
            mac=mock.ANY)
        actual_macs = self.find_mock.call_args[1]['mac']
        self.assertEqual(sorted(self.all_macs), sorted(actual_macs))
        mock_record.assert_called_once_with(
            bmc_addresses=['1.2.3.4',
                           '2001:1234:1234:1234:1234:1234:1234:1234/64'],
            macs=mock.ANY)
        actual_macs = mock_record.call_args[1]['macs']
        self.assertEqual(sorted(self.all_macs), sorted(actual_macs))
        self.cli.node.get.assert_not_called()
        self.process_mock.assert_called_once_with(
            mock.ANY, mock.ANY, self.data)
Beispiel #2
0
def find_node(**attributes):
    """Find node in cache.

    :param attributes: attributes known about this node (like macs, BMC etc)
                       also ironic client instance may be passed under 'ironic'
    :returns: structure NodeInfo with attributes ``uuid`` and ``created_at``
    :raises: Error if node is not found
    """
    ironic = attributes.pop('ironic', None)
    # NOTE(dtantsur): sorting is not required, but gives us predictability
    found = set()

    for (name, value) in sorted(attributes.items()):
        if not value:
            LOG.debug('Empty value for attribute %s', name)
            continue
        if not isinstance(value, list):
            value = [value]

        LOG.debug('Trying to use %s of value %s for node look up' %
                  (name, value))
        value_list = []
        for v in value:
            value_list.append('name="%s" AND value="%s"' % (name, v))
        stmt = ('select distinct uuid from attributes where ' +
                ' OR '.join(value_list))
        rows = (db.model_query(db.Attribute.uuid).from_statement(
            text(stmt)).all())
        if rows:
            found.update(item.uuid for item in rows)

    if not found:
        raise utils.NotFoundInCacheError(
            _('Could not find a node for attributes %s') % attributes)
    elif len(found) > 1:
        raise utils.Error(_('Multiple matching nodes found for attributes '
                            '%(attr)s: %(found)s') % {
                                'attr': attributes,
                                'found': list(found)
                            },
                          code=404)

    uuid = found.pop()
    row = (db.model_query(db.Node.started_at,
                          db.Node.finished_at).filter_by(uuid=uuid).first())

    if not row:
        raise utils.Error(_('Could not find node %s in introspection cache, '
                            'probably it\'s not on introspection now') % uuid,
                          code=404)

    if row.finished_at:
        raise utils.Error(
            _('Introspection for node %(node)s already finished on '
              '%(finish)s') % {
                  'node': uuid,
                  'finish': row.finished_at
              })

    return NodeInfo(uuid=uuid, started_at=row.started_at, ironic=ironic)
Beispiel #3
0
 def test_node_not_found_hook_exception(self, hook_mock):
     CONF.set_override('node_not_found_hook', 'example', 'processing')
     self.find_mock.side_effect = utils.NotFoundInCacheError('BOOM')
     hook_mock.side_effect = Exception('Hook Error')
     self.assertRaisesRegex(utils.Error,
                            'Node not found hook failed: Hook Error',
                            process.process, self.data)
     hook_mock.assert_called_once_with(self.data)
Beispiel #4
0
 def test_node_not_found_hook_run_none(self, hook_mock):
     CONF.set_override('node_not_found_hook', 'example', 'processing')
     self.find_mock.side_effect = utils.NotFoundInCacheError('BOOM')
     hook_mock.return_value = None
     self.assertRaisesRegex(utils.Error,
                            'Node not found hook returned nothing',
                            process.process, self.data)
     hook_mock.assert_called_once_with(self.data)
Beispiel #5
0
 def test_node_not_found_hook_run_ok(self, hook_mock):
     CONF.set_override('node_not_found_hook', 'example', 'processing')
     self.find_mock.side_effect = utils.NotFoundInCacheError('BOOM')
     hook_mock.return_value = node_cache.NodeInfo(
         uuid=self.node.uuid, started_at=self.started_at)
     res = process.process(self.data)
     self.assertEqual(self.fake_result_json, res)
     hook_mock.assert_called_once_with(self.data)
 def version_id(self):
     """Get the version id"""
     if self._version_id is None:
         row = db.model_query(db.Node).get(self.uuid)
         if row is None:
             raise utils.NotFoundInCacheError(_('Node not found in the '
                                                'cache'), node_info=self)
         self._version_id = row.version_id
     return self._version_id
def record_node(ironic=None, bmc_addresses=None, macs=None):
    """Create a cache record for a known active node.

    :param ironic: ironic client instance.
    :param bmc_addresses: list of BMC addresses.
    :param macs: list of MAC addresses.
    :return: NodeInfo
    """
    if not bmc_addresses and not macs:
        raise utils.NotFoundInCacheError(
            _("Existing node cannot be found since neither MAC addresses "
              "nor BMC addresses are present in the inventory"))

    if ironic is None:
        ironic = ir_utils.get_client()

    node = ir_utils.lookup_node(macs=macs,
                                bmc_addresses=bmc_addresses,
                                ironic=ironic)
    if not node:
        bmc_addresses = ', '.join(bmc_addresses) if bmc_addresses else None
        macs = ', '.join(macs) if macs else None
        raise utils.NotFoundInCacheError(
            _("Existing node was not found by MAC address(es) %(macs)s "
              "and BMC address(es) %(addr)s") % {
                  'macs': macs,
                  'addr': bmc_addresses
              })

    node = ironic.node.get(node, fields=['uuid', 'provision_state'])
    # TODO(dtantsur): do we want to allow updates in all states?
    if node.provision_state not in ir_utils.VALID_ACTIVE_STATES:
        raise utils.Error(
            _("Node %(node)s is not active, its provision "
              "state is %(state)s") % {
                  'node': node.uuid,
                  'state': node.provision_state
              })

    return add_node(node.uuid,
                    istate.States.waiting,
                    manage_boot=False,
                    mac=macs,
                    bmc_address=bmc_addresses)
 def test_node_not_found_hook_exception(self, cli, pop_mock, process_mock):
     CONF.set_override('node_not_found_hook', 'example', 'processing')
     plugins_base._NOT_FOUND_HOOK_MGR = None
     pop_mock.side_effect = iter([utils.NotFoundInCacheError('BOOM')])
     with mock.patch.object(example_plugin,
                            'example_not_found_hook') as hook_mock:
         hook_mock.side_effect = Exception('Hook Error')
         self.assertRaisesRegexp(utils.Error,
                                 'Node not found hook failed: Hook Error',
                                 process.process, self.data)
         hook_mock.assert_called_once_with(self.data)
 def test_node_not_found_hook_run_ok(self, cli, pop_mock, process_mock):
     CONF.set_override('node_not_found_hook', 'example', 'processing')
     plugins_base._NOT_FOUND_HOOK_MGR = None
     pop_mock.side_effect = iter([utils.NotFoundInCacheError('BOOM')])
     with mock.patch.object(example_plugin,
                            'example_not_found_hook') as hook_mock:
         hook_mock.return_value = node_cache.NodeInfo(
             uuid=self.node.uuid, started_at=self.started_at)
         res = process.process(self.data)
         self.assertEqual(self.fake_result_json, res)
         hook_mock.assert_called_once_with(self.data)
def find_node(**attributes):
    """Find node in cache.

    Looks up a node based on attributes in a best-match fashion.
    This function acquires a lock on a node.

    :param attributes: attributes known about this node (like macs, BMC etc)
                       also ironic client instance may be passed under 'ironic'
    :returns: structure NodeInfo with attributes ``uuid`` and ``created_at``
    :raises: Error if node is not found or multiple nodes match the attributes
    """
    ironic = attributes.pop('ironic', None)
    # NOTE(dtantsur): sorting is not required, but gives us predictability
    found = collections.Counter()

    for (name, value) in sorted(attributes.items()):
        if not value:
            LOG.debug('Empty value for attribute %s', name)
            continue
        if not isinstance(value, list):
            value = [value]

        LOG.debug('Trying to use %s of value %s for node look up', name, value)
        value_list = []
        for v in value:
            value_list.append("name='%s' AND value='%s'" % (name, v))
        stmt = ('select distinct node_uuid from attributes where ' +
                ' OR '.join(value_list))
        rows = (db.model_query(db.Attribute.node_uuid).from_statement(
            text(stmt)).all())
        found.update(row.node_uuid for row in rows)

    if not found:
        raise utils.NotFoundInCacheError(
            _('Could not find a node for attributes %s') % attributes)

    most_common = found.most_common()
    LOG.debug(
        'The following nodes match the attributes: %(attributes)s, '
        'scoring: %(most_common)s', {
            'most_common': ', '.join('%s: %d' % tpl for tpl in most_common),
            'attributes': ', '.join('%s=%s' % tpl
                                    for tpl in attributes.items())
        })

    # NOTE(milan) most_common is sorted, higher scores first
    highest_score = most_common[0][1]
    found = [item[0] for item in most_common if highest_score == item[1]]
    if len(found) > 1:
        raise utils.Error(
            _('Multiple nodes match the same number of attributes '
              '%(attr)s: %(found)s') % {
                  'attr': attributes,
                  'found': found
              },
            code=404)

    uuid = found.pop()
    node_info = NodeInfo(uuid=uuid, ironic=ironic)
    node_info.acquire_lock()

    try:
        row = (db.model_query(
            db.Node.started_at,
            db.Node.finished_at).filter_by(uuid=uuid).first())

        if not row:
            raise utils.Error(
                _('Could not find node %s in introspection cache, '
                  'probably it\'s not on introspection now') % uuid,
                code=404)

        if row.finished_at:
            raise utils.Error(
                _('Introspection for node %(node)s already finished on '
                  '%(finish)s') % {
                      'node': uuid,
                      'finish': row.finished_at
                  })

        node_info.started_at = row.started_at
        return node_info
    except Exception:
        with excutils.save_and_reraise_exception():
            node_info.release_lock()
Beispiel #11
0
 def test_error_if_node_not_found_hook(self):
     plugins_base._NOT_FOUND_HOOK_MGR = None
     self.find_mock.side_effect = utils.NotFoundInCacheError('BOOM')
     self.assertRaisesRegex(utils.Error, 'Look up error: BOOM',
                            process.process, self.data)
Beispiel #12
0
 def test_error_if_node_not_found_hook(self):
     self.find_mock.side_effect = utils.NotFoundInCacheError('BOOM')
     self.assertRaisesRegex(utils.Error, 'Look up error: BOOM',
                            process.process, self.data)
 def test_node_not_in_db(self, fsm_event_mock, add_node_mock):
     fsm_event_mock.side_effect = utils.NotFoundInCacheError('Oops!')
     node_cache.start_introspection(self.node_info.uuid)
     add_node_mock.assert_called_once_with(self.node_info.uuid,
                                           istate.States.starting)
 def test_error_if_node_not_found_hook(self, cli, pop_mock, process_mock):
     plugins_base._NOT_FOUND_HOOK_MGR = None
     pop_mock.side_effect = iter([utils.NotFoundInCacheError('BOOM')])
     self.assertRaisesRegexp(utils.Error, 'Look up error: BOOM',
                             process.process, self.data)