diff options
46 files changed, 283 insertions, 342 deletions
diff --git a/api-ref/source/baremetal-api-v1-indicators.inc b/api-ref/source/baremetal-api-v1-indicators.inc index 0c4b11885..181951982 100644 --- a/api-ref/source/baremetal-api-v1-indicators.inc +++ b/api-ref/source/baremetal-api-v1-indicators.inc @@ -90,7 +90,7 @@ Response Parameters Get Indicator State =================== -.. rest_method:: GET /v1/nodes/{node_ident}/management/indicators/{component}/{ind_ident} +.. rest_method:: GET /v1/nodes/{node_ident}/management/indicators/{ind_ident}@{component} .. versionadded:: 1.63 @@ -127,7 +127,7 @@ Response Parameters Set Indicator State =================== -.. rest_method:: PUT /v1/nodes/{node_ident}/management/indicators/{component}/{ind_ident} +.. rest_method:: PUT /v1/nodes/{node_ident}/management/indicators/{ind_ident}@{component} .. versionadded:: 1.63 diff --git a/api-ref/source/samples/node-inventory-response.json b/api-ref/source/samples/node-inventory-response.json index 7916f6717..9355bcf1b 100644 --- a/api-ref/source/samples/node-inventory-response.json +++ b/api-ref/source/samples/node-inventory-response.json @@ -24,7 +24,6 @@ "52:54:00:90:35:d6" ], "local_gb":10, - "cpus":1, "cpu_arch":"x86_64", "memory_mb":2048 } diff --git a/devstack/lib/ironic b/devstack/lib/ironic index b81b9f38b..d82e1882d 100644 --- a/devstack/lib/ironic +++ b/devstack/lib/ironic @@ -3248,7 +3248,7 @@ function ironic_configure_tempest { else iniset $TEMPEST_CONFIG baremetal whole_disk_image_url "http://$IRONIC_HTTP_SERVER:$IRONIC_HTTP_PORT/${IRONIC_WHOLEDISK_IMAGE_NAME}.img" fi - iniset $TEMPEST_CONFIG baremetal whole_disk_image_checksum $(md5sum $FILES/${IRONIC_WHOLEDISK_IMAGE_NAME}.img) + iniset $TEMPEST_CONFIG baremetal whole_disk_image_checksum $(sha256sum $FILES/${IRONIC_WHOLEDISK_IMAGE_NAME}.img) if [[ "$IRONIC_RAMDISK_IMAGE" != "" ]]; then iniset $TEMPEST_CONFIG baremetal ramdisk_iso_image_ref "$IRONIC_RAMDISK_IMAGE" diff --git a/doc/source/admin/drivers/idrac.rst b/doc/source/admin/drivers/idrac.rst index a8d157ec8..41e17761e 100644 --- a/doc/source/admin/drivers/idrac.rst +++ b/doc/source/admin/drivers/idrac.rst @@ -250,7 +250,6 @@ without affecting the operation of the system being inspected. The inspection discovers the following properties: * ``cpu_arch``: cpu architecture -* ``cpus``: number of cpus * ``local_gb``: disk size in gigabytes * ``memory_mb``: memory size in megabytes diff --git a/doc/source/admin/drivers/ilo.rst b/doc/source/admin/drivers/ilo.rst index b6825fc40..34401db0f 100644 --- a/doc/source/admin/drivers/ilo.rst +++ b/doc/source/admin/drivers/ilo.rst @@ -908,13 +908,10 @@ The hardware type ``ilo`` supports hardware inspection. * ``snmp_auth_priv_protocol`` : The Privacy protocol. The valid values are "AES" and "DES". The iLO default value is "DES". -The inspection process will discover the following essential properties -(properties required for scheduling deployment): +The inspection process will discover the following properties: * ``memory_mb``: memory size -* ``cpus``: number of cpus - * ``cpu_arch``: cpu architecture * ``local_gb``: disk size diff --git a/doc/source/admin/drivers/irmc.rst b/doc/source/admin/drivers/irmc.rst index 9ddfa3b3d..6bc6a1248 100644 --- a/doc/source/admin/drivers/irmc.rst +++ b/doc/source/admin/drivers/irmc.rst @@ -465,13 +465,10 @@ configuration: Supported properties ~~~~~~~~~~~~~~~~~~~~ -The inspection process will discover the following essential properties -(properties required for scheduling deployment): +The inspection process will discover the following properties: * ``memory_mb``: memory size -* ``cpus``: number of cpus - * ``cpu_arch``: cpu architecture * ``local_gb``: disk size diff --git a/doc/source/admin/troubleshooting.rst b/doc/source/admin/troubleshooting.rst index 72e969b6e..eef51cbf3 100644 --- a/doc/source/admin/troubleshooting.rst +++ b/doc/source/admin/troubleshooting.rst @@ -104,23 +104,6 @@ A few things should be checked in this case: ``True``; make sure the target nodes are in ``available`` and ``maintenance`` is ``False``; -#. If you do not use scheduling based on resource classes, then the node's - properties must have been set either manually or via inspection. - For each node with ``available`` state check that the ``properties`` - JSON field has valid values for the keys ``cpus``, ``cpu_arch``, - ``memory_mb`` and ``local_gb``. Example of valid properties:: - - $ baremetal node show <IRONIC NODE> --fields properties - +------------+------------------------------------------------------------------------------------+ - | Property | Value | - +------------+------------------------------------------------------------------------------------+ - | properties | {u'memory_mb': u'8192', u'cpu_arch': u'x86_64', u'local_gb': u'41', u'cpus': u'4'} | - +------------+------------------------------------------------------------------------------------+ - - .. warning:: - If you're using exact match filters in the Nova Scheduler, make sure - the flavor and the node properties match exactly. - #. The Nova flavor that you are using does not match any properties of the available Ironic nodes. Use :: diff --git a/doc/source/conf.py b/doc/source/conf.py index a5b2edcfa..37d00f70d 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -59,7 +59,7 @@ apidoc_excluded_paths = [ apidoc_separate_modules = True openstackdocs_repo_name = 'openstack/ironic' -openstackdocs_use_storyboard = True +openstackdocs_use_storyboard = False openstackdocs_pdf_link = True openstackdocs_projects = [ 'bifrost', diff --git a/doc/source/install/enrollment.rst b/doc/source/install/enrollment.rst index 40c63b4bb..97e2d33d4 100644 --- a/doc/source/install/enrollment.rst +++ b/doc/source/install/enrollment.rst @@ -285,12 +285,13 @@ Adding scheduling information This is not required for standalone deployments, only for those using the Compute service for provisioning bare metal instances. -#. Update the node's properties to match the actual hardware of the node: +#. Update the node's properties to match the actual hardware of the node. + These are optional. When provided, ``memory_mb`` can be used for checking + if the instance image fits into the node's memory: .. code-block:: console $ baremetal node set $NODE_UUID \ - --property cpus=$CPU_COUNT \ --property memory_mb=$RAM_MB \ --property local_gb=$DISK_GB @@ -303,14 +304,13 @@ Adding scheduling information --driver-info ipmi_username=$USER \ --driver-info ipmi_password=$PASS \ --driver-info ipmi_address=$ADDRESS \ - --property cpus=$CPU_COUNT \ --property memory_mb=$RAM_MB \ --property local_gb=$DISK_GB These values can also be discovered during `Hardware Inspection`_. - .. warning:: - The value provided for the ``local_gb`` property must match the size of + .. note:: + The value provided for the ``local_gb`` property should match the size of the root device you're going to deploy on. By default **ironic-python-agent** picks the smallest disk which is not smaller than 4 GiB. diff --git a/doc/source/install/standalone/enrollment.rst b/doc/source/install/standalone/enrollment.rst index 305091070..4fd9ebb48 100644 --- a/doc/source/install/standalone/enrollment.rst +++ b/doc/source/install/standalone/enrollment.rst @@ -87,9 +87,7 @@ Enrolling nodes #. As in case of Compute service, you can also provide ``capabilities`` to node properties, but they will be used only by Bare Metal service (for example, - boot mode). Although you don't need to add properties like ``memory_mb``, - ``cpus`` etc. as Bare Metal service will require UUID of a node you're - going to deploy. + boot mode). #. Then create a port to inform Bare Metal service of the network interface cards which are part of the node by creating a port with each NIC's MAC diff --git a/doc/source/install/troubleshooting.rst b/doc/source/install/troubleshooting.rst index 75f19c729..a40c19efd 100644 --- a/doc/source/install/troubleshooting.rst +++ b/doc/source/install/troubleshooting.rst @@ -33,8 +33,7 @@ service and Bare Metal service:: | Property | Value | +------------------------+----------------------------------------------------------------------+ | instance_uuid | None | - | properties | {u'memory_mb': u'1024', u'cpu_arch': u'x86_64', u'local_gb': u'10', | - | | u'cpus': u'1'} | + | properties | {u'memory_mb': u'1024', u'cpu_arch': u'x86_64', u'local_gb': u'10'} | | maintenance | False | | driver_info | { [SNIP] } | | extra | {} | diff --git a/ironic/common/hash_ring.py b/ironic/common/hash_ring.py index 1f1107008..8986d5bd2 100644 --- a/ironic/common/hash_ring.py +++ b/ironic/common/hash_ring.py @@ -40,7 +40,7 @@ class HashRingManager(object): @property def ring(self): interval = CONF.hash_ring_reset_interval - limit = time.time() - interval + limit = time.monotonic() - interval if not self.cache: return self._load_hash_rings() @@ -56,7 +56,7 @@ class HashRingManager(object): if hash_rings is None or updated_at < limit: LOG.debug('Rebuilding cached hash rings') hash_rings = self._load_hash_rings() - self.__class__._hash_rings = hash_rings, time.time() + self.__class__._hash_rings = hash_rings, time.monotonic() LOG.debug('Finished rebuilding hash rings, available drivers ' 'are %s', ', '.join(hash_rings)) return hash_rings diff --git a/ironic/conductor/manager.py b/ironic/conductor/manager.py index bbd2355bd..ef850753d 100644 --- a/ironic/conductor/manager.py +++ b/ironic/conductor/manager.py @@ -2284,10 +2284,13 @@ class ConductorManager(base_manager.BaseConductorManager): LOG.debug("RPC create_port called for port %s.", port_uuid) with task_manager.acquire(context, port_obj.node_id, - purpose='port create') as task: + purpose='port create', + shared=True) as task: + # NOTE(TheJulia): We're creating a port, we don't need + # an exclusive parent lock to do so. utils.validate_port_physnet(task, port_obj) port_obj.create() - return port_obj + return port_obj @METRICS.timer('ConductorManager.update_port') @messaging.expected_exceptions(exception.NodeLocked, @@ -2373,7 +2376,7 @@ class ConductorManager(base_manager.BaseConductorManager): port_obj.save() - return port_obj + return port_obj @METRICS.timer('ConductorManager.update_portgroup') @messaging.expected_exceptions(exception.NodeLocked, @@ -2452,7 +2455,7 @@ class ConductorManager(base_manager.BaseConductorManager): portgroup_obj.save() - return portgroup_obj + return portgroup_obj @METRICS.timer('ConductorManager.update_volume_connector') @messaging.expected_exceptions( @@ -2496,7 +2499,7 @@ class ConductorManager(base_manager.BaseConductorManager): connector.save() LOG.info("Successfully updated volume connector %(connector)s.", {'connector': connector.uuid}) - return connector + return connector @METRICS.timer('ConductorManager.update_volume_target') @messaging.expected_exceptions( @@ -2537,7 +2540,7 @@ class ConductorManager(base_manager.BaseConductorManager): target.save() LOG.info("Successfully updated volume target %(target)s.", {'target': target.uuid}) - return target + return target @METRICS.timer('ConductorManager.get_driver_properties') @messaging.expected_exceptions(exception.DriverNotFound) @@ -3564,7 +3567,7 @@ class ConductorManager(base_manager.BaseConductorManager): {'node': task.node.uuid}) utils.add_secret_token(task.node) task.node.save() - return task.node + return objects.Node.get(context, node_id) @METRICS.timer('ConductorManager.manage_node_history') @periodics.periodic( diff --git a/ironic/conductor/utils.py b/ironic/conductor/utils.py index 2272c0df7..39886bfd8 100644 --- a/ironic/conductor/utils.py +++ b/ironic/conductor/utils.py @@ -499,6 +499,11 @@ def cleaning_error_handler(task, logmsg, errmsg=None, traceback=False, # NOTE(dtantsur): avoid overwriting existing maintenance_reason if not node.maintenance_reason and set_maintenance: node.maintenance_reason = errmsg + + if CONF.conductor.poweroff_in_cleanfail: + # NOTE(NobodyCam): Power off node in clean fail + node_power_action(task, states.POWER_OFF) + node.save() if set_fail_state and node.provision_state != states.CLEANFAIL: @@ -803,7 +808,6 @@ def power_state_error_handler(e, node, power_state): {'node': node.uuid, 'power_state': power_state}) -@task_manager.require_exclusive_lock def validate_port_physnet(task, port_obj): """Validate the consistency of physical networks of ports in a portgroup. diff --git a/ironic/conf/conductor.py b/ironic/conf/conductor.py index 653e30f56..2452fafe7 100644 --- a/ironic/conf/conductor.py +++ b/ironic/conf/conductor.py @@ -349,6 +349,14 @@ opts = [ 'is a global setting applying to all requests this ' 'conductor receives, regardless of access rights. ' 'The concurrent clean limit cannot be disabled.')), + + cfg.BoolOpt('poweroff_in_cleanfail', + default=False, + help=_('If True power off nodes in the ``clean failed`` ' + 'state. Default False. Option may be unsafe ' + 'when using Cleaning to perform ' + 'hardware-transformative actions such as ' + 'firmware upgrade.')), ] diff --git a/ironic/conf/opts.py b/ironic/conf/opts.py index a7ebcfb30..0d8aeafd2 100644 --- a/ironic/conf/opts.py +++ b/ironic/conf/opts.py @@ -78,7 +78,6 @@ def update_opt_defaults(): # This comes in two flavors 'oslo.messaging=INFO', 'oslo_messaging=INFO', - 'sqlalchemy=WARNING', 'stevedore=INFO', 'eventlet.wsgi.server=INFO', 'iso8601=WARNING', diff --git a/ironic/db/sqlalchemy/__init__.py b/ironic/db/sqlalchemy/__init__.py index 173b91fcc..f92a0b600 100644 --- a/ironic/db/sqlalchemy/__init__.py +++ b/ironic/db/sqlalchemy/__init__.py @@ -10,9 +10,26 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_config import cfg from oslo_db.sqlalchemy import enginefacade +from sqlalchemy.engine import Engine +from sqlalchemy import event + +CONF = cfg.CONF -# FIXME(stephenfin): we need to remove reliance on autocommit semantics ASAP -# since it's not compatible with SQLAlchemy 2.0 # NOTE(dtantsur): we want sqlite as close to a real database as possible. -enginefacade.configure(sqlite_fk=True, __autocommit=True) +enginefacade.configure(sqlite_fk=True) + + +# NOTE(TheJulia): Setup a listener to trigger the sqlite write-ahead +# log to be utilized to permit concurrent access, which is needed +# as we can get read requests while we are writing via the API +# surface *when* we're using sqlite as the database backend. +@event.listens_for(Engine, "connect") +def _setup_journal_mode(dbapi_connection, connection_record): + # NOTE(TheJulia): The string may not be loaded in some unit + # tests so handle whatever the output is as a string so we + # can lower/compare it and send the appropriate command to + # the database. + if 'sqlite' in str(CONF.database.connection).lower(): + dbapi_connection.execute("PRAGMA journal_mode=WAL") diff --git a/ironic/db/sqlalchemy/api.py b/ironic/db/sqlalchemy/api.py index 31ec9647e..3ba31e233 100644 --- a/ironic/db/sqlalchemy/api.py +++ b/ironic/db/sqlalchemy/api.py @@ -324,7 +324,9 @@ def _paginate_query(model, limit=None, marker=None, sort_key=None, # object is garbage collected as ORM Query objects allow # for DB interactions to occur after the fact, so it remains # connected to the DB.. - return query.all() + # Save the query.all() results, but don't return yet, so we + # begin to exit and unwind the session. + ref = query.all() else: # In this case, we have a sqlalchemy.sql.selectable.Select # (most likely) which utilizes the unified select interface. @@ -338,7 +340,10 @@ def _paginate_query(model, limit=None, marker=None, sort_key=None, # Everything is a tuple in a resultset from the unified interface # but for objects, our model expects just object access, # so we extract and return them. - return [r[0] for r in res] + ref = [r[0] for r in res] + # Return the results to the caller, outside of the session context + # if an ORM object, because we want the session to close. + return ref def _filter_active_conductors(query, interval=None): @@ -1341,6 +1346,12 @@ class Connection(api.Connection): def get_active_hardware_type_dict(self, use_groups=False): with _session_for_read() as session: + # TODO(TheJulia): We should likely take a look at this + # joined query, as we may not be getting what we expect. + # Metal3 logs upwards of 200 rows returned with multiple datetime + # columns. + # Given dualing datetime fields, we really can't just expect + # requesting a unique set to "just work". query = (session.query(models.ConductorHardwareInterfaces, models.Conductor) .join(models.Conductor)) @@ -1375,7 +1386,8 @@ class Connection(api.Connection): with _session_for_read() as session: query = (session.query(models.ConductorHardwareInterfaces) .filter_by(conductor_id=conductor_id)) - return query.all() + ref = query.all() + return ref def list_hardware_type_interfaces(self, hardware_types): with _session_for_read() as session: @@ -1397,6 +1409,8 @@ class Connection(api.Connection): conductor_hw_iface['conductor_id'] = conductor_id for k, v in iface.items(): conductor_hw_iface[k] = v + # TODO(TheJulia): Uhh... We should try to do this as one + # bulk operation and not insert each row. session.add(conductor_hw_iface) session.flush() except db_exc.DBDuplicateEntry as e: @@ -2080,9 +2094,10 @@ class Connection(api.Connection): query = session.query(models.Allocation).filter_by( id=allocation_id) try: - return query.one() + ref = query.one() except NoResultFound: raise exception.AllocationNotFound(allocation=allocation_id) + return ref def get_allocation_by_uuid(self, allocation_uuid): """Return an allocation representation. @@ -2095,9 +2110,10 @@ class Connection(api.Connection): query = session.query(models.Allocation).filter_by( uuid=allocation_uuid) try: - return query.one() + ref = query.one() except NoResultFound: raise exception.AllocationNotFound(allocation=allocation_uuid) + return ref def get_allocation_by_name(self, name): """Return an allocation representation. @@ -2109,9 +2125,10 @@ class Connection(api.Connection): with _session_for_read() as session: query = session.query(models.Allocation).filter_by(name=name) try: - return query.one() + ref = query.one() except NoResultFound: raise exception.AllocationNotFound(allocation=name) + return ref def get_allocation_list(self, filters=None, limit=None, marker=None, sort_key=None, sort_dir=None): @@ -2455,7 +2472,7 @@ class Connection(api.Connection): session.flush() except db_exc.DBDuplicateEntry: raise exception.NodeHistoryAlreadyExists(uuid=values['uuid']) - return history + return history @oslo_db_api.retry_on_deadlock def destroy_node_history_by_uuid(self, history_uuid): @@ -2469,9 +2486,10 @@ class Connection(api.Connection): def get_node_history_by_id(self, history_id): query = model_query(models.NodeHistory).filter_by(id=history_id) try: - return query.one() + res = query.one() except NoResultFound: raise exception.NodeHistoryNotFound(history=history_id) + return res def get_node_history_by_uuid(self, history_uuid): query = model_query(models.NodeHistory).filter_by(uuid=history_uuid) diff --git a/ironic/drivers/base.py b/ironic/drivers/base.py index bdd017b91..953ac056e 100644 --- a/ironic/drivers/base.py +++ b/ironic/drivers/base.py @@ -1224,7 +1224,7 @@ class InspectInterface(BaseInterface): """Interface for inspection-related actions.""" interface_type = 'inspect' - ESSENTIAL_PROPERTIES = {'memory_mb', 'local_gb', 'cpus', 'cpu_arch'} + ESSENTIAL_PROPERTIES = {'memory_mb', 'local_gb', 'cpu_arch'} """The properties required by scheduler/deploy.""" @abc.abstractmethod @@ -1662,7 +1662,7 @@ class NetworkInterface(BaseInterface): """ def need_power_on(self, task): - """Check if ironic node must be powered on before applying network changes + """Check if node must be powered on before applying network changes :param task: A TaskManager instance. :returns: Boolean. diff --git a/ironic/drivers/modules/drac/inspect.py b/ironic/drivers/modules/drac/inspect.py index 8ba0be088..c9c58fbe9 100644 --- a/ironic/drivers/modules/drac/inspect.py +++ b/ironic/drivers/modules/drac/inspect.py @@ -176,8 +176,6 @@ class DracWSManInspect(base.InspectInterface): [memory.size_mb for memory in client.list_memory()]) cpus = client.list_cpus() if cpus: - properties['cpus'] = sum( - [self._calculate_cpus(cpu) for cpu in cpus]) properties['cpu_arch'] = 'x86_64' if cpus[0].arch64 else 'x86' bios_settings = client.list_bios_settings() @@ -264,18 +262,6 @@ class DracWSManInspect(base.InspectInterface): if disk.size_mb >= min_size_required_mb: return disk - def _calculate_cpus(self, cpu): - """Find actual CPU count. - - :param cpu: Pass cpu. - - :returns: returns total cpu count. - """ - if cpu.ht_enabled: - return cpu.cores * 2 - else: - return cpu.cores - def _calculate_gpus(self, video_controllers): """Find actual GPU count. diff --git a/ironic/drivers/modules/drac/raid.py b/ironic/drivers/modules/drac/raid.py index 8bad02bba..404222f5d 100644 --- a/ironic/drivers/modules/drac/raid.py +++ b/ironic/drivers/modules/drac/raid.py @@ -1022,7 +1022,7 @@ def _commit_to_controllers(node, controllers, substep="completed"): # all realtime controllers all_realtime = all( (cntlr['is_reboot_required'] == optional) - and not(cntlr.get('is_ehba_mode')) + and not (cntlr.get('is_ehba_mode')) for cntlr in controllers) # check any controller with ehba mode diff --git a/ironic/drivers/modules/irmc/inspect.py b/ironic/drivers/modules/irmc/inspect.py index f7c2ad7ba..64708ff9b 100644 --- a/ironic/drivers/modules/irmc/inspect.py +++ b/ironic/drivers/modules/irmc/inspect.py @@ -236,7 +236,7 @@ def _inspect_hardware(node, existing_traits=None, **kwargs): "with the server, please check if you have set them " "correctly.") error = (_("Inspection failed for node %(node_id)s " - "with the following error: %(error)s. (advice)s") % + "with the following error: %(error)s. %(advice)s") % {'node_id': node.uuid, 'error': e, 'advice': advice}) raise exception.HardwareInspectionFailure(error=error) diff --git a/ironic/drivers/modules/redfish/inspect.py b/ironic/drivers/modules/redfish/inspect.py index 4c5f7c344..c514df4fe 100644 --- a/ironic/drivers/modules/redfish/inspect.py +++ b/ironic/drivers/modules/redfish/inspect.py @@ -108,9 +108,7 @@ class RedfishInspect(base.InspectInterface): system.memory_summary.size_gib * units.Ki) if system.processors and system.processors.summary: - cpus, arch = system.processors.summary - if cpus: - inspected_properties['cpus'] = cpus + arch = system.processors.summary[1] if arch: try: diff --git a/ironic/drivers/modules/redfish/management.py b/ironic/drivers/modules/redfish/management.py index a669d09bc..8bd7058a5 100644 --- a/ironic/drivers/modules/redfish/management.py +++ b/ironic/drivers/modules/redfish/management.py @@ -1197,9 +1197,18 @@ class RedfishManagement(base.ManagementInterface): :raises: RedfishError on an error from the Sushy library :returns: A list of MAC addresses for the node """ + system = redfish_utils.get_system(task.node) try: - system = redfish_utils.get_system(task.node) return list(redfish_utils.get_enabled_macs(task, system)) + # NOTE(janders) we should handle MissingAttributeError separately + # from other SushyErrors - some servers (e.g. some Cisco UCSB and UCSX + # blades) are missing EthernetInterfaces attribute yet could be + # provisioned successfully if MAC information is provided manually AND + # this exception is caught and handled accordingly. + except sushy.exceptions.MissingAttributeError as exc: + LOG.warning('Cannot get MAC addresses for node %(node)s: %(exc)s', + {'node': task.node.uuid, 'exc': exc}) + # if the exception is not a MissingAttributeError, raise it except sushy.exceptions.SushyError as exc: msg = (_('Failed to get network interface information on node ' '%(node)s: %(exc)s') diff --git a/ironic/hacking/checks.py b/ironic/hacking/checks.py index 6c5b49776..57a3cbfe8 100644 --- a/ironic/hacking/checks.py +++ b/ironic/hacking/checks.py @@ -51,4 +51,4 @@ def check_explicit_underscore_import(logical_line, filename): UNDERSCORE_IMPORT_FILES.append(filename) elif (translated_log.match(logical_line) or string_translation.match(logical_line)): - yield(0, "N323: Found use of _() without explicit import of _!") + yield (0, "N323: Found use of _() without explicit import of _!") diff --git a/ironic/objects/node.py b/ironic/objects/node.py index 93df5b3c1..b680ac60a 100644 --- a/ironic/objects/node.py +++ b/ironic/objects/node.py @@ -29,7 +29,7 @@ from ironic.objects import base from ironic.objects import fields as object_fields from ironic.objects import notification -REQUIRED_INT_PROPERTIES = ['local_gb', 'cpus', 'memory_mb'] +REQUIRED_INT_PROPERTIES = ['local_gb', 'memory_mb'] CONF = cfg.CONF LOG = log.getLogger(__name__) @@ -191,7 +191,7 @@ class Node(base.IronicObject, object_base.VersionedObjectDictCompat): return d def _validate_property_values(self, properties): - """Check if the input of local_gb, cpus and memory_mb are valid. + """Check if the input of local_gb and memory_mb are valid. :param properties: a dict contains the node's information. """ diff --git a/ironic/tests/unit/common/test_hash_ring.py b/ironic/tests/unit/common/test_hash_ring.py index 9ecf34562..a84b9ffcf 100644 --- a/ironic/tests/unit/common/test_hash_ring.py +++ b/ironic/tests/unit/common/test_hash_ring.py @@ -129,7 +129,7 @@ class HashRingManagerTestCase(db_base.DbTestCase): self.ring_manager.__class__._hash_rings = ( self.ring_manager.__class__._hash_rings[0], - time.time() - 31 + time.monotonic() - 31 ) ring = self.ring_manager.get_ring('hardware-type', '') self.assertEqual(2, len(ring)) diff --git a/ironic/tests/unit/conductor/test_cleaning.py b/ironic/tests/unit/conductor/test_cleaning.py index 34e805deb..cdfbf14ee 100644 --- a/ironic/tests/unit/conductor/test_cleaning.py +++ b/ironic/tests/unit/conductor/test_cleaning.py @@ -436,6 +436,36 @@ class DoNodeCleanTestCase(db_base.DbTestCase): self.assertFalse(node.maintenance) self.assertIsNone(node.fault) + @mock.patch('ironic.drivers.modules.fake.FakePower.set_power_state', + autospec=True) + @mock.patch.object(n_flat.FlatNetwork, 'validate', autospec=True) + @mock.patch.object(conductor_steps, 'set_node_cleaning_steps', + autospec=True) + def test_do_node_clean_steps_fail_poweroff(self, mock_steps, mock_validate, + mock_power, clean_steps=None, + invalid_exc=True): + if invalid_exc: + mock_steps.side_effect = exception.InvalidParameterValue('invalid') + else: + mock_steps.side_effect = exception.NodeCleaningFailure('failure') + tgt_prov_state = states.MANAGEABLE if clean_steps else states.AVAILABLE + self.config(poweroff_in_cleanfail=True, group='conductor') + node = obj_utils.create_test_node( + self.context, driver='fake-hardware', + uuid=uuidutils.generate_uuid(), + provision_state=states.CLEANING, + power_state=states.POWER_ON, + target_provision_state=tgt_prov_state) + with task_manager.acquire( + self.context, node.uuid, shared=False) as task: + cleaning.do_node_clean(task, clean_steps=clean_steps) + mock_validate.assert_called_once_with(mock.ANY, task) + node.refresh() + self.assertEqual(states.CLEANFAIL, node.provision_state) + self.assertEqual(tgt_prov_state, node.target_provision_state) + mock_steps.assert_called_once_with(mock.ANY, disable_ramdisk=False) + self.assertTrue(mock_power.called) + def test__do_node_clean_automated_steps_fail(self): for invalid in (True, False): self.__do_node_clean_steps_fail(invalid_exc=invalid) diff --git a/ironic/tests/unit/conductor/test_manager.py b/ironic/tests/unit/conductor/test_manager.py index 5244e4c4b..f0fb075b7 100644 --- a/ironic/tests/unit/conductor/test_manager.py +++ b/ironic/tests/unit/conductor/test_manager.py @@ -3568,6 +3568,23 @@ class MiscTestCase(mgr_utils.ServiceSetUpMixin, mgr_utils.CommonMixIn, filters=mock.sentinel.filters)) self.assertEqual([], result) + def test_get_node_with_token(self): + node = obj_utils.create_test_node( + self.context, driver='fake-hardware', + network_interface='noop') + self.assertNotIn('agent_secret_token', node.driver_internal_info) + res = self.service.get_node_with_token(self.context, node.id) + self.assertIn('agent_secret_token', res.driver_internal_info) + + def test_node_with_token_already_set(self): + node = obj_utils.create_test_node( + self.context, driver='fake-hardware', + network_interface='noop', + driver_internal_info={'agent_secret_token': 'secret'}) + res = self.service.get_node_with_token(self.context, node.id) + self.assertEqual('******', + res.driver_internal_info['agent_secret_token']) + @mgr_utils.mock_record_keepalive class ConsoleTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase): @@ -3953,18 +3970,6 @@ class CreatePortTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase): self.assertEqual({'foo': 'bar'}, res.extra) mock_validate.assert_called_once_with(mock.ANY, port) - def test_create_port_node_locked(self): - node = obj_utils.create_test_node(self.context, driver='fake-hardware', - reservation='fake-reserv') - port = obj_utils.get_test_port(self.context, node_id=node.id) - exc = self.assertRaises(messaging.rpc.ExpectedException, - self.service.create_port, - self.context, port) - # Compare true exception hidden by @messaging.expected_exceptions - self.assertEqual(exception.NodeLocked, exc.exc_info[0]) - self.assertRaises(exception.PortNotFound, port.get_by_uuid, - self.context, port.uuid) - @mock.patch.object(conductor_utils, 'validate_port_physnet', autospec=True) def test_create_port_mac_exists(self, mock_validate): node = obj_utils.create_test_node(self.context, driver='fake-hardware') diff --git a/ironic/tests/unit/db/utils.py b/ironic/tests/unit/db/utils.py index 10055d829..fc5bee226 100644 --- a/ironic/tests/unit/db/utils.py +++ b/ironic/tests/unit/db/utils.py @@ -165,7 +165,6 @@ def get_test_snmp_info(**kw): def get_test_node(**kw): properties = { "cpu_arch": "x86_64", - "cpus": "8", "local_gb": "10", "memory_mb": "4096", } @@ -509,7 +508,6 @@ def create_test_node_tag(**kw): def get_test_xclarity_properties(): return { "cpu_arch": "x86_64", - "cpus": "8", "local_gb": "10", "memory_mb": "4096", } diff --git a/ironic/tests/unit/drivers/modules/drac/test_inspect.py b/ironic/tests/unit/drivers/modules/drac/test_inspect.py index d12adba34..0c04ad887 100644 --- a/ironic/tests/unit/drivers/modules/drac/test_inspect.py +++ b/ironic/tests/unit/drivers/modules/drac/test_inspect.py @@ -185,7 +185,6 @@ class DracInspectionTestCase(test_utils.BaseDracTest): expected_node_properties = { 'memory_mb': 32768, 'local_gb': 1116, - 'cpus': 18, 'cpu_arch': 'x86_64', 'capabilities': 'boot_mode:uefi,pci_gpu_devices:1'} mock_client = mock.Mock() @@ -235,7 +234,6 @@ class DracInspectionTestCase(test_utils.BaseDracTest): expected_node_properties = { 'memory_mb': 32768, 'local_gb': 279, - 'cpus': 18, 'cpu_arch': 'x86_64', 'capabilities': 'boot_mode:uefi,pci_gpu_devices:1'} mock_client = mock.Mock() @@ -305,7 +303,6 @@ class DracInspectionTestCase(test_utils.BaseDracTest): expected_node_properties = { 'memory_mb': 32768, 'local_gb': 279, - 'cpus': 18, 'cpu_arch': 'x86_64', 'capabilities': 'boot_mode:uefi,pci_gpu_devices:0'} mock_client = mock.Mock() @@ -355,7 +352,6 @@ class DracInspectionTestCase(test_utils.BaseDracTest): expected_node_properties = { 'memory_mb': 32768, 'local_gb': 279, - 'cpus': 18, 'cpu_arch': 'x86_64', 'capabilities': 'boot_mode:uefi,pci_gpu_devices:2'} mock_client = mock.Mock() @@ -387,7 +383,6 @@ class DracInspectionTestCase(test_utils.BaseDracTest): expected_node_properties = { 'memory_mb': 32768, 'local_gb': 279, - 'cpus': 18, 'cpu_arch': 'x86_64', 'capabilities': 'boot_mode:uefi,pci_gpu_devices:0'} mock_client = mock.Mock() @@ -417,7 +412,6 @@ class DracInspectionTestCase(test_utils.BaseDracTest): expected_node_properties = { 'memory_mb': 32768, 'local_gb': 1116, - 'cpus': 18, 'cpu_arch': 'x86_64', 'capabilities': 'boot_mode:uefi,pci_gpu_devices:1'} mock_client = mock.Mock() @@ -449,22 +443,6 @@ class DracInspectionTestCase(test_utils.BaseDracTest): self.assertEqual(285888, root_disk.size_mb) - def test__calculate_cpus(self): - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - cpu = task.driver.inspect._calculate_cpus( - self.cpus[0]) - - self.assertEqual(12, cpu) - - def test__calculate_cpus_without_ht_enabled(self): - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - cpu = task.driver.inspect._calculate_cpus( - self.cpus[1]) - - self.assertEqual(6, cpu) - @mock.patch.object(drac_common, 'get_drac_client', spec_set=True, autospec=True) def test__get_pxe_dev_nics_with_UEFI_boot_mode(self, mock_get_drac_client): diff --git a/ironic/tests/unit/drivers/modules/ilo/test_inspect.py b/ironic/tests/unit/drivers/modules/ilo/test_inspect.py index 1a85d5b28..af262c0d0 100644 --- a/ironic/tests/unit/drivers/modules/ilo/test_inspect.py +++ b/ironic/tests/unit/drivers/modules/ilo/test_inspect.py @@ -65,7 +65,7 @@ class IloInspectTestCase(test_common.BaseIloTest): get_capabilities_mock): ilo_object_mock = get_ilo_object_mock.return_value properties = {'memory_mb': '512', 'local_gb': '10', - 'cpus': '1', 'cpu_arch': 'x86_64'} + 'cpu_arch': 'x86_64'} macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'} capabilities = {} result = {'properties': properties, 'macs': macs} @@ -103,8 +103,7 @@ class IloInspectTestCase(test_common.BaseIloTest): get_capabilities_mock, log_mock): ilo_object_mock = get_ilo_object_mock.return_value - properties = {'memory_mb': '512', 'local_gb': 0, - 'cpus': '1', 'cpu_arch': 'x86_64'} + properties = {'memory_mb': '512', 'local_gb': 0, 'cpu_arch': 'x86_64'} macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'} capabilities = {} result = {'properties': properties, 'macs': macs} @@ -118,7 +117,7 @@ class IloInspectTestCase(test_common.BaseIloTest): task.node.properties = properties task.node.save() expected_properties = {'memory_mb': '512', 'local_gb': 10, - 'cpus': '1', 'cpu_arch': 'x86_64'} + 'cpu_arch': 'x86_64'} task.driver.inspect.inspect_hardware(task) self.assertEqual(expected_properties, task.node.properties) power_mock.assert_called_once_with(mock.ANY, task) @@ -149,8 +148,7 @@ class IloInspectTestCase(test_common.BaseIloTest): get_capabilities_mock, log_mock): ilo_object_mock = get_ilo_object_mock.return_value - properties = {'memory_mb': '512', 'local_gb': 10, - 'cpus': '1', 'cpu_arch': 'x86_64'} + properties = {'memory_mb': '512', 'local_gb': 10, 'cpu_arch': 'x86_64'} macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'} capabilities = {'server_model': 'Gen8'} result = {'properties': properties, 'macs': macs} @@ -160,7 +158,7 @@ class IloInspectTestCase(test_common.BaseIloTest): with task_manager.acquire(self.context, self.node.uuid, shared=False) as task: expected_properties = {'memory_mb': '512', 'local_gb': 10, - 'cpus': '1', 'cpu_arch': 'x86_64', + 'cpu_arch': 'x86_64', 'capabilities': 'server_model:Gen8'} task.driver.inspect.inspect_hardware(task) self.assertEqual(expected_properties, task.node.properties) @@ -195,8 +193,7 @@ class IloInspectTestCase(test_common.BaseIloTest): get_security_params_mock, log_mock): ilo_object_mock = get_ilo_object_mock.return_value - properties = {'memory_mb': '512', 'local_gb': 10, - 'cpus': '1', 'cpu_arch': 'x86_64'} + properties = {'memory_mb': '512', 'local_gb': 10, 'cpu_arch': 'x86_64'} macs = {'NIC.LOM.1.1': 'aa:aa:aa:aa:aa:aa'} capabilities = {'server_model': 'Gen10'} security_params = ( @@ -209,7 +206,7 @@ class IloInspectTestCase(test_common.BaseIloTest): with task_manager.acquire(self.context, self.node.uuid, shared=False) as task: expected_properties = { - 'memory_mb': '512', 'local_gb': 10, 'cpus': '1', + 'memory_mb': '512', 'local_gb': 10, 'cpu_arch': 'x86_64', 'capabilities': 'server_model:Gen10', 'security_parameters': {'Password Complexity': 'ok'}} task.driver.inspect.inspect_hardware(task) @@ -243,7 +240,7 @@ class IloInspectTestCase(test_common.BaseIloTest): get_capabilities_mock): ilo_object_mock = get_ilo_object_mock.return_value properties = {'memory_mb': '512', 'local_gb': '10', - 'cpus': '1', 'cpu_arch': 'x86_64'} + 'cpu_arch': 'x86_64'} macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'} capabilities = {} result = {'properties': properties, 'macs': macs} @@ -280,7 +277,7 @@ class IloInspectTestCase(test_common.BaseIloTest): get_capabilities_mock): ilo_object_mock = get_ilo_object_mock.return_value properties = {'memory_mb': '512', 'local_gb': '10', - 'cpus': '1', 'cpu_arch': 'x86_64'} + 'cpu_arch': 'x86_64'} macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'} capability_str = 'sriov_enabled:true' capabilities = {'sriov_enabled': 'true'} @@ -292,7 +289,7 @@ class IloInspectTestCase(test_common.BaseIloTest): shared=False) as task: task.driver.inspect.inspect_hardware(task) expected_properties = {'memory_mb': '512', 'local_gb': '10', - 'cpus': '1', 'cpu_arch': 'x86_64', + 'cpu_arch': 'x86_64', 'capabilities': capability_str} self.assertEqual(expected_properties, task.node.properties) power_mock.assert_called_once_with(mock.ANY, task) @@ -320,8 +317,7 @@ class IloInspectTestCase(test_common.BaseIloTest): get_capabilities_mock): ilo_object_mock = get_ilo_object_mock.return_value properties = {'memory_mb': '512', 'local_gb': '10', - 'cpus': '1', 'cpu_arch': 'x86_64', - 'somekey': 'somevalue'} + 'cpu_arch': 'x86_64', 'somekey': 'somevalue'} macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'} result = {'properties': properties, 'macs': macs} capabilities = {'sriov_enabled': 'true'} @@ -339,7 +335,7 @@ class IloInspectTestCase(test_common.BaseIloTest): set2 = set(end_capabilities.split(',')) self.assertEqual(set1, set2) expected_properties = {'memory_mb': '512', 'local_gb': '10', - 'cpus': '1', 'cpu_arch': 'x86_64', + 'cpu_arch': 'x86_64', 'capabilities': end_capabilities} power_mock.assert_called_once_with(mock.ANY, task) self.assertEqual(task.node.properties, expected_properties) @@ -356,7 +352,7 @@ class TestInspectPrivateMethods(test_common.BaseIloTest): def test__get_essential_properties_ok(self): ilo_mock = mock.MagicMock(spec=['get_essential_properties']) properties = {'memory_mb': '512', 'local_gb': '10', - 'cpus': '1', 'cpu_arch': 'x86_64'} + 'cpu_arch': 'x86_64'} macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'} result = {'properties': properties, 'macs': macs} ilo_mock.get_essential_properties.return_value = result @@ -368,8 +364,7 @@ class TestInspectPrivateMethods(test_common.BaseIloTest): ilo_mock = mock.MagicMock( spec=['get_additional_capabilities', 'get_essential_properties']) # Missing key: cpu_arch - properties = {'memory_mb': '512', 'local_gb': '10', - 'cpus': '1'} + properties = {'memory_mb': '512', 'local_gb': '10'} macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'} result = {'properties': properties, 'macs': macs} ilo_mock.get_essential_properties.return_value = result @@ -386,8 +381,7 @@ class TestInspectPrivateMethods(test_common.BaseIloTest): ilo_mock = mock.MagicMock( spec=['get_additional_capabilities', 'get_essential_properties']) # Not a dict - properties = ['memory_mb', '512', 'local_gb', '10', - 'cpus', '1'] + properties = ['memory_mb', '512', 'local_gb', '10'] macs = ['aa:aa:aa:aa:aa:aa', 'bb:bb:bb:bb:bb:bb'] capabilities = '' result = {'properties': properties, 'macs': macs} @@ -400,7 +394,7 @@ class TestInspectPrivateMethods(test_common.BaseIloTest): def test__get_essential_properties_fail_mac_invalid_format(self): ilo_mock = mock.MagicMock(spec=['get_essential_properties']) properties = {'memory_mb': '512', 'local_gb': '10', - 'cpus': '1', 'cpu_arch': 'x86_64'} + 'cpu_arch': 'x86_64'} # Not a dict macs = 'aa:aa:aa:aa:aa:aa' result = {'properties': properties, 'macs': macs} @@ -413,7 +407,7 @@ class TestInspectPrivateMethods(test_common.BaseIloTest): ilo_mock = mock.MagicMock( spec=['get_additional_capabilities', 'get_essential_properties']) properties = {'memory_mb': '512', 'local_gb': '10', - 'cpus': '1', 'cpu_arch': 'x86_64'} + 'cpu_arch': 'x86_64'} # Not a dictionary macs = None result = {'properties': properties, 'macs': macs} @@ -427,7 +421,7 @@ class TestInspectPrivateMethods(test_common.BaseIloTest): def test__get_essential_properties_hardware_port_not_dict(self): ilo_mock = mock.MagicMock(spec=['get_essential_properties']) properties = {'memory_mb': '512', 'local_gb': '10', - 'cpus': '1', 'cpu_arch': 'x86_64'} + 'cpu_arch': 'x86_64'} # Not a dict macs = 'aa:bb:cc:dd:ee:ff' result = {'properties': properties, 'macs': macs} @@ -447,7 +441,7 @@ class TestInspectPrivateMethods(test_common.BaseIloTest): def test__validate_ok(self): properties = {'memory_mb': '512', 'local_gb': '10', - 'cpus': '2', 'cpu_arch': 'x86_arch'} + 'cpu_arch': 'x86_arch'} macs = {'Port 1': 'aa:aa:aa:aa:aa:aa'} data = {'properties': properties, 'macs': macs} valid_keys = ilo_inspect.IloInspect.ESSENTIAL_PROPERTIES @@ -455,8 +449,7 @@ class TestInspectPrivateMethods(test_common.BaseIloTest): self.assertEqual(sorted(set(properties)), sorted(valid_keys)) def test__validate_essential_keys_fail_missing_key(self): - properties = {'memory_mb': '512', 'local_gb': '10', - 'cpus': '1'} + properties = {'memory_mb': '512', 'local_gb': '10'} macs = {'Port 1': 'aa:aa:aa:aa:aa:aa'} data = {'properties': properties, 'macs': macs} self.assertRaises(exception.HardwareInspectionFailure, diff --git a/ironic/tests/unit/drivers/modules/irmc/test_inspect.py b/ironic/tests/unit/drivers/modules/irmc/test_inspect.py index 2cec2429f..e8762391f 100644 --- a/ironic/tests/unit/drivers/modules/irmc/test_inspect.py +++ b/ironic/tests/unit/drivers/modules/irmc/test_inspect.py @@ -83,7 +83,6 @@ class IRMCInspectInternalMethodsTestCase(test_common.BaseIRMCTest): inspected_props = { 'memory_mb': '1024', 'local_gb': 10, - 'cpus': 2, 'cpu_arch': 'x86_64'} inspected_capabilities = { 'irmc_firmware_version': 'iRMC S4-7.82F', @@ -142,7 +141,6 @@ class IRMCInspectInternalMethodsTestCase(test_common.BaseIRMCTest): inspected_props = { 'memory_mb': '1024', 'local_gb': 10, - 'cpus': 2, 'cpu_arch': 'x86_64'} inspected_capabilities = { 'irmc_firmware_version': 'iRMC S6-2.00S', @@ -260,7 +258,6 @@ class IRMCInspectTestCase(test_common.BaseIRMCTest): inspected_props = { 'memory_mb': '1024', 'local_gb': 10, - 'cpus': 2, 'cpu_arch': 'x86_64'} inspected_macs = ['aa:aa:aa:aa:aa:aa', 'bb:bb:bb:bb:bb:bb'] new_traits = ['CUSTOM_CPU_FPGA'] @@ -318,7 +315,6 @@ class IRMCInspectTestCase(test_common.BaseIRMCTest): inspected_props = { 'memory_mb': '1024', 'local_gb': 10, - 'cpus': 2, 'cpu_arch': 'x86_64'} inspected_macs = ['aa:aa:aa:aa:aa:aa', 'bb:bb:bb:bb:bb:bb'] new_traits = ['CUSTOM_CPU_FPGA'] @@ -398,7 +394,6 @@ class IRMCInspectTestCase(test_common.BaseIRMCTest): inspected_props = { 'memory_mb': '1024', 'local_gb': 10, - 'cpus': 2, 'cpu_arch': 'x86_64'} inspected_macs = ['aa:aa:aa:aa:aa:aa', 'bb:bb:bb:bb:bb:bb'] existing_traits = ['CUSTOM_CPU_FPGA'] @@ -459,7 +454,6 @@ class IRMCInspectTestCase(test_common.BaseIRMCTest): inspected_props = { 'memory_mb': '1024', 'local_gb': 10, - 'cpus': 2, 'cpu_arch': 'x86_64'} inspected_macs = ['aa:aa:aa:aa:aa:aa', 'bb:bb:bb:bb:bb:bb'] diff --git a/ironic/tests/unit/drivers/modules/network/test_common.py b/ironic/tests/unit/drivers/modules/network/test_common.py index 7b907ad22..e58fcaa20 100644 --- a/ironic/tests/unit/drivers/modules/network/test_common.py +++ b/ironic/tests/unit/drivers/modules/network/test_common.py @@ -1065,10 +1065,14 @@ class TestNeutronVifPortIDMixin(db_base.DbTestCase): expected_dhcp_opts = [{'opt_name': '61', 'opt_value': 'fake2'}] self.port.extra = expected_extra self.port.internal_info = expected_ii + what_changed_mock = mock.Mock() + what_changed_mock.return_value = ['extra', 'internal_info'] + self.port.obj_what_changed = what_changed_mock with task_manager.acquire(self.context, self.node.id) as task: self.interface.port_changed(task, self.port) dhcp_update_mock.assert_called_once_with( mock.ANY, 'fake-id', expected_dhcp_opts, context=task.context) + self.assertEqual(2, what_changed_mock.call_count) @mock.patch('ironic.dhcp.neutron.NeutronDHCPApi.update_port_dhcp_opts', autospec=True) @@ -1087,14 +1091,16 @@ class TestNeutronVifPortIDMixin(db_base.DbTestCase): def test_port_changed_client_id_fail(self, dhcp_update_mock): self.port.internal_info = {'tenant_vif_port_id': 'fake-id'} self.port.extra = {'client-id': 'fake3'} - # NOTE(TheJulia): Does not save, because it attempts to figure - # out what has changed as part of the test. + what_changed_mock = mock.Mock() + what_changed_mock.return_value = ['extra'] + self.port.obj_what_changed = what_changed_mock dhcp_update_mock.side_effect = ( exception.FailedToUpdateDHCPOptOnPort(port_id=self.port.uuid)) with task_manager.acquire(self.context, self.node.id) as task: self.assertRaises(exception.FailedToUpdateDHCPOptOnPort, self.interface.port_changed, task, self.port) + self.assertEqual(2, what_changed_mock.call_count) @mock.patch('ironic.dhcp.neutron.NeutronDHCPApi.update_port_dhcp_opts', autospec=True) diff --git a/ironic/tests/unit/drivers/modules/redfish/test_inspect.py b/ironic/tests/unit/drivers/modules/redfish/test_inspect.py index dc1ffff4d..995dbb6d9 100644 --- a/ironic/tests/unit/drivers/modules/redfish/test_inspect.py +++ b/ironic/tests/unit/drivers/modules/redfish/test_inspect.py @@ -93,8 +93,7 @@ class RedfishInspectTestCase(db_base.DbTestCase): mock_get_system): expected_properties = { 'capabilities': 'boot_mode:uefi', - 'cpu_arch': 'mips', 'cpus': '8', - 'local_gb': '3', 'memory_mb': '2048' + 'cpu_arch': 'mips', 'local_gb': '3', 'memory_mb': '2048' } self.init_system_mock(mock_get_system.return_value) @@ -140,8 +139,7 @@ class RedfishInspectTestCase(db_base.DbTestCase): shared=True) as task: expected_properties = { 'capabilities': 'boot_mode:uefi', - 'cpu_arch': 'x86_64', 'cpus': '8', - 'local_gb': '3', 'memory_mb': '2048' + 'cpu_arch': 'x86_64', 'local_gb': '3', 'memory_mb': '2048' } task.driver.inspect.inspect_hardware(task) self.assertEqual(expected_properties, task.node.properties) @@ -156,8 +154,7 @@ class RedfishInspectTestCase(db_base.DbTestCase): shared=True) as task: expected_properties = { 'capabilities': 'boot_mode:uefi', - 'cpu_arch': 'mips', 'cpus': '8', - 'local_gb': '0', 'memory_mb': '2048' + 'cpu_arch': 'mips', 'local_gb': '0', 'memory_mb': '2048' } task.driver.inspect.inspect_hardware(task) self.assertEqual(expected_properties, task.node.properties) @@ -182,8 +179,7 @@ class RedfishInspectTestCase(db_base.DbTestCase): shared=True) as task: expected_properties = { 'capabilities': 'boot_mode:uefi', - 'cpu_arch': 'mips', 'cpus': '8', - 'local_gb': '3', 'memory_mb': '4096' + 'cpu_arch': 'mips', 'local_gb': '3', 'memory_mb': '4096' } task.driver.inspect.inspect_hardware(task) self.assertEqual(expected_properties, task.node.properties) @@ -212,8 +208,7 @@ class RedfishInspectTestCase(db_base.DbTestCase): } expected_properties = { 'capabilities': 'boot_mode:bios', - 'cpu_arch': 'mips', 'cpus': '8', - 'local_gb': '3', 'memory_mb': '2048' + 'cpu_arch': 'mips', 'local_gb': '3', 'memory_mb': '2048' } task.driver.inspect.inspect_hardware(task) self.assertEqual(expected_properties, task.node.properties) @@ -226,8 +221,7 @@ class RedfishInspectTestCase(db_base.DbTestCase): with task_manager.acquire(self.context, self.node.uuid, shared=True) as task: expected_properties = { - 'cpu_arch': 'mips', 'cpus': '8', - 'local_gb': '3', 'memory_mb': '2048' + 'cpu_arch': 'mips', 'local_gb': '3', 'memory_mb': '2048' } task.driver.inspect.inspect_hardware(task) self.assertEqual(expected_properties, task.node.properties) diff --git a/ironic/tests/unit/drivers/modules/redfish/test_management.py b/ironic/tests/unit/drivers/modules/redfish/test_management.py index f8c82949a..1d752d909 100644 --- a/ironic/tests/unit/drivers/modules/redfish/test_management.py +++ b/ironic/tests/unit/drivers/modules/redfish/test_management.py @@ -1598,3 +1598,13 @@ class RedfishManagementTestCase(db_base.DbTestCase): shared=True) as task: self.assertEqual([], task.driver.management.get_mac_addresses(task)) + + @mock.patch.object(redfish_utils, 'get_enabled_macs', autospec=True) + @mock.patch.object(redfish_utils, 'get_system', autospec=True) + def test_get_mac_addresses_missing_attr(self, mock_get_system, + mock_get_enabled_macs): + redfish_utils.get_enabled_macs.side_effect = (sushy.exceptions. + MissingAttributeError) + with task_manager.acquire(self.context, self.node.uuid, + shared=True) as task: + self.assertIsNone(task.driver.management.get_mac_addresses(task)) diff --git a/ironic/tests/unit/objects/test_node.py b/ironic/tests/unit/objects/test_node.py index 146120644..5ba823a89 100644 --- a/ironic/tests/unit/objects/test_node.py +++ b/ironic/tests/unit/objects/test_node.py @@ -534,10 +534,10 @@ class TestNodeObject(db_base.DbTestCase, obj_utils.SchemasTestMixIn): autospec=True) as mock_get_node: mock_get_node.return_value = self.fake_node node = objects.Node.get(self.context, uuid) - node.properties = {"local_gb": "5G", "memory_mb": "5", - 'cpus': '-1', 'cpu_arch': 'x86_64'} + node.properties = {"local_gb": "5G", "memory_mb": "-5", + 'cpu_arch': 'x86_64'} self.assertRaisesRegex(exception.InvalidParameterValue, - ".*local_gb=5G, cpus=-1$", node.save) + ".*local_gb=5G, memory_mb=-5$", node.save) mock_get_node.assert_called_once_with(uuid) def test__validate_property_values_success(self): @@ -549,7 +549,6 @@ class TestNodeObject(db_base.DbTestCase, obj_utils.SchemasTestMixIn): values = self.fake_node expect = { 'cpu_arch': 'x86_64', - "cpus": '8', "local_gb": '10', "memory_mb": '4096', } diff --git a/releasenotes/notes/Cleanfail-power-off-13b5fdcc2727866a.yaml b/releasenotes/notes/Cleanfail-power-off-13b5fdcc2727866a.yaml new file mode 100644 index 000000000..2856bac06 --- /dev/null +++ b/releasenotes/notes/Cleanfail-power-off-13b5fdcc2727866a.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + Add new conductor conf option: [conductor]poweroff_in_cleanfail + (default: False). when True nodes entering clean failed state + will be powered off. This option may be unsafe when using + Cleaning to perform hardware-transformative actions such as + firmware upgrade. diff --git a/releasenotes/notes/fix-db-sqlite-OperationalError-7934dbda2a21c69e.yaml b/releasenotes/notes/fix-db-sqlite-OperationalError-7934dbda2a21c69e.yaml new file mode 100644 index 000000000..85a604ae4 --- /dev/null +++ b/releasenotes/notes/fix-db-sqlite-OperationalError-7934dbda2a21c69e.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Fixes issues in Ironic's use of SQLAlchemy with SQLite Databases, + which is common with users like Metal3, which prevented Ironic from + supporting SQLAlchemy 2.0 properly, as autocommit was re-enabled. diff --git a/releasenotes/notes/handle-missing-ethernetinterfaces-attr-7e52f7259fe66762.yaml b/releasenotes/notes/handle-missing-ethernetinterfaces-attr-7e52f7259fe66762.yaml new file mode 100644 index 000000000..225238506 --- /dev/null +++ b/releasenotes/notes/handle-missing-ethernetinterfaces-attr-7e52f7259fe66762.yaml @@ -0,0 +1,9 @@ +--- +fixes: + - | + Fixes the bug where provisioning a Redfish managed node fails if the BMC + doesn't support EthernetInterfaces attribute, even if MAC address + information is provided manually. This is done by handling of + MissingAttributeError sushy exception in get_mac_addresses() method. + This fix is needed to successfully provision machines such as Cisco UCSB + and UCSX. diff --git a/releasenotes/notes/no-cpus-c79717303470bf3c.yaml b/releasenotes/notes/no-cpus-c79717303470bf3c.yaml new file mode 100644 index 000000000..4f264e79b --- /dev/null +++ b/releasenotes/notes/no-cpus-c79717303470bf3c.yaml @@ -0,0 +1,6 @@ +--- +upgrade: + - | + Fully removes the ``cpus`` property from the documentation and inspect + interface implementations. It was never used internally by Ironic, + and is no longer used by Nova. diff --git a/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po b/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po index c3581ff0d..05bc0a046 100644 --- a/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po +++ b/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po @@ -8,11 +8,11 @@ msgid "" msgstr "" "Project-Id-Version: Ironic Release Notes\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-03-08 03:30+0000\n" +"POT-Creation-Date: 2023-05-09 03:34+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2023-02-03 04:37+0000\n" +"PO-Revision-Date: 2023-05-08 10:24+0000\n" "Last-Translator: Andi Chandler <andi@gowling.com>\n" "Language-Team: English (United Kingdom)\n" "Language: en_GB\n" @@ -287,8 +287,11 @@ msgstr "18.2.1" msgid "18.2.2" msgstr "18.2.2" -msgid "18.2.2-6" -msgstr "18.2.2-6" +msgid "18.3.0" +msgstr "18.3.0" + +msgid "18.3.0-1" +msgstr "18.3.0-1" msgid "19.0.0" msgstr "19.0.0" @@ -302,6 +305,9 @@ msgstr "20.1.0" msgid "20.1.1" msgstr "20.1.1" +msgid "20.1.1-10" +msgstr "20.1.1-10" + msgid "20.2.0" msgstr "20.2.0" @@ -311,12 +317,21 @@ msgstr "21.0.0" msgid "21.1.0" msgstr "21.1.0" +msgid "21.1.0-17" +msgstr "21.1.0-17" + msgid "21.2.0" msgstr "21.2.0" msgid "21.3.0" msgstr "21.3.0" +msgid "21.4.0" +msgstr "21.4.0" + +msgid "21.4.0-7" +msgstr "21.4.0-7" + msgid "4.0.0 First semver release" msgstr "4.0.0 First semver release" @@ -1829,6 +1844,14 @@ msgstr "" "of the Driver API to support future improvements." msgid "" +"Drivers may register their own periodic tasks to be run by the Conductor." +msgstr "" +"Drivers may register their own periodic tasks to be run by the Conductor." + +msgid "Drivers may store their own \"internal\" information about Nodes." +msgstr "Drivers may store their own \"internal\" information about Nodes." + +msgid "" "Drivers using the \"agent\" deploy mechanism do not support \"rebuild --" "preserve-ephemeral\"" msgstr "" @@ -1938,6 +1961,13 @@ msgid "Support for the new ENROLL workflow during Node creation" msgstr "Support for the new ENROLL workflow during Node creation" msgid "" +"Support for third-party and out-of-tree drivers is enhanced by the following " +"two changes:" +msgstr "" +"Support for third-party and out-of-tree drivers is enhanced by the following " +"two changes:" + +msgid "" "The \"agent\" class of drivers now support both whole-disk and partition " "based images." msgstr "" @@ -2110,3 +2140,17 @@ msgstr "v1.7 exposes a new 'clean_step' property on the Node resource." msgid "v1.8 and v1.9 improve query and filter support" msgstr "v1.8 and v1.9 improve query and filter support" + +msgid "" +"vendor_passthru methods are now discoverable in the REST API. See node " +"vendor passthru and driver vendor passthru" +msgstr "" +"vendor_passthru methods are now discoverable in the REST API. See node " +"vendor passthru and driver vendor passthru" + +msgid "" +"vendor_passthru methods now support additional HTTP methods (eg, PUT and " +"POST)." +msgstr "" +"vendor_passthru methods now support additional HTTP methods (eg, PUT and " +"POST)." diff --git a/releasenotes/source/locale/ja/LC_MESSAGES/releasenotes.po b/releasenotes/source/locale/ja/LC_MESSAGES/releasenotes.po deleted file mode 100644 index 81262b544..000000000 --- a/releasenotes/source/locale/ja/LC_MESSAGES/releasenotes.po +++ /dev/null @@ -1,159 +0,0 @@ -# OpenStack Infra <zanata@openstack.org>, 2015. #zanata -# Akihiro Motoki <amotoki@gmail.com>, 2016. #zanata -# Akihito INOH <aki-inou@rs.jp.nec.com>, 2018. #zanata -msgid "" -msgstr "" -"Project-Id-Version: Ironic Release Notes\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-03-08 03:30+0000\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2018-02-15 11:45+0000\n" -"Last-Translator: Akihito INOH <aki-inou@rs.jp.nec.com>\n" -"Language-Team: Japanese\n" -"Language: ja\n" -"X-Generator: Zanata 4.3.3\n" -"Plural-Forms: nplurals=1; plural=0\n" - -msgid "" -"\"Port group\" support allows users to take advantage of bonded network " -"interfaces." -msgstr "" -"\"Port group\" のサポートにより、ユーザーはボンディングされたネットワークイン" -"ターフェースが利用できるようになります。" - -msgid "10.0.0" -msgstr "10.0.0" - -msgid "10.1.0" -msgstr "10.1.0" - -msgid "4.2.2" -msgstr "4.2.2" - -msgid "4.2.3" -msgstr "4.2.3" - -msgid "4.2.4" -msgstr "4.2.4" - -msgid "4.2.5" -msgstr "4.2.5" - -msgid "4.3.0" -msgstr "4.3.0" - -msgid "443, 80" -msgstr "443, 80" - -msgid "5.0.0" -msgstr "5.0.0" - -msgid "5.1.0" -msgstr "5.1.0" - -msgid "5.1.1" -msgstr "5.1.1" - -msgid "5.1.2" -msgstr "5.1.2" - -msgid "5.1.3" -msgstr "5.1.3" - -msgid "6.0.0" -msgstr "6.0.0" - -msgid "6.1.0" -msgstr "6.1.0" - -msgid "6.2.0" -msgstr "6.2.0" - -msgid "6.2.2" -msgstr "6.2.2" - -msgid "6.2.3" -msgstr "6.2.3" - -msgid "6.2.4" -msgstr "6.2.4" - -msgid "6.3.0" -msgstr "6.3.0" - -msgid "7.0.0" -msgstr "7.0.0" - -msgid "7.0.1" -msgstr "7.0.1" - -msgid "7.0.2" -msgstr "7.0.2" - -msgid "7.0.3" -msgstr "7.0.3" - -msgid "7.0.4" -msgstr "7.0.4" - -msgid "8.0.0" -msgstr "8.0.0" - -msgid "9.0.0" -msgstr "9.0.0" - -msgid "9.0.1" -msgstr "9.0.1" - -msgid "9.1.0" -msgstr "9.1.0" - -msgid "9.1.1" -msgstr "9.1.1" - -msgid "9.1.2" -msgstr "9.1.2" - -msgid "9.1.3" -msgstr "9.1.3" - -msgid "9.2.0" -msgstr "9.2.0" - -msgid "" -"A few major changes are worth mentioning. This is not an exhaustive list:" -msgstr "" -"いくつかの主要な変更がありました。全てではありませんが以下にリストを示しま" -"す。" - -msgid "A few major changes since 9.1.x (Pike) are worth mentioning:" -msgstr "9.1.x (Pike) からの主要な変更がいくつかありました。" - -msgid "Bug Fixes" -msgstr "バグ修正" - -msgid "Current Series Release Notes" -msgstr "開発中バージョンのリリースノート" - -msgid "Deprecation Notes" -msgstr "廃止予定の機能" - -msgid "Known Issues" -msgstr "既知の問題" - -msgid "New Features" -msgstr "新機能" - -msgid "Option" -msgstr "オプション" - -msgid "Other Notes" -msgstr "その他の注意点" - -msgid "Security Issues" -msgstr "セキュリティー上の問題" - -msgid "Upgrade Notes" -msgstr "アップグレード時の注意" @@ -37,7 +37,7 @@ commands = {toxinidir}/tools/states_to_dot.py -f {toxinidir}/doc/source/images/s [testenv:pep8] usedevelop = False deps= - hacking>=4.1.0,<5.0.0 # Apache-2.0 + hacking~=6.0.0 # Apache-2.0 doc8>=0.6.0 # Apache-2.0 pycodestyle>=2.0.0,<3.0.0 # MIT flake8-import-order>=0.17.1 # LGPLv3 diff --git a/zuul.d/ironic-jobs.yaml b/zuul.d/ironic-jobs.yaml index ca1757417..d2e91875f 100644 --- a/zuul.d/ironic-jobs.yaml +++ b/zuul.d/ironic-jobs.yaml @@ -896,6 +896,7 @@ SWIFT_TEMPURL_KEY: secretkey EBTABLES_RACE_FIX: True LIBVIRT_STORAGE_POOL_PATH: /opt/libvirt/images + INSTANCE_WAIT: 120 old: IRONIC_VM_LOG_DIR: '{{ devstack_bases.old }}/ironic-bm-logs' grenade_localrc: diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml index 8fbfbb929..cf3a696db 100644 --- a/zuul.d/project.yaml +++ b/zuul.d/project.yaml @@ -13,7 +13,10 @@ - ironic-cross-sushy: voting: false - ironic-tempest-functional-python3 - - ironic-tempest-functional-rbac-scope-enforced + # NOTE(rpittau) moving to non-voting until we fix the tests + # see also https://review.opendev.org/c/openstack/ironic-tempest-plugin/+/882312 + - ironic-tempest-functional-rbac-scope-enforced: + voting: false - ironic-grenade - ironic-standalone - ironic-standalone-redfish @@ -63,7 +66,9 @@ jobs: - ironic-tox-unit-with-driver-libs - ironic-tempest-functional-python3 - - ironic-tempest-functional-rbac-scope-enforced + # NOTE(rpittau) disabled until we fix the tests + # see also https://review.opendev.org/c/openstack/ironic-tempest-plugin/+/882312 + #- ironic-tempest-functional-rbac-scope-enforced - ironic-grenade - ironic-standalone - ironic-standalone-redfish |