diff --git a/common/src/stack/command/stack/argument_processors/firmware.py b/common/src/stack/command/stack/argument_processors/firmware.py
deleted file mode 100644
index 7c9068d89..000000000
--- a/common/src/stack/command/stack/argument_processors/firmware.py
+++ /dev/null
@@ -1,511 +0,0 @@
-from pathlib import Path
-from collections import namedtuple, OrderedDict
-from stack.exception import CommandError
-
-class FirmwareArgProcessor:
- """A mixin to process firmware command arguments."""
-
- def get_make_id(self, make):
- """Get the ID of the make with the provided name.
-
- This will raise a CommandError if the make with the provided name doesn't exist.
- """
- row = self.db.select("id FROM firmware_make WHERE name=%s", make)
- if not row:
- raise CommandError(cmd = self, msg = f"Firmware make {make} doesn't exist.")
-
- return row[0][0]
-
- def make_exists(self, make):
- """Returns whether the given make exists in the database."""
- return self.db.count("(id) FROM firmware_make WHERE name=%s", make)
-
- def ensure_make_exists(self, make):
- """Ensures that the provided make exists.
-
- If the make does not exist in the database, a CommandError is raised.
-
- If an empty string is provided, a CommandError is raised.
- """
- if not make:
- raise CommandError(cmd = self, msg = "A make is required.")
-
- if not self.make_exists(make = make):
- raise CommandError(cmd = self, msg = f"The make {make} does not exist.")
-
- def ensure_unique_makes(self, makes):
- """Ensures that none of the names in the list of makes provided already exist in the database.
-
- If an empty iterable is provided, a CommandError is raised.
-
- If any make name is blank, a CommandError is raised.
- """
- makes = tuple(makes)
- if not makes:
- raise CommandError(cmd = self, msg = "Makes are required.")
-
- # No empty strings allowed.
- if not all(makes):
- raise CommandError(cmd = self, msg = "A make cannot be an empty string.")
-
- # ensure the make names don't already exist
- existing_makes = [
- make
- for make, exists in (
- (make, self.make_exists(make)) for make in makes
- )
- if exists
- ]
- if existing_makes:
- raise CommandError(cmd = self, msg = f"The following firmware makes already exist: {existing_makes}.")
-
- def ensure_makes_exist(self, makes):
- """Ensures that all of the names in the list of makes provided already exist in the datbase.
-
- If an empty iterable is provided, a CommandError is raised.
-
- If any make name is blank, a CommandError is raised.
- """
- makes = tuple(makes)
- if not makes:
- raise CommandError(cmd = self, msg = "Makes are required.")
-
- # No empty strings allowed.
- if not all(makes):
- raise CommandError(cmd = self, msg = "A make cannot be an empty string.")
-
- # ensure the make names already exist
- missing_makes = [
- make
- for make, exists in (
- (make, self.make_exists(make)) for make in makes
- )
- if not exists
- ]
- if missing_makes:
- raise CommandError(cmd = self, msg = f"The following firmware makes don't exist: {missing_makes}.")
-
- def get_model_id(self, make, model):
- """Get the ID of the model with the provided name related to the provided make.
-
- This will raise a CommandError if the make + model combo doesn't exist.
- """
- row = self.db.select(
- """
- firmware_model.id
- FROM firmware_model
- INNER JOIN firmware_make
- ON firmware_model.make_id=firmware_make.id
- WHERE firmware_make.name=%s AND firmware_model.name=%s
- """,
- (make, model),
- )
- if not row:
- raise CommandError(cmd = self, msg = f"Firmware model {model} doesn't exist for make {make}.")
-
- return row[0][0]
-
- def model_exists(self, make, model):
- """Returns whether the given model exists for the given make."""
- return self.db.count(
- """
- (firmware_model.id)
- FROM firmware_model
- INNER JOIN firmware_make
- ON firmware_model.make_id=firmware_make.id
- WHERE firmware_make.name=%s AND firmware_model.name=%s
- """,
- (make, model),
- )
-
- def ensure_model_exists(self, make, model):
- """Ensures that a given model exists for a given make.
-
- If the make does not exist in the database, a CommandError is raised.
-
- If an empty string is provided for either make or model, a CommandError is raised.
- """
- self.ensure_make_exists(make = make)
-
- if not model:
- raise CommandError(cmd = self, msg = "A model is required.")
-
- if not self.model_exists(make = make, model = model):
- raise CommandError(cmd = self, msg = f"Make {make} does not exist for model {model}.")
-
- def ensure_unique_models(self, make, models):
- """Ensures that none of the given model names already exist in the database for the given make.
-
- If an empty iterable is provided, a CommandError is raised.
-
- If any model name is blank, a CommandError is raised.
- """
- # We don't require that the make exists because a set of models for a non-existent make would be
- # entirely new in that they are going to be added under a make that doesn't already exist.
- models = tuple(models)
- if not models:
- raise CommandError(cmd = self, msg = "Models are required.")
-
- # No empty strings allowed.
- if not all(models):
- raise CommandError(cmd = self, msg = "A model cannot be an empty string.")
-
- # ensure the model name doesn't already exist for the given make
- existing_makes_models = [
- (make, model)
- for make, model, exists in (
- (make, model, self.model_exists(make, model)) for model in models
- )
- if exists
- ]
- if existing_makes_models:
- raise CommandError(cmd = self, msg = f"The following make and model combinations already exist {existing_makes_models}.")
-
- def ensure_models_exist(self, make, models):
- """Ensures that all of the given model names already exist in the database for the given make.
-
- If the make does not exist, a CommandError is raised.
-
- If an empty iterable is provided, a CommandError is raised.
-
- If an empty string is provided for either make or one of the models, a CommandError is raised.
-
- If any model name is blank, a CommandError is raised.
- """
- self.ensure_make_exists(make = make)
-
- models = tuple(models)
- if not models:
- raise CommandError(cmd = self, msg = "Models are required.")
-
- # No empty strings allowed.
- if not all(models):
- raise CommandError(cmd = self, msg = "A model cannot be an empty string.")
-
- # ensure the models exist
- missing_models = [
- model
- for model, exists in (
- (model, self.model_exists(make, model)) for model in models
- )
- if not exists
- ]
- if missing_models:
- raise CommandError(
- cmd = self,
- msg = f"The following firmware models don't exist for make {make}: {missing_models}."
- )
-
- def firmware_exists(self, make, model, version):
- """Returns whether the given firmware version exists for the make + model combo."""
- return self.db.count(
- """
- (firmware.id)
- FROM firmware
- INNER JOIN firmware_model
- ON firmware.model_id=firmware_model.id
- INNER JOIN firmware_make
- ON firmware_model.make_id=firmware_make.id
- WHERE firmware_make.name=%s AND firmware_model.name=%s AND firmware.version=%s
- """,
- (make, model, version)
- )
-
- def ensure_firmware_exists(self, make, model, version):
- """Ensure that the provided firmware version for the make and model exists.
-
- If the make does not exist, a CommandError is raised.
-
- If the model does not exist, a CommandError is raised.
-
- If an empty string is provided for make, model, or version, a CommandError is raised.
- """
- self.ensure_model_exists(make = make, model = model)
-
- if not version:
- raise CommandError(cmd = self, msg = "A version is required.")
-
- if not self.firmware_exists(make = make, model = model, version = version):
- raise CommandError(
- cmd = self,
- msg = f"The firmware version {version} does not exist for make {make} and model {model}.",
- )
-
- def ensure_firmwares_exist(self, make, model, versions):
- """Ensures that all the firmware versions provided exist for the given make and model.
-
- If the make does not exist, a CommandError is raised.
-
- If the model does not exist, a CommandError is raised.
-
- If an empty iterable is provided, a CommandError is raised.
-
- If an empty string is provided for make, model, or version, a CommandError is raised.
- """
- self.ensure_model_exists(make = make, model = model)
-
- versions = tuple(versions)
- if not versions:
- raise CommandError(cmd = self, msg = "Versions are required.")
-
- # No empty strings allowed.
- if not all(versions):
- raise CommandError(cmd = self, msg = "A version cannot be an empty string.")
-
- # ensure the versions exist in the DB
- missing_versions = [
- version
- for version, exists in (
- (version, self.firmware_exists(make, model, version)) for version in versions
- )
- if not exists
- ]
- if missing_versions:
- raise CommandError(
- cmd = self,
- msg = f"The following firmware versions don't exist for make {make} and model {model}: {missing_versions}."
- )
-
- def get_firmware_id(self, make, model, version):
- """Get the ID of the firmware version with the provided version for the provided make and model.
-
- This will raise a CommandError if the firmware version matching the provided information doesn't exist.
- """
- row = self.db.select(
- """
- firmware.id
- FROM firmware
- INNER JOIN firmware_model
- ON firmware.model_id=firmware_model.id
- INNER JOIN firmware_make
- ON firmware_model.make_id=firmware_make.id
- WHERE firmware_make.name=%s AND firmware_model.name=%s AND firmware.version=%s
- """,
- (make, model, version)
- )
- if not row:
- raise CommandError(cmd = self, msg = f"Firmware version {version} doesn't exist for make {make} and model {model}.")
-
- return row[0][0]
-
- def get_common_frontend_ip(self, hostname):
- """Attempts to get an IP of one of the front end interfaces that shares a network with the provided host.
-
- If the frontend and the host have no common networks, a CommandError is raised.
- If none of the interfaces on the common network have IP addresses, a CommandError is raised.
- """
- host_interface_frontend = self.call(command = "list.host.interface", args = ["a:frontend"])
- # try to get the set of all common networks between the front end and the target host
- host_networks = set(host["network"] for host in self.call(command = "list.host.interface", args = [hostname]))
- frontend_networks = set(frontend_interface["network"] for frontend_interface in host_interface_frontend)
- common_networks = list(host_networks & frontend_networks)
-
- if not common_networks:
- raise CommandError(
- cmd = self,
- msg = (
- f"{hostname} does not share a network with the frontend, and thus cannot fetch firmware"
- f" from it. Please configure {hostname} to share a common network with the frontend."
- )
- )
-
- # try to get the IP addresses of frontend interfaces on the common networks
- ip_addr = [
- frontend_interface["ip"] for frontend_interface in host_interface_frontend
- if frontend_interface["network"] in common_networks and frontend_interface["ip"]
- ]
-
- if not ip_addr:
- raise CommandError(
- cmd = self,
- msg = (
- f"None of the network interfaces on the frontend attached to the following common networks"
- f"have an IP address. Please configure at least one interface to have an IP address on one"
- f"of the following networks: {common_networks}"
- )
- )
- # pick the first one and use it
- return ip_addr[0]
-
- def get_firmware_url(self, hostname, firmware_file):
- """Attempts to get a url to allow a backend to download the provided firmware file from the front end.
-
- If the frontend and the backend have no common networks, a CommandError is raised.
- If none of the interfaces on the common network have IP addresses, a CommandError is raised.
- If the file doesn't exist on disk, a CommandError is raised.
- """
- try:
- firmware_file = Path(firmware_file).resolve(strict = True)
- except FileNotFoundError as exception:
- raise CommandError(
- cmd = self,
- msg = f"Cannot resolve frontend URL for firmware file that does not exist: {exception}",
- )
-
- ip_addr = self.get_common_frontend_ip(hostname = hostname)
- # remove the /export/stack prefix from the file path, as /install points to /export/stack
- firmware_file = Path().joinpath(*(part for part in firmware_file.parts if part not in ("/", "export", "stack")))
-
- return f"http://{ip_addr}/install/{firmware_file}"
-
- def imp_exists(self, imp):
- """Returns whether the given implementation name exists in the database."""
- return self.db.count("(id) FROM firmware_imp WHERE name=%s", imp)
-
- def ensure_imp_exists(self, imp):
- """Ensures that the provided implementation exists in the database.
-
- If the provided name is an empty string, a CommandError is raised.
-
- If the imp name does not exist in the database, a CommandError is raised.
- """
- if not imp:
- raise CommandError(cmd = self, msg = "Imp is required.")
-
- if not self.imp_exists(imp = imp):
- raise CommandError(cmd = self, msg = f"Imp {imp} does not exist in the database.")
-
- def ensure_imps_exist(self, imps):
- """Ensures that all of the given imp names already exist in the database.
-
- If an empty iterable is provided, a CommandError is raised.
-
- If an empty string is provided for any imp name, a CommandError is raised.
- """
- imps = tuple(imps)
- if not imps:
- raise CommandError(cmd = self, msg = "Imps are required.")
-
- # No empty strings allowed.
- if not all(imps):
- raise CommandError(cmd = self, msg = "A imp cannot be an empty string.")
-
- # ensure the imps exist
- missing_imps = [
- imp
- for imp, exists in (
- (imp, self.imp_exists(imp)) for imp in imps
- )
- if not exists
- ]
- if missing_imps:
- raise CommandError(
- cmd = self,
- msg = f"The following firmware implementations don't exist in the database: {missing_imps}."
- )
-
- def get_imp_id(self, imp):
- """Get the ID of the implementation with the provided name.
-
- This will raise a CommandError if the implementation with the provided name doesn't exist.
- """
- row = self.db.select('id FROM firmware_imp WHERE name=%s', imp)
- if not row:
- raise CommandError(cmd = self, msg = f"Firmware implementation {imp} doesn't exist in the database.")
-
- return row[0][0]
-
- def version_regex_exists(self, name):
- """Returns whether the given version_regex name exists in the database."""
- return self.db.count("(id) FROM firmware_version_regex WHERE name=%s", name)
-
- def ensure_version_regex_exists(self, name):
- """Ensures that a version_regex with the provided name exists in the database.
-
- If the name is an empty string, a CommandError is raised.
-
- If the version_regex does not exist, a CommandError is raised.
- """
- if not name:
- raise CommandError(cmd = self, msg = "A version regex name is required.")
-
- if not self.version_regex_exists(name = name):
- raise CommandError(cmd = self, msg = f"A version regex named {name} does not exist.")
-
- def ensure_version_regexes_exist(self, names):
- """Ensures that all of the given version_regex names already exist in the database.
-
- If an empty iterable is provided, a CommandError is raised.
-
- If an empty string is provided for any version_regex name, a CommandError is raised.
- """
- names = tuple(names)
- if not names:
- raise CommandError(cmd = self, msg = "Version regex names are required.")
-
- # No empty strings allowed.
- if not all(names):
- raise CommandError(cmd = self, msg = "A version regex name cannot be an empty string.")
-
- # ensure the version_regexes exist
- missing_version_regexes = [
- name
- for name, exists in (
- (name, self.version_regex_exists(name)) for name in names
- )
- if not exists
- ]
- if missing_version_regexes:
- raise CommandError(
- cmd = self,
- msg = f"The following firmware version regexes don't exist in the database: {missing_version_regexes}."
- )
-
- def get_version_regex_id(self, name):
- """Get the ID of the version_regex with the provided name.
-
- This will raise a CommandError if the version_regex with the provided name doesn't exist.
- """
- row = self.db.select("id FROM firmware_version_regex WHERE name=%s", name)
- if not row:
- raise CommandError(cmd = self, msg = f"Firmware version_regex {name} doesn't exist in the database.")
-
- return row[0][0]
-
- def try_get_version_regex(self, make, model):
- """Attempts to return the most relevant version regex for the make and model combination provided.
-
- If found, a namedtuple is returned with the members 'regex', 'name', and 'description'. The
- regex member is the actual regular expression to use for validation while name and description
- are the user friendly name and an optional description of the regex, respectively.
-
- A version regex set on the model will be preferred to one set on the make.
-
- If neither the make nor the model have a version regex set, None will be returned.
- """
- regex = None
- row = self.db.select(
- """
- make_regex.regex, make_regex.name, make_regex.description, model_regex.regex, model_regex.name, model_regex.description
- FROM firmware_make
- INNER JOIN firmware_model
- ON firmware_model.make_id = firmware_make.id
- LEFT JOIN firmware_version_regex as make_regex
- ON firmware_make.version_regex_id = make_regex.id
- LEFT JOIN firmware_version_regex as model_regex
- ON firmware_model.version_regex_id = model_regex.id
- WHERE firmware_make.name = %s AND firmware_model.name = %s
- """,
- (make, model)
- )
- if row:
- make_regex, make_regex_name, make_regex_description = row[0][0:3]
- model_regex, model_regex_name, model_regex_description = row[0][3:6]
- RegexInfo = namedtuple("RegexInfo", ("regex", "name", "description"))
- # prefer the model regex
- if model_regex:
- regex = RegexInfo(
- regex = model_regex,
- name = model_regex_name,
- description = model_regex_description
- )
- # else use the make regex
- elif make_regex:
- regex = RegexInfo(
- regex = make_regex,
- name = make_regex_name,
- description = make_regex_description
- )
- # Otherwise we return nothing.
-
- return regex
diff --git a/common/src/stack/command/stack/commands/add/firmware/__init__.py b/common/src/stack/command/stack/commands/add/firmware/__init__.py
deleted file mode 100644
index 2357b8805..000000000
--- a/common/src/stack/command/stack/commands/add/firmware/__init__.py
+++ /dev/null
@@ -1,82 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-from stack.argument_processors.firmware import FirmwareArgProcessor
-from stack.argument_processors.host import HostArgProcessor
-import stack.commands
-
-class command(stack.commands.add.command, FirmwareArgProcessor):
- pass
-
-class Command(command, HostArgProcessor):
- """
- Adds a firmware image to stacki.
-
-
- The firmware version being added. This must be unique for make + model.
-
-
-
- The URL or local file path pointing to where to retrieve the firmware image.
-
-
-
- The firmware make to add this firmware image to. If the make does not already exist, it will be created.
-
-
-
- The firmware model to add this firmware image to. If the model does not already exist, it will be created.
-
-
-
- The optional firmware implementation to run for the model this firmware is for. If the make + model provided does not already
- exist, this parameter is required. If the implementation does not already exist, it will be created.
-
-
-
- The optional host names to associate with this firmware. This can either be a single host name, a comma separate list of hosts,
- or one of the common host name specifiers like 'a:switch'.
-
-
-
- The optional hash to use when verifying the integrity of the fetched image.
-
-
-
- The optional hash algorithm to use to verify the integrity of the fetched image. If not specified this defaults to MD5.
-
-
-
- Fetches the firmware file from the source (a local file on the front end in /export/some/path), associates it with the
- Mellanox make and 7800 model, and sets the version to 3.6.5002, and adds it to be tracked in the stacki database.
-
-
-
- This performs the same steps as the previous example except the image is fetched via HTTP.
-
-
-
- This performs the same steps as the previous example except the firmware gets associated with the hosts named switch-0-1 and switch-0-2.
-
-
-
- This performs the same steps as the previous example except the firmware gets associated with all hosts that are of the appliance switch type.
-
-
-
- Assuming that the make, model, and implementation do no already exist, this adds a new firmware version 1.2.3.4
- associated with new_make and new_model that will use new_imp to read and write to mapped hosts.
-
- """
-
- def run(self, params, args):
- self.runPlugins(args = (params, args))
diff --git a/common/src/stack/command/stack/commands/add/firmware/imp/__init__.py b/common/src/stack/command/stack/commands/add/firmware/imp/__init__.py
deleted file mode 100644
index 207b8f9a0..000000000
--- a/common/src/stack/command/stack/commands/add/firmware/imp/__init__.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2019 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-
-class Command(stack.commands.add.firmware.command):
- """
- Adds a firmware implementation to the stacki database.
-
-
- The name of the implementation file to run, without the "imp_" prepended or the extension appended.
-
-
-
- Zero or more models that this implementation applies to. Multiple models should be specified as a comma separated list.
- If this is specified, make is also required.
-
-
-
- The optional make of the models this implementation applies to. If this is specified, models are also required.
-
-
-
- Adds the firmware implementation named mellanox_6xxx_7xxx and sets it to be the one run for make mellanox and models m7800 and m6036.
-
-
-
- This simply adds the implementation named mellanox_6xxx_7xxx to be tracked by stacki.
-
- """
-
- def run(self, params, args):
- self.runPlugins(args = (params, args))
diff --git a/common/src/stack/command/stack/commands/add/firmware/imp/plugin_basic.py b/common/src/stack/command/stack/commands/add/firmware/imp/plugin_basic.py
deleted file mode 100644
index 7847ea990..000000000
--- a/common/src/stack/command/stack/commands/add/firmware/imp/plugin_basic.py
+++ /dev/null
@@ -1,97 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-from contextlib import ExitStack
-from pathlib import Path
-import inspect
-from stack.util import lowered
-import stack.commands
-import stack.commands.list.host.firmware
-import stack.commands.sync.host.firmware
-from stack.exception import CommandError, ArgRequired, ArgUnique, ArgError, ParamRequired, ParamError
-
-class Plugin(stack.commands.Plugin):
- """Attempts to add an implementation to the database and optionally associate it with models."""
-
- def provides(self):
- return "basic"
-
- def validate_args(self, args):
- """Validate that there is only one implementation name passed."""
- # Require a implementation name
- if not args:
- raise ArgRequired(cmd = self.owner, arg = "name")
- # Should only be one
- if len(args) != 1:
- raise ArgUnique(cmd = self.owner, arg = "name")
-
- def validate_imp(self, imp):
- """Validate that the implementation doesn't already exist in the database, and that the implementation files are on disk."""
- # Should not already exist
- if self.owner.imp_exists(imp = imp):
- raise ArgError(
- cmd = self.owner,
- arg = "name",
- msg = f"An implementation named {imp} already exists in the database.",
- )
-
- # Should exist on disk
- list_firmware_imp = Path(
- inspect.getsourcefile(stack.commands.list.host.firmware),
- ).parent.resolve() / f"imp_{imp}.py"
- sync_firmware_imp = Path(
- inspect.getsourcefile(stack.commands.sync.host.firmware),
- ).parent.resolve() / f"imp_{imp}.py"
- if not list_firmware_imp.exists() or not sync_firmware_imp.exists():
- raise ArgError(
- cmd = self.owner,
- arg = "name",
- msg = (
- f"Could not find an implementation named imp_{imp}.py. Please ensure an"
- " implementation file is placed into each of the following locations:\n"
- f"{list_firmware_imp}\n{sync_firmware_imp}"
- ),
- )
-
- def run(self, args):
- params, args = args
- self.validate_args(args = args)
- imp = args[0].lower()
- self.validate_imp(imp = imp)
-
- make, models, = lowered(
- self.owner.fillParams(
- names = [("make", ""), ("models", "")],
- params = params,
- )
- )
- # If either make or model are set, both arguments are required and must be valid.
- if make or models:
- # process a comma separated list of models
- models = [model.strip() for model in models.split(",") if model.strip()]
- self.owner.ensure_models_exist(make = make, models = models)
-
- with ExitStack() as cleanup:
- # Add the implementation
- self.owner.db.execute("INSERT INTO firmware_imp (name) VALUES (%s)", (imp,))
- cleanup.callback(self.owner.call, command = "remove.firmware.imp", args = [imp])
-
- # If the make and model are specified associate the imp with the make + model
- if make and models:
- self.owner.call(
- command = "set.firmware.model.imp",
- args = [*models, f"make={make}", f"imp={imp}"],
- )
- # else no association, just put it in the database.
-
- # everything worked, dismiss cleanup
- cleanup.pop_all()
diff --git a/common/src/stack/command/stack/commands/add/firmware/make/__init__.py b/common/src/stack/command/stack/commands/add/firmware/make/__init__.py
deleted file mode 100644
index 814068e9c..000000000
--- a/common/src/stack/command/stack/commands/add/firmware/make/__init__.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2019 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-
-class Command(stack.commands.add.firmware.command):
- """
- Adds firmware makes to the stacki database.
-
-
- One or more make names to add. Make names are required to be unique, and any duplicates will be ignored.
-
-
-
- Adds two makes with the names 'mellanox' and 'dell' to the set of available firmware makes.
-
- """
-
- def run(self, params, args):
- self.runPlugins(args = args)
diff --git a/common/src/stack/command/stack/commands/add/firmware/make/plugin_basic.py b/common/src/stack/command/stack/commands/add/firmware/make/plugin_basic.py
deleted file mode 100644
index 05683e24b..000000000
--- a/common/src/stack/command/stack/commands/add/firmware/make/plugin_basic.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2019 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-from stack.util import unique_everseen, lowered
-from stack.exception import ArgRequired, ArgError, CommandError
-
-class Plugin(stack.commands.Plugin):
- """Attempts to add all provided make names to the database."""
-
- def provides(self):
- return "basic"
-
- def run(self, args):
- # get rid of any duplicate names
- makes = tuple(unique_everseen(lowered(args)))
- # ensure the make names don't already exist
- self.owner.ensure_unique_makes(makes = makes)
-
- self.owner.db.execute(
- """
- INSERT INTO firmware_make (
- name
- )
- VALUES (%s)
- """,
- [(make,) for make in makes],
- many = True,
- )
diff --git a/common/src/stack/command/stack/commands/add/firmware/model/__init__.py b/common/src/stack/command/stack/commands/add/firmware/model/__init__.py
deleted file mode 100644
index 63626919d..000000000
--- a/common/src/stack/command/stack/commands/add/firmware/model/__init__.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-
-class Command(stack.commands.add.firmware.command):
- """
- Adds firmware models to the stacki database.
-
-
- One or more model names to add. Model names are required to be unique, and any duplicates will be ignored.
-
-
-
- The make of the models being added. If this does not correspond to an already existing make, one will be added.
-
-
-
- The implementation name to run for the models being added. This should be the name of the implementation file minus the 'imp_' prefix and file extension.
- If this does not correspond to an already existing implementation, one will be added.
-
-
-
- Adds two models with the names 'awesome_9001' and 'mediocre_5200' to the set of available firmware models under the 'boss hardware corp' make.
- This also sets the implementation to run for those models as the one named 'imp_boss_hardware_corp.py'.
-
- """
-
- def run(self, params, args):
- self.runPlugins(args = (params, args))
diff --git a/common/src/stack/command/stack/commands/add/firmware/model/plugin_basic.py b/common/src/stack/command/stack/commands/add/firmware/model/plugin_basic.py
deleted file mode 100644
index 4abdccab9..000000000
--- a/common/src/stack/command/stack/commands/add/firmware/model/plugin_basic.py
+++ /dev/null
@@ -1,85 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-from contextlib import ExitStack
-import stack.commands
-from stack.util import unique_everseen, lowered
-from stack.exception import ArgRequired, CommandError, ParamRequired
-
-class Plugin(stack.commands.Plugin):
- """Attempts to add all provided model names to the database associated with the given make and implementation."""
-
- def provides(self):
- return "basic"
-
- def create_missing_make(self, make, cleanup):
- """Create the make if it does not already exist."""
- if not self.owner.make_exists(make = make):
- self.owner.call(command = "add.firmware.make", args = [make])
- cleanup.callback(self.owner.call, command = "remove.firmware.make", args = [make])
-
- def create_missing_imp(self, imp, cleanup):
- """Create the implementation if it does not already exist."""
- if not self.owner.imp_exists(imp = imp):
- self.owner.call(command = "add.firmware.imp", args = [imp])
- cleanup.callback(self.owner.call, command = "remove.firmware.imp", args = [imp])
-
- def run(self, args):
- params, args = args
- make, imp, = lowered(
- self.owner.fillParams(
- names = [
- ("make", ""),
- ("imp", ""),
- ],
- params = params,
- ),
- )
-
- # require a make
- if not make:
- raise ParamRequired(cmd = self.owner, param = "make")
- # require an implementation
- if not imp:
- raise ParamRequired(cmd = self.owner, param = "imp")
-
- # get rid of any duplicate names
- models = tuple(unique_everseen(lowered(args)))
- # ensure the model name doesn't already exist for the given make
- self.owner.ensure_unique_models(make = make, models = models)
-
- with ExitStack() as cleanup:
- # create the make if it doesn't already exist
- self.create_missing_make(make = make, cleanup = cleanup)
- # create the implementation if it doesn't already exist
- self.create_missing_imp(imp = imp, cleanup = cleanup)
-
- # get the ID of the make to associate with
- make_id = self.owner.get_make_id(make = make)
- # get the ID of the imp to associate with
- imp_id = self.owner.get_imp_id(imp = imp)
-
- self.owner.db.execute(
- """
- INSERT INTO firmware_model (
- name,
- make_id,
- imp_id
- )
- VALUES (%s, %s, %s)
- """,
- [(model, make_id, imp_id) for model in models],
- many = True,
- )
-
- # everything was successful, dismiss cleanup.
- cleanup.pop_all()
diff --git a/common/src/stack/command/stack/commands/add/firmware/plugin_basic.py b/common/src/stack/command/stack/commands/add/firmware/plugin_basic.py
deleted file mode 100644
index 742b86355..000000000
--- a/common/src/stack/command/stack/commands/add/firmware/plugin_basic.py
+++ /dev/null
@@ -1,290 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-from stack.exception import ArgRequired, ArgUnique, ArgError, ParamRequired, ParamError
-import stack.firmware
-from stack.util import unique_everseen, lowered
-from pathlib import Path
-from contextlib import ExitStack
-import re
-
-class Plugin(stack.commands.Plugin):
- """Attempts to add firmware to be tracked by stacki."""
-
- def provides(self):
- return "basic"
-
- def validate_args(self, args):
- """Validate that a version number is provided and that there is only one."""
- # Require a version name
- if not args:
- raise ArgRequired(cmd = self.owner, arg = "version")
- # should only be one version name
- if len(args) != 1:
- raise ArgUnique(cmd = self.owner, arg = "version")
-
- def validate_required_params(self, source, make, model):
- """Validate that the required parameters are provided."""
- # require a source
- if not source:
- raise ParamRequired(cmd = self.owner, param = "source")
- # require both make and model
- if not make:
- raise ParamRequired(cmd = self.owner, param = "make")
- if not model:
- raise ParamRequired(cmd = self.owner, param = "model")
-
- def validate_imp(self, make, model, imp):
- """Validate whether an imp is required due to the model not already existing,
- and validate that if the model exists that any requested implementation matches.
- """
- # require an implementation if the model does not exist.
- if not imp and not self.owner.model_exists(make = make, model = model):
- # Get the list of valid makes + models and add them to the error message in an attempt to be helpful
- makes_and_models = "\n".join(
- f"{make_model['make']} + {make_model['model']}" for make_model in
- self.owner.call(command = "list.firmware.model")
- )
- raise ParamError(
- cmd = self.owner,
- param = "imp",
- msg = (
- f"is required because make and model combination {make} + {model} doesn't exist."
- f" Did you mean to use one of the below makes and/or models?\n{makes_and_models}"
- ),
- )
- # Raise an error if the implementation was provided for an already existing model, and it does not match.
- if imp and self.owner.model_exists(make = make, model = model):
- existing_imp = self.owner.call(
- command = "list.firmware.model",
- args = [model, f"make={make}"]
- )[0]["implementation"]
-
- if imp != existing_imp:
- raise ParamError(
- cmd = self.owner,
- param = "imp",
- msg = (
- f"The provided imp {imp} doesn't match the existing imp {existing_imp} for the already existing"
- f" make + model {make} + {model}. Did you not mean to specify an imp? The parameter is not required"
- " for existing models.\nIf you want to change what implementation is used, run"
- " 'stack set firmware model imp'."
- ),
- )
-
- def validate_hash_alg_supported(self, hash_alg):
- """Validates that the requested hash algorithm is supported."""
- try:
- stack.firmware.ensure_hash_alg_supported(hash_alg = hash_alg)
- except stack.firmware.FirmwareError as exception:
- raise ParamError(
- cmd = self.owner,
- param = "hash_alg",
- msg = f"{exception}",
- ) from exception
-
- def validate_version(self, version, make, model):
- """Attempts to validate the version number against a version_regex if one is set for the
- make or model provided as well as ensuring the firmware version doesn't already exist.
- """
- # Attempt to get the version regex entry.
- regex = self.owner.try_get_version_regex(make = make, model = model)
- if regex and not re.search(pattern = regex.regex, string = version, flags = re.IGNORECASE):
- raise ArgError(
- cmd = self.owner,
- arg = "version",
- msg = (
- f"The format of the version number {version} does not validate based on the regex {regex.regex}"
- f" named {regex.name}{f' with description {regex.description}' if regex.description else ''}."
- ),
- )
-
- if self.owner.firmware_exists(make = make, model = model, version = version):
- raise ArgError(
- cmd = self.owner,
- arg = "version",
- msg = f"The firmware version {version} for make {make} and model {model} already exists.",
- )
-
- def validate_inputs(self, source, make, model, version, imp, hash_alg):
- """Validate the input params and version arg."""
- # Make sure all require parameters are present.
- self.validate_required_params(source = source, make = make, model = model)
- # Check if an implementation is required, and whether one was provided.
- self.validate_imp(make = make, model = model, imp = imp)
- # Validate that the hash algorithm is supported
- self.validate_hash_alg_supported(hash_alg = hash_alg)
- # validate the version matches the version_regex if one is set and that it doesn't already exist.
- self.validate_version(version = version, make = make, model = model)
-
- def create_missing_imp(self, imp, cleanup):
- """Adds an implementation to the database if provided and it doesn't already exist."""
- # create the implementation if provided one and it doesn't already exist
- if imp and not self.owner.imp_exists(imp = imp):
- self.owner.call(command = "add.firmware.imp", args = [imp])
- cleanup.callback(self.owner.call, command = "remove.firmware.imp", args = [imp])
-
- def create_missing_make(self, make, cleanup):
- """Adds a make to the database if it doesn't already exist."""
- # create the make if it doesn't already exist
- if not self.owner.make_exists(make = make):
- self.owner.call(command = "add.firmware.make", args = [make])
- cleanup.callback(self.owner.call, command = "remove.firmware.make", args = [make])
-
- def create_missing_model(self, make, model, imp, cleanup):
- """Adds a model to the database if it doesn't already exist."""
- # create the model if it doesn't already exist
- if not self.owner.model_exists(make = make, model = model):
- self.owner.call(command = "add.firmware.model", args = [model, f"make={make}", f"imp={imp}"])
- cleanup.callback(self.owner.call, command = "remove.firmware.model", args = [model, f"make={make}"])
-
- def create_missing_related_entries(self, make, model, imp, cleanup):
- """Create any missing related entries as necessary."""
- self.create_missing_imp(imp = imp, cleanup = cleanup)
- self.create_missing_make(make = make, cleanup = cleanup)
- self.create_missing_model(make = make, model = model, imp = imp, cleanup = cleanup)
-
- def file_cleanup(self, file_path):
- """Remove the file if it exists.
-
- Needed because "stack remove firmware" also removes the file
- and that might have been run as part of the exit stack unwinding.
- """
- if file_path.exists():
- file_path.unlink()
-
- def fetch_firmware(self, source, make, model, cleanup):
- """Try to fetch the firmware from the source and return the path to the file."""
- try:
- file_path = stack.firmware.fetch_firmware(
- source = source,
- make = make,
- model = model,
- )
-
- cleanup.callback(self.file_cleanup, file_path = file_path)
- return file_path
-
- except stack.firmware.FirmwareError as exception:
- raise ParamError(
- cmd = self.owner,
- param = "source",
- msg = f"{exception}",
- ) from exception
-
- def calculate_hash(self, file_path, hash_alg, hash_value):
- """Calculate the file hash and verify it against the provided hash, if present."""
- try:
- return stack.firmware.calculate_hash(file_path = file_path, hash_alg = hash_alg, hash_value = hash_value)
- except stack.firmware.FirmwareError as exception:
- raise ParamError(
- cmd = self.owner,
- param = "hash",
- msg = f"{exception}",
- ) from exception
-
- def add_firmware(self, source, make, model, version, imp, hash_value, hash_alg, cleanup):
- """Adds a firmware file to be managed by stacki.
-
- This adds the file locally on disk as well as inserts metadata into the database.
- """
- # fetch the firmware from the source and copy the firmware into a stacki managed file
- file_path = self.fetch_firmware(
- source = source,
- make = make,
- model = model,
- cleanup = cleanup,
- )
- # calculate the file hash and compare it with the user provided value if present.
- file_hash = self.calculate_hash(
- file_path = file_path,
- hash_alg = hash_alg,
- hash_value = hash_value,
- )
- # add imp, make, and model database entries if needed.
- self.create_missing_related_entries(make = make, model = model, imp = imp, cleanup = cleanup)
-
- # get the ID of the model to associate with
- model_id = self.owner.get_model_id(make, model)
- # insert into DB associated with make + model
- self.owner.db.execute(
- """
- INSERT INTO firmware (
- model_id,
- source,
- version,
- hash_alg,
- hash,
- file
- )
- VALUES (%s, %s, %s, %s, %s, %s)
- """,
- (model_id, source, version, hash_alg, file_hash, str(file_path)),
- )
- cleanup.callback(self.owner.call, command = "remove.firmware", args = [version, f"make={make}", f"model={model}"])
-
- def run(self, args):
- params, args = args
- self.validate_args(args = args)
- version = args[0].lower()
-
- *params_to_lower, hash_value, source = self.owner.fillParams(
- names = [
- ("make", ""),
- ("model", ""),
- ("imp", ""),
- ("hosts", ""),
- ("hash_alg", "md5"),
- ("hash", ""),
- ("source", ""),
- ],
- params = params,
- )
- # Lowercase all params that can be.
- make, model, imp, hosts, hash_alg = lowered(params_to_lower)
- self.validate_inputs(
- source = source,
- make = make,
- model = model,
- version = version,
- imp = imp,
- hash_alg = hash_alg,
- )
-
- # Convert hosts to a list if set
- if hosts:
- # Make sure the host names are unique.
- hosts = tuple(unique_everseen(host.strip() for host in hosts.split(",") if host.strip()))
- # Validate the hosts exist.
- hosts = self.owner.getHosts(args = hosts)
-
- # we use ExitStack to hold our cleanup operations and roll back should something fail.
- with ExitStack() as cleanup:
- # Get the firmware file on disk in the right location and add the metadata to the database.
- self.add_firmware(
- source = source,
- make = make,
- model = model,
- version = version,
- imp = imp,
- hash_value = hash_value,
- hash_alg = hash_alg,
- cleanup = cleanup,
- )
-
- # if hosts are provided, set the firmware relation
- if hosts:
- self.owner.call(command = "add.host.firmware.mapping", args = [*hosts, f"version={version}", f"make={make}", f"model={model}"])
-
- # everything went down without a problem, dismiss the cleanup
- cleanup.pop_all()
diff --git a/common/src/stack/command/stack/commands/add/firmware/version_regex/__init__.py b/common/src/stack/command/stack/commands/add/firmware/version_regex/__init__.py
deleted file mode 100644
index 765dfc940..000000000
--- a/common/src/stack/command/stack/commands/add/firmware/version_regex/__init__.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-
-class Command(stack.commands.add.firmware.command):
- """
- Adds a firmware version regex to the stacki database for use in parsing and validating firmware version numbers.
-
-
- A valid Python regex to use to use against the version number returned from the target hardware.
-
-
-
- The human readable name for this regex.
-
-
-
- An optional description for this regex. This is useful to describe what the regex does for future travellers.
-
-
-
- The make that this regex should apply to.
-
-
-
- The optional models for the given make that this regex applies to. Multiple models should be specified as a comma separated list.
-
-
-
- Adds a regex with the name mellanox_version and the description provided that looks for three number groups separated by dots to the Stacki database.
- It also associates the regex with the m7800 and m6036 models for the mellanox make.
-
- """
-
- def run(self, params, args):
- self.runPlugins(args = (params, args))
diff --git a/common/src/stack/command/stack/commands/add/firmware/version_regex/plugin_basic.py b/common/src/stack/command/stack/commands/add/firmware/version_regex/plugin_basic.py
deleted file mode 100644
index abf17375c..000000000
--- a/common/src/stack/command/stack/commands/add/firmware/version_regex/plugin_basic.py
+++ /dev/null
@@ -1,130 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import re
-from contextlib import ExitStack
-from stack.util import unique_everseen
-import stack.commands
-from stack.exception import ArgError, ArgRequired, ArgUnique, ParamRequired, ParamError
-
-class Plugin(stack.commands.Plugin):
- """Attempts to add a version_regex to the database and associate it with the specified makes and/or models."""
-
- def provides(self):
- return "basic"
-
- def validate_args(self, args):
- """Validate that the arguments to this plugin are as expected."""
- # Require a version regex
- if not args:
- raise ArgRequired(cmd = self.owner, arg = "regex")
-
- # Should only be one
- if len(args) != 1:
- raise ArgUnique(cmd = self.owner, arg = "regex")
-
- def validate_regex(self, regex):
- """Make sure the provided regex is a valid Python regex."""
- # Don't allow empty string, which will compile into a regex fine.
- if not regex:
- raise ArgError(
- cmd = self.owner,
- arg = "regex",
- msg = f"Regex cannot be an empty string."
- )
-
- # Require the version_regex to be a valid regex
- try:
- re.compile(regex)
- except re.error as exception:
- raise ArgError(
- cmd = self.owner,
- arg = "regex",
- msg = f"Invalid regex supplied: {exception}."
- )
-
- def validate_name(self, name):
- """Validate the name is provided and is unique."""
- # A name is required
- if not name:
- raise ParamRequired(cmd = self.owner, param = "name")
-
- # The name must not already exist
- if self.owner.version_regex_exists(name = name):
- raise ParamError(
- cmd = self.owner,
- param = "name",
- msg = f"A version_regex with the name {name} already exists in the database."
- )
-
- def run(self, args):
- params, args = args
- self.validate_args(args = args)
- version_regex = args[0]
- self.validate_regex(regex = version_regex)
-
- name, description, make, models = self.owner.fillParams(
- names = [
- ("name", ""),
- ("description", ""),
- ("make", ""),
- ("models", ""),
- ],
- params = params,
- )
- name = name.lower()
- self.validate_name(name = name)
- make = make.lower()
- models = models.lower()
- # Process models if specified
- if models:
- models = tuple(
- unique_everseen(
- (model.strip() for model in models.split(',') if model.strip())
- )
- )
- # The make and models must exist
- self.owner.ensure_models_exist(make = make, models = models)
- else:
- # Only need to check that the make exists.
- self.owner.ensure_make_exists(make = make)
-
- with ExitStack() as cleanup:
- # add the regex
- self.owner.db.execute(
- """
- INSERT INTO firmware_version_regex (
- regex,
- name,
- description
- )
- VALUES (%s, %s, %s)
- """,
- (version_regex, name, description),
- )
- cleanup.callback(self.owner.call, command = "remove.firmware.version_regex", args = [name])
-
- # If models are specified, associate it with the relevant models for the given make
- if models:
- self.owner.call(
- command = "set.firmware.model.version_regex",
- args = [*models, f"make={make}", f"version_regex={name}"],
- )
- # else associate it with just the make
- else:
- self.owner.call(
- command = "set.firmware.make.version_regex",
- args = [make, f"version_regex={name}"],
- )
-
- # everything worked, dismiss cleanup
- cleanup.pop_all()
diff --git a/common/src/stack/command/stack/commands/add/host/firmware/__init__.py b/common/src/stack/command/stack/commands/add/host/firmware/__init__.py
deleted file mode 100644
index ab26ebde2..000000000
--- a/common/src/stack/command/stack/commands/add/host/firmware/__init__.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2019 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-from stack.argument_processors.firmware import FirmwareArgProcessor
-import stack.commands
-
-class command(stack.commands.add.host.command, FirmwareArgProcessor):
- pass
diff --git a/common/src/stack/command/stack/commands/add/host/firmware/mapping/__init__.py b/common/src/stack/command/stack/commands/add/host/firmware/mapping/__init__.py
deleted file mode 100644
index af4d99e89..000000000
--- a/common/src/stack/command/stack/commands/add/host/firmware/mapping/__init__.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-
-class Command(stack.commands.add.host.firmware.command):
- """
- Maps firmware files to hosts so 'stack sync host firmware' can find them.
-
-
- One or more hostnames to associate with a firmware version.
-
-
-
- The firmware version to map to the provided hosts.
-
-
-
- The make of the firmware version.
-
-
-
- The model for the given make of the firmware version.
-
-
-
- Maps firmware version 3.6.2010 for make mellanox and model m7800 to the host with the name switch-11-3.
-
-
-
- This is the same as the previous example except the firmware will be mapped to both switch-11-3 and switch-10-15.
-
-
-
- This is the same as the previous example except the firmware will be mapped to all hosts that are switch appliances.
-
- """
-
- def run(self, params, args):
- self.runPlugins(args = (params, args))
diff --git a/common/src/stack/command/stack/commands/add/host/firmware/mapping/plugin_basic.py b/common/src/stack/command/stack/commands/add/host/firmware/mapping/plugin_basic.py
deleted file mode 100644
index 44b31913a..000000000
--- a/common/src/stack/command/stack/commands/add/host/firmware/mapping/plugin_basic.py
+++ /dev/null
@@ -1,88 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-from stack.util import unique_everseen, lowered
-from stack.exception import CommandError
-
-class Plugin(stack.commands.Plugin):
- """Attempts to map firmware versions to hosts."""
-
- def provides(self):
- return "basic"
-
- def ensure_unique_mappings(self, hosts, make, model, version):
- """Ensure the proposed mappings are unique."""
- # The mappings must not already exist
- existing_mappings = [
- f"{host} mapped to firmware {version} for make {make} and model {model}" for host, make, model, version in (
- row for row in self.owner.db.select(
- """
- nodes.Name, firmware_make.name, firmware_model.name, firmware.version
- FROM firmware_mapping
- INNER JOIN nodes
- ON firmware_mapping.node_id = nodes.ID
- INNER JOIN firmware
- ON firmware_mapping.firmware_id = firmware.id
- INNER JOIN firmware_model
- ON firmware.model_id = firmware_model.id
- INNER JOIN firmware_make
- ON firmware_model.make_id = firmware_model.id
- WHERE nodes.Name IN %s AND firmware_make.name = %s AND firmware_model.name = %s AND firmware.version = %s
- """,
- (hosts, make, model, version)
- )
- )
- ]
- if existing_mappings:
- existing_mappings = "\n".join(existing_mappings)
- raise CommandError(cmd = self.owner, msg = f"The following firmware mappings already exist:\n{existing_mappings}")
-
- def run(self, args):
- params, args = args
- args = tuple(unique_everseen(lowered(args)))
- hosts = self.owner.getHosts(args = args)
-
- version, make, model, = lowered(
- self.owner.fillParams(
- names = [
- ("version", ""),
- ("make", ""),
- ("model", ""),
- ],
- params = params,
- )
- )
- # Make, model, and version are required. This checks them all.
- self.owner.ensure_firmware_exists(make = make, model = model, version = version)
- # Make sure the proposed mappings are unique.
- self.ensure_unique_mappings(hosts = hosts, make = make, model = model, version = version)
-
- # Get the ID's of all the hosts
- node_ids = (
- row[0] for row in self.owner.db.select("ID FROM nodes WHERE Name in %s", (hosts,))
- )
- # Get the firmware version ID
- firmware_id = self.owner.get_firmware_id(make = make, model = model, version = version)
-
- # Add the mapping entries.
- self.owner.db.execute(
- """
- INSERT INTO firmware_mapping (
- node_id,
- firmware_id
- )
- VALUES (%s, %s)
- """,
- [(node_id, firmware_id) for node_id in node_ids],
- many = True,
- )
diff --git a/common/src/stack/command/stack/commands/list/firmware/__init__.py b/common/src/stack/command/stack/commands/list/firmware/__init__.py
deleted file mode 100644
index 47c5f220a..000000000
--- a/common/src/stack/command/stack/commands/list/firmware/__init__.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-from stack.argument_processors.firmware import FirmwareArgProcessor
-import stack.commands
-
-class command(stack.commands.list.command, FirmwareArgProcessor):
- pass
-
-class Command(command):
- """
- Lists all firmware images tracked by stacki.
-
-
- Set this to list more detailed firmware image information
-
-
-
- Lists all firmware files tracked in the stacki database.
-
- """
-
- def run(self, params, args):
- expanded, = self.fillParams(
- names = [("expanded", False)],
- params = params,
- )
- expanded = self.str2bool(expanded)
-
- header = []
- values = []
- for provides, results in self.runPlugins(args = expanded):
- header.extend(results["keys"])
- values.extend(results["values"])
-
- self.beginOutput()
- for owner, vals in values:
- self.addOutput(owner = owner, vals = vals)
- self.endOutput(header = header)
diff --git a/common/src/stack/command/stack/commands/list/firmware/imp/__init__.py b/common/src/stack/command/stack/commands/list/firmware/imp/__init__.py
deleted file mode 100644
index cd4cfa6e8..000000000
--- a/common/src/stack/command/stack/commands/list/firmware/imp/__init__.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2019 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-
-class Command(stack.commands.list.firmware.command):
- """
- Lists all firmware implementations tracked by stacki.
-
-
- Lists all firmware implementations tracked in the stacki database.
-
- """
-
- def run(self, params, args):
- header = []
- values = []
- for provides, results in self.runPlugins():
- header.extend(results["keys"])
- values.extend(results["values"])
-
- self.beginOutput()
- for owner, vals in values:
- self.addOutput(owner = owner, vals = vals)
- self.endOutput(header = header)
diff --git a/common/src/stack/command/stack/commands/list/firmware/imp/plugin_basic.py b/common/src/stack/command/stack/commands/list/firmware/imp/plugin_basic.py
deleted file mode 100644
index af1f081e5..000000000
--- a/common/src/stack/command/stack/commands/list/firmware/imp/plugin_basic.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2019 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-
-class Plugin(stack.commands.Plugin):
- """Returns the names of all implementations in the database."""
-
- def provides(self):
- return "basic"
-
- def run(self, args):
- return {
- "keys": ["imp"],
- "values": [(row[0], []) for row in self.owner.db.select("name FROM firmware_imp")]
- }
diff --git a/common/src/stack/command/stack/commands/list/firmware/make/__init__.py b/common/src/stack/command/stack/commands/list/firmware/make/__init__.py
deleted file mode 100644
index dacd50480..000000000
--- a/common/src/stack/command/stack/commands/list/firmware/make/__init__.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-
-class Command(stack.commands.list.firmware.command):
- """
- Lists all firmware makes tracked by stacki.
-
-
- Set this to list more detailed firmware make information
-
-
-
- Lists all firmware makes tracked in the stacki database.
-
- """
-
- def run(self, params, args):
- expanded, = self.fillParams(
- names = [("expanded", False)],
- params = params,
- )
- expanded = self.str2bool(expanded)
- header = []
- values = []
- for provides, results in self.runPlugins(args = expanded):
- header.extend(results["keys"])
- values.extend(results["values"])
-
- self.beginOutput()
- for owner, vals in values:
- self.addOutput(owner = owner, vals = vals)
- self.endOutput(header = header)
diff --git a/common/src/stack/command/stack/commands/list/firmware/make/plugin_basic.py b/common/src/stack/command/stack/commands/list/firmware/make/plugin_basic.py
deleted file mode 100644
index 6d3e97311..000000000
--- a/common/src/stack/command/stack/commands/list/firmware/make/plugin_basic.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-
-class Plugin(stack.commands.Plugin):
- """Returns the names of all makes in the database."""
-
- def provides(self):
- return "basic"
-
- def run(self, args):
- # If expanded is true, also list any user defined implementations and version regexes
- if args:
- return {
- "keys": ["make", "version_regex_name"],
- "values": [
- (row[0], row[1:]) for row in self.owner.db.select(
- """
- firmware_make.name, firmware_version_regex.name
- FROM firmware_make
- LEFT JOIN firmware_version_regex
- ON firmware_make.version_regex_id = firmware_version_regex.id
- """
- )
- ]
- }
-
- # Otherwise just return the names of the makes.
- return {
- "keys": ["make"],
- "values": [(row[0], []) for row in self.owner.db.select("name FROM firmware_make")]
- }
diff --git a/common/src/stack/command/stack/commands/list/firmware/model/__init__.py b/common/src/stack/command/stack/commands/list/firmware/model/__init__.py
deleted file mode 100644
index 27127a904..000000000
--- a/common/src/stack/command/stack/commands/list/firmware/model/__init__.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-
-class Command(stack.commands.list.firmware.command):
- """
- Lists firmware models tracked by stacki.
-
-
- Zero or more models to list information for. If no models are specified, all models are listed.
-
-
-
- The optional make of the models to list. This is required if models are specified as arguments.
- Setting this with no models specified will list all models for the given make.
-
-
-
- Set this to list more detailed firmware model information.
-
-
-
- Lists all firmware models tracked in the stacki database.
-
-
-
- Lists information for all firmware models under the mellanox make.
-
-
-
- Lists information for the firmware models m7800 and m6036 under the mellanox make.
-
-
-
- Lists additional information for all firmware models tracked in the database.
-
- """
-
- def run(self, params, args):
- header = []
- values = []
- for provides, results in self.runPlugins(args = (params, args)):
- header.extend(results["keys"])
- values.extend(results["values"])
-
- self.beginOutput()
- for owner, vals in values:
- self.addOutput(owner = owner, vals = vals)
- self.endOutput(header = header)
diff --git a/common/src/stack/command/stack/commands/list/firmware/model/plugin_basic.py b/common/src/stack/command/stack/commands/list/firmware/model/plugin_basic.py
deleted file mode 100644
index 8a9bff15e..000000000
--- a/common/src/stack/command/stack/commands/list/firmware/model/plugin_basic.py
+++ /dev/null
@@ -1,122 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-from stack.util import unique_everseen, lowered
-
-class Plugin(stack.commands.Plugin):
- """Returns the make name and model name of all models in the database."""
-
- def provides(self):
- return "basic"
-
- def validate_make(self, make):
- """If a make is provided, ensure it exists."""
- if make:
- self.owner.ensure_make_exists(make = make)
-
- def validate_models(self, make, models):
- """If models are provided, ensure both the make and the models exist."""
- if models:
- # This will also fail if the make is not provided or doesn't exist.
- self.owner.ensure_models_exist(make = make, models = models)
-
- def get_expanded_results(self, make, models):
- """Return the expanded results set from the database.
-
- If make is provided, this will filter results to the ones that match the make.
-
- If both make and models are provided, this will filter the results to the ones that match
- on make and in models.
- """
- query = """
- firmware_make.name, firmware_model.name, firmware_imp.name, firmware_version_regex.name
- FROM firmware_make
- INNER JOIN firmware_model
- ON firmware_model.make_id=firmware_make.id
- INNER JOIN firmware_imp
- ON firmware_model.imp_id=firmware_imp.id
- LEFT JOIN firmware_version_regex
- ON firmware_model.version_regex_id=firmware_version_regex.id
- """
- query_params = []
- # Filter on make and models if both are provided.
- if make and models:
- query += " WHERE firmware_model.name IN %s AND firmware_make.name=%s"
- query_params.extend((models, make))
- # Filter on only make if models aren't provided but a make is.
- elif make:
- query += " WHERE firmware_make.name=%s"
- query_params.append(make)
- # Otherwise return everything
-
- return {
- "keys": ["make", "model", "implementation", "version_regex_name"],
- "values": [
- (row[0], row[1:])
- for row in self.owner.db.select(query, query_params)
- ]
- }
-
- def get_results(self, make, models):
- """Return the simple results set from the database.
-
- If make is provided, this will filter results to the ones that match the make.
-
- If both make and models are provided, this will filter the results to the ones that match
- on make and in models.
- """
- query = """
- firmware_make.name, firmware_model.name
- FROM firmware_make
- INNER JOIN firmware_model
- ON firmware_model.make_id=firmware_make.id
- """
- query_params = []
- # Filter on make and models if both are provided.
- if make and models:
- query += " WHERE firmware_model.name IN %s AND firmware_make.name=%s"
- query_params.extend((models, make))
- # Filter on only make if models aren't provided but a make is.
- elif make:
- query += " WHERE firmware_make.name=%s"
- query_params.append(make)
- # Otherwise return everything
-
- return {
- "keys": ["make", "model"],
- "values": [
- (row[0], row[1:])
- for row in self.owner.db.select(query, query_params)
- ]
- }
-
- def run(self, args):
- params, args = args
- models = tuple(unique_everseen(lowered(args)))
-
- expanded, make = lowered(
- self.owner.fillParams(
- names = [("expanded", "false"), ("make", "")],
- params = params,
- )
- )
- expanded = self.owner.str2bool(expanded)
- self.validate_make(make = make)
- self.validate_models(make = make, models = models)
-
- # If expanded is true, also list the implementation and any version regex associated with the model.
- if expanded:
- return self.get_expanded_results(make = make, models = models)
-
- # Otherwise just return the names of the makes and models.
- return self.get_results(make = make, models = models)
diff --git a/common/src/stack/command/stack/commands/list/firmware/plugin_basic.py b/common/src/stack/command/stack/commands/list/firmware/plugin_basic.py
deleted file mode 100644
index 0f2a4bf43..000000000
--- a/common/src/stack/command/stack/commands/list/firmware/plugin_basic.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-
-class Plugin(stack.commands.Plugin):
- """Returns information about all firmware versions tracked in the database.
-
- If expanded is set to True, additional information is returned.
- """
-
- def provides(self):
- return "basic"
-
- def run(self, args):
- expanded = args
- keys = ["make", "model", "version"]
- if expanded:
- keys.extend(["source", "hash", "hash_alg"])
-
- values = [
- (row[0], row[1:])
- for row in self.owner.db.select(
- f"""
- firmware_make.name, firmware_model.name, firmware.version{
- ", firmware.source, firmware.hash, firmware.hash_alg" if expanded else ""
- }
- FROM firmware
- INNER JOIN firmware_model
- ON firmware.model_id=firmware_model.id
- INNER JOIN firmware_make
- ON firmware_model.make_id=firmware_make.id
- """,
- )
- ]
-
- return {"keys": keys, "values": values}
diff --git a/common/src/stack/command/stack/commands/list/firmware/version_regex/__init__.py b/common/src/stack/command/stack/commands/list/firmware/version_regex/__init__.py
deleted file mode 100644
index fdd3d8dc4..000000000
--- a/common/src/stack/command/stack/commands/list/firmware/version_regex/__init__.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-
-class Command(stack.commands.list.firmware.command):
- """
- Lists all firmware version regexes tracked by stacki.
-
-
- Lists all firmware version regexes tracked in the stacki database.
-
- """
-
- def run(self, params, args):
- header = []
- values = []
- for provides, results in self.runPlugins():
- header.extend(results["keys"])
- values.extend(results["values"])
-
- self.beginOutput()
- for owner, vals in values:
- self.addOutput(owner = owner, vals = vals)
- self.endOutput(header = header)
diff --git a/common/src/stack/command/stack/commands/list/firmware/version_regex/plugin_basic.py b/common/src/stack/command/stack/commands/list/firmware/version_regex/plugin_basic.py
deleted file mode 100644
index c5f630ff8..000000000
--- a/common/src/stack/command/stack/commands/list/firmware/version_regex/plugin_basic.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-
-class Plugin(stack.commands.Plugin):
- """Returns the names, regex, and description of all version regexes in the database."""
-
- def provides(self):
- return "basic"
-
- def run(self, args):
- return {
- "keys": ["name", "regex", "description"],
- "values": [
- (row[0], row[1:]) for row in self.owner.db.select(
- "name, regex, description FROM firmware_version_regex"
- )
- ],
- }
diff --git a/common/src/stack/command/stack/commands/list/host/firmware/__init__.py b/common/src/stack/command/stack/commands/list/host/firmware/__init__.py
deleted file mode 100644
index 1d1f95c22..000000000
--- a/common/src/stack/command/stack/commands/list/host/firmware/__init__.py
+++ /dev/null
@@ -1,105 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import os
-import re
-from collections import namedtuple
-
-from stack.argument_processors.firmware import FirmwareArgProcessor
-from stack.argument_processors.host import HostArgProcessor
-import stack.commands
-
-class command(
- FirmwareArgProcessor,
- HostArgProcessor,
- stack.commands.list.command,
-):
- pass
-
-
-class Command(command):
- """
- List the hosts, and their corresponding available and installed firmwares.
-
-
- Zero, one or more host names. If no host names are supplied, info about
- all the known hosts is listed.
-
-
-
- List info for backend-0-0.
-
-
-
- List info for all known hosts.
-
-
- """
-
- def run(self, params, args):
- # Resolve any provided hostnames
- hosts = self.getHostnames(names = args)
-
- header = ["host", "make", "model",]
- # build a dictionary keyed by (host + make + model) so that the plugins and implementations
- # can return the data mapped appropriately. We do this by getting all the firmware mappings
- # and looking at the make and model of firmwares mapped to hosts.
- CommonKey = namedtuple("CommonKey", ("host", "make", "model"))
- CommonData = namedtuple("CommonData", ("firmware_version", "firmware_imp"))
- values = {
- CommonKey(*row[0:3]): CommonData(*row[3:]) for row in self.db.select(
- """
- nodes.Name, firmware_make.name, firmware_model.name, firmware.version, firmware_imp.name
- FROM firmware_mapping
- INNER JOIN nodes
- ON firmware_mapping.node_id = nodes.ID
- INNER JOIN firmware
- ON firmware_mapping.firmware_id = firmware.id
- INNER JOIN firmware_model
- ON firmware.model_id = firmware_model.id
- INNER JOIN firmware_make
- ON firmware_model.make_id = firmware_make.id
- INNER JOIN firmware_imp
- ON firmware_model.imp_id = firmware_imp.id
- WHERE nodes.Name IN %s
- """,
- (hosts,)
- )
- }
- results = {
- key: [] for key in values
- }
-
- # loop through all the plugin results and extend header and values as necessary.
- CommonResult = namedtuple("CommonResult", ("header", "values"))
- for provides, result in self.runPlugins((CommonResult, values)):
- header.extend(result.header)
- for host_make_model, items in result.values.items():
- results[host_make_model].extend(items)
-
- # add empty entries for hosts with no firmware mappings.
- results.update(
- {
- # pad out with None for each extra column header added by the plugins
- CommonKey(host, None, None): [None for i in range(len(header) - 3)] for host in hosts
- if host not in (host_make_model.host for host_make_model in values)
- }
- )
-
- # output the results
- self.beginOutput()
- for host_make_model, result in results.items():
- self.addOutput(
- host_make_model.host,
- [host_make_model.make, host_make_model.model, *result]
- )
- self.endOutput(header = header)
diff --git a/common/src/stack/command/stack/commands/list/host/firmware/imp_dell_x1052.py b/common/src/stack/command/stack/commands/list/host/firmware/imp_dell_x1052.py
deleted file mode 100644
index 4022cd5bb..000000000
--- a/common/src/stack/command/stack/commands/list/host/firmware/imp_dell_x1052.py
+++ /dev/null
@@ -1,75 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-
-from concurrent.futures import ThreadPoolExecutor
-import stack.commands
-from stack.exception import CommandError
-from stack.switch.x1052 import SwitchDellX1052
-
-class Implementation(stack.commands.Implementation):
-
- def list_firmware(self, host, switch_attrs):
- kwargs = {
- "username" : switch_attrs.get("switch_username"),
- "password" : switch_attrs.get("switch_password"),
- }
-
- kwargs = {key: value for key, value in kwargs.items() if value is not None}
-
- # the context manager ensures we disconnect from the switch
- with SwitchDellX1052(switch_ip_address = host, switchname = host, **kwargs) as x1052_switch:
- x1052_switch.connect()
- return x1052_switch.get_versions()
-
- def run(self, args):
- errors = []
- # Since we can get both the boot and the software version based on the hostname, collapse the
- # "software" and the "boot" models together into one.
- x1052_attrs_per_host = {
- host_make_model.host: {} for host_make_model in args
- }
- for host_make_model, attrs in args.items():
- x1052_attrs_per_host[host_make_model.host].update(attrs)
-
- # now run each image listing in parallel
- with ThreadPoolExecutor(thread_name_prefix = "dell_firmware_listing") as executor:
- futures_by_host = {
- host: executor.submit(
- self.list_firmware,
- host = host,
- switch_attrs = attrs
- )
- for host, attrs in x1052_attrs_per_host.items()
- }
- # now collect the results, adding any errors into the errors list
- results_by_host = {}
- for host, future in futures_by_host.items():
- if future.exception() is not None:
- errors.append(future.exception())
- else:
- results_by_host[host] = future.result()
-
- # if there were any errors, aggregate all the errors into one
- error_messages = []
- for error in errors:
- # if this looks like a stacki exception type, grab the message from it.
- if hasattr(error, "message") and callable(getattr(error, "message")):
- error_messages.append(error.message())
- else:
- error_messages.append(f'{error}')
-
- if error_messages:
- error_message = '\n'.join(error_messages)
- raise CommandError(
- cmd = self.owner,
- msg = f"Errors occurred during Dell firmware listing:\n{error_message}"
- )
-
- # Split results back out into host_make_model.
- return {
- host_make_model: results_by_host[host_make_model.host].boot if "boot" in host_make_model.model else results_by_host[host_make_model.host].software
- for host_make_model in args
- }
diff --git a/common/src/stack/command/stack/commands/list/host/firmware/imp_mellanox.py b/common/src/stack/command/stack/commands/list/host/firmware/imp_mellanox.py
deleted file mode 100644
index 60710043a..000000000
--- a/common/src/stack/command/stack/commands/list/host/firmware/imp_mellanox.py
+++ /dev/null
@@ -1,66 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-
-from concurrent.futures import ThreadPoolExecutor
-import stack.commands
-from stack.exception import CommandError
-from stack.switch.m7800 import SwitchMellanoxM7800
-
-class Implementation(stack.commands.Implementation):
-
- def list_firmware(self, host, switch_attrs):
- kwargs = {
- 'username' : switch_attrs.get('switch_username', None),
- 'password' : switch_attrs.get('switch_password', None),
- }
-
- kwargs = {key: value for key, value in kwargs.items() if value is not None}
-
- # the context manager ensures we disconnect from the switch
- with SwitchMellanoxM7800(host, **kwargs) as m7800_switch:
- m7800_switch.connect()
- image_listing = m7800_switch.show_images()
- installed_image = image_listing.installed_images[image_listing.last_boot_partition]
-
- return installed_image
-
- def run(self, args):
- errors = []
- # now run each image listing in parallel
- with ThreadPoolExecutor(thread_name_prefix = 'mellanox_firmware_listing') as executor:
- futures_by_host_make_model = {
- host_make_model: executor.submit(
- self.list_firmware,
- host = host_make_model.host,
- switch_attrs = attrs
- )
- for host_make_model, attrs in args.items()
- }
- # now collect the results, adding any errors into the errors list
- results_by_host_make_model = {}
- for host_make_model, future in futures_by_host_make_model.items():
- if future.exception() is not None:
- errors.append(future.exception())
- else:
- results_by_host_make_model[host_make_model] = future.result()
-
- # if there were any errors, aggregate all the errors into one
- error_messages = []
- for error in errors:
- # if this looks like a stacki exception type, grab the message from it.
- if hasattr(error, 'message') and callable(getattr(error, 'message')):
- error_messages.append(error.message())
- else:
- error_messages.append(f'{error}')
-
- if error_messages:
- error_message = '\n'.join(error_messages)
- raise CommandError(
- cmd = self.owner,
- msg = f"Errors occurred during Mellanox firmware listing:\n{error_message}"
- )
-
- return results_by_host_make_model
diff --git a/common/src/stack/command/stack/commands/list/host/firmware/mapping/__init__.py b/common/src/stack/command/stack/commands/list/host/firmware/mapping/__init__.py
deleted file mode 100644
index 7881757c4..000000000
--- a/common/src/stack/command/stack/commands/list/host/firmware/mapping/__init__.py
+++ /dev/null
@@ -1,85 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2019 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-
-class Command(stack.commands.list.host.firmware.command):
- """
- Lists the firmware mappings that exist in the stacki database.
-
-
- Zero or more hostnames to filter the results by.
-
-
-
- Zero or more firmware versions to filter by. Multiple versions should be specified as a comma separated list.
- If one or more versions are specified, the make and model parameters are required.
-
-
-
- The optional make of the firmware to filter by.
-
-
-
- The optional model of the firmware to filter by.
- If this is specified, make is required.
-
-
-
- The value to sort the results by. Should be one of 'host', 'make', 'model', or 'version'.
-
-
-
- Lists all of the firmware mappings for all hosts.
-
-
-
- Lists all of the firmware mappings for the host named switch-13-11.
-
-
-
- Lists all of the firmware mappings for firmware versions 3.6.5002 and 3.6.8010 for make mellanox and model m7800 that are
- mapped to the hosts named switch-13-11 and switch-13-12.
-
-
-
- Lists all of the firmware mappings for firmware versions 3.6.5002 and 3.6.8010 for make mellanox and model m7800 for all hosts.
-
-
-
- Lists all of the firmware mappings for make mellanox and model m7800 for all hosts that are switch appliances.
-
-
-
- Lists all of the firmware mappings for make mellanox and model m7800 for all hosts.
-
-
-
- Lists all of the firmware mappings for make mellanox for the host with the name switch-13-11.
-
-
-
- Lists all of the firmware mappings for make mellanox for all hosts.
-
- """
-
- def run(self, params, args):
- header = []
- values = []
- for provides, results in self.runPlugins(args = (params, args)):
- header.extend(results['keys'])
- values.extend(results['values'])
-
- self.beginOutput()
- for owner, vals in values:
- self.addOutput(owner = owner, vals = vals)
- self.endOutput(header = header)
diff --git a/common/src/stack/command/stack/commands/list/host/firmware/mapping/plugin_basic.py b/common/src/stack/command/stack/commands/list/host/firmware/mapping/plugin_basic.py
deleted file mode 100644
index 3d30d946c..000000000
--- a/common/src/stack/command/stack/commands/list/host/firmware/mapping/plugin_basic.py
+++ /dev/null
@@ -1,132 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-from stack.util import unique_everseen
-from stack.exception import ParamError
-
-class Plugin(stack.commands.Plugin):
- """Lists firmware mappings and filters the results on any provided arguments."""
-
- def provides(self):
- return "basic"
-
- def get_firmware_mappings(self, hosts, versions, make, model, sort):
- """Gets the mappings using the provided arguments as a filter."""
- where_clause = []
- query_args = []
-
- if hosts:
- where_clause.append("nodes.Name IN %s")
- query_args.append(hosts)
-
- if make:
- where_clause.append("firmware_make.name=%s")
- query_args.append(make)
-
- if model:
- where_clause.append("firmware_model.name=%s")
- query_args.append(model)
-
- if versions:
- where_clause.append("firmware.version IN %s")
- query_args.append(versions)
-
- if where_clause:
- where_clause = f"WHERE {' AND '.join(where_clause)}"
- else:
- where_clause = ""
-
- return [
- (row[0], row[1:]) for row in self.owner.db.select(
- f"""
- nodes.Name, firmware.version, firmware_make.name, firmware_model.name
- FROM firmware_mapping
- INNER JOIN nodes
- ON firmware_mapping.node_id = nodes.ID
- INNER JOIN firmware
- ON firmware_mapping.firmware_id = firmware.id
- INNER JOIN firmware_model
- ON firmware.model_id = firmware_model.id
- INNER JOIN firmware_make
- ON firmware_model.make_id = firmware_make.id
- {where_clause}
- ORDER BY {sort}
- """,
- query_args,
- )
- ]
-
- def run(self, args):
- params, hosts = args
- make, model, versions, sort, = self.owner.fillParams(
- names = [
- ("make", ""),
- ("model", ""),
- ("versions", ""),
- ("sort", "host"),
- ],
- params = params,
- )
-
- sort_map = {
- "host": "nodes.Name",
- "make": "firmware_make.name",
- "model": "firmware_model.name",
- "version": "firmware.version",
- }
- # sort must be one of the allowed values
- try:
- # also convert to the column name for use in ORDER BY
- sort = sort_map[sort]
- except KeyError:
- raise ParamError(
- cmd = self.owner,
- param = "sort",
- msg = f"Sort must be one of: {list(sort_map.keys())}",
- )
- # process hosts if present
- if hosts:
- # hosts must exist
- hosts = self.owner.getHosts(args = hosts)
-
- # Process versions if present. This will check the make and model as well since those are
- # now required.
- if versions:
- # turn a comma separated string into a list of versions and
- # get rid of any duplicate names
- versions = tuple(
- unique_everseen(
- (version.strip() for version in versions.split(",") if version.strip())
- )
- )
- self.owner.ensure_firmwares_exist(make = make, model = model, versions = versions)
- # Process model if present. This will check the make as well since that is now required.
- elif model:
- self.owner.ensure_model_exists(make = make, model = model)
- # Process make if present.
- elif make:
- self.owner.ensure_make_exists(make = make)
-
- results = self.get_firmware_mappings(
- hosts = hosts,
- make = make,
- model = model,
- versions = versions,
- sort = sort,
- )
-
- # return the results
- return {
- "keys": ["host", "version", "make", "model"],
- "values": results
- }
diff --git a/common/src/stack/command/stack/commands/list/host/firmware/plugin_basic.py b/common/src/stack/command/stack/commands/list/host/firmware/plugin_basic.py
deleted file mode 100644
index 88fb580dd..000000000
--- a/common/src/stack/command/stack/commands/list/host/firmware/plugin_basic.py
+++ /dev/null
@@ -1,47 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-
-import re
-import stack.commands
-
-class Plugin(stack.commands.Plugin):
-
- def provides(self):
- return 'basic'
-
- def run(self, args):
- # Unpack args.
- CommonResult, database_values_dict = args
-
- # This plugin gets the desired firmware version, so add that column to the header.
- header = ["desired_firmware_version"]
- results = {host_make_model: [] for host_make_model in database_values_dict}
-
- # For each host_make_model + firmware_version, firmware_imp combination, get the desired firmware version and validate it.
- for host_make_model, firmware_info in database_values_dict.items():
- # Ensure that if a version regex exists, that we check the version in the database matches.
- # Since they are optional, it could have been set after the fact and a bad version number was
- # snuck in.
- regex_obj = self.owner.try_get_version_regex(
- make = host_make_model.make,
- model = host_make_model.model,
- )
- if regex_obj and not re.search(regex_obj.regex, firmware_info.firmware_version, re.IGNORECASE):
- results[host_make_model].append(
- f"{firmware_info.firmware_version} (Invalid format per the version_regex named {regex_obj.name} and will be ignored by sync unless forced)"
- )
- else:
- results[host_make_model].append(firmware_info.firmware_version)
-
- # add empty values for host + make + model combos that have no results and
- # join together mutiple version results into one string for host + make + model combos with multiple results.
- for host_make_model, value in results.items():
- if not value:
- results[host_make_model].append(None)
- elif len(value) > 1:
- results[host_make_model] = [f"(ambiguous, sync will ignore unless forced) {', '.join(value)}"]
-
- return CommonResult(header, results)
diff --git a/common/src/stack/command/stack/commands/list/host/firmware/plugin_version.py b/common/src/stack/command/stack/commands/list/host/firmware/plugin_version.py
deleted file mode 100644
index 4ec3eb525..000000000
--- a/common/src/stack/command/stack/commands/list/host/firmware/plugin_version.py
+++ /dev/null
@@ -1,98 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-
-import re
-import stack.commands
-from stack.exception import CommandError
-from stack.util import flatten
-
-class Plugin(stack.commands.Plugin):
-
- def provides(self):
- return 'version'
-
- def requires(self):
- return ['basic']
-
- def check_errors(self, results):
- """Checks for any errors in the results of run_implementations_parallel. If there are errors,
- this will aggregate them all into one CommandError and raise it.
- """
- # drop any results that didn't have any errors and aggregate the rest into one exception
- error_messages = []
- for error in (
- value.exception for value in results.values()
- if value is not None and value.exception is not None
- ):
- # if this looks like a stacki exception type, grab the message from it.
- if hasattr(error, 'message') and callable(getattr(error, 'message')):
- error_messages.append(error.message())
- else:
- error_messages.append(f'{error}')
-
- if error_messages:
- error_message = '\n'.join(error_messages)
- raise CommandError(
- cmd = self.owner,
- msg = f"Errors occurred while listing firmware:\n{error_message}"
- )
-
- def run(self, args):
- # Unpack args.
- CommonResult, database_values_dict = args
- # get all host attrs up front
- host_attrs = self.owner.getHostAttrDict(
- host = [host_make_model.host for host_make_model in database_values_dict],
- )
-
- # Re-map the required information by implementation name so that run_implementations_parallel can
- # run each implementation in parallel, and each implementation has all the info it needs.
- mapped_by_imp_name = {}
- for host_make_model, firmware_info in database_values_dict.items():
- if firmware_info.firmware_imp in mapped_by_imp_name:
- mapped_by_imp_name[firmware_info.firmware_imp][host_make_model] = host_attrs[host_make_model.host]
- else:
- mapped_by_imp_name[firmware_info.firmware_imp] = {host_make_model: host_attrs[host_make_model.host]}
-
- # run the implementations in parallel.
- results_by_imp = self.owner.run_implementations_parallel(
- implementation_mapping = mapped_by_imp_name,
- display_progress = True,
- )
- # Check for any errors. This will raise an exception if any implementations raised an exception.
- self.check_errors(results = results_by_imp)
- # rebuild the results as (host, make, model) mapped to version
- results_by_host_make_model = {
- host_make_model: version for host_make_model, version in flatten(
- results.result.items() for results in results_by_imp.values()
- if results is not None and results.result is not None
- )
- }
-
- # Use the version_regex (if set) to parse out and validate the version numbers returned by the implementations
- for host_make_model, version in results_by_host_make_model.items():
- regex_obj = self.owner.try_get_version_regex(
- make = host_make_model.make,
- model = host_make_model.model,
- )
- if regex_obj:
- match = re.search(regex_obj.regex, version, re.IGNORECASE)
- if not match:
- results_by_host_make_model[host_make_model] = (
- f"{version} (Doesn't validate using the version_regex named {regex_obj.name} and will be ignored by sync)"
- )
- else:
- results_by_host_make_model[host_make_model] = match.group()
-
- # Do a final pass to turn the results into a list as the top level command expects.
- # Also set None for host + make + model combos that had no results.
- for host_make_model in database_values_dict:
- if host_make_model not in results_by_host_make_model:
- results_by_host_make_model[host_make_model] = [None]
- else:
- results_by_host_make_model[host_make_model] = [results_by_host_make_model[host_make_model]]
-
- return CommonResult(header = ['current_firmware_version'], values = results_by_host_make_model)
diff --git a/common/src/stack/command/stack/commands/remove/firmware/__init__.py b/common/src/stack/command/stack/commands/remove/firmware/__init__.py
deleted file mode 100644
index 6cc650c80..000000000
--- a/common/src/stack/command/stack/commands/remove/firmware/__init__.py
+++ /dev/null
@@ -1,57 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-from stack.argument_processors.firmware import FirmwareArgProcessor
-import stack.commands
-
-class command(stack.commands.remove.command, FirmwareArgProcessor):
- pass
-
-class Command(command):
- """
- Removes firmware images from stacki.
-
-
- Zero or more firmware versions to be removed. If no versions are specified, all will be removed.
- If one or more versions are specified, the make and model parameters are required.
-
-
-
- The optional make of the firmware to remove.
- If this is specified but no versions or model are specified, this will remove all firmware versions for the make.
-
-
-
- The optional model of the firmware to remove.
- If this is specified, make is required.
- If no versions are specified but make and model are specified, all firmware versions for that make and model will be removed.
-
-
-
- Removes all firmware.
-
-
-
- Removes the firmware with version 3.6.5002 for the mellanox m7800 make and model.
-
-
-
- Removes all firmware for the mellanox make.
-
-
-
- Removes all firmware for the mellanox make and m7800 model.
-
- """
-
- def run(self, params, args):
- self.runPlugins(args = (params, args))
diff --git a/common/src/stack/command/stack/commands/remove/firmware/imp/__init__.py b/common/src/stack/command/stack/commands/remove/firmware/imp/__init__.py
deleted file mode 100644
index 3ecb5886e..000000000
--- a/common/src/stack/command/stack/commands/remove/firmware/imp/__init__.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2019 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-
-class Command(stack.commands.remove.firmware.command):
- """
- Removes firmware implementations from the stacki database.
-
-
- One or more implementations to remove.
-
-
-
- Removes the mellanox_m6xxx_7xxx and intel_special implementations from the database.
-
- """
-
- def run(self, params, args):
- self.runPlugins(args = args)
diff --git a/common/src/stack/command/stack/commands/remove/firmware/imp/plugin_basic.py b/common/src/stack/command/stack/commands/remove/firmware/imp/plugin_basic.py
deleted file mode 100644
index 75aaf5974..000000000
--- a/common/src/stack/command/stack/commands/remove/firmware/imp/plugin_basic.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2019 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-from pymysql import IntegrityError
-import stack.commands
-from stack.util import unique_everseen, lowered
-from stack.exception import ArgRequired, CommandError
-
-class Plugin(stack.commands.Plugin):
- """Attempts to remove implementations."""
-
- def provides(self):
- return "basic"
-
- def run(self, args):
- # remove any duplicates
- args = tuple(unique_everseen(lowered(args)))
- # The imps must exist
- self.owner.ensure_imps_exist(imps = args)
-
- # remove the implementations
- try:
- self.owner.db.execute("DELETE FROM firmware_imp WHERE name IN %s", (args,))
- except IntegrityError:
- raise CommandError(
- cmd = self.owner,
- msg = (
- "Failed to remove all implementations because some are still in use."
- " Please run 'stack list firmware model expanded=true' to list the"
- " models still using the implementation and 'stack remove firmware model'"
- " to remove them."
- )
- )
diff --git a/common/src/stack/command/stack/commands/remove/firmware/make/__init__.py b/common/src/stack/command/stack/commands/remove/firmware/make/__init__.py
deleted file mode 100644
index e51433375..000000000
--- a/common/src/stack/command/stack/commands/remove/firmware/make/__init__.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-
-class Command(stack.commands.remove.firmware.command):
- """
- Removes firmware makes from the stacki database.
-
-
- One or more make names to remove. This will also remove any associated models and firmware associated with those models.
-
-
-
- Removes two makes with the names 'mellanox' and 'dell'. This also removes any associated models and firmware associated with the models removed.
-
- """
-
- def run(self, params, args):
- self.runPlugins(args = args)
diff --git a/common/src/stack/command/stack/commands/remove/firmware/make/plugin_basic.py b/common/src/stack/command/stack/commands/remove/firmware/make/plugin_basic.py
deleted file mode 100644
index 95068ac9d..000000000
--- a/common/src/stack/command/stack/commands/remove/firmware/make/plugin_basic.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-from stack.util import unique_everseen, lowered
-from stack.exception import ArgRequired, ArgError, CommandError
-
-class Plugin(stack.commands.Plugin):
- """Attempts to remove all provided makes and any associated models and firmware from the database."""
-
- def provides(self):
- return "basic"
-
- def remove_related_models(self, makes):
- """Remove any models related to the provided makes."""
- # remove associated models
- for make in makes:
- # get all the models associated with this make
- models_to_remove = [
- row[0] for row in
- self.owner.db.select(
- """
- firmware_model.name
- FROM firmware_model
- INNER JOIN firmware_make
- ON firmware_model.make_id=firmware_make.id
- WHERE firmware_make.name=%s
- """,
- make,
- )
- ]
-
- # and remove them if we found any
- if models_to_remove:
- self.owner.call(command = "remove.firmware.model", args = [*models_to_remove, f"make={make}"])
-
- def run(self, args):
- # get rid of any duplicate names
- makes = tuple(unique_everseen(lowered(args)))
- # ensure the make names already exist
- self.owner.ensure_makes_exist(makes = makes)
-
- # Remove any related models.
- self.remove_related_models(makes = makes)
-
- # now delete the makes
- self.owner.db.execute(
- "DELETE FROM firmware_make WHERE name IN %s",
- (makes,)
- )
diff --git a/common/src/stack/command/stack/commands/remove/firmware/make/version_regex/__init__.py b/common/src/stack/command/stack/commands/remove/firmware/make/version_regex/__init__.py
deleted file mode 100644
index ad7b1aef7..000000000
--- a/common/src/stack/command/stack/commands/remove/firmware/make/version_regex/__init__.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2019 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-
-class Command(stack.commands.remove.firmware.command):
- """
- Disassociates firmware version_regexes from one or more makes.
-
-
- One or more makes to disassociate from a version_regex.
-
-
-
- Disassociates the mellanox and intel makes from any version_regexes that were set for them.
-
- """
-
- def run(self, params, args):
- self.runPlugins(args = args)
diff --git a/common/src/stack/command/stack/commands/remove/firmware/make/version_regex/plugin_basic.py b/common/src/stack/command/stack/commands/remove/firmware/make/version_regex/plugin_basic.py
deleted file mode 100644
index 99c07a235..000000000
--- a/common/src/stack/command/stack/commands/remove/firmware/make/version_regex/plugin_basic.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2019 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-from stack.util import lowered, unique_everseen
-from stack.exception import ArgRequired
-
-class Plugin(stack.commands.Plugin):
- """Attempts to disassociate version_regexes from makes."""
-
- def provides(self):
- return "basic"
-
- def run(self, args):
- # Require make names
- if not args:
- raise ArgRequired(cmd = self.owner, arg = "makes")
- # lowercase all args and remove any duplicates
- args = tuple(unique_everseen(lowered(args)))
- # The makes must exist
- self.owner.ensure_makes_exist(makes = args)
-
- # disassociate the makes from version_regexes
- self.owner.db.execute("UPDATE firmware_make SET version_regex_id=NULL WHERE name IN %s", (args,))
diff --git a/common/src/stack/command/stack/commands/remove/firmware/model/__init__.py b/common/src/stack/command/stack/commands/remove/firmware/model/__init__.py
deleted file mode 100644
index 39c6a1957..000000000
--- a/common/src/stack/command/stack/commands/remove/firmware/model/__init__.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-
-class Command(stack.commands.remove.firmware.command):
- """
- Removes a firmware model from the stacki database.
-
-
- One or more model names to remove. Any firmware associated with the models will also be removed.
-
-
-
- The maker of the models being removed. This must correspond to an already existing make.
-
-
-
- Removes two models with the names 'awesome_9001' and 'mediocre_5200' from the set of available firmware models under the 'boss hardware corp' make.
- This also removes any firmware associated with those models.
-
- """
-
- def run(self, params, args):
- self.runPlugins(args = (params, args))
diff --git a/common/src/stack/command/stack/commands/remove/firmware/model/plugin_basic.py b/common/src/stack/command/stack/commands/remove/firmware/model/plugin_basic.py
deleted file mode 100644
index 26609b162..000000000
--- a/common/src/stack/command/stack/commands/remove/firmware/model/plugin_basic.py
+++ /dev/null
@@ -1,76 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-from stack.util import unique_everseen, lowered
-from stack.exception import ArgRequired, ArgError, ParamError, ParamRequired, CommandError
-from pathlib import Path
-
-class Plugin(stack.commands.Plugin):
- """Attempts to remove all provided models from the system."""
-
- def provides(self):
- return "basic"
-
- def remove_related_firmware(self, make, models):
- """Remove any firmware related to the provided make + model combinations."""
- for model in models:
- # get all the firmware associated with this make and model
- firmware_to_remove = [
- row[0] for row in
- self.owner.db.select(
- """
- firmware.version
- FROM firmware
- INNER JOIN firmware_model
- ON firmware.model_id=firmware_model.id
- INNER JOIN firmware_make
- ON firmware_model.make_id=firmware_make.id
- WHERE firmware_make.name=%s AND firmware_model.name=%s
- """,
- (make, model),
- )
- ]
- # and remove them if we found any
- if firmware_to_remove:
- self.owner.call(
- command = "remove.firmware",
- args = [*firmware_to_remove, f"make={make}", f"model={model}"],
- )
-
- def run(self, args):
- params, args = args
- make, = lowered(
- self.owner.fillParams(
- names = [("make", "")],
- params = params,
- )
- )
-
- # get rid of any duplicate names
- models = tuple(unique_everseen(lowered(args)))
- # ensure the make and models exist
- self.owner.ensure_models_exist(make = make, models = models)
-
- # remove associated firmware
- self.remove_related_firmware(make = make, models = models)
-
- # now delete the models
- self.owner.db.execute(
- """
- DELETE firmware_model FROM firmware_model
- INNER JOIN firmware_make
- ON firmware_model.make_id=firmware_make.id
- WHERE firmware_model.name IN %s AND firmware_make.name=%s
- """,
- (models, make),
- )
diff --git a/common/src/stack/command/stack/commands/remove/firmware/model/version_regex/__init__.py b/common/src/stack/command/stack/commands/remove/firmware/model/version_regex/__init__.py
deleted file mode 100644
index 679a9b24c..000000000
--- a/common/src/stack/command/stack/commands/remove/firmware/model/version_regex/__init__.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2019 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-
-class Command(stack.commands.remove.firmware.command):
- """
- Disassociates firmware version_regexes from one or more models.
-
-
- One or more models to disassociate from a version_regex.
-
-
-
- The make of the provided models.
-
-
-
- Disassociates the m7800 and m6036 models for the mellanox make from any version_regexes that were set for them.
-
- """
-
- def run(self, params, args):
- self.runPlugins(args = (params, args))
diff --git a/common/src/stack/command/stack/commands/remove/firmware/model/version_regex/plugin_basic.py b/common/src/stack/command/stack/commands/remove/firmware/model/version_regex/plugin_basic.py
deleted file mode 100644
index af6199444..000000000
--- a/common/src/stack/command/stack/commands/remove/firmware/model/version_regex/plugin_basic.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2019 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-from stack.util import lowered, unique_everseen
-from stack.exception import ArgRequired, ParamRequired, ParamError
-
-class Plugin(stack.commands.Plugin):
- """Attempts to disassociate version_regexes from models."""
-
- def provides(self):
- return "basic"
-
- def run(self, args):
- params, args = args
- # Lowercase all args and remove any duplicates.
- models = tuple(unique_everseen(lowered(args)))
-
- make, = lowered(
- self.owner.fillParams(names = [("make", "")], params = params)
- )
- # The make and models must exist.
- self.owner.ensure_models_exist(models = models, make = make)
-
- # disassociate the models from version_regexes
- self.owner.db.execute(
- """
- UPDATE firmware_model
- INNER JOIN firmware_make ON firmware_make.id = firmware_model.make_id
- SET firmware_model.version_regex_id=NULL WHERE firmware_model.name IN %s AND firmware_make.name=%s
- """,
- (models, make),
- )
diff --git a/common/src/stack/command/stack/commands/remove/firmware/plugin_basic.py b/common/src/stack/command/stack/commands/remove/firmware/plugin_basic.py
deleted file mode 100644
index e26c8d9d3..000000000
--- a/common/src/stack/command/stack/commands/remove/firmware/plugin_basic.py
+++ /dev/null
@@ -1,85 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-from stack.util import unique_everseen, lowered
-from pathlib import Path
-from contextlib import suppress
-
-class Plugin(stack.commands.Plugin):
- """Attempts to remove all provided firmware versions for the given make and model from the database and the file system."""
-
- def provides(self):
- return "basic"
-
- def validate_inputs(self, make, model, versions):
- """Validate that the provided inputs are valid."""
- # process make if present
- if make:
- self.owner.ensure_make_exists(make = make)
- # process model if present
- if model:
- self.owner.ensure_model_exists(make = make, model = model)
- # Process versions if present
- if versions:
- self.owner.ensure_firmwares_exist(make = make, model = model, versions = versions)
-
- def build_query(self, make, model, versions):
- """Build the select query based on the arguments."""
- query = """
- firmware.id, firmware.file
- FROM firmware
- INNER JOIN firmware_model
- ON firmware.model_id=firmware_model.id
- INNER JOIN firmware_make
- ON firmware_model.make_id=firmware_make.id
- WHERE {}
- """
- query_params = []
- # If versions are specified, get the specific versions to remove
- if versions:
- query = query.format("firmware.version IN %s AND firmware_make.name=%s AND firmware_model.name=%s")
- query_params.extend((versions, make, model))
- # Else if make and model are specified, remove all firmwares for that make and model
- elif make and model:
- query = query.format("firmware_make.name=%s AND firmware_model.name=%s")
- query_params.extend((make, model))
- # Else if make is specified, remove all firmwares for that make
- elif make:
- query = query.format("firmware_make.name=%s")
- query_params.extend((make,))
- # otherwise remove all firmware
- else:
- query = "firmware.id, firmware.file FROM firmware"
-
- return query, query_params
-
- def run(self, args):
- params, args = args
- versions = tuple(unique_everseen(lowered(args)))
- make, model = lowered(
- self.owner.fillParams(
- names = [("make", ""), ("model", "")],
- params = params,
- )
- )
- self.validate_inputs(make = make, model = model, versions = versions)
-
- # Get the query and query_params based on the arguments and parameters.
- query, query_params = self.build_query(make = make, model = model, versions = versions)
- # remove the file and then the db entry for each firmware to remove
- for firmware_id, file_path in ((row[0], row[1]) for row in self.owner.db.select(query, query_params)):
- # If the file doesn't exist, that's ok since we were trying to delete it anyways.
- with suppress(FileNotFoundError):
- Path(file_path).resolve(strict = True).unlink()
-
- self.owner.db.execute("DELETE FROM firmware WHERE id=%s", firmware_id)
diff --git a/common/src/stack/command/stack/commands/remove/firmware/version_regex/__init__.py b/common/src/stack/command/stack/commands/remove/firmware/version_regex/__init__.py
deleted file mode 100644
index 0328583d6..000000000
--- a/common/src/stack/command/stack/commands/remove/firmware/version_regex/__init__.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2019 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-
-class Command(stack.commands.remove.firmware.command):
- """
- Removes firmware version_regexes from the stacki database.
-
-
- One or more version_regexes to remove.
-
-
-
- Removes the mellanox_version and intel_version version_regexes from the database.
-
- """
-
- def run(self, params, args):
- self.runPlugins(args = args)
diff --git a/common/src/stack/command/stack/commands/remove/firmware/version_regex/plugin_basic.py b/common/src/stack/command/stack/commands/remove/firmware/version_regex/plugin_basic.py
deleted file mode 100644
index 59ccc6931..000000000
--- a/common/src/stack/command/stack/commands/remove/firmware/version_regex/plugin_basic.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2019 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-from stack.util import lowered, unique_everseen
-from stack.exception import ArgRequired
-
-class Plugin(stack.commands.Plugin):
- """Attempts to remove version_regexes."""
-
- def provides(self):
- return "basic"
-
- def run(self, args):
- # lowercase all args and remove any duplicates
- names = tuple(unique_everseen(lowered(args)))
- # The version_regexes must exist
- self.owner.ensure_version_regexes_exist(names = names)
-
- # remove the version_regexes
- self.owner.db.execute("DELETE FROM firmware_version_regex WHERE name IN %s", (names,))
diff --git a/common/src/stack/command/stack/commands/remove/host/firmware/__init__.py b/common/src/stack/command/stack/commands/remove/host/firmware/__init__.py
deleted file mode 100644
index 6defb7b20..000000000
--- a/common/src/stack/command/stack/commands/remove/host/firmware/__init__.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2019 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-from stack.argument_processors.firmware import FirmwareArgProcessor
-import stack.commands
-
-class command(stack.commands.remove.host.command, FirmwareArgProcessor):
- pass
diff --git a/common/src/stack/command/stack/commands/remove/host/firmware/mapping/__init__.py b/common/src/stack/command/stack/commands/remove/host/firmware/mapping/__init__.py
deleted file mode 100644
index 5c862171b..000000000
--- a/common/src/stack/command/stack/commands/remove/host/firmware/mapping/__init__.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2019 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-
-class Command(stack.commands.remove.host.firmware.command):
- """
- Removes firmware mappings.
-
-
- Zero or more hosts to have their mapped firmware versions removed.
- If no hosts are specified, all mappings for all hosts are removed.
-
-
-
- Zero or more firmware versions to be unmapped. Multiple versions should be specified as a comma separated list.
- If no versions are specified, all will be removed.
- If one or more versions are specified, the make and model parameters are required.
-
-
-
- The optional make of the firmware to unmap.
- If this is specified but no versions or model are specified, this will remove all firmware mappings that match the make.
-
-
-
- The optional model of the firmware to unmap.
- If this is specified, make is required.
- If no versions are specified but make and model are specified, all firmwares for that make and model will be removed.
-
-
-
- Removes all firmware mappings for all hosts.
-
-
-
- Unmaps the firmware versions 3.6.5002 and 3.6.8010 for the mellanox m7800 make and model from the hosts named switch-13-11 and switch-13-12.
-
-
-
- Unmaps all firmware for the mellanox make and m7800 model from all hosts.
-
-
-
- Unmaps all of the firmware for the mellanox make from the host named switch-13-11.
-
-
-
- Unmaps all of the firmware for the mellanox make from all hosts.
-
- """
-
- def run(self, params, args):
- self.runPlugins(args = (params, args))
diff --git a/common/src/stack/command/stack/commands/remove/host/firmware/mapping/plugin_basic.py b/common/src/stack/command/stack/commands/remove/host/firmware/mapping/plugin_basic.py
deleted file mode 100644
index 33a66430f..000000000
--- a/common/src/stack/command/stack/commands/remove/host/firmware/mapping/plugin_basic.py
+++ /dev/null
@@ -1,145 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-from stack.util import unique_everseen, lowered
-from stack.exception import ArgError, ParamError, ParamRequired, CommandError
-
-class Plugin(stack.commands.Plugin):
- """Attempts to remove firmware mappings based on the provided arguments."""
-
- def provides(self):
- return "basic"
-
- def validate_make(self, make):
- """If the make is provided, ensure it exists."""
- if make:
- # ensure the make exists
- self.owner.ensure_make_exists(make = make)
-
- def validate_model(self, make, model):
- """If the model is provided, ensure the make and model exist."""
- if model:
- # ensure the model exists
- self.owner.ensure_model_exists(make = make, model = model)
-
- def get_firmware_mappings_to_remove(self, hosts, versions, make, model):
- """Gets the mappings to remove using the provided arguments as a filter."""
- # If specific hosts are specified, build the query based on host.
- if hosts:
- query = """
- firmware_mapping.id
- FROM firmware_mapping
- INNER JOIN nodes
- ON firmware_mapping.node_id = nodes.ID
- INNER JOIN firmware
- ON firmware_mapping.firmware_id = firmware.id
- INNER JOIN firmware_model
- ON firmware.model_id = firmware_model.id
- INNER JOIN firmware_make
- ON firmware_model.make_id = firmware_make.id
- WHERE nodes.Name IN %s {}
- """
- query_params = [hosts]
- # If versions and hosts are specified, get the specific mappings to remove for the hosts
- if versions:
- query = query.format(
- "AND firmware.version IN %s AND firmware_make.name=%s AND firmware_model.name=%s",
- )
- query_params.extend((versions, make, model))
- # Else if make, model, and hosts are specified, remove all mappings for that make and model for the specified hosts
- elif make and model:
- query = query.format("AND firmware_make.name=%s AND firmware_model.name=%s")
- query_params.extend((make, model))
- # Else if make and hosts are specified, remove all mappings for that make for the specified hosts.
- elif make:
- query = query.format("AND firmware_make.name=%s")
- query_params.extend((make,))
- # Else only hosts are specified, so remove all firmware mappings from the specified hosts
- else:
- query = query.format("")
- # If no hosts are specified, build the query based solely on the other params.
- else:
- query = """
- firmware_mapping.id
- FROM firmware_mapping
- INNER JOIN firmware
- ON firmware_mapping.firmware_id = firmware.id
- INNER JOIN firmware_model
- ON firmware.model_id = firmware_model.id
- INNER JOIN firmware_make
- ON firmware_model.make_id = firmware_make.id
- {}
- """
- query_params = []
- # If versions are specified, get all mappings for the specified firmware versions.
- if versions:
- query = query.format("WHERE firmware.version IN %s AND firmware_make.name=%s AND firmware_model.name=%s")
- query_params.extend((versions, make, model))
- # Else if make and model are specified, remove all mappings for that make and model for all hosts
- elif make and model:
- query = query.format("WHERE firmware_make.name=%s AND firmware_model.name=%s")
- query_params.extend((make, model))
- # Else if make is specified, remove all mappings for that make for all hosts.
- elif make:
- query = query.format("WHERE firmware_make.name=%s")
- query_params.extend((make,))
- # otherwise remove all mappings
- else:
- query = "firmware_mapping.id FROM firmware_mapping"
-
- return [row[0] for row in self.owner.db.select(query, query_params)]
-
- def run(self, args):
- params, args = args
- hosts = tuple(unique_everseen(lowered(args)))
- # process hosts if present
- if hosts:
- # hosts must exist
- hosts = self.owner.getHosts(args = hosts)
-
- make, model, versions = lowered(
- self.owner.fillParams(
- names = [
- ("make", ""),
- ("model", ""),
- ("versions", ""),
- ],
- params = params,
- ),
- )
- # process make if present
- self.validate_make(make = make)
- # process model if present
- self.validate_model(make = make, model = model)
- # Process versions if present
- if versions:
- # turn a comma separated string into a list of versions and
- # get rid of any duplicate names
- versions = tuple(
- unique_everseen(
- (version.strip() for version in versions.split(",") if version.strip())
- )
- )
- # ensure the versions exist
- self.owner.ensure_firmwares_exist(make = make, model = model, versions = versions)
-
- mappings_to_remove = self.get_firmware_mappings_to_remove(
- hosts = hosts,
- versions = versions,
- make = make,
- model = model,
- )
-
- # remove the mappings
- if mappings_to_remove:
- self.owner.db.execute("DELETE FROM firmware_mapping WHERE id IN %s", (mappings_to_remove,))
diff --git a/common/src/stack/command/stack/commands/set/firmware/__init__.py b/common/src/stack/command/stack/commands/set/firmware/__init__.py
deleted file mode 100644
index 1f55f2747..000000000
--- a/common/src/stack/command/stack/commands/set/firmware/__init__.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2019 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-from stack.argument_processors.firmware import FirmwareArgProcessor
-import stack.commands
-
-class command(stack.commands.set.command, FirmwareArgProcessor):
- pass
diff --git a/common/src/stack/command/stack/commands/set/firmware/make/__init__.py b/common/src/stack/command/stack/commands/set/firmware/make/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/common/src/stack/command/stack/commands/set/firmware/make/version_regex/__init__.py b/common/src/stack/command/stack/commands/set/firmware/make/version_regex/__init__.py
deleted file mode 100644
index 4955b0450..000000000
--- a/common/src/stack/command/stack/commands/set/firmware/make/version_regex/__init__.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2019 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-
-class Command(stack.commands.set.firmware.command):
- """
- Associates a firmware version_regex with one or more makes.
-
-
- One or more makes to associate the version_regex with.
-
-
-
- The name of the version_regex to associate with the provided makes.
-
-
-
- Sets the firmware make mellanox to use the mellanox_version regex when parsing version numbers.
-
- """
-
- def run(self, params, args):
- self.runPlugins(args = (params, args))
diff --git a/common/src/stack/command/stack/commands/set/firmware/make/version_regex/plugin_basic.py b/common/src/stack/command/stack/commands/set/firmware/make/version_regex/plugin_basic.py
deleted file mode 100644
index 621f9373e..000000000
--- a/common/src/stack/command/stack/commands/set/firmware/make/version_regex/plugin_basic.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2019 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-from stack.util import lowered, unique_everseen
-from stack.exception import ArgRequired, ParamRequired, ParamError
-
-class Plugin(stack.commands.Plugin):
- """Attempts to associate a version_regex with makes."""
-
- def provides(self):
- return "basic"
-
- def run(self, args):
- params, args = args
- makes = tuple(unique_everseen(lowered(args)))
-
- version_regex, = lowered(
- self.owner.fillParams(names = [("version_regex", "")], params = params),
- )
-
- # The makes must exist
- self.owner.ensure_makes_exist(makes = makes)
- # The version_regex must exist
- self.owner.ensure_version_regex_exists(name = version_regex)
-
- # get the version_regex ID
- version_regex_id = self.owner.get_version_regex_id(name = version_regex)
- # associate the makes with the version_regex
- self.owner.db.execute(
- "UPDATE firmware_make SET version_regex_id=%s WHERE name in %s",
- (version_regex_id, makes),
- )
diff --git a/common/src/stack/command/stack/commands/set/firmware/model/__init__.py b/common/src/stack/command/stack/commands/set/firmware/model/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/common/src/stack/command/stack/commands/set/firmware/model/imp/__init__.py b/common/src/stack/command/stack/commands/set/firmware/model/imp/__init__.py
deleted file mode 100644
index 730d40eb6..000000000
--- a/common/src/stack/command/stack/commands/set/firmware/model/imp/__init__.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2019 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-
-class Command(stack.commands.set.firmware.command):
- """
- Associates a firmware implementation with one or more models.
-
-
- One or more models to associate the implementation with.
-
-
-
- The name of the implementation to associate with the provided models.
-
-
-
- The make of the models.
-
-
-
- Sets the mellanox_6xxx_7xxx implementation as the one to run for the models m7800 and m6036 for make mellanox.
-
- """
-
- def run(self, params, args):
- self.runPlugins(args = (params, args))
diff --git a/common/src/stack/command/stack/commands/set/firmware/model/imp/plugin_basic.py b/common/src/stack/command/stack/commands/set/firmware/model/imp/plugin_basic.py
deleted file mode 100644
index ad3f9bdf2..000000000
--- a/common/src/stack/command/stack/commands/set/firmware/model/imp/plugin_basic.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2019 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-from stack.util import unique_everseen, lowered
-from stack.exception import ArgRequired, ParamRequired, ParamError
-
-class Plugin(stack.commands.Plugin):
- """Attempts to associate an implementation with models."""
-
- def provides(self):
- return "basic"
-
- def run(self, args):
- params, args = args
- models = tuple(unique_everseen(lowered(args)))
-
- imp, make, = lowered(
- self.owner.fillParams(
- names = [
- ("imp", ""),
- ("make", ""),
- ],
- params = params,
- ),
- )
- self.owner.ensure_models_exist(make = make, models = models)
- self.owner.ensure_imp_exists(imp = imp)
-
- # get the implementation ID
- imp_id = self.owner.get_imp_id(imp = imp)
- # associate the models with the imp
- self.owner.db.execute(
- """
- UPDATE firmware_model
- INNER JOIN firmware_make
- ON firmware_model.make_id = firmware_make.id
- SET firmware_model.imp_id=%s
- WHERE firmware_make.name = %s AND firmware_model.name IN %s
- """,
- (imp_id, make, models),
- )
diff --git a/common/src/stack/command/stack/commands/set/firmware/model/version_regex/__init__.py b/common/src/stack/command/stack/commands/set/firmware/model/version_regex/__init__.py
deleted file mode 100644
index 14d26727f..000000000
--- a/common/src/stack/command/stack/commands/set/firmware/model/version_regex/__init__.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2019 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-
-class Command(stack.commands.set.firmware.command):
- """
- Associates a firmware version_regex with one or more models
-
-
- One or more models to associate the version_regex with.
-
-
-
- The make of the provided models.
-
-
-
- The name of the version_regex to associate with the provided models.
-
-
-
- Sets the firmware models m7800 and m6036 for make mellanox to use the mellanox_version regex when parsing version numbers.
-
- """
-
- def run(self, params, args):
- self.runPlugins(args = (params, args))
diff --git a/common/src/stack/command/stack/commands/set/firmware/model/version_regex/plugin_basic.py b/common/src/stack/command/stack/commands/set/firmware/model/version_regex/plugin_basic.py
deleted file mode 100644
index ebe0f8da1..000000000
--- a/common/src/stack/command/stack/commands/set/firmware/model/version_regex/plugin_basic.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2019 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-from stack.util import lowered, unique_everseen
-from stack.exception import ArgRequired, ParamRequired, ParamError
-
-class Plugin(stack.commands.Plugin):
- """Attempts to associate a version_regex with models."""
-
- def provides(self):
- return "basic"
-
- def run(self, args):
- params, args = args
- # Lowercase and make all args unique.
- models = tuple(unique_everseen(lowered(args)))
-
- make, version_regex, = lowered(
- self.owner.fillParams(
- names = [
- ("make", ""),
- ("version_regex", ""),
- ],
- params = params,
- ),
- )
- self.owner.ensure_models_exist(make = make, models = models)
- self.owner.ensure_version_regex_exists(name = version_regex)
-
- # get the version_regex ID
- version_regex_id = self.owner.get_version_regex_id(name = version_regex)
- # associate the models with the version_regex
- self.owner.db.execute(
- """
- UPDATE firmware_model
- INNER JOIN firmware_make ON firmware_make.id = firmware_model.make_id
- SET firmware_model.version_regex_id=%s WHERE firmware_model.name IN %s AND firmware_make.name=%s
- """,
- (version_regex_id, models, make),
- )
diff --git a/common/src/stack/command/stack/commands/sync/firmware/__init__.py b/common/src/stack/command/stack/commands/sync/firmware/__init__.py
deleted file mode 100644
index ed2b0d41c..000000000
--- a/common/src/stack/command/stack/commands/sync/firmware/__init__.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# @copyright@
-# Copyright (c) 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-
-from stack.argument_processors.firmware import FirmwareArgProcessor
-import stack.commands
-
-class command(stack.commands.sync.command, FirmwareArgProcessor):
- pass
-
-
-class Command(command):
- """
- Syncs the firmware files on the frontend with what the expected firmware is in the stacki database
-
-
- Zero or more firmware versions to sync. If none are specified, all firmware files tracked by stacki will be synced.
-
-
-
- The make of the firmware versions to be synced. This is required if version arguments are specified.
-
-
-
- The model of the firmware versions to be synced. This is required if version arguments are specified.
-
-
-
- Makes sure the firmware file with version 3.6.8010 for Mellanox m7800 devices exists on the filesystem
- and has the correct hash. It will be re-fetched from the source if necessary.
-
-
-
- Syncs all known firmware files, checking that they exist on the filesystem and have the correct hash.
- They will be re-fetched from the source if necessary.
-
- """
-
- def run(self, params, args):
- self.notify('Sync Firmware')
- self.runPlugins(args = (params, args))
diff --git a/common/src/stack/command/stack/commands/sync/firmware/plugin_basic.py b/common/src/stack/command/stack/commands/sync/firmware/plugin_basic.py
deleted file mode 100644
index ed44d0174..000000000
--- a/common/src/stack/command/stack/commands/sync/firmware/plugin_basic.py
+++ /dev/null
@@ -1,92 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-from pathlib import Path
-import stack.commands
-import stack.firmware
-from stack.util import unique_everseen
-from stack.exception import ArgError, ParamRequired, CommandError
-
-class Plugin(stack.commands.Plugin):
- """Attempts to sync firmware files on the filesystem with what is in the stacki database"""
-
- def provides(self):
- return 'basic'
-
- def run(self, args):
- params, args = args
- make, model = self.owner.fillParams(
- names = [
- ('make', None),
- ('model', None)
- ],
- params = params
- )
- # get rid of any duplicate names
- versions = tuple(unique_everseen(args))
- # set up for a potential where clause
- where_clause = ""
- query_args = tuple()
-
- if versions:
- # ensure the versions exist in the DB
- self.owner.ensure_firmwares_exist(make = make, model = model, versions = versions)
- # limit query to the selected versions
- where_clause = "WHERE firmware.version IN %s AND firmware_make.name=%s AND firmware_model.name=%s"
- query_args = (versions, make, model)
-
- for row in self.owner.db.select(
- f'''
- firmware.version, firmware.source, firmware.hash_alg, firmware.hash, firmware.file, firmware_make.name, firmware_model.name
- FROM firmware
- INNER JOIN firmware_model
- ON firmware.model_id=firmware_model.id
- INNER JOIN firmware_make
- ON firmware_model.make_id=firmware_make.id
- {where_clause}
- ''',
- query_args,
- ):
- version, source, hash_alg, hash_value, local_file, make, model = row
- local_file = Path(local_file)
- # check that the local file exists, and fetch it if not
- if not local_file.exists():
- try:
- local_file = stack.firmware.fetch_firmware(
- source = source,
- make = make,
- model = model,
- filename = local_file.name
- )
- except stack.firmware.FirmwareError as exception:
- raise CommandError(
- cmd = self.owner,
- msg = f'Error while fetching version {version} for make {make} and model {model}: {exception}'
- )
- # verify the hash
- try:
- stack.firmware.calculate_hash(file_path = local_file, hash_alg = hash_alg, hash_value = hash_value)
- except stack.firmware.FirmwareError as exception:
- raise CommandError(
- cmd = self.owner,
- msg = f'Error during file verification: {exception}'
- )
-
- # prune any files that shouldn't be there
- files_expected = [
- Path(file_path).resolve()
- for file_path in (row[0] for row in self.owner.db.select('firmware.file FROM firmware'))
- ]
- for file_path in stack.firmware.BASE_PATH.glob('**/*'):
- file_path = file_path.resolve(strict = True)
- if file_path.is_file() and file_path not in files_expected:
- file_path.unlink()
diff --git a/common/src/stack/command/stack/commands/sync/host/firmware/__init__.py b/common/src/stack/command/stack/commands/sync/host/firmware/__init__.py
deleted file mode 100644
index da3b016f3..000000000
--- a/common/src/stack/command/stack/commands/sync/host/firmware/__init__.py
+++ /dev/null
@@ -1,238 +0,0 @@
-# @copyright@
-# Copyright (c) 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-
-import re
-from pathlib import Path
-from collections import namedtuple
-from dataclasses import make_dataclass
-import itertools
-from stack.argument_processors.firmware import FirmwareArgProcessor
-import stack.commands
-import stack.firmware
-
-class Command(stack.commands.sync.host.command, FirmwareArgProcessor):
- """
- Syncs firmware to hosts that are compatible with the firmware
-
-
- Zero or more hosts to sync. If none are specified, all hosts will have their firmware synced.
-
-
-
- Force the firmware update process to run for hosts that are already in sync.
-
-
-
- If a compatible firmware version is tracked by stacki, the firmware will be synced to switch-18-11.
-
-
-
- For each host, if a compatible firmware version is tracked by stacki, it will be synced to the host.
-
- """
-
- def _get_invalid_mappings(self, results, force, host_current_firmwares):
- """Processes the result set and excludes any invalid mappings.
-
- This is where, if force is not set, multiple firmware versions for the same host + make + model
- are rejected and already up to date hosts are skipped. This will also perform the version regex
- validation if one is found.
- """
- invalid_mappings = set()
- for host_make_model, firmware_info in results.items():
- # don't allow multiple firmware versions for a host + make + model combo unless force is specified.
- if not force and len(firmware_info.firmware_files) > 1:
- invalid_mappings.add(host_make_model)
- self.notify(
- f"The host + make + model combination of {host_make_model.host} {host_make_model.make} {host_make_model.model}"
- f" has multiple firmware versions mapped to it and is being skipped. Check your configuration or use force=true to override."
- )
- continue
-
- # Ensure that if a version regex exists, that we check the version in the database matches.
- # Since they are optional, it could have been set after the fact and a bad version number was
- # snuck in.
- regex_obj = self.try_get_version_regex(
- make = host_make_model.make,
- model = host_make_model.model,
- )
- for firmware_file in firmware_info.firmware_files:
- # force does not override the regex check because version_regexes are optional.
- if regex_obj and not re.search(regex_obj.regex, firmware_file.version, re.IGNORECASE):
- invalid_mappings.add(host_make_model)
- self.notify(
- f"Skipping The host + make + model combination of {host_make_model.host} {host_make_model.make} {host_make_model.model}"
- f" because the firmware version {firmware_file.version} does not validate using the version_regex named {regex_obj.name}."
- f" Check that your version_regex is correct using 'stack list firmware version_regex'."
- )
- break
- # don't upgrade to the same version for a host + make + model combo unless force is specified.
- if not force and firmware_file.version == host_current_firmwares[host_make_model]:
- invalid_mappings.add(host_make_model)
- self.notify(
- f"Skipping The host + make + model combination of {host_make_model.host} {host_make_model.make} {host_make_model.model}"
- f" because the current version {host_current_firmwares[host_make_model]} matches the desired version {firmware_file.version}"
- f" Use force=true to override."
- )
- break
-
- return invalid_mappings
-
- def _sync_required_firmware_files(self, results):
- """Ensures the required firmware files are on disk based on the resulting firmware information to sync."""
- # build a dictionary of make + model mapped to versions
- keyfunc = lambda items: (items[0].make, items[0].model)
- firmware_files_to_sync = {
- make_model: [
- firmware_file.version
- for _, firmware_info in values
- for firmware_file in firmware_info.firmware_files
- ]
- for make_model, values in itertools.groupby(
- sorted(results.items(), key = keyfunc),
- key = keyfunc,
- )
- }
- # sync all firmware we need first to ensure the local filesystem
- # is consistent with what the DB
- for make_model, versions in firmware_files_to_sync.items():
- self.call(command = 'sync.firmware', args = [*versions, f"make={make_model[0]}", f"model={make_model[1]}"])
-
- def _get_host_firmwares(self, common_key, hosts, host_attrs, host_current_firmwares, force):
- """Get all the firmware information from the database needed to perform the sync operation."""
- results = {}
- # The firmware information for a given host + make + model combination.
- FirmwareInfo = make_dataclass(
- "FirmwareInfo", (
- # Can potentially support multiple firmware files
- "firmware_files",
- "current_version",
- "imp",
- "host_attrs",
- "force",
- "frontend_ip",
- )
- )
- # The information for the firmware file to be applied.
- FirmwareFile = make_dataclass(
- "FirmwareFile", (
- "file",
- "version",
- "url",
- )
- )
- for row in self.db.select(
- """
- nodes.Name, firmware_make.name, firmware_model.name, firmware.version, firmware.file, firmware_imp.name
- FROM firmware_mapping
- INNER JOIN nodes
- ON firmware_mapping.node_id = nodes.ID
- INNER JOIN firmware
- ON firmware_mapping.firmware_id = firmware.id
- INNER JOIN firmware_model
- ON firmware.model_id = firmware_model.id
- INNER JOIN firmware_make
- ON firmware_model.make_id = firmware_make.id
- INNER JOIN firmware_imp
- ON firmware_model.imp_id = firmware_imp.id
- WHERE nodes.Name IN %s
- """,
- (hosts,)
- ):
- host_make_model = common_key(*row[:3])
- version, firmware_file, imp = row[3:]
- path = Path(firmware_file).resolve()
- # If the key does not already exist in the map, add it. Otherwise, update the existing information.
- if host_make_model in results:
- results[host_make_model].firmware_files.append(
- FirmwareFile(
- file = path,
- version = version,
- # This will be resolved later after pruning invalid mappings.
- url = None,
- )
- )
- else:
- results[host_make_model] = FirmwareInfo(
- current_version = host_current_firmwares[host_make_model],
- imp = imp,
- host_attrs = host_attrs[host_make_model.host],
- force = force,
- frontend_ip = self.get_common_frontend_ip(hostname = host_make_model.host),
- firmware_files = [
- FirmwareFile(
- file = path,
- version = version,
- # This will be resolved later after pruning invalid mappings.
- url = None,
- )
- ]
- )
-
- # Perform validation now that we have all the info from the DB
- invalid_mappings = self._get_invalid_mappings(
- results = results,
- force = force,
- host_current_firmwares = host_current_firmwares,
- )
- # Drop the invalid mappings.
- for host_make_model in invalid_mappings:
- results.pop(host_make_model)
-
- # Notify about the hosts being skipped because of no mapped firmware.
- hosts_with_mapped_firmware = [host_make_model.host for host_make_model in results]
- for host in hosts:
- if host not in hosts_with_mapped_firmware:
- self.notify(
- f"Skipping {host} because no firmware is mapped to it."
- )
-
- # Make sure the files we need exist on disk based on the results.
- # We do this after the results are pruned because we don't want to run a bunch of
- # potentially long file fetch operations on files we aren't going to use.
- self._sync_required_firmware_files(results)
-
- # Finally, post process the remaining mappings to insert data we skipped adding earlier.
- # We had to wait because `get_firmware_url` requires that the path resolves to an existing
- # file on disk, which wasn't guaranteed until we called `_sync_required_firmware_files`.
- for host_make_model, firmware_info in results.items():
- for firmware_file in firmware_info.firmware_files:
- firmware_file.url = self.get_firmware_url(
- hostname = host_make_model.host,
- firmware_file = firmware_file.file,
- )
-
- return results
-
- def run(self, params, args):
- self.notify('Sync Host Firmware')
- hosts = self.getHostnames(names = args)
- force, = self.fillParams(
- names = [('force', False)],
- params = params
- )
- force = self.str2bool(force)
-
- host_attrs = self.getHostAttrDict(host = hosts)
- # grab all the current firmware versions
- CommonKey = namedtuple("CommonKey", ("host", "make", "model"))
- current_firmware_versions = {
- CommonKey(result["host"], result["make"], result["model"]): result["current_firmware_version"]
- for result in self.call(command = 'list.host.firmware', args = hosts)
- }
-
- # get the firmware info for all hosts
- results = self._get_host_firmwares(
- common_key = CommonKey,
- hosts = hosts,
- host_attrs = host_attrs,
- host_current_firmwares = current_firmware_versions,
- force = force,
- )
-
- # only run plugins if we have hosts to sync.
- if results:
- self.runPlugins(args = results)
diff --git a/common/src/stack/command/stack/commands/sync/host/firmware/imp_dell_x1052.py b/common/src/stack/command/stack/commands/sync/host/firmware/imp_dell_x1052.py
deleted file mode 100644
index ccfde322b..000000000
--- a/common/src/stack/command/stack/commands/sync/host/firmware/imp_dell_x1052.py
+++ /dev/null
@@ -1,120 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-from concurrent.futures import ThreadPoolExecutor
-import syslog
-import stack.commands
-from stack.exception import CommandError
-from stack.switch.x1052 import SwitchDellX1052
-from stack.switch import SwitchException
-from stack.expectmore import ExpectMoreException
-
-class Implementation(stack.commands.Implementation):
-
- def update_firmware(self, switch_name, tftp_ip, software_file_path = None, boot_file_path = None, **kwargs):
- """Updates the software and/or boot firmware on the provided switch."""
- # No need to continue if we weren't provided any software to upgrade
- if software_file_path is None and boot_file_path is None:
- return
-
- try:
- x1052_switch = SwitchDellX1052(switch_ip_address = switch_name, switchname = switch_name, **kwargs)
- x1052_switch.set_tftp_ip(ip = tftp_ip)
- x1052_switch.connect()
- # Upload the new software to the switch
- if software_file_path is not None:
- x1052_switch.upload_software(software_file = software_file_path)
-
- # Upload the new boot firmware to the switch
- if boot_file_path is not None:
- x1052_switch.upload_boot(boot_file = boot_file_path)
-
- # Reboot the switch to apply the updates
- x1052_switch.reload()
- # Turn some potentially verbose and detailed error messages into something more end user friendly
- # while keeping the dirty details available in the logs.
- except (SwitchException, ExpectMoreException) as exception:
- stack.commands.Log(
- message = f"Error during firmware update on {switch_name}: {exception}",
- level = syslog.LOG_ERR
- )
- raise CommandError(
- cmd = self.owner,
- msg = f"Failed to update firmware on {switch_name}."
- )
-
- def run(self, args):
- """Runs the firmware update for each provided Dell x1052 switch in parallel."""
- # Since we can update both the boot and the software version based on the hostname, collapse the
- # "software" and the "boot" models together into one.
- software_key = "software_file_path"
- boot_key = "boot_file_path"
- x1052_firmware_args_per_host = {
- switch_name_make_model.host: {software_key: None, boot_key: None}
- for switch_name_make_model in args
- }
- for switch_name_make_model, firmware_info in args.items():
- # There is no currently known valid multi-file use case for Dell for a given model grouping (x1052-software and/or x1052-boot).
- # We don't care if the user tried to force multiple firmware files down our throats.
- if len(firmware_info.firmware_files) > 1:
- raise CommandError(
- self.owner,
- msg = (
- "Firmware update for Dell switches cannot operate on multiple firmware files at once."
- " Please fix your configuration and try again."
- )
- )
-
- # Generate a set of switch kwargs
- kwargs = {
- "username": firmware_info.host_attrs.get("switch_username"),
- "password": firmware_info.host_attrs.get("switch_password"),
- }
- kwargs = {key: value for key, value in kwargs.items() if value is not None}
-
- # Update the software or boot dictionary key based on the model for this firmware file.
- firmware_file = firmware_info.firmware_files.pop()
- if "boot" in switch_name_make_model.model:
- update_key = boot_key
- else:
- update_key = software_key
-
- x1052_firmware_args_per_host[switch_name_make_model.host].update(
- {update_key: firmware_file.file, "tftp_ip": firmware_info.frontend_ip, **kwargs}
- )
-
- errors = []
- # now run each switch upgrade in parallel
- with ThreadPoolExecutor(thread_name_prefix = "dell_firmware_update") as executor:
- futures = [
- executor.submit(self.update_firmware, switch_name = switch_name, **switch_args)
- for switch_name, switch_args in x1052_firmware_args_per_host.items()
- ]
- # Collect any errors, we don't expect there to be any return values.
- for future in futures:
- errors.append(future.exception())
-
- # drop any Nones returned because of no exceptions and aggregate all remaining errors into one
- error_messages = []
- for error in [error for error in errors if error is not None]:
- # if this looks like a stacki exception type, grab the message from it.
- if hasattr(error, 'message') and callable(getattr(error, 'message')):
- error_messages.append(error.message())
- else:
- error_messages.append(f'{error}')
-
- if error_messages:
- error_message = '\n'.join(error_messages)
- raise CommandError(
- cmd = self.owner,
- msg = f"Errors occurred during Dell firmware upgrade:\n{error_message}"
- )
diff --git a/common/src/stack/command/stack/commands/sync/host/firmware/imp_mellanox.py b/common/src/stack/command/stack/commands/sync/host/firmware/imp_mellanox.py
deleted file mode 100644
index 01e603b3b..000000000
--- a/common/src/stack/command/stack/commands/sync/host/firmware/imp_mellanox.py
+++ /dev/null
@@ -1,147 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-from threading import Timer
-from contextlib import suppress
-from concurrent.futures import ThreadPoolExecutor
-import syslog
-import stack.commands
-from stack.exception import CommandError
-from stack.switch.m7800 import SwitchMellanoxM7800
-from stack.switch import SwitchException
-from stack.expectmore import ExpectMoreException
-
-class Implementation(stack.commands.Implementation):
-
- def update_firmware(self, switch_name, firmware_url, downgrade, **kwargs):
- try:
- # connect to the switch and run the firmware upgrade procedure
- m7800_switch = SwitchMellanoxM7800(switch_name, **kwargs)
- m7800_switch.connect()
- # delete all stored images on the switch before sending ours over
- for image in m7800_switch.show_images().images_fetched_and_available:
- m7800_switch.image_delete(image = image.filename)
-
- m7800_switch.image_fetch(url = firmware_url)
- # install the firmware we just sent to the switch
- m7800_switch.install_firmware(
- # grab the filename from the switch on purpose in case it does something funky with it
- image = m7800_switch.show_images().images_fetched_and_available[0].filename
- )
- # set the switch to boot from our installed image
- m7800_switch.image_boot_next()
- # perform extra downgrade steps if necessary
- if downgrade:
- # need to force a boot, even if the old code parsing the new configuration fails.
- m7800_switch.disable_fallback_reboot()
- m7800_switch.write_configuration()
- m7800_switch.reload()
- # now wait for the switch to come back.
- reconnected = False
- # timeout after 30 minutes. We use a no-op lambda because we just want to know when the timer expired.
- timer = Timer(1800, lambda: ())
- timer.start()
- while timer.is_alive():
- # swallow the expected exceptions while trying to connect to a switch that isn't ready yet.
- with suppress(SwitchException, ExpectMoreException):
- # use the switch as a context manager so every time the connect or factory reset fails,
- # we disconnect from the switch.
- with m7800_switch:
- m7800_switch.connect()
- # now factory reset the switch, which will reboot it again.
- # The successful connect above doesn't seem to guarantee that we can fire a
- # factory reset command, so we try in this loop.
- m7800_switch.factory_reset()
- timer.cancel()
- reconnected = True
-
- if not reconnected:
- raise CommandError(
- cmd = self.owner,
- msg = f'Unable to reconnect {switch_name} to switch while performing downgrade procedure.'
- )
- else:
- m7800_switch.reload()
- # Turn some potentially verbose and detailed error messages into something more end user friendly
- # while keeping the dirty details available in the logs.
- except (SwitchException, ExpectMoreException) as exception:
- stack.commands.Log(
- message = f'Error during firmware update on {switch_name}: {exception}',
- level = syslog.LOG_ERR
- )
- raise CommandError(
- cmd = self.owner,
- msg = f'Failed to update firmware on {switch_name}.'
- )
-
- def run(self, args):
- switch_upgrade_args = {}
- # for each switch, build a set of args
- for switch_name_make_model, firmware_info in args.items():
- # There is no currently known valid multi-file use case for mellanox.
- # We don't care if the user tried to force multiple firmware files down our throats.
- if len(firmware_info.firmware_files) > 1:
- raise CommandError(
- self.owner,
- msg = (
- "Firmware update for mellanox switches cannot operate on multiple firmware files at once."
- " Please fix your configuration and try again."
- )
- )
- firmware_file = firmware_info.firmware_files.pop()
-
- kwargs = {
- 'username': firmware_info.host_attrs.get('switch_username', None),
- 'password': firmware_info.host_attrs.get('switch_password', None)
- }
-
- kwargs = {key: value for key, value in kwargs.items() if value is not None}
-
- notice = f'Syncing firmware {firmware_file.version} for {switch_name_make_model.host}.'
- # check for downgrade as we have to do extra steps
- downgrade = firmware_info.current_version > firmware_file.version
- if downgrade:
- notice += f' This is a downgrade from {firmware_info.current_version} and will perform a factory reset.'
- self.owner.notify(notice)
- # build the args
- switch_upgrade_args[switch_name_make_model.host] = {
- 'firmware_url': firmware_file.url,
- 'downgrade': downgrade,
- **kwargs,
- }
-
- errors = []
- # now run each switch upgrade in parallel
- with ThreadPoolExecutor(thread_name_prefix = 'mellanox_firmware_update') as executor:
- futures = [
- executor.submit(self.update_firmware, switch_name = switch_name, **switch_args)
- for switch_name, switch_args in switch_upgrade_args.items()
- ]
- # Collect any errors, we don't expect there to be any return values.
- for future in futures:
- errors.append(future.exception())
-
- # drop any Nones returned because of no exceptions and aggregate all remaining errors into one
- error_messages = []
- for error in [error for error in errors if error is not None]:
- # if this looks like a stacki exception type, grab the message from it.
- if hasattr(error, 'message') and callable(getattr(error, 'message')):
- error_messages.append(error.message())
- else:
- error_messages.append(f'{error}')
-
- if error_messages:
- error_message = '\n'.join(error_messages)
- raise CommandError(
- cmd = self.owner,
- msg = f"Errors occurred during Mellanox firmware upgrade:\n{error_message}"
- )
diff --git a/common/src/stack/command/stack/commands/sync/host/firmware/plugin_basic.py b/common/src/stack/command/stack/commands/sync/host/firmware/plugin_basic.py
deleted file mode 100644
index fb6cb273c..000000000
--- a/common/src/stack/command/stack/commands/sync/host/firmware/plugin_basic.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# @copyright@
-# Copyright (c) 2006 - 2018 Teradata
-# All rights reserved. Stacki(r) v5.x stacki.com
-# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
-# @copyright@
-#
-# @rocks@
-# Copyright (c) 2000 - 2010 The Regents of the University of California
-# All rights reserved. Rocks(r) v5.4 www.rocksclusters.org
-# https://github.com/Teradata/stacki/blob/master/LICENSE-ROCKS.txt
-# @rocks@
-
-import stack.commands
-from stack.exception import CommandError
-import stack.firmware
-
-class Plugin(stack.commands.Plugin):
- """Attempts to sync firmware to hosts"""
-
- def provides(self):
- return "basic"
-
- def run(self, args):
- mapped_by_imp_name = {}
- # Remap by implementation name in preparation for running implementations in parallel.
- for host_make_model, firmware_info in args.items():
- # update if the imp already exists in the map, otherwise add a new key.
- if firmware_info.imp in mapped_by_imp_name:
- mapped_by_imp_name[firmware_info.imp].update(
- {host_make_model: firmware_info}
- )
- else:
- mapped_by_imp_name[firmware_info.imp] = {host_make_model: firmware_info}
-
-
- # we don't expect return values, but the implementations might raise exceptions, so gather them here
- results = self.owner.run_implementations_parallel(
- implementation_mapping = mapped_by_imp_name,
- display_progress = True,
- )
- # drop any results that didn't have any errors and aggregate the rest into one exception
- error_messages = []
- for error in (
- value.exception for value in results.values()
- if value is not None and value.exception is not None
- ):
- # if this looks like a stacki exception type, grab the message from it.
- if hasattr(error, 'message') and callable(getattr(error, 'message')):
- error_messages.append(error.message())
- else:
- error_messages.append(f'{error}')
-
- if error_messages:
- error_message = '\n'.join(error_messages)
- raise CommandError(
- cmd = self.owner,
- msg = f"Errors occurred during firmware sync:\n{error_message}"
- )
diff --git a/common/src/stack/pylib/stack/firmware.py b/common/src/stack/pylib/stack/firmware.py
deleted file mode 100644
index 5579be207..000000000
--- a/common/src/stack/pylib/stack/firmware.py
+++ /dev/null
@@ -1,137 +0,0 @@
-import hashlib
-from pathlib import Path
-import uuid
-from urllib.parse import urlparse
-from enum import Enum, auto, unique
-import stack.download
-
-# Base path to store managed firmware files under
-BASE_PATH = Path("/export/stack/firmware/")
-
-@unique
-class SUPPORTED_SCHEMES(Enum):
- """Supported schemes to fetch firmware from a source to be managed by stacki"""
- file = auto()
- http = auto()
- https = auto()
-
- @classmethod
- def pretty_string(cls):
- """Return a nice human readable list of names."""
- return ", ".join(cls.__members__.keys())
-
- def __str__(self):
- """Return the human readable name of the enum when printing or stringifying."""
- return f"{self.name}"
-
-
-# Require the supported hash algorithms to be the always present ones
-SUPPORTED_HASH_ALGS = hashlib.algorithms_guaranteed
-
-class FirmwareError(Exception):
- """The exception type raised by the firmware utilities in this module."""
- pass
-
-def ensure_hash_alg_supported(hash_alg):
- """Ensures that the provided hash algorithm is supported.
-
- If it is not supported, a FirmwareError is raised.
- """
- if hash_alg not in SUPPORTED_HASH_ALGS:
- raise FirmwareError(
- f"hash_alg must be one of the following: {SUPPORTED_HASH_ALGS}"
- )
-
-def calculate_hash(file_path, hash_alg, hash_value = "", digest_length = 256):
- """Calculates the hash of the provided file using the provided algorithm and returns it as a hex string.
-
- hash_alg is required to be one of the SUPPORTED_HASH_ALGS and a FirmwareError will be raised if it is not.
-
- If a hash value is provided, this checks the calculated hash against the provided hash. The hash_value should
- be a string of the form provided by hash.hexdigest(). A FirmwareError is raised if the hashes do not match.
-
- For some algorithms a length is required (shake_128 and shake_256 at the time of this writing). The digest_length
- parameter allows the overriding of the default length used. This parameter is ignored if the algorithm does not
- allow specifying the digest length.
- """
- ensure_hash_alg_supported(hash_alg = hash_alg)
-
- hasher = hashlib.new(name = hash_alg, data = Path(file_path).read_bytes())
- # Handle case where the hash algorithm requires a digest length.
- try:
- calculated_hash = hasher.hexdigest()
- except TypeError:
- calculated_hash = hasher.hexdigest(digest_length)
-
- # check the hash if one was provided to check against
- if hash_value and hash_value != calculated_hash:
- raise FirmwareError(
- f"Calculated hash {calculated_hash} does not match expected hash {hash_value}. Algorithm was {hash_alg}."
- )
-
- return calculated_hash
-
-def fetch_firmware(source, make, model, filename = None, **kwargs):
- """Fetches the firmware file from the provided source and copies it into a stacki managed file.
-
- source should be the URL from which to pull the firmware image from. If this is not one of the
- supported schemes, a FirmwareError is raised.
-
- make and model must be set to the make and model the firmware image applies to.
-
- filename is the optional file name to write the image out to locally. This does not change the
- destination folder, only the file name.
-
- The remaining kwargs will be captured and passed through as necessary to the underlying mechanism
- used to fetch the file from the source. For example, fetching via HTTP might need a username and
- a password for authentication.
-
- A FirmwareError is raised if fetching the file from the source fails.
- """
- # parse the URL to figure out how we're going to fetch it
- url = urlparse(url = source)
-
- # build file path to write out to
- dest_dir = BASE_PATH / make / model
- dest_dir = dest_dir.resolve()
- dest_dir.mkdir(parents = True, exist_ok = True)
- # set a random file name if the name is not set
- final_file = dest_dir / (uuid.uuid4().hex if filename is None else filename)
-
- try:
- scheme = SUPPORTED_SCHEMES[url.scheme]
- except KeyError as exception:
- # Assume if there is no scheme but there is a path,
- # assume that we were passed a local file path.
- if not url.scheme and url.path:
- scheme = SUPPORTED_SCHEMES.file
- else:
- raise FirmwareError(
- f"Scheme {url.scheme} is not supported. The source must use one of the following supported"
- f" schemes: {SUPPORTED_SCHEMES.pretty_string()}."
- ) from exception
-
- if scheme == SUPPORTED_SCHEMES.file:
- # grab the source file and copy it into the destination file
- try:
- source_file = Path(url.path).resolve(strict = True)
- except FileNotFoundError as exception:
- raise FirmwareError(f"{exception}") from exception
-
- final_file.write_bytes(source_file.read_bytes())
-
- elif scheme in (SUPPORTED_SCHEMES.http, SUPPORTED_SCHEMES.https):
- try:
- stack.download.fetch(url = source, file_path = final_file, verbose = True, **kwargs)
- except stack.download.FetchError as exception:
- raise FirmwareError(f"{exception}") from exception
-
- # add more supported schemes here
- # elif scheme == SUPPORTED_SCHEMES.foo:
- else:
- # Case where we forgot to add a elif case for a new scheme that was added.
- raise RuntimeError(
- f"Someone wrote a bug! Code needs to be added to handle the {scheme} scheme."
- )
-
- return final_file
diff --git a/test-framework/test-suites/integration/files/list/mock_list_firmware_run_plugins.py b/test-framework/test-suites/integration/files/list/mock_list_firmware_run_plugins.py
deleted file mode 100644
index 52f31e317..000000000
--- a/test-framework/test-suites/integration/files/list/mock_list_firmware_run_plugins.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from collections import namedtuple
-from unittest.mock import patch
-import stack.commands.sync.host.firmware
-import stack.commands.list.host.firmware.imp_mellanox
-import stack.commands.list.host.firmware.imp_dell_x1052
-
-# Make the mellanox implementation for list host firmware return a low version
-# number that will be a candidate for upgrading.
-mock_mellanox_list_firmware = patch.object(
- target = stack.commands.list.host.firmware.imp_mellanox.Implementation,
- attribute = "list_firmware",
- autospec = True,
-).start()
-mock_mellanox_list_firmware.return_value = "0.0.0"
-
-# Make the dell implementation for list host firmware return a low version
-# number that will be a candidate for upgrading.
-mock_dell_list_firmware = patch.object(
- target = stack.commands.list.host.firmware.imp_dell_x1052.Implementation,
- attribute = "list_firmware",
- autospec = True,
-).start()
-mock_dell_list_firmware.return_value = namedtuple("Versions", ("software", "boot", "hardware"))(
- software = "0.0.0.0",
- boot = "0.0.0.0",
- hardware = "0.0.0.0",
-)
-
-# Mock runPlugins calls
-patch.object(
- target = stack.commands.sync.host.firmware.Command,
- attribute = "runPlugins",
- autospec = True,
-).start()
diff --git a/test-framework/test-suites/integration/files/sync/mock_firmware_run_plugins.py b/test-framework/test-suites/integration/files/sync/mock_firmware_run_plugins.py
deleted file mode 100644
index 52f31e317..000000000
--- a/test-framework/test-suites/integration/files/sync/mock_firmware_run_plugins.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from collections import namedtuple
-from unittest.mock import patch
-import stack.commands.sync.host.firmware
-import stack.commands.list.host.firmware.imp_mellanox
-import stack.commands.list.host.firmware.imp_dell_x1052
-
-# Make the mellanox implementation for list host firmware return a low version
-# number that will be a candidate for upgrading.
-mock_mellanox_list_firmware = patch.object(
- target = stack.commands.list.host.firmware.imp_mellanox.Implementation,
- attribute = "list_firmware",
- autospec = True,
-).start()
-mock_mellanox_list_firmware.return_value = "0.0.0"
-
-# Make the dell implementation for list host firmware return a low version
-# number that will be a candidate for upgrading.
-mock_dell_list_firmware = patch.object(
- target = stack.commands.list.host.firmware.imp_dell_x1052.Implementation,
- attribute = "list_firmware",
- autospec = True,
-).start()
-mock_dell_list_firmware.return_value = namedtuple("Versions", ("software", "boot", "hardware"))(
- software = "0.0.0.0",
- boot = "0.0.0.0",
- hardware = "0.0.0.0",
-)
-
-# Mock runPlugins calls
-patch.object(
- target = stack.commands.sync.host.firmware.Command,
- attribute = "runPlugins",
- autospec = True,
-).start()
diff --git a/test-framework/test-suites/integration/tests/fixtures/reverts.py b/test-framework/test-suites/integration/tests/fixtures/reverts.py
index c4d7ce772..366f48c99 100644
--- a/test-framework/test-suites/integration/tests/fixtures/reverts.py
+++ b/test-framework/test-suites/integration/tests/fixtures/reverts.py
@@ -9,7 +9,6 @@
import pytest
-import stack.firmware
from stack.argument_processors.pallet import PALLET_HOOK_ROOT
@pytest.fixture
@@ -197,17 +196,6 @@ def revert_routing_table():
if route not in new_routes:
result = subprocess.run(f"ip route add {route}", shell=True)
-@pytest.fixture
-def revert_firmware(request):
- """Revert the filesystem where the firmware files get laid down."""
- # Gotta make the directry first before this overlay stuff works.
- stack.firmware.BASE_PATH.mkdir(parents = True, exist_ok = True)
- _add_overlay(stack.firmware.BASE_PATH)
-
- yield
-
- _remove_overlay(stack.firmware.BASE_PATH, request)
-
@pytest.fixture
def revert_pallet_patches(request):
"""Revert the filesystem where pallet patches are laid down."""
diff --git a/test-framework/test-suites/integration/tests/list/test_list_host_firmware.py b/test-framework/test-suites/integration/tests/list/test_list_host_firmware.py
deleted file mode 100644
index 4a6610033..000000000
--- a/test-framework/test-suites/integration/tests/list/test_list_host_firmware.py
+++ /dev/null
@@ -1,60 +0,0 @@
-import json
-import pytest
-
-@pytest.mark.parametrize(
- "hosts, expected_results",
- (
- (
- "",
- [
- {"host": "backend-0-0", "make": "mellanox", "model": "m7800", "desired_firmware_version": "1.2.3", "current_firmware_version": "0.0.0"},
- {"host": "backend-0-1", "make": "dell", "model": "x1052-software", "desired_firmware_version": "1.2.3.4", "current_firmware_version": "0.0.0.0"},
- {"host": "frontend-0-0", "make": None, "model": None, "desired_firmware_version": None, "current_firmware_version": None},
- ],
- ),
- (
- "backend-0-0",
- [{"host": "backend-0-0", "make": "mellanox", "model": "m7800", "desired_firmware_version": "1.2.3", "current_firmware_version": "0.0.0"}],
- ),
- (
- "backend-0-1",
- [{"host": "backend-0-1", "make": "dell", "model": "x1052-software", "desired_firmware_version": "1.2.3.4", "current_firmware_version": "0.0.0.0"}],
- ),
- ),
-)
-def test_list_host_firmware(
- host,
- add_host_with_net,
- fake_local_firmware_file,
- revert_firmware,
- inject_code,
- test_file,
- hosts,
- expected_results,
-):
- """Test that list host firmware filters correctly based on provided arguments."""
- # Add a backend-0-1
- add_host_with_net(
- hostname = "backend-0-1",
- rack = 0,
- rank = 1,
- appliance = "backend",
- interface = "eth0",
- ip = "192.168.1.1",
- network = "fake_net",
- address = "192.168.1.0",
- pxe = True,
- )
- # Add a piece of mellanox firmware to backend-0-0.
- result = host.run(f"stack add firmware 1.2.3 make=mellanox model=m7800 source={fake_local_firmware_file} hosts=backend-0-0")
- assert result.rc == 0
- # Add a piece of dell firmware to backend-0-1
- result = host.run(f"stack add firmware 1.2.3.4 make=dell model=x1052-software source={fake_local_firmware_file} hosts=backend-0-1")
- assert result.rc == 0
-
- # Now list the firmware. We need to mock out the running of plugins so it doesn't
- # actually try to talk to hardware that doesn't exist.
- with inject_code(test_file("list/mock_list_firmware_run_plugins.py")):
- result = host.run(f"stack list host firmware {hosts} output-format=json")
- assert result.rc == 0
- assert json.loads(result.stdout) == expected_results
diff --git a/test-framework/test-suites/integration/tests/list/test_list_host_firmware_mapping.py b/test-framework/test-suites/integration/tests/list/test_list_host_firmware_mapping.py
deleted file mode 100644
index 0a5f301c1..000000000
--- a/test-framework/test-suites/integration/tests/list/test_list_host_firmware_mapping.py
+++ /dev/null
@@ -1,107 +0,0 @@
-import json
-import pytest
-
-@pytest.mark.parametrize(
- "hosts, expected_results",
- (
- (
- "",
- [
- {"host": "backend-0-0", "version": "1.2.3", "make": "mellanox", "model": "m7800"},
- {"host": "backend-0-1", "version": "1.2.3.4", "make": "dell", "model": "x1052-software"},
- ],
- ),
- ("backend-0-0", [{"host": "backend-0-0", "version": "1.2.3", "make": "mellanox", "model": "m7800"}]),
- ("backend-0-1", [{"host": "backend-0-1", "version": "1.2.3.4", "make": "dell", "model": "x1052-software"}]),
- ),
-)
-def test_list_host_firmware_mapping_host_filter(
- host,
- add_host_with_net,
- fake_local_firmware_file,
- revert_firmware,
- hosts,
- expected_results,
-):
- """Test that list host firmware mapping filters correctly based on provided arguments."""
- # Add a backend-0-1
- add_host_with_net(
- hostname = "backend-0-1",
- rack = 0,
- rank = 1,
- appliance = "backend",
- interface = "eth0",
- ip = "192.168.1.1",
- network = "fake_net",
- address = "192.168.1.0",
- pxe = True,
- )
- # Add a piece of mellanox firmware to backend-0-0.
- result = host.run(f"stack add firmware 1.2.3 make=mellanox model=m7800 source={fake_local_firmware_file} hosts=backend-0-0")
- assert result.rc == 0
- # Add a piece of dell firmware to backend-0-1
- result = host.run(f"stack add firmware 1.2.3.4 make=dell model=x1052-software source={fake_local_firmware_file} hosts=backend-0-1")
- assert result.rc == 0
-
- # List the firmware mappings
- result = host.run(f"stack list host firmware mapping {hosts} output-format=json")
- assert result.rc == 0
- assert expected_results == json.loads(result.stdout)
-
-@pytest.mark.parametrize(
- "make, model, versions, expected_results",
- (
- (
- "",
- "",
- "",
- [
- {"host": "backend-0-0", "version": "1.2.3", "make": "mellanox", "model": "m7800"},
- {"host": "backend-0-1", "version": "1.2.3.4", "make": "dell", "model": "x1052-software"},
- ],
- ),
- ("mellanox", "", "", [{"host": "backend-0-0", "version": "1.2.3", "make": "mellanox", "model": "m7800"}]),
- ("mellanox", "m7800", "", [{"host": "backend-0-0", "version": "1.2.3", "make": "mellanox", "model": "m7800"}]),
- ("mellanox", "m7800", "1.2.3", [{"host": "backend-0-0", "version": "1.2.3", "make": "mellanox", "model": "m7800"}]),
- ("dell", "", "", [{"host": "backend-0-1", "version": "1.2.3.4", "make": "dell", "model": "x1052-software"}]),
- ("dell", "x1052-software", "", [{"host": "backend-0-1", "version": "1.2.3.4", "make": "dell", "model": "x1052-software"}]),
- ("dell", "x1052-software", "1.2.3.4", [{"host": "backend-0-1", "version": "1.2.3.4", "make": "dell", "model": "x1052-software"}]),
- ),
-)
-def test_list_host_firmware_mapping_non_host_filter(
- host,
- add_host_with_net,
- fake_local_firmware_file,
- revert_firmware,
- make,
- model,
- versions,
- expected_results,
-):
- """Test that list host firmware mapping filters correctly based on provided arguments."""
- # Add a backend-0-1
- add_host_with_net(
- hostname = "backend-0-1",
- rack = 0,
- rank = 1,
- appliance = "backend",
- interface = "eth0",
- ip = "192.168.1.1",
- network = "fake_net",
- address = "192.168.1.0",
- pxe = True,
- )
- # Add a piece of mellanox firmware to backend-0-0.
- result = host.run(f"stack add firmware 1.2.3 make=mellanox model=m7800 source={fake_local_firmware_file} hosts=backend-0-0")
- assert result.rc == 0
- # Add a piece of dell firmware to backend-0-1
- result = host.run(f"stack add firmware 1.2.3.4 make=dell model=x1052-software source={fake_local_firmware_file} hosts=backend-0-1")
- assert result.rc == 0
-
- # List the firmware mappings
- result = host.run(
- f"stack list host firmware mapping {f'make={make}' if make else ''} {f'model={model}' if model else ''} "
- f"{f'versions={versions}' if versions else ''} output-format=json"
- )
- assert result.rc == 0
- assert expected_results == json.loads(result.stdout)
diff --git a/test-framework/test-suites/integration/tests/sync/test_sync_firmware.py b/test-framework/test-suites/integration/tests/sync/test_sync_firmware.py
deleted file mode 100644
index a58118ebd..000000000
--- a/test-framework/test-suites/integration/tests/sync/test_sync_firmware.py
+++ /dev/null
@@ -1,60 +0,0 @@
-import stack.firmware
-
-def test_sync_firmware_resync(host, fake_local_firmware_file, revert_firmware):
- """Add some firmware, nuke the firmware dir, and then sync firmware and make sure it shows back up."""
- # Add a fake piece of firmware.
- result = host.run(f"stack add firmware 1.2.3 make=mellanox model=m7800 source={fake_local_firmware_file}")
- assert result.rc == 0
-
- # Now find the file on disk and nuke it. The files are given random UUID4 names,
- # so we have to glob for the file.
- firmware_file_on_disk = [
- firmware_file for firmware_file in stack.firmware.BASE_PATH.glob(f"**/*")
- if firmware_file.is_file()
- ]
- assert len(firmware_file_on_disk) == 1
- firmware_file_on_disk = firmware_file_on_disk[0]
- firmware_file_on_disk.unlink()
- assert not firmware_file_on_disk.exists()
-
- # Now run the sync command.
- result = host.run("stack sync firmware")
- assert result.rc == 0
-
- # Make sure the file exists again.
- firmware_file_on_disk = [
- firmware_file for firmware_file in stack.firmware.BASE_PATH.glob(f"**/*")
- if firmware_file.is_file()
- ]
- assert len(firmware_file_on_disk) == 1
- assert firmware_file_on_disk[0].exists()
-
-def test_sync_firmware_selective_resync(host, fake_local_firmware_file, revert_firmware):
- """Add two firmware files, nuke em both, selectively sync one and ensure that's the only one that shows back up."""
- result = host.run(f"stack add firmware 1.2.3 make=mellanox model=m7800 source={fake_local_firmware_file}")
- assert result.rc == 0
- result = host.run(f"stack add firmware 2.3.4 make=mellanox model=m7800 source={fake_local_firmware_file}")
- assert result.rc == 0
-
- # Now find the files on disk and nuke em. The files are given random UUID4 names,
- # so we have to glob for the file.
- firmware_files_on_disk = [
- firmware_file for firmware_file in stack.firmware.BASE_PATH.glob(f"**/*")
- if firmware_file.is_file()
- ]
- assert len(firmware_files_on_disk) == 2
- for firmware_file in firmware_files_on_disk:
- firmware_file.unlink()
- assert not firmware_file.exists()
-
- # Now run the sync command.
- result = host.run(f"stack sync firmware 2.3.4 make=mellanox model=m7800")
- assert result.rc == 0
-
- # Make sure only one file exists
- firmware_files_on_disk = [
- firmware_file for firmware_file in stack.firmware.BASE_PATH.glob(f"**/*")
- if firmware_file.is_file()
- ]
- assert len(firmware_files_on_disk) == 1
- assert firmware_files_on_disk[0].exists()
diff --git a/test-framework/test-suites/integration/tests/sync/test_sync_host_firmware.py b/test-framework/test-suites/integration/tests/sync/test_sync_host_firmware.py
deleted file mode 100644
index 84b80b5ae..000000000
--- a/test-framework/test-suites/integration/tests/sync/test_sync_host_firmware.py
+++ /dev/null
@@ -1,50 +0,0 @@
-import stack.firmware
-
-def test_sync_only_needed_files(host, add_host_with_net, fake_local_firmware_file, inject_code, test_file, revert_firmware):
- """Test that sync host firmware only tries to sync firmware it needs, not all in the database.
-
- This adds multiple firmware versions for the same make + model combo on purpose to exercise clustered syncing of
- firmware files. This also requires passing the force flag to `sync host firmware`.
- """
- # Add multiple piecies of firmware for mellanox, and associate it with the host we are going to sync.
- result = host.run(f"stack add firmware 1.2.3 make=mellanox model=m7800 source={fake_local_firmware_file} hosts=backend-0-0")
- assert result.rc == 0
- result = host.run(f"stack add firmware 2.3.4 make=mellanox model=m7800 source={fake_local_firmware_file} hosts=backend-0-0")
- assert result.rc == 0
- # Add multiple firmwares for dell, and associate it with the host we are going to sync.
- result = host.run(f"stack add firmware 1.2.3.4 make=dell model=x1052-software source={fake_local_firmware_file} hosts=backend-0-0")
- assert result.rc == 0
- result = host.run(f"stack add firmware 2.3.4.5 make=dell model=x1052-software source={fake_local_firmware_file} hosts=backend-0-0")
- assert result.rc == 0
- result = host.run(f"stack add firmware 1.2.3.4 make=dell model=x1052-boot source={fake_local_firmware_file} hosts=backend-0-0")
- assert result.rc == 0
- result = host.run(f"stack add firmware 2.3.4.5 make=dell model=x1052-boot source={fake_local_firmware_file} hosts=backend-0-0")
- assert result.rc == 0
- # Add a piece of firmware we don't care about. Don't associate it with any hosts.
- result = host.run(f"stack add firmware 1.2.3 make=mellanox model=m6036 source={fake_local_firmware_file}")
- assert result.rc == 0
- # Now find the files on disk and nuke em. The files are given random UUID4 names,
- # so we have to glob for the file.
- firmware_files_on_disk = [
- firmware_file for firmware_file in stack.firmware.BASE_PATH.glob(f"**/*")
- if firmware_file.is_file()
- ]
- assert len(firmware_files_on_disk) == 7
- for firmware_file in firmware_files_on_disk:
- firmware_file.unlink()
- assert not firmware_file.exists()
-
- # Now sync the firmware to the host. We need to mock out the running of plugins so it doesn't
- # actually try to sync to hardware that doesn't exist.
- with inject_code(test_file("sync/mock_firmware_run_plugins.py")):
- result = host.run("stack sync host firmware force=true")
- assert result.rc == 0
-
- # Now it should have added back only the firmware for the switch-0-0 host that was to be synced.
- firmware_files_on_disk = [
- firmware_file for firmware_file in stack.firmware.BASE_PATH.glob(f"**/*")
- if firmware_file.is_file()
- ]
- assert len(firmware_files_on_disk) == 6
- for firmware_file in firmware_files_on_disk:
- assert firmware_file.exists()
diff --git a/test-framework/test-suites/unit/tests/command/stack/argument_processors/test_firmware.py b/test-framework/test-suites/unit/tests/command/stack/argument_processors/test_firmware.py
deleted file mode 100644
index fe9adf07b..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/argument_processors/test_firmware.py
+++ /dev/null
@@ -1,691 +0,0 @@
-from unittest.mock import MagicMock, ANY, patch, call
-import pytest
-from stack.argument_processors.firmware import FirmwareArgProcessor
-import stack.commands
-from stack.exception import CommandError
-
-class TestFirmwareArgProcessor:
- """A test case for the firmware argument processor."""
-
- @pytest.fixture
- def argument_processor(self):
- test_argument_processor = FirmwareArgProcessor()
- test_argument_processor.db = MagicMock(spec_set = ["select", "count"])
- return test_argument_processor
-
- def test_get_make_id(self, argument_processor):
- """Test that get make ID works as expected in the normal case."""
- argument_processor.db.select.return_value = [[1]]
- mock_make = "foo"
-
- # Expect our result to be returned.
- assert argument_processor.db.select.return_value[0][0] == argument_processor.get_make_id(
- make = mock_make,
- )
- # Ensure that select was called appropriately.
- argument_processor.db.select.assert_called_once_with(ANY, mock_make)
-
- def test_get_make_id_error(self, argument_processor):
- """Test that get make ID fails if no make exists in the DB with that name."""
- argument_processor.db.select.return_value = []
-
- with pytest.raises(CommandError):
- argument_processor.get_make_id(make = "foo")
-
- @pytest.mark.parametrize("return_value", (0, 1))
- def test_make_exists(self, argument_processor, return_value):
- """Test that make exists returns the correct results."""
- argument_processor.db.count.return_value = return_value
-
- assert return_value == argument_processor.make_exists(make = "foo")
- argument_processor.db.count.assert_called_once_with(ANY, "foo")
-
- @patch.object(target = FirmwareArgProcessor, attribute = "make_exists", autospec = True)
- def test_ensure_make_exists(self, mock_make_exists, argument_processor):
- """Test that ensure make exists works when the input is valid and the make exists in the database."""
- mock_make_exists.return_value = True
- mock_make = "foo"
-
- argument_processor.ensure_make_exists(make = mock_make)
-
- mock_make_exists.assert_called_once_with(argument_processor, make = mock_make)
-
- @pytest.mark.parametrize("test_input, return_value", (("", True), ("foo", False)))
- @patch.object(target = FirmwareArgProcessor, attribute = "make_exists", autospec = True)
- def test_ensure_make_exists_errors(self, mock_make_exists, test_input, return_value, argument_processor):
- """Test that ensure make exists fails when the input is bad or the make doesn't exist in the database."""
- mock_make_exists.return_value = return_value
-
- with pytest.raises(CommandError):
- argument_processor.ensure_make_exists(make = test_input)
-
- @patch.object(target = FirmwareArgProcessor, attribute = "make_exists", autospec = True)
- def test_ensure_unique_makes(self, mock_make_exists, argument_processor):
- """Test that ensure_unique_makes works as expected in the case where the makes don't already exist."""
- mock_make_exists.return_value = False
- mock_makes = ("foo", "bar", "baz")
-
- argument_processor.ensure_unique_makes(makes = mock_makes)
-
- assert [call(argument_processor, mock_make) for mock_make in mock_makes] == mock_make_exists.mock_calls
-
- @pytest.mark.parametrize("test_input, return_value", (([], False), (["", "", ""], False), (["foo", "bar", "baz"], True)))
- @patch.object(target = FirmwareArgProcessor, attribute = "make_exists", autospec = True)
- def test_ensure_unique_makes_error(self, mock_make_exists, test_input, return_value, argument_processor):
- """Test that ensure_unique_makes raises an error if the input is invalid or the makes already exist."""
- mock_make_exists.return_value = return_value
-
- with pytest.raises(CommandError):
- argument_processor.ensure_unique_makes(makes = test_input)
-
- @patch.object(target = FirmwareArgProcessor, attribute = "make_exists", autospec = True)
- def test_ensure_makes_exist(self, mock_make_exists, argument_processor):
- """Test that ensure_makes_exist works as expected in the case where the makes already exist."""
- mock_make_exists.return_value = True
- mock_makes = ("foo", "bar", "baz")
-
- argument_processor.ensure_makes_exist(makes = mock_makes)
-
- assert [call(argument_processor, mock_make) for mock_make in mock_makes] == mock_make_exists.mock_calls
-
- @pytest.mark.parametrize("test_input, return_value", (([], True), (["", "", ""], True), (["foo", "bar", "baz"], False)))
- @patch.object(target = FirmwareArgProcessor, attribute = "make_exists", autospec = True)
- def test_ensure_makes_exist_error(self, mock_make_exists, test_input, return_value, argument_processor):
- """Test that ensure_makes_exist raises an error if the input is invalid or one or more makes don't already exist."""
- mock_make_exists.return_value = return_value
-
- with pytest.raises(CommandError):
- argument_processor.ensure_makes_exist(makes = test_input)
-
- def test_get_model_id(self, argument_processor):
- """Test that get model ID works as expected when the make + model exist in the database."""
- argument_processor.db.select.return_value = [[1]]
- mock_make = "foo"
- mock_model = "bar"
-
- assert argument_processor.db.select.return_value[0][0] == argument_processor.get_model_id(
- make = mock_make,
- model = mock_model,
- )
- argument_processor.db.select.assert_called_once_with(ANY, (mock_make, mock_model))
-
- def test_get_model_id_error(self, argument_processor):
- """Test that get model ID fails as expected when the make + model do not exist in the database."""
- argument_processor.db.select.return_value = []
- mock_make = "foo"
- mock_model = "bar"
-
- with pytest.raises(CommandError):
- argument_processor.get_model_id(make = mock_make, model = mock_model)
-
- @pytest.mark.parametrize("return_value", (0, 1))
- def test_model_exists(self, return_value, argument_processor):
- """Test that model exists returns the correct results."""
- mock_make = "foo"
- mock_model = "bar"
- argument_processor.db.count.return_value = return_value
-
- assert return_value == argument_processor.model_exists(make = mock_make, model = mock_model)
- argument_processor.db.count.assert_called_once_with(ANY, (mock_make, mock_model))
-
- @patch.object(target = FirmwareArgProcessor, attribute = "model_exists", autospec = True)
- @patch.object(target = FirmwareArgProcessor, attribute = "ensure_make_exists", autospec = True)
- def test_ensure_model_exists(self, mock_ensure_make_exists, mock_model_exists, argument_processor):
- """Test that ensure_model_exists works when the make + model both exist."""
- mock_make = "bar"
- mock_model = "foo"
-
- argument_processor.ensure_model_exists(model = mock_model, make = mock_make)
-
- mock_ensure_make_exists.assert_called_once_with(argument_processor, make = mock_make)
- mock_model_exists.assert_called_once_with(argument_processor, make = mock_make, model = mock_model)
-
- @pytest.mark.parametrize(
- "mock_model, side_effect, return_value",
- (
- ("", None, True),
- ("foo", CommandError(cmd = None, msg = "Test error"), True),
- ("foo", None, False),
- )
- )
- @patch.object(target = FirmwareArgProcessor, attribute = "model_exists", autospec = True)
- @patch.object(target = FirmwareArgProcessor, attribute = "ensure_make_exists", autospec = True)
- def test_ensure_model_exists_errors(
- self,
- mock_ensure_make_exists,
- mock_model_exists,
- mock_model,
- side_effect,
- return_value,
- argument_processor,
- ):
- """Test that ensure_model_exists fails if the input is invalid or when the make + model don't both exist."""
- mock_make = "bar"
- mock_ensure_make_exists.side_effect = side_effect
- mock_model_exists.return_value = return_value
-
- with pytest.raises(CommandError):
- argument_processor.ensure_model_exists(model = mock_model, make = mock_make)
-
- @patch.object(target = FirmwareArgProcessor, attribute = "model_exists", autospec = True)
- def test_ensure_unique_models(self, mock_model_exists, argument_processor):
- """Test that ensure_unique_models works as expected when the make + model combinations do not already exist."""
- mock_model_exists.return_value = False
- mock_make = "foo"
- mock_models = ("bar", "baz", "bag")
-
- argument_processor.ensure_unique_models(make = mock_make, models = mock_models)
-
- assert [
- call(argument_processor, mock_make, mock_model)
- for mock_model in mock_models
- ] == mock_model_exists.mock_calls
-
- @pytest.mark.parametrize("test_input, return_value", (([], False), (["", "", ""], False), (["foo", "bar", "baz"], True)))
- @patch.object(target = FirmwareArgProcessor, attribute = "model_exists", autospec = True)
- def test_ensure_unique_models_error(self, mock_model_exists, test_input, return_value, argument_processor):
- """Test that ensure_unique_models fails as expected when the inputs are invalid or the make + model combinations already exist."""
- mock_model_exists.return_value = return_value
- mock_make = "foo"
-
- with pytest.raises(CommandError):
- argument_processor.ensure_unique_models(make = mock_make, models = test_input)
-
- @patch.object(target = FirmwareArgProcessor, attribute = "model_exists", autospec = True)
- @patch.object(target = FirmwareArgProcessor, attribute = "ensure_make_exists", autospec = True)
- def test_ensure_models_exist(self, mock_ensure_make_exists, mock_model_exists, argument_processor):
- """Test that ensure_models_exist works as expected when the make + model combinations already exist."""
- mock_model_exists.return_value = True
- mock_make = "foo"
- mock_models = ("bar", "baz", "bag")
-
- argument_processor.ensure_models_exist(make = mock_make, models = mock_models)
-
- mock_ensure_make_exists.assert_called_once_with(argument_processor, make = mock_make)
- assert [
- call(argument_processor, mock_make, mock_model)
- for mock_model in mock_models
- ] == mock_model_exists.mock_calls
-
- @pytest.mark.parametrize(
- "mock_models, side_effect, return_value",
- (
- ([], None, True),
- (["", "", ""], None, True),
- (["bar", "baz", "bag"], CommandError(cmd = None, msg = "Test error"), True),
- (["bar", "baz", "bag"], None, False),
- )
- )
- @patch.object(target = FirmwareArgProcessor, attribute = "model_exists", autospec = True)
- @patch.object(target = FirmwareArgProcessor, attribute = "ensure_make_exists", autospec = True)
- def test_ensure_models_exist_error(
- self,
- mock_ensure_make_exists,
- mock_model_exists,
- mock_models,
- side_effect,
- return_value,
- argument_processor,
- ):
- """Test that ensure_models_exist fails as expected when the make + model combinations do not already exist."""
- mock_ensure_make_exists.side_effect = side_effect
- mock_model_exists.return_value = return_value
- mock_make = "foo"
-
- with pytest.raises(CommandError):
- argument_processor.ensure_models_exist(make = mock_make, models = mock_models)
-
- @pytest.mark.parametrize("return_value", (0, 1))
- def test_firmware_exists(self, return_value, argument_processor):
- """Test that firmware exists returns the correct results."""
- mock_make = "foo"
- mock_model = "bar"
- mock_version = "baz"
- argument_processor.db.count.return_value = return_value
-
- assert return_value == argument_processor.firmware_exists(
- make = mock_make,
- model = mock_model,
- version = mock_version,
- )
- argument_processor.db.count.assert_called_once_with(ANY, (mock_make, mock_model, mock_version))
-
- @patch.object(target = FirmwareArgProcessor, attribute = "firmware_exists", autospec = True)
- @patch.object(target = FirmwareArgProcessor, attribute = "ensure_model_exists", autospec = True)
- def test_ensure_firmware_exists(self, mock_ensure_model_exists, mock_firmware_exists, argument_processor):
- """Test that ensure_firmware_exists works when the firmware + make + model all exist in the database."""
- mock_firmware_exists.return_value = True
- mock_make = "foo"
- mock_model = "bar"
- mock_version = "baz"
-
- argument_processor.ensure_firmware_exists(make = mock_make, model = mock_model, version = mock_version)
-
- mock_ensure_model_exists.assert_called_once_with(
- argument_processor,
- make = mock_make,
- model = mock_model,
- )
- mock_firmware_exists.assert_called_once_with(
- argument_processor,
- make = mock_make,
- model = mock_model,
- version = mock_version,
- )
-
- @pytest.mark.parametrize(
- "mock_version, side_effect, return_value",
- (
- ("", None, True),
- ("foo", CommandError(cmd = None, msg = "Test message"), True),
- ("foo", None, False),
- )
- )
- @patch.object(target = FirmwareArgProcessor, attribute = "firmware_exists", autospec = True)
- @patch.object(target = FirmwareArgProcessor, attribute = "ensure_model_exists", autospec = True)
- def test_ensure_firmware_exists_errors(
- self,
- mock_ensure_model_exists,
- mock_firmware_exists,
- mock_version,
- side_effect,
- return_value,
- argument_processor,
- ):
- """Test that ensure_firmware_exists fails when given invalid input or the firmware + make + model do not all exist in the database."""
- mock_firmware_exists.return_value = True
- mock_make = "foo"
- mock_model = "bar"
- mock_ensure_model_exists.side_effect = side_effect
- mock_firmware_exists.return_value = return_value
-
- with pytest.raises(CommandError):
- argument_processor.ensure_firmware_exists(make = mock_make, model = mock_model, version = mock_version)
-
- @patch.object(target = FirmwareArgProcessor, attribute = "firmware_exists", autospec = True)
- @patch.object(target = FirmwareArgProcessor, attribute = "ensure_model_exists", autospec = True)
- def test_ensure_firmwares_exist(self, mock_ensure_model_exists, mock_firmware_exists, argument_processor):
- """Test that ensure_firmwares_exist works as expected when the firmware files exist for the given make + model."""
- mock_firmware_exists.return_value = True
- mock_make = "foo"
- mock_model = "bar"
- mock_versions = ("baz", "bag", "boo")
-
- argument_processor.ensure_firmwares_exist(
- make = mock_make,
- model = mock_model,
- versions = mock_versions,
- )
-
- mock_ensure_model_exists.assert_called_once_with(argument_processor, make = mock_make, model = mock_model)
- assert [
- call(argument_processor, mock_make, mock_model, mock_version)
- for mock_version in mock_versions
- ] == mock_firmware_exists.mock_calls
-
- @pytest.mark.parametrize(
- "mock_versions, side_effect, return_value",
- (
- ([], None, True),
- (["", "", ""], None, True),
- (["bar", "baz", "bag"], CommandError(cmd = None, msg = "Test error"), True),
- (["bar", "baz", "bag"], None, False),
- )
- )
- @patch.object(target = FirmwareArgProcessor, attribute = "firmware_exists", autospec = True)
- @patch.object(target = FirmwareArgProcessor, attribute = "ensure_model_exists", autospec = True)
- def test_ensure_firmwares_exist_errors(
- self,
- mock_ensure_model_exists,
- mock_firmware_exists,
- mock_versions,
- side_effect,
- return_value,
- argument_processor,
- ):
- """Test that ensure_firmwares_exist fails as expected when the firmware files don't exist for the given make + model."""
- mock_ensure_model_exists.side_effect = side_effect
- mock_firmware_exists.return_value = return_value
- mock_make = "foo"
- mock_model = "bar"
-
- with pytest.raises(CommandError):
- argument_processor.ensure_firmwares_exist(
- make = mock_make,
- model = mock_model,
- versions = mock_versions,
- )
-
- def test_get_firmware_id(self, argument_processor):
- """Test that get_firmware_id works as expected when the firmware exists."""
- argument_processor.db.select.return_value = [[1]]
- mock_make = "foo"
- mock_model = "bar"
- mock_version = "baz"
-
- assert argument_processor.db.select.return_value[0][0] == argument_processor.get_firmware_id(
- make = mock_make,
- model = mock_model,
- version = mock_version,
- )
- argument_processor.db.select.assert_called_once_with(ANY, (mock_make, mock_model, mock_version))
-
- def test_get_firmware_id_error(self, argument_processor):
- """Test that get_firmware_id works as expected."""
- argument_processor.db.select.return_value = []
- mock_make = "foo"
- mock_model = "bar"
- mock_version = "baz"
-
- with pytest.raises(CommandError):
- argument_processor.get_firmware_id(
- make = mock_make,
- model = mock_model,
- version = mock_version,
- )
-
- # Use create = True here because the self.call method comes from the Command class,
- # which the class under test is expected to be mixed in with.
- @patch.object(target = FirmwareArgProcessor, attribute = "call", create = True)
- def test_get_common_frontend_ip(self, mock_call, argument_processor):
- """Test that get_common_frontend_ip gets the expected common frontend IP when the frontend and the target host share a network."""
- mock_hostname = "sd-stacki-mock-backend"
- # Set up the call("list.host.interface") returns, ensuring each separate return value contains one shared network.
- mock_call_return_values = (
- # Mock front end interfaces
- [{"network": "foo", "ip": "1.2.3.4"}, {"network": "bar", "ip": "2.3.4.5"}],
- # Mock other host interfaces
- [{"network": "baz", "ip": "3.4.5.6"}, {"network": "foo", "ip": "1.2.3.10"}],
- )
- mock_call.side_effect = mock_call_return_values
-
- result = argument_processor.get_common_frontend_ip(hostname = mock_hostname)
-
- # Make sure the right IP was returned
- assert mock_call_return_values[0][0]["ip"] == result
- # Make sure the list host interface calls happened
- assert mock_call.mock_calls == [
- call(command = "list.host.interface", args = ["a:frontend"]),
- call(command = "list.host.interface", args = [mock_hostname]),
- ]
-
- # Use create = True here because the self.call method comes from the Command class,
- # which the class under test is expected to be mixed in with.
- @patch.object(target = FirmwareArgProcessor, attribute = "call", create = True)
- def test_get_common_frontend_ip_no_common_network(self, mock_call, argument_processor):
- """Test that get_common_frontend_ip fails when there is no common network."""
- mock_hostname = "sd-stacki-mock-backend"
- # Set up the call("list.host.interface") returns, ensuring each separate return value contains no shared networks.
- mock_call_return_values = (
- # Mock front end interfaces
- [{"network": "foo", "ip": "1.2.3.4"}, {"network": "bar", "ip": "2.3.4.5"}],
- # Mock other host interfaces
- [{"network": "baz", "ip": "3.4.5.6"}, {"network": "bag", "ip": "1.2.3.10"}],
- )
- mock_call.side_effect = mock_call_return_values
-
- with pytest.raises(CommandError):
- argument_processor.get_common_frontend_ip(hostname = mock_hostname)
-
- # Use create = True here because the self.call method comes from the Command class,
- # which the class under test is expected to be mixed in with.
- @patch.object(target = FirmwareArgProcessor, attribute = "call", create = True)
- def test_get_common_frontend_ip_no_interface_ip(self, mock_call, argument_processor):
- """Test that get_common_frontend_ip fails when there is no IP on the interface on the common network."""
- mock_hostname = "sd-stacki-mock-backend"
- # Set up the call("list.host.interface") returns, ensuring each separate return value contains one shared network.
- mock_call_return_values = (
- # Mock front end interfaces
- [{"network": "foo", "ip": ""}, {"network": "bar", "ip": "2.3.4.5"}],
- # Mock other host interfaces
- [{"network": "baz", "ip": "3.4.5.6"}, {"network": "foo", "ip": "1.2.3.10"}],
- )
- mock_call.side_effect = mock_call_return_values
-
- with pytest.raises(CommandError):
- argument_processor.get_common_frontend_ip(hostname = mock_hostname)
-
- @patch.object(target = FirmwareArgProcessor, attribute = "get_common_frontend_ip", autospec = True)
- @patch(target = "stack.argument_processors.firmware.Path", autospec = True)
- def test_get_firmware_url(self, mock_path, mock_get_common_frontend_ip, argument_processor):
- """Test that get_firmware_url returns the correct URL."""
- mock_get_common_frontend_ip.return_value = "1.2.3.4"
- # need to mock out Path.parts.
- mock_path_parts = (
- "/",
- "export",
- "stack",
- "firmware",
- "make",
- "model",
- "file",
- )
- mock_path.return_value.resolve.return_value.parts = mock_path_parts
- expected_path_parts = mock_path_parts[3:]
- expected_path = "/".join(expected_path_parts)
- mock_path.return_value.joinpath.return_value.__str__.return_value = expected_path
- expected_url = f"http://{mock_get_common_frontend_ip.return_value}/install/{expected_path}"
- mock_firmware_file = "foo"
- mock_hostname = "bar"
-
- result = argument_processor.get_firmware_url(
- hostname = mock_hostname,
- firmware_file = mock_firmware_file,
- )
-
- # Make sure the returned URL is correct.
- assert expected_url == result
- # Check that the path was resolved.
- assert all(
- call in mock_path.mock_calls
- for call in call(mock_firmware_file).resolve(strict = True).call_list()
- )
- # Make sure the IP was retrieved
- mock_get_common_frontend_ip.assert_called_once_with(argument_processor, hostname = mock_hostname)
- # Make sure the path was rebuilt excluding the right elements
- mock_path.return_value.joinpath.assert_called_once_with(*expected_path_parts)
-
- @patch.object(target = FirmwareArgProcessor, attribute = "get_common_frontend_ip", autospec = True)
- @patch(target = "stack.argument_processors.firmware.Path", autospec = True)
- def test_get_firmware_url_file_does_not_exist(self, mock_path, mock_get_common_frontend_ip, argument_processor):
- """Test that get_firmware_url raises a CommandError if the firmware file does not exist."""
- mock_path.return_value.resolve.side_effect = FileNotFoundError("Test error")
-
- with pytest.raises(CommandError):
- argument_processor.get_firmware_url(
- hostname = "foo",
- firmware_file = "bar",
- )
-
- @patch.object(target = FirmwareArgProcessor, attribute = "get_common_frontend_ip", autospec = True)
- @patch(target = "stack.argument_processors.firmware.Path", autospec = True)
- def test_get_firmware_url_no_common_ip(self, mock_path, mock_get_common_frontend_ip, argument_processor):
- """Test that get_firmware_url passes through the CommandError if get_common_frontend_ip fails."""
- mock_get_common_frontend_ip.side_effect = CommandError(cmd = "", msg = "Test error")
-
- with pytest.raises(CommandError):
- argument_processor.get_firmware_url(
- hostname = "foo",
- firmware_file = "bar",
- )
-
- @pytest.mark.parametrize("return_value", (0, 1))
- def test_imp_exists(self, return_value, argument_processor):
- """Test that imp_exists works as expected."""
- mock_imp = "foo"
- argument_processor.db.count.return_value = return_value
-
- assert return_value == argument_processor.imp_exists(imp = mock_imp)
- argument_processor.db.count.assert_called_once_with(ANY, mock_imp)
-
- @patch.object(target = FirmwareArgProcessor, attribute = "imp_exists", autospec = True)
- def test_ensure_imp_exists(self, mock_imp_exists, argument_processor):
- """Test that ensure_imp_exists works when the input is valid and the imp exists in the database."""
- mock_imp = "foo"
- mock_imp_exists.return_value = True
-
- argument_processor.ensure_imp_exists(imp = mock_imp)
-
- mock_imp_exists.assert_called_once_with(argument_processor, imp = mock_imp)
-
- @pytest.mark.parametrize("mock_imp, return_value", (("", True), ("foo", False)))
- @patch.object(target = FirmwareArgProcessor, attribute = "imp_exists", autospec = True)
- def test_ensure_imp_exists_errors(self, mock_imp_exists, mock_imp, return_value, argument_processor):
- """Test that ensure_imp_exists fails when the input is invalid or the imp does not exist in the database."""
- mock_imp_exists.return_value = return_value
-
- with pytest.raises(CommandError):
- argument_processor.ensure_imp_exists(imp = mock_imp)
-
- @patch.object(target = FirmwareArgProcessor, attribute = "imp_exists", autospec = True)
- def test_ensure_imps_exist(self, mock_imp_exists, argument_processor):
- """Test that ensure_imps_exist works as expected when all implementations exist in the database."""
- mock_imp_exists.return_value = True
- mock_imps = ("foo", "bar", "baz")
-
- argument_processor.ensure_imps_exist(imps = mock_imps)
-
- assert [call(argument_processor, mock_imp) for mock_imp in mock_imps] == mock_imp_exists.mock_calls
-
- @pytest.mark.parametrize(
- "mock_imps, return_value",
- (
- ([], True),
- (["", "", ""], True),
- (["bar", "baz", "bag"], False),
- )
- )
- @patch.object(target = FirmwareArgProcessor, attribute = "imp_exists", autospec = True)
- def test_ensure_imps_exist_error(
- self,
- mock_imp_exists,
- mock_imps,
- return_value,
- argument_processor,
- ):
- """Test that ensure_imps_exist fails when at least one implementation doesn't exist in the database."""
- mock_imp_exists.return_value = return_value
-
- with pytest.raises(CommandError):
- argument_processor.ensure_imps_exist(imps = mock_imps)
-
- def test_get_imp_id(self, argument_processor):
- """Test that get_imp_id works as expected when the implementation exists in the database."""
- argument_processor.db.select.return_value = [[1]]
- mock_imp = "foo"
-
- assert argument_processor.db.select.return_value[0][0] == argument_processor.get_imp_id(
- imp = mock_imp,
- )
- argument_processor.db.select.assert_called_once_with(ANY, mock_imp)
-
- def test_get_imp_id_error(self, argument_processor):
- """Test that get_imp_id fails as expected when the implementation does not exist in the database."""
- argument_processor.db.select.return_value = []
- mock_imp = "foo"
-
- with pytest.raises(CommandError):
- argument_processor.get_imp_id(imp = mock_imp)
-
- @pytest.mark.parametrize("return_value", (0, 1))
- def test_version_regex_exists(self, return_value, argument_processor):
- """Test that version_regex_exists works as expected."""
- argument_processor.db.count.return_value = return_value
- mock_name = "foo"
-
- assert return_value == argument_processor.version_regex_exists(name = mock_name)
- argument_processor.db.count.assert_called_once_with(ANY, mock_name)
-
- @patch.object(target = FirmwareArgProcessor, attribute = "version_regex_exists", autospec = True)
- def test_ensure_version_regex_exists(self, mock_version_regex_exists, argument_processor):
- """Test that ensure_version_regex_exists works in the case where the input is valid and the version regex exists in the database."""
- mock_version_regex_exists.return_value = True
- mock_name = "foo"
-
- argument_processor.ensure_version_regex_exists(name = mock_name)
-
- mock_version_regex_exists.assert_called_once_with(argument_processor, name = mock_name)
-
- @pytest.mark.parametrize("mock_name, return_value", (("", True), ("foo", False)))
- @patch.object(target = FirmwareArgProcessor, attribute = "version_regex_exists", autospec = True)
- def test_ensure_version_regex_exists_errors(self, mock_version_regex_exists, mock_name, return_value, argument_processor):
- """Test that ensure_version_regex_exists works in the case where the input is valid and the version regex exists in the database."""
- mock_version_regex_exists.return_value = return_value
-
- with pytest.raises(CommandError):
- argument_processor.ensure_version_regex_exists(name = mock_name)
-
- @patch.object(target = FirmwareArgProcessor, attribute = "version_regex_exists", autospec = True)
- def test_ensure_version_regexes_exist(self, mock_version_regex_exists, argument_processor):
- """Test that ensure_version_regexes_exist works in the case where all the version regexes exist in the database."""
- mock_version_regex_exists.return_value = True
- mock_names = ("foo", "bar", "baz")
-
- argument_processor.ensure_version_regexes_exist(names = mock_names)
-
- assert [call(argument_processor, mock_name) for mock_name in mock_names] == mock_version_regex_exists.mock_calls
-
- @pytest.mark.parametrize(
- "mock_names, return_value",
- (
- ([], True),
- (["", "", ""], True),
- (["bar", "baz", "bag"], False),
- )
- )
- @patch.object(target = FirmwareArgProcessor, attribute = "version_regex_exists", autospec = True)
- def test_ensure_version_regexes_exist_error(self, mock_version_regex_exists, mock_names, return_value, argument_processor):
- """Test that ensure_version_regexes_exist fails in the case where at least one of the version regexes does not exist in the database."""
- mock_version_regex_exists.return_value = return_value
-
- with pytest.raises(CommandError):
- argument_processor.ensure_version_regexes_exist(names = mock_names)
-
- def test_get_version_regex_id(self, argument_processor):
- """Test that get_version_regex_id works as expected when the version_regex exists in the database."""
- argument_processor.db.select.return_value = [[1]]
- mock_name = "foo"
-
- assert argument_processor.db.select.return_value[0][0] == argument_processor.get_version_regex_id(
- name = mock_name,
- )
- argument_processor.db.select.assert_called_once_with(ANY, mock_name)
-
- def test_get_version_regex_id_error(self, argument_processor):
- """Test that get_version_regex_id fails as expected when the version_regex does not exist in the database."""
- argument_processor.db.select.return_value = []
- mock_name = "foo"
-
- with pytest.raises(CommandError):
- argument_processor.get_version_regex_id(name = mock_name)
-
- @pytest.mark.parametrize(
- "test_input, expected",
- (
- (
- [[None, None, None, "mock_model_regex", "mock_model_regex_name", "mock_model_regex_description"]],
- ("mock_model_regex", "mock_model_regex_name", "mock_model_regex_description"),
- ),
- (
- [["mock_make_regex", "mock_make_regex_name", "mock_make_regex_description", None, None, None,]],
- ("mock_make_regex", "mock_make_regex_name", "mock_make_regex_description"),
- ),
- (
- [["mock_make_regex", "mock_make_regex_name", "mock_make_regex_description", "mock_model_regex", "mock_model_regex_name", "mock_model_regex_description"]],
- ("mock_model_regex", "mock_model_regex_name", "mock_model_regex_description"),
- ),
- ([], None),
- )
- )
- def test_try_get_version_regex(self, test_input, expected, argument_processor):
- """Test that try_get_version_regex works as expected, preferring a model regex over a make one."""
- argument_processor.db.select.return_value = test_input
- mock_make = "foo"
- mock_model = "bar"
-
- # Make sure the expected result is returned.
- assert expected == argument_processor.try_get_version_regex(
- make = mock_make,
- model = mock_model,
- )
- argument_processor.db.select.assert_called_once_with(ANY, (mock_make, mock_model))
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/imp/test_command_stack_commands_add_firmware_imp__init__.py b/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/imp/test_command_stack_commands_add_firmware_imp__init__.py
deleted file mode 100644
index 3f86337a8..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/imp/test_command_stack_commands_add_firmware_imp__init__.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from unittest.mock import patch
-import pytest
-from stack.commands.add.firmware.imp import Command
-
-class TestAddFirmwareImpCommand:
- """A test case to hold the tests for the add firmware imp stacki Command class."""
-
- class CommandUnderTest(Command):
- """A class derived from the Command class under test used to override __init__.
-
- This allows easier instantiation for testing purposes by excluding the base Command
- class initialization code.
- """
- def __init__(self):
- pass
-
- @pytest.fixture
- def command(self):
- """Fixture to create and return a Command class instance for testing."""
- return self.CommandUnderTest()
-
- @patch.object(target = Command, attribute = "runPlugins", autospec = True)
- def test_run(self, mock_runPlugins, command):
- """Test that run will run the plugins passing through the args and params."""
- mock_params = {"foo": "bar", "baz": "bag"}
- mock_args = ["foo", "bar", "baz"]
-
- command.run(params = mock_params, args = mock_args)
-
- mock_runPlugins.assert_called_once_with(command, args = (mock_params, mock_args))
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/imp/test_command_stack_commands_add_firmware_imp_plugin_basic.py b/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/imp/test_command_stack_commands_add_firmware_imp_plugin_basic.py
deleted file mode 100644
index 0bc49e3f9..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/imp/test_command_stack_commands_add_firmware_imp_plugin_basic.py
+++ /dev/null
@@ -1,230 +0,0 @@
-from unittest.mock import create_autospec, patch, ANY
-import pytest
-import stack.commands.list.host.firmware
-import stack.commands.sync.host.firmware
-from stack.commands import DatabaseConnection
-from stack.commands.add.firmware import Command
-from stack.commands.add.firmware.imp.plugin_basic import Plugin
-from stack.exception import ArgError, ParamError, CommandError
-
-class TestAddImpBasicPlugin:
- """A test case for the add firmware imp basic plugin."""
-
- @pytest.fixture
- def basic_plugin(self):
- """A fixture that returns the plugin instance for use in tests.
-
- This sets up the required mocks needed to construct the plugin class.
- """
- mock_command = create_autospec(
- spec = Command,
- instance = True,
- )
- mock_command.db = create_autospec(
- spec = DatabaseConnection,
- spec_set = True,
- instance = True,
- )
- return Plugin(command = mock_command)
-
- def test_provides(self, basic_plugin):
- """Make sure the plugin returns the correct provides information."""
- assert basic_plugin.provides() == "basic"
-
- def test_validate_args(self, basic_plugin):
- """Test that validate_args works if the args list has one element."""
- basic_plugin.validate_args(args = ["foo"])
-
- @pytest.mark.parametrize("test_input", ([], ["foo", "bar"]))
- def test_validate_args_failure(self, test_input, basic_plugin):
- """Test that validate_args fails with bad input."""
- with pytest.raises(ArgError):
- basic_plugin.validate_args(args = test_input)
-
- @patch(target = "stack.commands.add.firmware.imp.plugin_basic.Path", autospec = True)
- @patch(target = "inspect.getsourcefile", autospec = True)
- def test_validate_imp(self, mock_getsourcefile, mock_path, basic_plugin):
- """Test that validate_imp works when the imp doesn't already exist in the database and the files are found on disk."""
- basic_plugin.owner.imp_exists.return_value = False
- mock_path.return_value.parent.resolve.return_value.__truediv__.return_value.exists.return_value = True
- mock_imp = "foo"
-
- basic_plugin.validate_imp(imp = mock_imp)
-
- # Make sure the database was checked for duplicates.
- basic_plugin.owner.imp_exists.assert_called_once_with(imp = mock_imp)
- # Make sure the files were checked for existence on disk.
- mock_getsourcefile.assert_any_call(stack.commands.list.host.firmware)
- mock_getsourcefile.assert_any_call(stack.commands.sync.host.firmware)
- mock_path.assert_any_call(mock_getsourcefile.return_value)
- mock_path.return_value.parent.resolve.return_value.__truediv__.return_value.exists.assert_called_with()
-
- @patch(target = "stack.commands.add.firmware.imp.plugin_basic.Path", autospec = True)
- @patch(target = "inspect.getsourcefile", autospec = True)
- def test_validate_imp_already_exists(self, mock_getsourcefile, mock_path, basic_plugin):
- """Test that validate_imp fails when the implementation already exists in the database."""
- basic_plugin.owner.imp_exists.return_value = True
- mock_path.return_value.parent.resolve.return_value.__truediv__.return_value.exists.return_value = True
- mock_imp = "foo"
-
- with pytest.raises(ArgError):
- basic_plugin.validate_imp(imp = mock_imp)
-
- @pytest.mark.parametrize("return_values", ((True, False), (False, True), (False, False)))
- @patch(target = "stack.commands.add.firmware.imp.plugin_basic.Path", autospec = True)
- @patch(target = "inspect.getsourcefile", autospec = True)
- def test_validate_imp_missing_files(self, mock_getsourcefile, mock_path, return_values, basic_plugin):
- """Test that validate_imp fails when the implementation files are missing from the filesystem."""
- basic_plugin.owner.imp_exists.return_value = False
- mock_path.return_value.parent.resolve.return_value.__truediv__.return_value.exists.side_effect = return_values
- mock_imp = "foo"
-
- with pytest.raises(ArgError):
- basic_plugin.validate_imp(imp = mock_imp)
-
- @patch(target = "stack.commands.add.firmware.imp.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.add.firmware.imp.plugin_basic.ExitStack", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_imp", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_args", autospec = True)
- def test_run(
- self,
- mock_validate_args,
- mock_validate_imp,
- mock_exit_stack,
- mock_lowered,
- basic_plugin,
- ):
- """Test that run modifies the database as expected when all the params and args are valid."""
- expected_imp = "foo"
- mock_args = [expected_imp]
- expected_models = ["baz", "fizz", "buzz"]
- mock_params = {"make": "bar", "models": ", ".join(expected_models)}
- mock_lowered.return_value = (param for param in mock_params.values())
-
- basic_plugin.run(args = (mock_params, mock_args))
-
- # Make sure the arguments and parameters were validated.
- mock_validate_args.assert_called_once_with(basic_plugin, args = mock_args)
- mock_validate_imp.assert_called_once_with(basic_plugin, imp = expected_imp)
- basic_plugin.owner.fillParams.assert_called_once_with(
- names = [("make", ""), ("models", "")],
- params = mock_params,
- )
- basic_plugin.owner.ensure_models_exist.assert_called_once_with(
- make = mock_params["make"],
- models = expected_models,
- )
- # Expect the database to be updated
- basic_plugin.owner.db.execute.assert_called_once_with(ANY, (expected_imp,))
- basic_plugin.owner.call.assert_called_once_with(
- command = "set.firmware.model.imp",
- args = [*expected_models, f"make={mock_params['make']}", f"imp={expected_imp}"],
- )
- # Make sure cleanup was setup and then dismissed.
- mock_exit_stack.return_value.__enter__.return_value.callback.assert_called_once_with(
- basic_plugin.owner.call,
- command = "remove.firmware.imp",
- args = [expected_imp],
- )
- mock_exit_stack.return_value.__enter__.return_value.pop_all.assert_called_once_with()
-
- @patch(target = "stack.commands.add.firmware.imp.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.add.firmware.imp.plugin_basic.ExitStack", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_imp", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_args", autospec = True)
- def test_run_no_make_or_models(
- self,
- mock_validate_args,
- mock_validate_imp,
- mock_exit_stack,
- mock_lowered,
- basic_plugin,
- ):
- """Test that run modifies the database as expected when no make or models are provided."""
- expected_imp = "foo"
- mock_args = [expected_imp]
- mock_params = {}
- mock_lowered.return_value = ("", "")
-
- basic_plugin.run(args = (mock_params, mock_args))
-
- # Make sure the arguments and parameters were validated.
- mock_validate_args.assert_called_once_with(basic_plugin, args = mock_args)
- mock_validate_imp.assert_called_once_with(basic_plugin, imp = expected_imp)
- basic_plugin.owner.fillParams.assert_called_once_with(
- names = [("make", ""), ("models", "")],
- params = mock_params,
- )
- basic_plugin.owner.ensure_models_exist.assert_not_called()
- # Expect the database to be updated
- basic_plugin.owner.db.execute.assert_called_once_with(ANY, (expected_imp,))
- basic_plugin.owner.call.assert_not_called()
- # Make sure cleanup was setup and then dismissed.
- mock_exit_stack.return_value.__enter__.return_value.callback.assert_called_once_with(
- basic_plugin.owner.call,
- command = "remove.firmware.imp",
- args = [expected_imp],
- )
- mock_exit_stack.return_value.__enter__.return_value.pop_all.assert_called_once_with()
-
- @pytest.mark.parametrize("failure_mock", ("ensure_models_exist", "validate_imp", "validate_args"))
- @patch(target = "stack.commands.add.firmware.imp.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.add.firmware.imp.plugin_basic.ExitStack", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_imp", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_args", autospec = True)
- def test_run_errors(
- self,
- mock_validate_args,
- mock_validate_imp,
- mock_exit_stack,
- mock_lowered,
- failure_mock,
- basic_plugin,
- ):
- """Test that run fails if any of the params or args are invalid and does not touch the database."""
- mock_args = ["foo"]
- mock_params = {"make": "bar", "models": "baz, fizz, buzz"}
- mock_lowered.return_value = (param for param in mock_params.values())
- mock_validation_functions = {
- "ensure_models_exist": basic_plugin.owner.ensure_models_exist,
- "validate_imp": mock_validate_imp,
- "validate_args": mock_validate_args,
- }
- # Set the error on the correct mock validation function.
- mock_validation_functions[failure_mock].side_effect = CommandError(cmd = basic_plugin.owner, msg = "Test error")
-
- with pytest.raises(CommandError):
- basic_plugin.run(args = (mock_params, mock_args))
-
- # Expect the database to not be updated
- basic_plugin.owner.db.execute.assert_not_called()
-
- @patch(target = "stack.commands.add.firmware.imp.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.add.firmware.imp.plugin_basic.ExitStack", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_imp", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_args", autospec = True)
- def test_run_cleanup(
- self,
- mock_validate_args,
- mock_validate_imp,
- mock_exit_stack,
- mock_lowered,
- basic_plugin,
- ):
- """Test that run cleans up if setting the implementation relation fails."""
- mock_args = ["foo"]
- mock_params = {"make": "bar", "models": "baz, fizz, buzz"}
- mock_lowered.return_value = (param for param in mock_params.values())
- # Set the call to fail.
- basic_plugin.owner.call.side_effect = CommandError(cmd = basic_plugin.owner, msg = "Test error")
-
- with pytest.raises(CommandError):
- basic_plugin.run(args = (mock_params, mock_args))
-
- # Expect the cleanup to be set up, but not dismissed due to the error.
- mock_exit_stack.return_value.__enter__.return_value.callback.assert_called_once_with(
- basic_plugin.owner.call,
- command = "remove.firmware.imp",
- args = [mock_args[0].lower()],
- )
- mock_exit_stack.return_value.__enter__.return_value.pop_all.assert_not_called()
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/make/test_command_stack_commands_add_firmware_make__init__.py b/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/make/test_command_stack_commands_add_firmware_make__init__.py
deleted file mode 100644
index 69bf524b9..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/make/test_command_stack_commands_add_firmware_make__init__.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from unittest.mock import patch
-import pytest
-from stack.commands.add.firmware.make import Command
-
-class TestAddFirmwareMakeCommand:
- """A test case to hold the tests for the add firmware make stacki Command class."""
-
- class CommandUnderTest(Command):
- """A class derived from the Command class under test used to override __init__.
-
- This allows easier instantiation for testing purposes by excluding the base Command
- class initialization code.
- """
- def __init__(self):
- pass
-
- @pytest.fixture
- def command(self):
- """Fixture to create and return a Command class instance for testing."""
- return self.CommandUnderTest()
-
- @patch.object(target = Command, attribute = "runPlugins", autospec = True)
- def test_run(self, mock_runPlugins, command):
- """Test that run will run the plugins passing through the args."""
- mock_args = ["foo", "bar", "baz"]
-
- command.run(args = mock_args, params = "unused")
-
- mock_runPlugins.assert_called_once_with(command, args = mock_args)
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/make/test_command_stack_commands_add_firmware_make_plugin_basic.py b/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/make/test_command_stack_commands_add_firmware_make_plugin_basic.py
deleted file mode 100644
index 1d5f37cc8..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/make/test_command_stack_commands_add_firmware_make_plugin_basic.py
+++ /dev/null
@@ -1,81 +0,0 @@
-from unittest.mock import create_autospec, patch, ANY
-import pytest
-from stack.commands import DatabaseConnection
-from stack.commands.add.firmware import Command
-from stack.commands.add.firmware.make.plugin_basic import Plugin
-from stack.exception import CommandError
-
-class TestAddMakeBasicPlugin:
- """A test case for the add firmware make basic plugin."""
-
- @pytest.fixture
- def basic_plugin(self):
- """A fixture that returns the plugin instance for use in tests.
-
- This sets up the required mocks needed to construct the plugin class.
- """
- mock_command = create_autospec(
- spec = Command,
- instance = True,
- )
- mock_command.db = create_autospec(
- spec = DatabaseConnection,
- spec_set = True,
- instance = True,
- )
- return Plugin(command = mock_command)
-
- def test_provides(self, basic_plugin):
- """Make sure the plugin returns the correct provides information."""
- assert basic_plugin.provides() == "basic"
-
- @patch(target = "stack.commands.add.firmware.make.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.add.firmware.make.plugin_basic.unique_everseen", autospec = True)
- def test_run(
- self,
- mock_unique_everseen,
- mock_lowered,
- basic_plugin,
- ):
- """Test that run modifies the database as expected when all the args are valid."""
- expected_makes = ("foo", "bar", "baz")
- mock_args = [*expected_makes]
- mock_unique_everseen.return_value = (arg for arg in mock_args)
-
- basic_plugin.run(args = mock_args)
-
- # Make sure the arguments were validated.
- mock_lowered.assert_called_once_with(mock_args)
- mock_unique_everseen.assert_called_once_with(mock_lowered.return_value)
- basic_plugin.owner.ensure_unique_makes.assert_called_once_with(
- makes = expected_makes,
- )
- # Expect the database to be updated
- basic_plugin.owner.db.execute.assert_called_once_with(
- ANY,
- [(expected_make,) for expected_make in expected_makes],
- many = True,
- )
-
- @patch(target = "stack.commands.add.firmware.make.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.add.firmware.make.plugin_basic.unique_everseen", autospec = True)
- def test_run_error(
- self,
- mock_unique_everseen,
- mock_lowered,
- basic_plugin,
- ):
- """Test that run fails when argument validation fails and does not touch the database."""
- expected_makes = ("foo", "bar", "baz")
- mock_args = [*expected_makes]
- mock_unique_everseen.return_value = (arg for arg in mock_args)
- basic_plugin.owner.ensure_unique_makes.side_effect = CommandError(
- cmd = basic_plugin.owner,
- msg = "Test error",
- )
-
- with pytest.raises(CommandError):
- basic_plugin.run(args = mock_args)
-
- # Expect the database to be untouched
- basic_plugin.owner.db.execute.assert_not_called()
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/model/test_command_stack_commands_add_firmware_model__init__.py b/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/model/test_command_stack_commands_add_firmware_model__init__.py
deleted file mode 100644
index 64b535cd9..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/model/test_command_stack_commands_add_firmware_model__init__.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from unittest.mock import patch
-import pytest
-from stack.commands.add.firmware.model import Command
-
-class TestAddFirmwareModelCommand:
- """A test case to hold the tests for the add firmware model stacki Command class."""
-
- class CommandUnderTest(Command):
- """A class derived from the Command class under test used to override __init__.
-
- This allows easier instantiation for testing purposes by excluding the base Command
- class initialization code.
- """
- def __init__(self):
- pass
-
- @pytest.fixture
- def command(self):
- """Fixture to create and return a Command class instance for testing."""
- return self.CommandUnderTest()
-
- @patch.object(target = Command, attribute = "runPlugins", autospec = True)
- def test_run(self, mock_runPlugins, command):
- """Test that run will run the plugins passing through the args."""
- mock_args = ["foo", "bar", "baz"]
- mock_params = {"fizz": "buzz"}
-
- command.run(args = mock_args, params = mock_params)
-
- mock_runPlugins.assert_called_once_with(command, args = (mock_params, mock_args))
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/model/test_command_stack_commands_add_firmware_model_plugin_basic.py b/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/model/test_command_stack_commands_add_firmware_model_plugin_basic.py
deleted file mode 100644
index fb26ef919..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/model/test_command_stack_commands_add_firmware_model_plugin_basic.py
+++ /dev/null
@@ -1,202 +0,0 @@
-from unittest.mock import create_autospec, patch, ANY, call
-import pytest
-from contextlib import ExitStack
-from stack.commands import DatabaseConnection
-from stack.commands.add.firmware import Command
-from stack.commands.add.firmware.model.plugin_basic import Plugin
-from stack.exception import CommandError
-
-class TestAddFirmwareModelBasicPlugin:
- """A test case for the add firmware model basic plugin."""
-
- @pytest.fixture
- def basic_plugin(self):
- """A fixture that returns the plugin instance for use in tests.
-
- This sets up the required mocks needed to construct the plugin class.
- """
- mock_command = create_autospec(
- spec = Command,
- instance = True,
- )
- mock_command.db = create_autospec(
- spec = DatabaseConnection,
- spec_set = True,
- instance = True,
- )
- return Plugin(command = mock_command)
-
- def test_provides(self, basic_plugin):
- """Ensure the plugin returns the correct provides information."""
- assert basic_plugin.provides() == "basic"
-
- def test_create_missing_make(self, basic_plugin):
- """Test that create missing make works as expected when the make doesn't exist."""
- basic_plugin.owner.make_exists.return_value = False
- mock_exit_stack = create_autospec(
- spec = ExitStack,
- spec_set = True,
- )
- mock_make = "foo"
-
- with mock_exit_stack() as mock_cleanup:
- basic_plugin.create_missing_make(make = mock_make, cleanup = mock_cleanup)
-
- basic_plugin.owner.make_exists.assert_called_once_with(make = mock_make)
- basic_plugin.owner.call.assert_called_once_with(command = "add.firmware.make", args = [mock_make])
- mock_exit_stack.return_value.__enter__.return_value.callback.assert_called_once_with(
- basic_plugin.owner.call,
- command = "remove.firmware.make",
- args = [mock_make],
- )
-
- def test_create_missing_make_make_exists(self, basic_plugin):
- """Test that create missing make works as expected when the make exists."""
- basic_plugin.owner.make_exists.return_value = True
- mock_exit_stack = create_autospec(
- spec = ExitStack,
- spec_set = True,
- )
- mock_make = "foo"
-
- with mock_exit_stack() as mock_cleanup:
- basic_plugin.create_missing_make(make = mock_make, cleanup = mock_cleanup)
-
- basic_plugin.owner.make_exists.assert_called_once_with(make = mock_make)
- basic_plugin.owner.call.assert_not_called()
- mock_exit_stack.return_value.__enter__.return_value.assert_not_called()
-
- def test_create_missing_imp(self, basic_plugin):
- """Test that create missing imp works as expected when the imp doesn't exist."""
- basic_plugin.owner.imp_exists.return_value = False
- mock_exit_stack = create_autospec(
- spec = ExitStack,
- spec_set = True,
- )
- mock_imp = "foo"
-
- with mock_exit_stack() as mock_cleanup:
- basic_plugin.create_missing_imp(imp = mock_imp, cleanup = mock_cleanup)
-
- basic_plugin.owner.imp_exists.assert_called_once_with(imp = mock_imp)
- basic_plugin.owner.call.assert_called_once_with(command = "add.firmware.imp", args = [mock_imp])
- mock_exit_stack.return_value.__enter__.return_value.callback.assert_called_once_with(
- basic_plugin.owner.call,
- command = "remove.firmware.imp",
- args = [mock_imp],
- )
-
- def test_create_missing_imp_make_exists(self, basic_plugin):
- """Test that create missing imp works as expected when the imp exists."""
- basic_plugin.owner.imp_exists.return_value = True
- mock_exit_stack = create_autospec(
- spec = ExitStack,
- spec_set = True,
- )
- mock_imp = "foo"
-
- with mock_exit_stack() as mock_cleanup:
- basic_plugin.create_missing_imp(imp = mock_imp, cleanup = mock_cleanup)
-
- basic_plugin.owner.imp_exists.assert_called_once_with(imp = mock_imp)
- basic_plugin.owner.call.assert_not_called()
- mock_exit_stack.return_value.__enter__.return_value.assert_not_called()
-
- @patch.object(target = Plugin, attribute = "create_missing_imp", autospec = True)
- @patch.object(target = Plugin, attribute = "create_missing_make", autospec = True)
- @patch(target = "stack.commands.add.firmware.model.plugin_basic.ExitStack", autospec = True)
- @patch(target = "stack.commands.add.firmware.model.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.add.firmware.model.plugin_basic.unique_everseen", autospec = True)
- def test_run(
- self,
- mock_unique_everseen,
- mock_lowered,
- mock_exit_stack,
- mock_create_missing_make,
- mock_create_missing_imp,
- basic_plugin,
- ):
- """Test that run modifies the database as expected when all the args are valid."""
- expected_models = ("foo", "bar", "baz")
- mock_args = [*expected_models]
- mock_params = {"make": "fizz", "imp": "buzz"}
- mock_unique_everseen.return_value = (arg for arg in mock_args)
- mock_lowered.return_value = mock_params.values()
-
- basic_plugin.run(args = (mock_params, mock_args))
-
- # model sure the arguments were validated.
- assert [call(basic_plugin.owner.fillParams.return_value), call(mock_args)] == mock_lowered.mock_calls
- mock_unique_everseen.assert_called_once_with(mock_lowered.return_value)
- basic_plugin.owner.ensure_unique_models.assert_called_once_with(
- models = expected_models,
- make = mock_params["make"],
- )
- # Expect the make and imp to be created if needed
- mock_create_missing_make.assert_called_once_with(
- basic_plugin,
- make = mock_params["make"],
- cleanup = mock_exit_stack.return_value.__enter__.return_value,
- )
- mock_create_missing_imp.assert_called_once_with(
- basic_plugin,
- mock_params["imp"],
- cleanup = mock_exit_stack.return_value.__enter__.return_value,
- )
- # Expect the IDs of the imp and make to be fetched.
- basic_plugin.owner.get_make_id.assert_called_once_with(make = mock_params["make"])
- basic_plugin.owner.get_imp_id.assert_called_once_with(imp = mock_params["imp"])
- # Expect the database to be updated
- basic_plugin.owner.db.execute.assert_called_once_with(
- ANY,
- [
- (
- model,
- basic_plugin.owner.get_make_id.return_value,
- basic_plugin.owner.get_imp_id.return_value,
- )
- for model in expected_models
- ],
- many = True,
- )
- # Expect cleanup to be dismissed.
- mock_exit_stack.return_value.__enter__.return_value.pop_all.assert_called_once_with()
-
- @pytest.mark.parametrize(
- "mock_make, mock_imp, side_effect",
- (
- ("", "bar", None),
- ("foo", "", None),
- ("foo", "bar", CommandError(cmd = None, msg = "Test message")),
- )
- )
- @patch.object(target = Plugin, attribute = "create_missing_imp", autospec = True)
- @patch.object(target = Plugin, attribute = "create_missing_make", autospec = True)
- @patch(target = "stack.commands.add.firmware.model.plugin_basic.ExitStack", autospec = True)
- @patch(target = "stack.commands.add.firmware.model.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.add.firmware.model.plugin_basic.unique_everseen", autospec = True)
- def test_run_errors(
- self,
- mock_unique_everseen,
- mock_lowered,
- mock_exit_stack,
- mock_create_missing_make,
- mock_create_missing_imp,
- mock_make,
- mock_imp,
- side_effect,
- basic_plugin,
- ):
- """Test that run fails when the arguments or parameters are invalid and does not touch the database."""
- expected_models = ("foo", "bar", "baz")
- mock_args = [*expected_models]
- mock_params = {"make": mock_make, "imp": mock_imp}
- mock_unique_everseen.return_value = (arg for arg in mock_args)
- mock_lowered.return_value = mock_params.values()
- basic_plugin.owner.ensure_unique_models.side_effect = side_effect
-
- with pytest.raises(CommandError):
- basic_plugin.run(args = (mock_params, mock_args))
-
- # Expect the database to not be touched
- basic_plugin.owner.db.execute.assert_not_called()
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/test_command_stack_commands_add_firmware__init__.py b/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/test_command_stack_commands_add_firmware__init__.py
deleted file mode 100644
index efbf7b1c5..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/test_command_stack_commands_add_firmware__init__.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from unittest.mock import patch
-import pytest
-from stack.commands.add.firmware import Command
-
-class TestAddFirmwareCommand:
- """A test case to hold the tests for the add firmware stacki Command class."""
-
- class CommandUnderTest(Command):
- """A class derived from the Command class under test used to override __init__.
-
- This allows easier instantiation for testing purposes by excluding the base Command
- class initialization code.
- """
- def __init__(self):
- pass
-
- @pytest.fixture
- def command(self):
- """Fixture to create and return a Command class instance for testing."""
- return self.CommandUnderTest()
-
- @patch.object(target = Command, attribute = "runPlugins", autospec = True)
- def test_run(self, mock_runPlugins, command):
- """Test that run will run the plugins passing through the args."""
- mock_args = ["foo", "bar", "baz"]
- mock_params = {"fizz": "buzz"}
-
- command.run(args = mock_args, params = mock_params)
-
- mock_runPlugins.assert_called_once_with(command, args = (mock_params, mock_args))
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/test_command_stack_commands_add_firmware_plugin_basic.py b/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/test_command_stack_commands_add_firmware_plugin_basic.py
deleted file mode 100644
index 03c606d42..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/test_command_stack_commands_add_firmware_plugin_basic.py
+++ /dev/null
@@ -1,852 +0,0 @@
-from unittest.mock import create_autospec, patch, ANY, call
-from collections import namedtuple
-from contextlib import ExitStack
-from pathlib import Path
-import re
-import pytest
-from stack.commands import DatabaseConnection
-from stack.commands.add.firmware import Command
-from stack.commands.add.firmware.plugin_basic import Plugin
-from stack.exception import CommandError, ArgError, ParamRequired, ParamError
-import stack.firmware
-
-class TestAddFirmwareBasicPlugin:
- """A test case for the add firmware basic plugin."""
-
- @pytest.fixture
- def basic_plugin(self):
- """A fixture that returns the plugin instance for use in tests.
-
- This sets up the required mocks needed to construct the plugin class.
- """
- mock_command = create_autospec(
- spec = Command,
- instance = True,
- )
- mock_command.db = create_autospec(
- spec = DatabaseConnection,
- spec_set = True,
- instance = True,
- )
- return Plugin(command = mock_command)
-
- def test_provides(self, basic_plugin):
- """Ensure the plugin returns the correct provides information."""
- assert basic_plugin.provides() == "basic"
-
- def test_validate_args(self, basic_plugin):
- """Test that validate args works when only one argument is passed."""
- basic_plugin.validate_args(args = ["foo"])
-
- @pytest.mark.parametrize("args", (["foo", "bar"], []))
- def test_validate_args_errors(self, args, basic_plugin):
- """Test that validate args fails when invalid arguments are passed."""
- with pytest.raises(ArgError):
- basic_plugin.validate_args(args = args)
-
- def test_validate_required_params(self, basic_plugin):
- """Test that validate_required_params works if all required params are provided."""
- basic_plugin.validate_required_params(make = "foo", model = "bar", source = "baz")
-
- @pytest.mark.parametrize(
- "make, model, source",
- (
- ("foo", "bar", ""),
- ("foo", "", "baz"),
- ("", "bar", "baz"),
- )
- )
- def test_validate_required_params_errors(self, make, model, source, basic_plugin):
- """Test that validate_required_params fails when required params are missing."""
- with pytest.raises(ParamRequired):
- basic_plugin.validate_required_params(make = make, model = model, source = source)
-
- @pytest.mark.parametrize(
- "imp, model_exists_return, call_return",
- (
- ("", True, [{"implementation": ""}]),
- ("foo", False, [{"implementation": ""}]),
- ("foo", True, [{"implementation": "foo"}]),
- )
- )
- def test_validate_imp(self, imp, model_exists_return, call_return, basic_plugin):
- """Test that validate_imp works with the expected valid combinations of parameters."""
- mock_make = "fizz"
- mock_model = "buzz"
- basic_plugin.owner.model_exists.return_value = model_exists_return
- basic_plugin.owner.call.return_value = call_return
-
- basic_plugin.validate_imp(make = mock_make, model = mock_model, imp = imp)
-
- # Ensure the expected calls were made when checking if the parameters were valid.
- basic_plugin.owner.model_exists.assert_any_call(make = mock_make, model = mock_model)
- if call_return[0]["implementation"]:
- basic_plugin.owner.call.assert_called_once_with(
- command = "list.firmware.model",
- args = [mock_model, f"make={mock_make}"]
- )
-
- @pytest.mark.parametrize(
- "imp, model_exists_return, call_return",
- (
- ("", False, [{"make": "blah", "model": "blah"}]),
- ("foo", True, [{"implementation": "bar"}]),
- )
- )
- def test_validate_imp_errors(self, imp, model_exists_return, call_return, basic_plugin):
- """Test that validate_imp fails with the invalid combinations of parameters."""
- mock_make = "fizz"
- mock_model = "buzz"
- basic_plugin.owner.model_exists.return_value = model_exists_return
- basic_plugin.owner.call.return_value = call_return
-
- with pytest.raises(ParamError):
- basic_plugin.validate_imp(make = mock_make, model = mock_model, imp = imp)
-
- @patch(target = "stack.firmware.ensure_hash_alg_supported", autospec = True)
- def test_validate_hash_alg_supported(self, mock_ensure_hash_alg_supported, basic_plugin):
- """Test that validate_hash_alg_supported works when the algorithm is supported."""
- mock_alg = "md5"
-
- basic_plugin.validate_hash_alg_supported(hash_alg = mock_alg)
-
- # Make sure it was validated
- mock_ensure_hash_alg_supported.assert_called_once_with(hash_alg = mock_alg)
-
- @patch(target = "stack.firmware.ensure_hash_alg_supported", autospec = True)
- def test_validate_hash_alg_supported_error(self, mock_ensure_hash_alg_supported, basic_plugin):
- """Test that validate_hash_alg_supported fails and re-raises the right exception when ensure_hash_alg_supported fails."""
- mock_alg = "md5"
- mock_ensure_hash_alg_supported.side_effect = stack.firmware.FirmwareError("Test error")
-
- with pytest.raises(ParamError):
- basic_plugin.validate_hash_alg_supported(hash_alg = mock_alg)
-
- MockRegexReturn = namedtuple("MockRegexReturn", ("name", "regex", "description"))
-
- @pytest.mark.parametrize(
- "regex",
- (tuple(), MockRegexReturn(name = "test", regex = "foo", description = "test"))
- )
- @patch(target = "re.search", autospec = True)
- def test_validate_version(self, mock_search, regex, basic_plugin):
- """Test that validate version regex works as expected when the version is valid."""
- mock_version = "fizz"
- mock_make = "buzz"
- mock_model = "bam!"
- basic_plugin.owner.try_get_version_regex.return_value = regex
- basic_plugin.owner.firmware_exists.return_value = False
- mock_search.return_value = True
-
- basic_plugin.validate_version(version = mock_version, make = mock_make, model = mock_model)
-
- # Make sure the version was validated
- basic_plugin.owner.firmware_exists.assert_called_once_with(
- version = mock_version,
- make = mock_make,
- model = mock_model,
- )
- basic_plugin.owner.try_get_version_regex.assert_called_once_with(
- make = mock_make,
- model = mock_model,
- )
- if regex:
- mock_search.assert_called_once_with(
- pattern = regex.regex,
- string = mock_version,
- flags = re.IGNORECASE,
- )
-
- @pytest.mark.parametrize(
- "regex, search_return, firmware_return",
- (
- (MockRegexReturn(name = "test", regex = "foo", description = "test"), False, False),
- (MockRegexReturn(name = "test", regex = "foo", description = "test"), False, True),
- (tuple(), False, True),
- )
- )
- @patch(target = "re.search", autospec = True)
- def test_validate_version_errors(self, mock_search, regex, search_return, firmware_return, basic_plugin):
- """Test that validate version regex works as expected when the version is valid."""
- mock_version = "fizz"
- mock_make = "buzz"
- mock_model = "bam!"
- basic_plugin.owner.try_get_version_regex.return_value = regex
- basic_plugin.owner.firmware_exists.return_value = firmware_return
- mock_search.return_value = search_return
-
- with pytest.raises(ArgError):
- basic_plugin.validate_version(version = mock_version, make = mock_make, model = mock_model)
-
- @patch.object(target = Plugin, attribute = "validate_required_params", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_hash_alg_supported", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_imp", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_version", autospec = True)
- def test_validate_inputs(
- self,
- mock_validate_version,
- mock_validate_imp,
- mock_validate_hash_alg_supported,
- mock_validate_required_params,
- basic_plugin,
- ):
- """Test that validate_inputs invokes the correct validation functions."""
- mock_source = "foo"
- mock_make = "bar"
- mock_model = "baz"
- mock_version = "bag"
- mock_imp = "fizz"
- mock_hash_alg = "md5"
-
- basic_plugin.validate_inputs(
- source = mock_source,
- make = mock_make,
- model = mock_model,
- version = mock_version,
- imp = mock_imp,
- hash_alg = mock_hash_alg,
- )
-
- mock_validate_version.assert_called_once_with(
- basic_plugin,
- make = mock_make,
- model = mock_model,
- version = mock_version,
- )
- mock_validate_imp.assert_called_once_with(
- basic_plugin,
- imp = mock_imp,
- make = mock_make,
- model = mock_model,
- )
- mock_validate_hash_alg_supported.assert_called_once_with(
- basic_plugin,
- hash_alg = mock_hash_alg,
- )
- mock_validate_required_params.assert_called_once_with(
- basic_plugin,
- source = mock_source,
- make = mock_make,
- model = mock_model,
- )
-
- @pytest.mark.parametrize(
- "failure_mock",
- ("validate_required_params", "validate_hash_alg_supported", "validate_imp", "validate_version"),
- )
- @patch.object(target = Plugin, attribute = "validate_required_params", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_hash_alg_supported", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_imp", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_version", autospec = True)
- def test_validate_inputs_errors(
- self,
- mock_validate_version,
- mock_validate_imp,
- mock_validate_hash_alg_supported,
- mock_validate_required_params,
- failure_mock,
- basic_plugin,
- ):
- """Test that validate_inputs fails if any of the validation functions fail."""
- mock_source = "foo"
- mock_make = "bar"
- mock_model = "baz"
- mock_version = "bag"
- mock_imp = "fizz"
- mock_hash_alg = "md5"
- mock_validation_functions = {
- "validate_required_params": mock_validate_required_params,
- "validate_hash_alg_supported": mock_validate_hash_alg_supported,
- "validate_imp": mock_validate_imp,
- "validate_version": mock_validate_version,
- }
- mock_validation_functions[failure_mock].side_effect = CommandError(
- cmd = basic_plugin.owner,
- msg = "test error",
- )
-
- with pytest.raises(CommandError):
- basic_plugin.validate_inputs(
- source = mock_source,
- make = mock_make,
- model = mock_model,
- version = mock_version,
- imp = mock_imp,
- hash_alg = mock_hash_alg,
- )
-
- def test_create_missing_imp(self, basic_plugin):
- """Test that create missing imp calls the expected functions when the imp doesn't exist."""
- mock_imp = "foo"
- basic_plugin.owner.imp_exists.return_value = False
- mock_exit_stack = create_autospec(spec = ExitStack, spec_set = True)
-
- with mock_exit_stack() as mock_cleanup:
- basic_plugin.create_missing_imp(imp = mock_imp, cleanup = mock_cleanup)
-
- # Make sure appropriate calls were made
- basic_plugin.owner.call.assert_called_once_with(
- command = "add.firmware.imp",
- args = [mock_imp],
- )
- mock_cleanup.callback.assert_called_once_with(
- basic_plugin.owner.call,
- command = "remove.firmware.imp",
- args = [mock_imp],
- )
-
- @pytest.mark.parametrize("imp, return_value", (("foo", True), ("", "unused")))
- def test_create_missing_imp_skipped(self, imp, return_value, basic_plugin):
- """Test that create missing imp skips implementation creation based on provided arguments."""
- mock_imp = imp
- basic_plugin.owner.imp_exists.return_value = return_value
- mock_exit_stack = create_autospec(spec = ExitStack, spec_set = True)
-
- with mock_exit_stack() as mock_cleanup:
- basic_plugin.create_missing_imp(imp = mock_imp, cleanup = mock_cleanup)
-
- # Make sure no implementations were attempted to be created
- basic_plugin.owner.call.assert_not_called()
- mock_cleanup.callback.assert_not_called()
-
- def test_create_missing_make(self, basic_plugin):
- """Test that create missing make calls the expected functions when the make doesn't exist."""
- mock_make = "foo"
- basic_plugin.owner.make_exists.return_value = False
- mock_exit_stack = create_autospec(spec = ExitStack, spec_set = True)
-
- with mock_exit_stack() as mock_cleanup:
- basic_plugin.create_missing_make(make = mock_make, cleanup = mock_cleanup)
-
- # Make sure appropriate calls were made
- basic_plugin.owner.call.assert_called_once_with(
- command = "add.firmware.make",
- args = [mock_make],
- )
- mock_cleanup.callback.assert_called_once_with(
- basic_plugin.owner.call,
- command = "remove.firmware.make",
- args = [mock_make],
- )
-
- def test_create_missing_make_skipped(self, basic_plugin):
- """Test that create missing make skips implementation creation based on provided arguments."""
- mock_make = "foo"
- basic_plugin.owner.imp_exists.return_value = True
- mock_exit_stack = create_autospec(spec = ExitStack, spec_set = True)
-
- with mock_exit_stack() as mock_cleanup:
- basic_plugin.create_missing_make(make = mock_make, cleanup = mock_cleanup)
-
- # Make sure no makes were attempted to be created
- basic_plugin.owner.call.assert_not_called()
- mock_cleanup.callback.assert_not_called()
-
- def test_create_missing_model(self, basic_plugin):
- """Test that create missing model calls the expected functions when the make doesn't exist."""
- mock_make = "foo"
- mock_model = "bar"
- mock_imp = "baz"
- basic_plugin.owner.model_exists.return_value = False
- mock_exit_stack = create_autospec(spec = ExitStack, spec_set = True)
-
- with mock_exit_stack() as mock_cleanup:
- basic_plugin.create_missing_model(
- make = mock_make,
- model = mock_model,
- imp = mock_imp,
- cleanup = mock_cleanup,
- )
-
- # Make sure appropriate calls were made
- basic_plugin.owner.call.assert_called_once_with(
- command = "add.firmware.model",
- args = [mock_model, f"make={mock_make}", f"imp={mock_imp}"],
- )
- mock_cleanup.callback.assert_called_once_with(
- basic_plugin.owner.call,
- command = "remove.firmware.model",
- args = [mock_model, f"make={mock_make}"],
- )
-
- def test_create_missing_model_skipped(self, basic_plugin):
- """Test that create missing model skips implementation creation based on provided arguments."""
- mock_make = "foo"
- mock_model = "bar"
- mock_imp = "baz"
- basic_plugin.owner.model_exists.return_value = True
- mock_exit_stack = create_autospec(spec = ExitStack, spec_set = True)
-
- with mock_exit_stack() as mock_cleanup:
- basic_plugin.create_missing_model(
- make = mock_make,
- model = mock_model,
- imp = mock_imp,
- cleanup = mock_cleanup,
- )
-
- # Make sure no makes were attempted to be created
- basic_plugin.owner.call.assert_not_called()
- mock_cleanup.callback.assert_not_called()
-
- @patch.object(target = Plugin, attribute = "create_missing_imp", autospec = True)
- @patch.object(target = Plugin, attribute = "create_missing_make", autospec = True)
- @patch.object(target = Plugin, attribute = "create_missing_model", autospec = True)
- def test_create_missing_related_entries(
- self,
- mock_create_missing_model,
- mock_create_missing_make,
- mock_create_missing_imp,
- basic_plugin,
- ):
- """Test that all missing related entries are attempted to be created."""
- mock_make = "foo"
- mock_model = "bar"
- mock_imp = "baz"
- mock_exit_stack = create_autospec(spec = ExitStack, spec_set = True)
-
- with mock_exit_stack() as mock_cleanup:
- basic_plugin.create_missing_related_entries(
- make = mock_make,
- model = mock_model,
- imp = mock_imp,
- cleanup = mock_cleanup,
- )
-
- mock_create_missing_model.assert_called_once_with(
- basic_plugin,
- make = mock_make,
- model = mock_model,
- imp = mock_imp,
- cleanup = mock_cleanup,
- )
- mock_create_missing_make.assert_called_once_with(
- basic_plugin,
- make = mock_make,
- cleanup = mock_cleanup,
- )
- mock_create_missing_imp.assert_called_once_with(
- basic_plugin,
- imp = mock_imp,
- cleanup = mock_cleanup,
- )
-
- @pytest.mark.parametrize("failure_mock", ("create_missing_imp", "create_missing_make", "create_missing_model"))
- @patch.object(target = Plugin, attribute = "create_missing_imp", autospec = True)
- @patch.object(target = Plugin, attribute = "create_missing_make", autospec = True)
- @patch.object(target = Plugin, attribute = "create_missing_model", autospec = True)
- def test_create_missing_related_entries_errors(
- self,
- mock_create_missing_model,
- mock_create_missing_make,
- mock_create_missing_imp,
- failure_mock,
- basic_plugin,
- ):
- """Test that create_missing_related_entries fails when adding any related entry fails."""
- mock_make = "foo"
- mock_model = "bar"
- mock_imp = "baz"
- mock_exit_stack = create_autospec(spec = ExitStack, spec_set = True)
- mock_creation_functions = {
- "create_missing_imp": mock_create_missing_imp,
- "create_missing_make": mock_create_missing_make,
- "create_missing_model": mock_create_missing_model,
- }
- mock_creation_functions[failure_mock].side_effect = CommandError(cmd = basic_plugin.owner, msg = "Test error")
-
- with mock_exit_stack() as mock_cleanup:
- with pytest.raises(CommandError):
- basic_plugin.create_missing_related_entries(
- make = mock_make,
- model = mock_model,
- imp = mock_imp,
- cleanup = mock_cleanup,
- )
-
- def test_file_cleanup(self, basic_plugin):
- """Test that file path works as expected when the file still exists."""
- mock_path = create_autospec(spec = Path, spec_set = True, instance = True)
- mock_path.exists.return_value = True
-
- basic_plugin.file_cleanup(file_path = mock_path)
-
- mock_path.exists.assert_called_once_with()
- mock_path.unlink.assert_called_once_with()
-
- def test_file_cleanup_skip(self, basic_plugin):
- """Test that file path skips unlinking when the file doesn't exist."""
- mock_path = create_autospec(spec = Path, spec_set = True, instance = True)
- mock_path.exists.return_value = False
-
- basic_plugin.file_cleanup(file_path = mock_path)
-
- mock_path.exists.assert_called_once_with()
- mock_path.unlink.assert_not_called()
-
- @patch(target = "stack.firmware.fetch_firmware", autospec = True)
- @patch.object(target = Plugin, attribute = "file_cleanup", autospec = True)
- def test_fetch_firmware(self, mock_file_cleanup, mock_fetch_firmware, basic_plugin):
- """Test that fetch_firmware works as expected when the firmware can be fetched from the source."""
- mock_source = "/foo/bar"
- mock_make = "foo"
- mock_model = "bar"
- mock_exit_stack = create_autospec(spec = ExitStack, spec_set = True)
-
- with mock_exit_stack() as mock_cleanup:
- result = basic_plugin.fetch_firmware(
- source = mock_source,
- make = mock_make,
- model = mock_model,
- cleanup = mock_cleanup,
- )
-
- mock_fetch_firmware.assert_called_once_with(
- source = mock_source,
- make = mock_make,
- model = mock_model,
- )
- mock_cleanup.callback.assert_called_once_with(
- basic_plugin.file_cleanup,
- file_path = mock_fetch_firmware.return_value,
- )
- assert mock_fetch_firmware.return_value == result
-
- @patch(target = "stack.firmware.fetch_firmware", autospec = True)
- @patch.object(target = Plugin, attribute = "file_cleanup", autospec = True)
- def test_fetch_firmware_error(self, mock_file_cleanup, mock_fetch_firmware, basic_plugin):
- """Test that fetch_firmware raises the correct exception type when stack.firmware.fetch_firmware fails."""
- mock_source = "/foo/bar"
- mock_make = "foo"
- mock_model = "bar"
- mock_fetch_firmware.side_effect = stack.firmware.FirmwareError("test error")
- mock_exit_stack = create_autospec(spec = ExitStack, spec_set = True)
-
- with mock_exit_stack() as mock_cleanup:
- with pytest.raises(ParamError):
- basic_plugin.fetch_firmware(
- source = mock_source,
- make = mock_make,
- model = mock_model,
- cleanup = mock_cleanup,
- )
-
- mock_fetch_firmware.assert_called_once_with(
- source = mock_source,
- make = mock_make,
- model = mock_model,
- )
- mock_cleanup.callback.assert_not_called()
-
- @patch(target = "stack.firmware.calculate_hash", autospec = True)
- def test_calculate_hash(self, mock_calculate_hash, basic_plugin):
- """Test that calculate hash works as expected when the hash is valid."""
- mock_file_path = "/foo"
- mock_hash_alg = "md5"
- mock_hash_value = "bar"
-
- result = basic_plugin.calculate_hash(
- file_path = mock_file_path,
- hash_alg = mock_hash_alg,
- hash_value = mock_hash_value,
- )
-
- assert mock_calculate_hash.return_value == result
- mock_calculate_hash.assert_called_once_with(
- file_path = mock_file_path,
- hash_alg = mock_hash_alg,
- hash_value = mock_hash_value,
- )
-
- @patch(target = "stack.firmware.calculate_hash", autospec = True)
- def test_calculate_hash_error(self, mock_calculate_hash, basic_plugin):
- """Test that calculate hash raises the correct exception type on a failure."""
- mock_file_path = "/foo"
- mock_hash_alg = "md5"
- mock_hash_value = "bar"
- mock_calculate_hash.side_effect = stack.firmware.FirmwareError("Test error")
-
- with pytest.raises(ParamError):
- basic_plugin.calculate_hash(
- file_path = mock_file_path,
- hash_alg = mock_hash_alg,
- hash_value = mock_hash_value,
- )
-
- @patch.object(target = Plugin, attribute = "create_missing_related_entries", autospec = True)
- @patch.object(target = Plugin, attribute = "calculate_hash", autospec = True)
- @patch.object(target = Plugin, attribute = "fetch_firmware", autospec = True)
- def test_add_firmware(
- self,
- mock_fetch_firmware,
- mock_calculate_hash,
- mock_create_missing_related_entries,
- basic_plugin,
- ):
- """Test that adding firmware performs the expected actions."""
- mock_source = "foo"
- mock_make = "bar"
- mock_model = "baz"
- mock_version = "bag"
- mock_imp = "fizz"
- mock_hash_value = "buzz"
- mock_hash_alg = "md5"
- mock_exit_stack = create_autospec(spec = ExitStack, spec_set = True)
-
- with mock_exit_stack() as mock_cleanup:
- basic_plugin.add_firmware(
- source = mock_source,
- make = mock_make,
- model = mock_model,
- version = mock_version,
- imp = mock_imp,
- hash_value = mock_hash_value,
- hash_alg = mock_hash_alg,
- cleanup = mock_cleanup,
- )
-
- mock_fetch_firmware.assert_called_once_with(
- basic_plugin,
- source = mock_source,
- make = mock_make,
- model = mock_model,
- cleanup = mock_cleanup,
- )
- mock_calculate_hash.assert_called_once_with(
- basic_plugin,
- file_path = mock_fetch_firmware.return_value,
- hash_value = mock_hash_value,
- hash_alg = mock_hash_alg,
- )
- mock_create_missing_related_entries.assert_called_once_with(
- basic_plugin,
- make = mock_make,
- model = mock_model,
- imp = mock_imp,
- cleanup = mock_cleanup,
- )
- basic_plugin.owner.get_model_id.assert_called_once_with(
- make = mock_make,
- model = mock_model,
- )
- basic_plugin.owner.db.execute.assert_called_once_with(
- ANY,
- (
- basic_plugin.owner.get_model_id.return_value,
- mock_source,
- mock_version,
- mock_hash_alg,
- mock_calculate_hash.return_value,
- str(mock_fetch_firmware.return_value),
- )
- )
- mock_cleanup.callback.assert_called_once_with(
- basic_plugin.owner.call,
- command = "remove.firmware",
- args = [mock_version, f"make={mock_make}", f"model={mock_model}"],
- )
-
- @pytest.mark.parametrize(
- "failure_mock",
- ("create_missing_related_entries", "calculate_hash", "fetch_firmware"),
- )
- @patch.object(target = Plugin, attribute = "create_missing_related_entries", autospec = True)
- @patch.object(target = Plugin, attribute = "calculate_hash", autospec = True)
- @patch.object(target = Plugin, attribute = "fetch_firmware", autospec = True)
- def test_add_firmware_errors(
- self,
- mock_fetch_firmware,
- mock_calculate_hash,
- mock_create_missing_related_entries,
- failure_mock,
- basic_plugin,
- ):
- """Test that adding firmware fails when performing related actions fails."""
- mock_source = "foo"
- mock_make = "bar"
- mock_model = "baz"
- mock_version = "bag"
- mock_imp = "fizz"
- mock_hash_value = "buzz"
- mock_hash_alg = "md5"
- mock_exit_stack = create_autospec(spec = ExitStack, spec_set = True)
- mock_functions = {
- "create_missing_related_entries": mock_create_missing_related_entries,
- "calculate_hash": mock_calculate_hash,
- "fetch_firmware": mock_fetch_firmware,
- }
- mock_functions[failure_mock].side_effect = CommandError(cmd = basic_plugin.owner, msg = "Test error")
-
- with mock_exit_stack() as mock_cleanup:
- with pytest.raises(CommandError):
- basic_plugin.add_firmware(
- source = mock_source,
- make = mock_make,
- model = mock_model,
- version = mock_version,
- imp = mock_imp,
- hash_value = mock_hash_value,
- hash_alg = mock_hash_alg,
- cleanup = mock_cleanup,
- )
-
- basic_plugin.owner.db.execute.assert_not_called()
-
- @patch.object(target = Plugin, attribute = "add_firmware", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_inputs", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_args", autospec = True)
- @patch(target = "stack.commands.add.firmware.plugin_basic.ExitStack", autospec = True)
- @patch(target = "stack.commands.add.firmware.plugin_basic.unique_everseen", autospec = True)
- @patch(target = "stack.commands.add.firmware.plugin_basic.lowered", autospec = True)
- def test_run(
- self,
- mock_lowered,
- mock_unique_everseen,
- mock_exit_stack,
- mock_validate_args,
- mock_validate_inputs,
- mock_add_firmware,
- basic_plugin,
- ):
- """Test that run performs all expected operations when all params and args are valid."""
- mock_params = {
- "make": "foo",
- "model": "bar",
- "imp": "baz",
- "hosts": "herp, derp, ferp",
- "hash_alg": "md5",
- "hash": "1234",
- "source": "/fizz/buzz",
- }
- mock_args = ["MOCK_VERSION"]
- expected_version = mock_args[0].lower()
- expected_hosts = tuple(host.strip() for host in mock_params["hosts"].split(",") if host.strip())
- expected_lower_params = list(mock_params.values())[:5]
- basic_plugin.owner.fillParams.return_value = mock_params.values()
- mock_lowered.return_value = expected_lower_params
- mock_unique_everseen.return_value = expected_hosts
- basic_plugin.owner.getHosts.return_value = expected_hosts
-
- basic_plugin.run(args = (mock_params, mock_args))
-
- mock_validate_args.assert_called_once_with(basic_plugin, args = mock_args)
- basic_plugin.owner.fillParams.assert_called_once_with(
- names = [
- ("make", ""),
- ("model", ""),
- ("imp", ""),
- ("hosts", ""),
- ("hash_alg", "md5"),
- ("hash", ""),
- ("source", ""),
- ],
- params = mock_params,
- )
- mock_lowered.assert_called_once_with(expected_lower_params)
- mock_validate_inputs.assert_called_once_with(
- basic_plugin,
- source = mock_params["source"],
- make = mock_params["make"],
- model = mock_params["model"],
- version = expected_version,
- imp = mock_params["imp"],
- hash_alg = mock_params["hash_alg"],
- )
- mock_unique_everseen.assert_called_once()
- args = mock_unique_everseen.call_args[0]
- assert expected_hosts == tuple(args[0])
- basic_plugin.owner.getHosts.assert_called_once_with(args = expected_hosts)
- mock_add_firmware.assert_called_once_with(
- basic_plugin,
- source = mock_params["source"],
- make = mock_params["make"],
- model = mock_params["model"],
- version = expected_version,
- imp = mock_params["imp"],
- hash_value = mock_params["hash"],
- hash_alg = mock_params["hash_alg"],
- cleanup = mock_exit_stack.return_value.__enter__.return_value,
- )
- basic_plugin.owner.call.assert_called_once_with(
- command = "add.host.firmware.mapping",
- args = [
- *expected_hosts,
- f"version={expected_version}",
- f"make={mock_params['make']}",
- f"model={mock_params['model']}",
- ],
- )
- mock_exit_stack.return_value.__enter__.return_value.pop_all.assert_called_once_with()
-
- @patch.object(target = Plugin, attribute = "add_firmware", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_inputs", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_args", autospec = True)
- @patch(target = "stack.commands.add.firmware.plugin_basic.ExitStack", autospec = True)
- @patch(target = "stack.commands.add.firmware.plugin_basic.unique_everseen", autospec = True)
- @patch(target = "stack.commands.add.firmware.plugin_basic.lowered", autospec = True)
- def test_run_no_hosts(
- self,
- mock_lowered,
- mock_unique_everseen,
- mock_exit_stack,
- mock_validate_args,
- mock_validate_inputs,
- mock_add_firmware,
- basic_plugin,
- ):
- """Test that run performs all expected operations when all params and args are valid and no hosts are provided."""
- mock_params = {
- "make": "foo",
- "model": "bar",
- "imp": "baz",
- "hosts": "",
- "hash_alg": "md5",
- "hash": "1234",
- "source": "/fizz/buzz",
- }
- mock_args = ["MOCK_VERSION"]
- expected_version = mock_args[0].lower()
- expected_lower_params = list(mock_params.values())[:5]
- basic_plugin.owner.fillParams.return_value = mock_params.values()
- mock_lowered.return_value = expected_lower_params
-
- basic_plugin.run(args = (mock_params, mock_args))
-
- mock_validate_args.assert_called_once_with(basic_plugin, args = mock_args)
- basic_plugin.owner.fillParams.assert_called_once_with(
- names = [
- ("make", ""),
- ("model", ""),
- ("imp", ""),
- ("hosts", ""),
- ("hash_alg", "md5"),
- ("hash", ""),
- ("source", ""),
- ],
- params = mock_params,
- )
- mock_lowered.assert_called_once_with(expected_lower_params)
- mock_validate_inputs.assert_called_once_with(
- basic_plugin,
- source = mock_params["source"],
- make = mock_params["make"],
- model = mock_params["model"],
- version = expected_version,
- imp = mock_params["imp"],
- hash_alg = mock_params["hash_alg"],
- )
- mock_unique_everseen.assert_not_called()
- basic_plugin.owner.getHosts.assert_not_called()
- mock_add_firmware.assert_called_once_with(
- basic_plugin,
- source = mock_params["source"],
- make = mock_params["make"],
- model = mock_params["model"],
- version = expected_version,
- imp = mock_params["imp"],
- hash_value = mock_params["hash"],
- hash_alg = mock_params["hash_alg"],
- cleanup = mock_exit_stack.return_value.__enter__.return_value,
- )
- basic_plugin.owner.call.assert_not_called()
- mock_exit_stack.return_value.__enter__.return_value.pop_all.assert_called_once_with()
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/version_regex/test_command_stack_commands_add_firmware_version_regex__init__.py b/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/version_regex/test_command_stack_commands_add_firmware_version_regex__init__.py
deleted file mode 100644
index e7c676334..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/version_regex/test_command_stack_commands_add_firmware_version_regex__init__.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from unittest.mock import patch
-import pytest
-from stack.commands.add.firmware.version_regex import Command
-
-class TestAddFirmwareVersionRegexCommand:
- """A test case to hold the tests for the add firmware version_regex stacki Command class."""
-
- class CommandUnderTest(Command):
- """A class derived from the Command class under test used to override __init__.
-
- This allows easier instantiation for testing purposes by excluding the base Command
- class initialization code.
- """
- def __init__(self):
- pass
-
- @pytest.fixture
- def command(self):
- """Fixture to create and return a Command class instance for testing."""
- return self.CommandUnderTest()
-
- @patch.object(target = Command, attribute = "runPlugins", autospec = True)
- def test_run(self, mock_runPlugins, command):
- """Test that run will run the plugins passing through the args and params."""
- mock_params = {"foo": "bar", "baz": "bag"}
- mock_args = ["foo", "bar", "baz"]
-
- command.run(params = mock_params, args = mock_args)
-
- mock_runPlugins.assert_called_once_with(command, args = (mock_params, mock_args))
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/version_regex/test_command_stack_commands_add_firmware_version_regex_plugin_basic.py b/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/version_regex/test_command_stack_commands_add_firmware_version_regex_plugin_basic.py
deleted file mode 100644
index 80cc58ac3..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/add/firmware/version_regex/test_command_stack_commands_add_firmware_version_regex_plugin_basic.py
+++ /dev/null
@@ -1,298 +0,0 @@
-from unittest.mock import create_autospec, patch, ANY
-import pytest
-from stack.commands import DatabaseConnection
-from stack.commands.add.firmware import command
-from stack.commands.add.firmware.version_regex.plugin_basic import Plugin
-from stack.exception import ArgError, ParamError, StackError
-
-class TestAddVersionRegexBasicPlugin:
- """A test case for the add firmware version_regex basic plugin."""
-
- @pytest.fixture
- def basic_plugin(self):
- """A fixture that returns the plugin instance for use in tests.
-
- This sets up the required mocks needed to construct the plugin class.
- """
- mock_command = create_autospec(
- spec = command,
- instance = True,
- )
- mock_command.db = create_autospec(
- spec = DatabaseConnection,
- spec_set = True,
- instance = True,
- )
- return Plugin(command = mock_command)
-
- def test_provides(self, basic_plugin):
- """Make sure the plugin returns the correct provides information."""
- assert basic_plugin.provides() == "basic"
-
- def test_validate_args(self, basic_plugin):
- """Test that validate_args works if the args list has one element."""
- basic_plugin.validate_args(args = ["foo"])
-
- @pytest.mark.parametrize("test_input", ([], ["foo", "bar"]))
- def test_validate_args_failure(self, test_input, basic_plugin):
- """Test that validate_args fails with bad input."""
- with pytest.raises(ArgError):
- basic_plugin.validate_args(args = test_input)
-
- def test_validate_regex(self, basic_plugin):
- """Test that validate_regex works if the regex provided is valid."""
- basic_plugin.validate_regex(regex = "(foo)bar")
-
- @pytest.mark.parametrize("test_input", ("", "(", ")", "(foobar"))
- def test_validate_regex_failure(self, test_input, basic_plugin):
- """Test that validate_regex fails with bad input."""
- with pytest.raises(ArgError):
- basic_plugin.validate_regex(regex = test_input)
-
- def test_validate_name(self, basic_plugin):
- """Test that validate_name works if the name provided is not empty and is unique."""
- basic_plugin.owner.version_regex_exists.return_value = False
- mock_name = "foo"
-
- basic_plugin.validate_name(name = mock_name)
-
- basic_plugin.owner.version_regex_exists.assert_called_once_with(name = mock_name)
-
- @pytest.mark.parametrize(
- "test_input, return_value",
- (
- ("", False),
- ("foo", True),
- )
- )
- def test_validate_name_failure(self, test_input, return_value, basic_plugin):
- """Test that validate_name fails with bad input."""
- basic_plugin.owner.version_regex_exists.return_value = return_value
-
- with pytest.raises(ParamError):
- basic_plugin.validate_name(name = test_input)
-
- @patch(target = "stack.commands.add.firmware.version_regex.plugin_basic.unique_everseen", autospec = True)
- @patch(target = "stack.commands.add.firmware.version_regex.plugin_basic.ExitStack", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_name", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_regex", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_args", autospec = True)
- def test_run(
- self,
- mock_validate_args,
- mock_validate_regex,
- mock_validate_name,
- mock_exit_stack,
- mock_unique_everseen,
- basic_plugin,
- ):
- """Test that run performs the expected action in the case where all arguments and parameters are valid."""
- mock_args = ["mock_regex"]
- # We're using uppercase to ensure that lower is used correctly when processing args and params.
- mock_models = ["MOCK_MODEL1", "MOCK_MODEL2", "MOCK_MODEL3"]
- expected_models = tuple((string.lower() for string in mock_models))
- mock_params = {
- "name": "MOCK_NAME",
- "description": "MOCK_DESCRIPTION",
- "make": "MOCK_MAKE",
- "models": ", ".join(mock_models),
- }
- expected_name = mock_params["name"].lower()
- expected_make = mock_params["make"].lower()
- basic_plugin.owner.fillParams.return_value = mock_params.values()
- mock_unique_everseen.return_value = (string for string in expected_models)
-
- basic_plugin.run(args = (mock_params, mock_args))
-
- # Make sure the params were filled with defaults.
- basic_plugin.owner.fillParams.assert_called_once_with(
- names = [
- ("name", ""),
- ("description", ""),
- ("make", ""),
- ("models", ""),
- ],
- params = mock_params,
- )
- # Expect all the validation functions to be called to validate params and args.
- mock_validate_args.assert_called_once_with(basic_plugin, args = mock_args)
- mock_validate_regex.assert_called_once_with(basic_plugin, regex = mock_args[0])
- mock_validate_name.assert_called_once_with(basic_plugin, name = expected_name)
- # Expect the models to be made unique, forced to lowercase, and validated.
- assert expected_models == tuple(*mock_unique_everseen.call_args[0])
- basic_plugin.owner.ensure_models_exist.assert_called_once_with(
- make = expected_make,
- models = expected_models,
- )
- # Expect the DB entries to be made.
- basic_plugin.owner.db.execute.assert_called_once_with(
- ANY,
- (mock_args[0], expected_name, mock_params["description"]),
- )
- basic_plugin.owner.call.assert_called_once_with(
- command = "set.firmware.model.version_regex",
- args = [*expected_models, f"make={expected_make}", f"version_regex={expected_name}"]
- )
- # Make sure that ExitStack was used to guard against failures.
- mock_exit_stack.return_value.__enter__.return_value.callback.assert_called_once_with(
- basic_plugin.owner.call,
- command = "remove.firmware.version_regex",
- args = [expected_name],
- )
- mock_exit_stack.return_value.__enter__.return_value.pop_all.assert_called_once_with()
-
- @patch(target = "stack.commands.add.firmware.version_regex.plugin_basic.unique_everseen", autospec = True)
- @patch(target = "stack.commands.add.firmware.version_regex.plugin_basic.ExitStack", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_name", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_regex", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_args", autospec = True)
- def test_run_no_models(
- self,
- mock_validate_args,
- mock_validate_regex,
- mock_validate_name,
- mock_exit_stack,
- mock_unique_everseen,
- basic_plugin,
- ):
- """Test that run performs the expected action in the case where all arguments and parameters are valid but not models are provided."""
- mock_args = ["mock_regex"]
- # We're using uppercase to ensure that lower is used correctly when processing args and params.
- mock_params = {
- "name": "MOCK_NAME",
- "description": "MOCK_DESCRIPTION",
- "make": "MOCK_MAKE",
- "models": "",
- }
- expected_name = mock_params["name"].lower()
- expected_make = mock_params["make"].lower()
- basic_plugin.owner.fillParams.return_value = mock_params.values()
-
- basic_plugin.run(args = (mock_params, mock_args))
-
- # Make sure the params were filled with defaults.
- basic_plugin.owner.fillParams.assert_called_once_with(
- names = [
- ("name", ""),
- ("description", ""),
- ("make", ""),
- ("models", ""),
- ],
- params = mock_params,
- )
- # Expect all the validation functions to be called to validate params and args.
- mock_validate_args.assert_called_once_with(basic_plugin, args = mock_args)
- mock_validate_regex.assert_called_once_with(basic_plugin, regex = mock_args[0])
- mock_validate_name.assert_called_once_with(basic_plugin, name = expected_name)
- basic_plugin.owner.ensure_make_exists.assert_called_once_with(make = expected_make)
- # Expect the models to not be validated.
- mock_unique_everseen.assert_not_called()
- basic_plugin.owner.ensure_models_exist.assert_not_called()
- # Expect the DB entries to be made.
- basic_plugin.owner.db.execute.assert_called_once_with(
- ANY,
- (mock_args[0], expected_name, mock_params["description"]),
- )
- basic_plugin.owner.call.assert_called_once_with(
- command = "set.firmware.make.version_regex",
- args = [expected_make, f"version_regex={expected_name}"]
- )
- # Make sure that ExitStack was used to guard against failures.
- mock_exit_stack.return_value.__enter__.return_value.callback.assert_called_once_with(
- basic_plugin.owner.call,
- command = "remove.firmware.version_regex",
- args = [expected_name],
- )
- mock_exit_stack.return_value.__enter__.return_value.pop_all.assert_called_once_with()
-
- @pytest.mark.parametrize(
- "failure_mock, mock_models",
- (
- ("ensure_make_exists", ""),
- ("ensure_models_exist", "MOCK_MODEL1, MOCK_MODEL2, MOCK_MODEL3"),
- ("validate_name", ""),
- ("validate_name", "MOCK_MODEL1, MOCK_MODEL2, MOCK_MODEL3"),
- ("validate_regex", ""),
- ("validate_regex", "MOCK_MODEL1, MOCK_MODEL2, MOCK_MODEL3"),
- ("validate_args", ""),
- ("validate_args", "MOCK_MODEL1, MOCK_MODEL2, MOCK_MODEL3"),
- ),
- )
- @patch(target = "stack.commands.add.firmware.version_regex.plugin_basic.unique_everseen", autospec = True)
- @patch(target = "stack.commands.add.firmware.version_regex.plugin_basic.ExitStack", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_name", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_regex", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_args", autospec = True)
- def test_run_validation_errors(
- self,
- mock_validate_args,
- mock_validate_regex,
- mock_validate_name,
- mock_exit_stack,
- mock_unique_everseen,
- failure_mock,
- mock_models,
- basic_plugin,
- ):
- """Test that run fails when the parameters do not validate."""
- mock_args = ["mock_regex"]
- mock_params = {
- "name": "MOCK_NAME",
- "description": "MOCK_DESCRIPTION",
- "make": "MOCK_MAKE",
- "models": mock_models,
- }
- basic_plugin.owner.fillParams.return_value = mock_params.values()
- validation_function_mocks = {
- "validate_args": mock_validate_args,
- "validate_regex": mock_validate_regex,
- "validate_name": mock_validate_name,
- "ensure_make_exists": basic_plugin.owner.ensure_make_exists,
- "ensure_models_exist": basic_plugin.owner.ensure_models_exist,
- }
- # Set the appropriate mock call to fail
- validation_function_mocks[failure_mock].side_effect = StackError("Test error")
-
- with pytest.raises(StackError):
- basic_plugin.run(args = (mock_params, mock_args))
-
- # Make sure the database calls are not made if the arguments don't validate
- basic_plugin.owner.db.execute.assert_not_called()
-
- @pytest.mark.parametrize("mock_models", ("", "MOCK_MODEL1, MOCK_MODEL2, MOCK_MODEL3"))
- @patch(target = "stack.commands.add.firmware.version_regex.plugin_basic.unique_everseen", autospec = True)
- @patch(target = "stack.commands.add.firmware.version_regex.plugin_basic.ExitStack", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_name", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_regex", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_args", autospec = True)
- def test_run_set_relation_fails(
- self,
- mock_validate_args,
- mock_validate_regex,
- mock_validate_name,
- mock_exit_stack,
- mock_unique_everseen,
- mock_models,
- basic_plugin,
- ):
- """Test that run fails and cleans up when setting the relation to the make or model fails."""
- mock_args = ["mock_regex"]
- mock_params = {
- "name": "MOCK_NAME",
- "description": "MOCK_DESCRIPTION",
- "make": "MOCK_MAKE",
- "models": mock_models,
- }
- basic_plugin.owner.fillParams.return_value = mock_params.values()
- basic_plugin.owner.call.side_effect = StackError("Test error")
-
- with pytest.raises(StackError):
- basic_plugin.run(args = (mock_params, mock_args))
-
- # Make sure the cleanup is setup to be run, and don't expect it to be dismissed because of the error.
- mock_exit_stack.return_value.__enter__.return_value.callback.assert_called_once_with(
- basic_plugin.owner.call,
- command = "remove.firmware.version_regex",
- args = [mock_params["name"].lower()],
- )
- mock_exit_stack.return_value.__enter__.return_value.pop_all.assert_not_called()
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/add/host/firmware/mapping/test_command_stack_commands_add_host_firmware_mapping__init__.py b/test-framework/test-suites/unit/tests/command/stack/commands/add/host/firmware/mapping/test_command_stack_commands_add_host_firmware_mapping__init__.py
deleted file mode 100644
index 692f37240..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/add/host/firmware/mapping/test_command_stack_commands_add_host_firmware_mapping__init__.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from unittest.mock import patch
-import pytest
-from stack.commands.add.host.firmware.mapping import Command
-
-class TestAddHostFirmwareMappingCommand:
- """A test case to hold the tests for the add host firmware mapping stacki Command class."""
-
- class CommandUnderTest(Command):
- """A class derived from the Command class under test used to override __init__.
-
- This allows easier instantiation for testing purposes by excluding the base Command
- class initialization code.
- """
- def __init__(self):
- pass
-
- @pytest.fixture
- def command(self):
- """Fixture to create and return a Command class instance for testing."""
- return self.CommandUnderTest()
-
- @patch.object(target = Command, attribute = "runPlugins", autospec = True)
- def test_run(self, mock_runPlugins, command):
- """Test that run will run the plugins passing through the args."""
- mock_args = ["foo", "bar", "baz"]
- mock_params = {"fizz": "buzz"}
-
- command.run(args = mock_args, params = mock_params)
-
- mock_runPlugins.assert_called_once_with(command, args = (mock_params, mock_args))
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/add/host/firmware/mapping/test_command_stack_commands_add_host_firmware_mapping_plugin_basic.py b/test-framework/test-suites/unit/tests/command/stack/commands/add/host/firmware/mapping/test_command_stack_commands_add_host_firmware_mapping_plugin_basic.py
deleted file mode 100644
index 84194c717..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/add/host/firmware/mapping/test_command_stack_commands_add_host_firmware_mapping_plugin_basic.py
+++ /dev/null
@@ -1,161 +0,0 @@
-from unittest.mock import create_autospec, patch, ANY, call
-import pytest
-from contextlib import ExitStack
-from stack.commands import DatabaseConnection
-from stack.commands.add.host.firmware.mapping import Command
-from stack.commands.add.host.firmware.mapping.plugin_basic import Plugin
-from stack.exception import CommandError
-
-class TestAddHostFirmwareMappingBasicPlugin:
- """A test case for the add host firmware mapping basic plugin."""
-
- @pytest.fixture
- def basic_plugin(self):
- """A fixture that returns the plugin instance for use in tests.
-
- This sets up the required mocks needed to construct the plugin class.
- """
- mock_command = create_autospec(
- spec = Command,
- instance = True,
- )
- mock_command.db = create_autospec(
- spec = DatabaseConnection,
- spec_set = True,
- instance = True,
- )
- return Plugin(command = mock_command)
-
- def test_provides(self, basic_plugin):
- """Ensure the plugin returns the correct provides information."""
- assert basic_plugin.provides() == "basic"
-
- def test_ensure_unique_mappings(self, basic_plugin):
- """Test that ensure_unique_mappings queries the db as expected."""
- mock_hosts = ["foo", "bar"]
- mock_make = "baz"
- mock_model = "bag"
- mock_version = "bam"
- basic_plugin.owner.db.select.return_value = []
-
- basic_plugin.ensure_unique_mappings(
- hosts = mock_hosts,
- version = mock_version,
- make = mock_make,
- model = mock_model,
- )
-
- basic_plugin.owner.db.select.assert_called_once_with(
- ANY,
- (mock_hosts, mock_make, mock_model, mock_version),
- )
-
- def test_ensure_unique_mappings_error(self, basic_plugin):
- """Test that ensure_unique_mappings fails if matching mappings are found."""
- mock_hosts = ["foo", "bar"]
- mock_make = "baz"
- mock_model = "bag"
- mock_version = "bam"
- basic_plugin.owner.db.select.return_value = [[mock_hosts[0], mock_make, mock_model, mock_version]]
-
- with pytest.raises(CommandError):
- basic_plugin.ensure_unique_mappings(
- hosts = mock_hosts,
- version = mock_version,
- make = mock_make,
- model = mock_model,
- )
-
- @patch.object(target = Plugin, attribute = "ensure_unique_mappings", autospec = True)
- @patch(target = "stack.commands.add.host.firmware.mapping.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.add.host.firmware.mapping.plugin_basic.unique_everseen", autospec = True)
- def test_run(
- self,
- mock_unique_everseen,
- mock_lowered,
- mock_ensure_unique_mappings,
- basic_plugin,
- ):
- """Test that run works as expected when the params and args are valid."""
- mock_args = ["foo", "bar", "baz"]
- mock_params = {"version": "bam!", "make": "fizz", "model": "buzz"}
- expected_hosts = tuple(mock_args)
- mock_lowered.return_value = mock_params.values()
- mock_unique_everseen.return_value = mock_args
- basic_plugin.owner.getHosts.return_value = expected_hosts
- basic_plugin.owner.db.select.return_value = [["1"]]
-
- basic_plugin.run(args = (mock_params, mock_args))
-
- assert [call(mock_args), call(basic_plugin.owner.fillParams.return_value)] == mock_lowered.mock_calls
- mock_unique_everseen.assert_called_once_with(mock_lowered.return_value)
- basic_plugin.owner.getHosts.assert_called_once_with(args = expected_hosts)
- basic_plugin.owner.fillParams.assert_called_once_with(
- names = [
- ("version", ""),
- ("make", ""),
- ("model", ""),
- ],
- params = mock_params,
- )
- basic_plugin.owner.ensure_firmware_exists.assert_called_once_with(
- make = mock_params["make"],
- model = mock_params["model"],
- version = mock_params["version"],
- )
- mock_ensure_unique_mappings.assert_called_once_with(
- basic_plugin,
- hosts = expected_hosts,
- make = mock_params["make"],
- model = mock_params["model"],
- version = mock_params["version"],
- )
- basic_plugin.owner.db.select.assert_called_once_with(ANY, (expected_hosts,))
- basic_plugin.owner.get_firmware_id.assert_called_once_with(
- make = mock_params["make"],
- model = mock_params["model"],
- version = mock_params["version"],
- )
- basic_plugin.owner.db.execute.assert_called_once_with(
- ANY,
- [(basic_plugin.owner.db.select.return_value[0][0], basic_plugin.owner.get_firmware_id.return_value)],
- many = True,
- )
-
- @pytest.mark.parametrize(
- "failure_mock",
- ("getHosts", "ensure_firmware_exists", "ensure_unique_mappings"),
- )
- @patch.object(target = Plugin, attribute = "ensure_unique_mappings", autospec = True)
- @patch(target = "stack.commands.add.host.firmware.mapping.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.add.host.firmware.mapping.plugin_basic.unique_everseen", autospec = True)
- def test_run_errors(
- self,
- mock_unique_everseen,
- mock_lowered,
- mock_ensure_unique_mappings,
- failure_mock,
- basic_plugin,
- ):
- """Test that run fails when invalid arguments or parameters are provided."""
- mock_args = ["foo", "bar", "baz"]
- mock_params = {"version": "bam!", "make": "fizz", "model": "buzz"}
- expected_hosts = tuple(mock_args)
- mock_lowered.return_value = mock_params.values()
- mock_unique_everseen.return_value = mock_args
- basic_plugin.owner.getHosts.return_value = expected_hosts
- basic_plugin.owner.db.select.return_value = [["1"]]
- mock_validation_functions = {
- "getHosts": basic_plugin.owner.getHosts,
- "ensure_firmware_exists": basic_plugin.owner.ensure_firmware_exists,
- "ensure_unique_mappings": mock_ensure_unique_mappings,
- }
- mock_validation_functions[failure_mock].side_effect = CommandError(
- cmd = basic_plugin.owner,
- msg = "Test error",
- )
-
- with pytest.raises(CommandError):
- basic_plugin.run(args = (mock_params, mock_args))
-
- basic_plugin.owner.db.execute.assert_not_called()
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/imp/test_command_stack_commands_list_firmware_imp__init__.py b/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/imp/test_command_stack_commands_list_firmware_imp__init__.py
deleted file mode 100644
index fcbc5e318..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/imp/test_command_stack_commands_list_firmware_imp__init__.py
+++ /dev/null
@@ -1,49 +0,0 @@
-from unittest.mock import patch, call
-import pytest
-from stack.commands.list.firmware.imp import Command
-
-class TestListFirmwareImpCommand:
- """A test case to hold the tests for the list firmware imp stacki Command class."""
-
- class CommandUnderTest(Command):
- """A class derived from the Command class under test used to override __init__.
-
- This allows easier instantiation for testing purposes by excluding the base Command
- class initialization code.
- """
- def __init__(self):
- pass
-
- @pytest.fixture
- def command(self):
- """Fixture to create and return a Command class instance for testing."""
- return self.CommandUnderTest()
-
- @patch.object(target = Command, attribute = "endOutput", autospec = True)
- @patch.object(target = Command, attribute = "addOutput", autospec = True)
- @patch.object(target = Command, attribute = "beginOutput", autospec = True)
- @patch.object(target = Command, attribute = "runPlugins", autospec = True)
- def test_run(self, mock_runPlugins, mock_beginOutput, mock_addOutput, mock_endOutput, command):
- """Test that run will run the plugins, gather the results, and output them as expected."""
- mock_header = ["imp"]
- mock_first_value = ("foo", [])
- mock_second_value = ("bar", [])
- mock_runPlugins.return_value = (
- (
- "basic",
- {
- "keys": mock_header,
- "values": [mock_first_value, mock_second_value],
- },
- ),
- )
-
- command.run("unused", "unused")
-
- mock_runPlugins.assert_called_once_with(command)
- mock_beginOutput.assert_called_once_with(command)
- assert [
- call(command, owner = mock_first_value[0], vals = mock_first_value[1]),
- call(command, owner = mock_second_value[0], vals = mock_second_value[1]),
- ] == mock_addOutput.mock_calls
- mock_endOutput.assert_called_once_with(command, header = mock_header)
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/imp/test_command_stack_commands_list_firmware_imp_plugin_basic.py b/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/imp/test_command_stack_commands_list_firmware_imp_plugin_basic.py
deleted file mode 100644
index 55d42ab80..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/imp/test_command_stack_commands_list_firmware_imp_plugin_basic.py
+++ /dev/null
@@ -1,41 +0,0 @@
-from unittest.mock import create_autospec, ANY
-import pytest
-from stack.commands import DatabaseConnection
-from stack.commands.list.firmware import Command
-from stack.commands.list.firmware.imp.plugin_basic import Plugin
-
-class TestListFirmwareImpBasicPlugin:
- """A test case for the list firmware imp basic plugin."""
-
- @pytest.fixture
- def basic_plugin(self):
- """A fixture that returns the plugin instance for use in tests.
-
- This sets up the required mocks needed to construct the plugin class.
- """
- mock_command = create_autospec(
- spec = Command,
- instance = True,
- )
- mock_command.db = create_autospec(
- spec = DatabaseConnection,
- spec_set = True,
- instance = True,
- )
- return Plugin(command = mock_command)
-
- def test_provides(self, basic_plugin):
- """Ensure that provides returns 'basic'."""
- assert basic_plugin.provides() == "basic"
-
- def test_run(self, basic_plugin):
- """Test that run queries the DB as expected."""
- basic_plugin.owner.db.select.return_value = [["foo"], ["bar"]]
- expected_results = {
- "keys": ["imp"],
- "values": [(row[0], []) for row in basic_plugin.owner.db.select.return_value],
- }
-
- assert expected_results == basic_plugin.run(args = "unused")
-
- basic_plugin.owner.db.select.assert_called_once_with(ANY)
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/make/test_command_stack_commands_list_firmware_make__init__.py b/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/make/test_command_stack_commands_list_firmware_make__init__.py
deleted file mode 100644
index c4aa4e4fb..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/make/test_command_stack_commands_list_firmware_make__init__.py
+++ /dev/null
@@ -1,65 +0,0 @@
-from unittest.mock import patch, call
-import pytest
-from stack.commands.list.firmware.make import Command
-
-class TestListFirmwareMakeCommand:
- """A test case to hold the tests for the list firmware make stacki Command class."""
-
- class CommandUnderTest(Command):
- """A class derived from the Command class under test used to override __init__.
-
- This allows easier instantiation for testing purposes by excluding the base Command
- class initialization code.
- """
- def __init__(self):
- pass
-
- @pytest.fixture
- def command(self):
- """Fixture to create and return a Command class instance for testing."""
- return self.CommandUnderTest()
-
- @patch.object(target = Command, attribute = "str2bool", autospec = True)
- @patch.object(target = Command, attribute = "fillParams", autospec = True)
- @patch.object(target = Command, attribute = "endOutput", autospec = True)
- @patch.object(target = Command, attribute = "addOutput", autospec = True)
- @patch.object(target = Command, attribute = "beginOutput", autospec = True)
- @patch.object(target = Command, attribute = "runPlugins", autospec = True)
- def test_run(
- self,
- mock_runPlugins,
- mock_beginOutput,
- mock_addOutput,
- mock_endOutput,
- mock_fillParams,
- mock_str2bool,
- command,
- ):
- """Test that run will run the plugins, gather the results, and output them as expected."""
- mock_header = ["make", "version_regex_name"]
- mock_first_value = ("foo", ["bar"])
- mock_second_value = ("baz", ["bag"])
- mock_runPlugins.return_value = (
- (
- "basic",
- {
- "keys": mock_header,
- "values": [mock_first_value, mock_second_value],
- },
- ),
- )
- mock_params = {"expanded": "True"}
- mock_fillParams.return_value = (mock_params["expanded"],)
- mock_str2bool.return_value = True
-
- command.run(args = "unused", params = mock_params)
-
- mock_fillParams.assert_called_once_with(command, names = [("expanded", False)], params = mock_params)
- mock_str2bool.assert_called_once_with(command, mock_params["expanded"])
- mock_runPlugins.assert_called_once_with(command, args = mock_str2bool.return_value)
- mock_beginOutput.assert_called_once_with(command)
- assert [
- call(command, owner = mock_first_value[0], vals = mock_first_value[1]),
- call(command, owner = mock_second_value[0], vals = mock_second_value[1]),
- ] == mock_addOutput.mock_calls
- mock_endOutput.assert_called_once_with(command, header = mock_header)
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/make/test_command_stack_commands_list_firmware_make_plugin_basic.py b/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/make/test_command_stack_commands_list_firmware_make_plugin_basic.py
deleted file mode 100644
index f3f43b619..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/make/test_command_stack_commands_list_firmware_make_plugin_basic.py
+++ /dev/null
@@ -1,53 +0,0 @@
-from unittest.mock import create_autospec, ANY
-import pytest
-from stack.commands import DatabaseConnection
-from stack.commands.list.firmware import Command
-from stack.commands.list.firmware.make.plugin_basic import Plugin
-
-class TestListFirmwareMakeBasicPlugin:
- """A test case for the list firmware make basic plugin."""
-
- @pytest.fixture
- def basic_plugin(self):
- """A fixture that returns the plugin instance for use in tests.
-
- This sets up the required mocks needed to construct the plugin class.
- """
- mock_command = create_autospec(
- spec = Command,
- instance = True,
- )
- mock_command.db = create_autospec(
- spec = DatabaseConnection,
- spec_set = True,
- instance = True,
- )
- return Plugin(command = mock_command)
-
- def test_provides(self, basic_plugin):
- """Ensure that provides returns 'basic'."""
- assert basic_plugin.provides() == "basic"
-
- def test_run(self, basic_plugin):
- """Test that run queries the DB as expected when expanded is true."""
- basic_plugin.owner.db.select.return_value = [["foo", "bar"], ["baz", "bag"]]
- expected_results = {
- "keys": ["make", "version_regex_name"],
- "values": [(row[0], row[1:]) for row in basic_plugin.owner.db.select.return_value],
- }
-
- assert expected_results == basic_plugin.run(args = True)
-
- basic_plugin.owner.db.select.assert_called_once_with(ANY)
-
- def test_run_expanded_false(self, basic_plugin):
- """Test that run queries the DB as expected when expanded is false."""
- basic_plugin.owner.db.select.return_value = [["foo"], ["bar"]]
- expected_results = {
- "keys": ["make"],
- "values": [(row[0], []) for row in basic_plugin.owner.db.select.return_value],
- }
-
- assert expected_results == basic_plugin.run(args = False)
-
- basic_plugin.owner.db.select.assert_called_once_with(ANY)
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/model/test_command_stack_commands_list_firmware_model__init__.py b/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/model/test_command_stack_commands_list_firmware_model__init__.py
deleted file mode 100644
index 94c2f5951..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/model/test_command_stack_commands_list_firmware_model__init__.py
+++ /dev/null
@@ -1,62 +0,0 @@
-from unittest.mock import patch, call
-import pytest
-from stack.commands.list.firmware.model import Command
-
-class TestListFirmwareModelCommand:
- """A test case to hold the tests for the list firmware model stacki Command class."""
-
- class CommandUnderTest(Command):
- """A class derived from the Command class under test used to override __init__.
-
- This allows easier instantiation for testing purposes by excluding the base Command
- class initialization code.
- """
- def __init__(self):
- pass
-
- @pytest.fixture
- def command(self):
- """Fixture to create and return a Command class instance for testing."""
- return self.CommandUnderTest()
-
- @patch.object(target = Command, attribute = "str2bool", autospec = True)
- @patch.object(target = Command, attribute = "fillParams", autospec = True)
- @patch.object(target = Command, attribute = "endOutput", autospec = True)
- @patch.object(target = Command, attribute = "addOutput", autospec = True)
- @patch.object(target = Command, attribute = "beginOutput", autospec = True)
- @patch.object(target = Command, attribute = "runPlugins", autospec = True)
- def test_run(
- self,
- mock_runPlugins,
- mock_beginOutput,
- mock_addOutput,
- mock_endOutput,
- mock_fillParams,
- mock_str2bool,
- command,
- ):
- """Test that run will run the plugins, gather the results, and output them as expected."""
- mock_header = ["make", "model", "implementation", "version_regex_name"]
- mock_first_value = ("foo", ["bar", "baz", "bag"])
- mock_second_value = ("fizz", ["buzz", "bizz", "bam!"])
- mock_runPlugins.return_value = (
- (
- "basic",
- {
- "keys": mock_header,
- "values": [mock_first_value, mock_second_value],
- },
- ),
- )
- mock_params = {"expanded": "True", "make": "foo"}
- mock_args = ["foo"]
-
- command.run(args = mock_args, params = mock_params)
-
- mock_runPlugins.assert_called_once_with(command, args = (mock_params, mock_args))
- mock_beginOutput.assert_called_once_with(command)
- assert [
- call(command, owner = mock_first_value[0], vals = mock_first_value[1]),
- call(command, owner = mock_second_value[0], vals = mock_second_value[1]),
- ] == mock_addOutput.mock_calls
- mock_endOutput.assert_called_once_with(command, header = mock_header)
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/model/test_command_stack_commands_list_firmware_model_plugin_basic.py b/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/model/test_command_stack_commands_list_firmware_model_plugin_basic.py
deleted file mode 100644
index 6475aa627..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/model/test_command_stack_commands_list_firmware_model_plugin_basic.py
+++ /dev/null
@@ -1,229 +0,0 @@
-from unittest.mock import create_autospec, ANY, patch, call
-import pytest
-from stack.commands import DatabaseConnection
-from stack.commands.list.firmware import Command
-from stack.commands.list.firmware.model.plugin_basic import Plugin
-from stack.exception import CommandError
-
-class TestListFirmwareModelBasicPlugin:
- """A test case for the list firmware model basic plugin."""
-
- @pytest.fixture
- def basic_plugin(self):
- """A fixture that returns the plugin instance for use in tests.
-
- This sets up the required mocks needed to construct the plugin class.
- """
- mock_command = create_autospec(
- spec = Command,
- instance = True,
- )
- mock_command.db = create_autospec(
- spec = DatabaseConnection,
- spec_set = True,
- instance = True,
- )
- return Plugin(command = mock_command)
-
- def test_provides(self, basic_plugin):
- """Ensure that provides returns 'basic'."""
- assert basic_plugin.provides() == "basic"
-
- def test_validate_make(self, basic_plugin):
- """Test that validate make works as expected in the case where a make is provided."""
- mock_make = "foo"
-
- basic_plugin.validate_make(make = mock_make)
-
- basic_plugin.owner.ensure_make_exists.assert_called_once_with(make = mock_make)
-
- def test_validate_make_skip(self, basic_plugin):
- """Test that validate make works as expected in the case where a make is not provided."""
- mock_make = ""
-
- basic_plugin.validate_make(make = mock_make)
-
- basic_plugin.owner.ensure_make_exists.assert_not_called()
-
- def test_validate_make_error(self, basic_plugin):
- """Test that validate fails when ensuring the make exists fails."""
- mock_make = "foo"
- basic_plugin.owner.ensure_make_exists.side_effect = CommandError(
- cmd = basic_plugin.owner,
- msg = "Test error",
- )
-
- with pytest.raises(CommandError):
- basic_plugin.validate_make(make = mock_make)
-
- def test_validate_models(self, basic_plugin):
- """Test that validate models works as expected in the case where a make and some models are provided."""
- mock_make = "foo"
- mock_models = ["bar", "baz"]
-
- basic_plugin.validate_models(make = mock_make, models = mock_models)
-
- basic_plugin.owner.ensure_models_exist.assert_called_once_with(make = mock_make, models = mock_models)
-
- def test_validate_models_skip(self, basic_plugin):
- """Test that validate models works as expected in the case where models are not provided."""
- mock_make = "foo"
- mock_models = []
-
- basic_plugin.validate_models(make = mock_make, models = mock_models)
-
- basic_plugin.owner.ensure_models_exist.assert_not_called()
-
- def test_validate_models_error(self, basic_plugin):
- """Test that validate fails when ensuring the models exist fails."""
- mock_make = "foo"
- mock_models = ["bar", "baz"]
- basic_plugin.owner.ensure_models_exist.side_effect = CommandError(
- cmd = basic_plugin.owner,
- msg = "Test error",
- )
-
- with pytest.raises(CommandError):
- basic_plugin.validate_models(make = mock_make, models = mock_models)
-
- @pytest.mark.parametrize(
- "make, models, expected",
- (
- ("foo", ["bar", "baz"], [["bar", "baz"], "foo"]),
- ("foo", [], ["foo"]),
- ("", [], []),
- )
- )
- def test_get_expanded_results(self, make, models, expected, basic_plugin):
- """Test that get expanded results is called as expected based on the provided args."""
- basic_plugin.owner.db.select.return_value = [
- ["foo", "bar", "baz", "bag"],
- ["fizz", "buzz", "bizz", "bam!"],
- ]
- expected_results = {
- "keys": ["make", "model", "implementation", "version_regex_name"],
- "values": [(row[0], row[1:]) for row in basic_plugin.owner.db.select.return_value],
- }
-
- assert expected_results == basic_plugin.get_expanded_results(make = make, models = models)
-
- basic_plugin.owner.db.select.assert_called_once_with(ANY, expected)
-
- @pytest.mark.parametrize(
- "make, models, expected",
- (
- ("foo", ["bar", "baz"], [["bar", "baz"], "foo"]),
- ("foo", [], ["foo"]),
- ("", [], []),
- )
- )
- def test_get_results(self, make, models, expected, basic_plugin):
- """Test that get results is called as expected based on the provided args."""
- basic_plugin.owner.db.select.return_value = [
- ["foo", "bar", "baz", "bag"],
- ["fizz", "buzz", "bizz", "bam!"],
- ]
- expected_results = {
- "keys": ["make", "model", "implementation", "version_regex_name"],
- "values": [(row[0], row[1:]) for row in basic_plugin.owner.db.select.return_value],
- }
-
- assert expected_results == basic_plugin.get_expanded_results(make = make, models = models)
-
- basic_plugin.owner.db.select.assert_called_once_with(ANY, expected)
-
- @patch.object(target = Plugin, attribute = "get_results", autospec = True)
- @patch.object(target = Plugin, attribute = "get_expanded_results", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_models", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_make", autospec = True)
- @patch(target = "stack.commands.list.firmware.model.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.list.firmware.model.plugin_basic.unique_everseen", autospec = True)
- def test_run(
- self,
- mock_unique_everseen,
- mock_lowered,
- mock_validate_make,
- mock_validate_models,
- mock_get_expanded_results,
- mock_get_results,
- basic_plugin,
- ):
- """Test that run works as expected when expanded is True."""
- mock_args = ["foo", "bar", "baz"]
- expected_args = tuple(mock_args)
- mock_params = {"expanded": "true", "make": "fizz"}
- mock_unique_everseen.return_value = mock_args
- mock_lowered.return_value = mock_params.values()
- basic_plugin.owner.str2bool.return_value = True
-
- basic_plugin.run(args = (mock_params, mock_args))
-
- # Ensure the params and args were processed and validated as expected
- assert [call(mock_args), call(basic_plugin.owner.fillParams.return_value)] == mock_lowered.mock_calls
- mock_unique_everseen.assert_called_once_with(mock_lowered.return_value)
- basic_plugin.owner.fillParams.assert_called_once_with(
- names = [("expanded", "false"), ("make", "")],
- params = mock_params,
- )
- basic_plugin.owner.str2bool.assert_called_once_with(mock_params["expanded"])
- mock_validate_make.assert_called_once_with(basic_plugin, make = mock_params["make"])
- mock_validate_models.assert_called_once_with(
- basic_plugin,
- make = mock_params["make"],
- models = expected_args,
- )
- # Since expanded was True, ensure that we called the right function to get the expanded results.
- mock_get_expanded_results.assert_called_once_with(
- basic_plugin,
- make = mock_params["make"],
- models = expected_args,
- )
- mock_get_results.assert_not_called()
-
- @patch.object(target = Plugin, attribute = "get_results", autospec = True)
- @patch.object(target = Plugin, attribute = "get_expanded_results", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_models", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_make", autospec = True)
- @patch(target = "stack.commands.list.firmware.model.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.list.firmware.model.plugin_basic.unique_everseen", autospec = True)
- def test_run_expanded_false(
- self,
- mock_unique_everseen,
- mock_lowered,
- mock_validate_make,
- mock_validate_models,
- mock_get_expanded_results,
- mock_get_results,
- basic_plugin,
- ):
- """Test that run works as expected when expanded is False."""
- mock_args = ["foo", "bar", "baz"]
- expected_args = tuple(mock_args)
- mock_params = {"expanded": "false", "make": "fizz"}
- mock_unique_everseen.return_value = mock_args
- mock_lowered.return_value = mock_params.values()
- basic_plugin.owner.str2bool.return_value = False
-
- basic_plugin.run(args = (mock_params, mock_args))
-
- # Ensure the params and args were processed and validated as expected
- assert [call(mock_args), call(basic_plugin.owner.fillParams.return_value)] == mock_lowered.mock_calls
- mock_unique_everseen.assert_called_once_with(mock_lowered.return_value)
- basic_plugin.owner.fillParams.assert_called_once_with(
- names = [("expanded", "false"), ("make", "")],
- params = mock_params,
- )
- basic_plugin.owner.str2bool.assert_called_once_with(mock_params["expanded"])
- mock_validate_make.assert_called_once_with(basic_plugin, make = mock_params["make"])
- mock_validate_models.assert_called_once_with(
- basic_plugin,
- make = mock_params["make"],
- models = expected_args,
- )
- # Since expanded was True, ensure that we called the right function to get the expanded results.
- mock_get_expanded_results.assert_not_called()
- mock_get_results.assert_called_once_with(
- basic_plugin,
- make = mock_params["make"],
- models = expected_args,
- )
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/test_command_stack_commands_list_firmware__init__.py b/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/test_command_stack_commands_list_firmware__init__.py
deleted file mode 100644
index 4d5dc2857..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/test_command_stack_commands_list_firmware__init__.py
+++ /dev/null
@@ -1,69 +0,0 @@
-from unittest.mock import patch, call
-import pytest
-from stack.commands.list.firmware import Command
-
-class TestListFirmwareCommand:
- """A test case to hold the tests for the list firmware stacki Command class."""
-
- class CommandUnderTest(Command):
- """A class derived from the Command class under test used to override __init__.
-
- This allows easier instantiation for testing purposes by excluding the base Command
- class initialization code.
- """
- def __init__(self):
- pass
-
- @pytest.fixture
- def command(self):
- """Fixture to create and return a Command class instance for testing."""
- return self.CommandUnderTest()
-
- @patch.object(target = Command, attribute = "str2bool", autospec = True)
- @patch.object(target = Command, attribute = "fillParams", autospec = True)
- @patch.object(target = Command, attribute = "endOutput", autospec = True)
- @patch.object(target = Command, attribute = "addOutput", autospec = True)
- @patch.object(target = Command, attribute = "beginOutput", autospec = True)
- @patch.object(target = Command, attribute = "runPlugins", autospec = True)
- def test_run(
- self,
- mock_runPlugins,
- mock_beginOutput,
- mock_addOutput,
- mock_endOutput,
- mock_fillParams,
- mock_str2bool,
- command,
- ):
- """Test that run will run the plugins, gather the results, and output them as expected."""
- mock_params = {"expanded": "true"}
- mock_fillParams.return_value = mock_params.values()
- mock_str2bool.return_value = True
- mock_header = ["make", "model", "version"]
- mock_first_value = ("foo", ["bar", "baz"])
- mock_second_value = ("bar", ["fizz", "buzz"])
- mock_runPlugins.return_value = (
- (
- "basic",
- {
- "keys": mock_header,
- "values": [mock_first_value, mock_second_value],
- },
- ),
- )
-
- command.run(params = mock_params, args = "unused")
-
- mock_fillParams.assert_called_once_with(
- command,
- names = [("expanded", False)],
- params = mock_params,
- )
- mock_str2bool.assert_called_once_with(command, mock_params["expanded"])
- mock_runPlugins.assert_called_once_with(command, args = mock_str2bool.return_value)
- mock_beginOutput.assert_called_once_with(command)
- assert [
- call(command, owner = mock_first_value[0], vals = mock_first_value[1]),
- call(command, owner = mock_second_value[0], vals = mock_second_value[1]),
- ] == mock_addOutput.mock_calls
- mock_endOutput.assert_called_once_with(command, header = mock_header)
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/test_command_stack_commands_list_firmware_plugin_basic.py b/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/test_command_stack_commands_list_firmware_plugin_basic.py
deleted file mode 100644
index 05e91cd6f..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/test_command_stack_commands_list_firmware_plugin_basic.py
+++ /dev/null
@@ -1,59 +0,0 @@
-from unittest.mock import create_autospec, ANY
-import pytest
-from stack.commands import DatabaseConnection
-from stack.commands.list.firmware import Command
-from stack.commands.list.firmware.plugin_basic import Plugin
-
-class TestListFirmwareBasicPlugin:
- """A test case for the list firmware imp basic plugin."""
-
- @pytest.fixture
- def basic_plugin(self):
- """A fixture that returns the plugin instance for use in tests.
-
- This sets up the required mocks needed to construct the plugin class.
- """
- mock_command = create_autospec(
- spec = Command,
- instance = True,
- )
- mock_command.db = create_autospec(
- spec = DatabaseConnection,
- spec_set = True,
- instance = True,
- )
- return Plugin(command = mock_command)
-
- def test_provides(self, basic_plugin):
- """Ensure that provides returns 'basic'."""
- assert basic_plugin.provides() == "basic"
-
- def test_run(self, basic_plugin):
- """Test that run queries the DB as expected."""
- basic_plugin.owner.db.select.return_value = [
- ["foo", "bar", "baz", "fizz", "buzz", "bam!"],
- ["this", "is", "a", "test", "return", "value"],
- ]
- expected_results = {
- "keys": ["make", "model", "version", "source", "hash", "hash_alg"],
- "values": [(row[0], row[1:]) for row in basic_plugin.owner.db.select.return_value],
- }
-
- assert expected_results == basic_plugin.run(args = True)
-
- basic_plugin.owner.db.select.assert_called_once_with(ANY)
-
- def test_run_expanded_false(self, basic_plugin):
- """Test that run queries the DB as expected when expanded is False."""
- basic_plugin.owner.db.select.return_value = [
- ["foo", "bar", "baz"],
- ["fizz", "buzz", "bam!"],
- ]
- expected_results = {
- "keys": ["make", "model", "version"],
- "values": [(row[0], row[1:]) for row in basic_plugin.owner.db.select.return_value],
- }
-
- assert expected_results == basic_plugin.run(args = False)
-
- basic_plugin.owner.db.select.assert_called_once_with(ANY)
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/version_regex/test_command_stack_commands_list_firmware_version_regex__init__.py b/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/version_regex/test_command_stack_commands_list_firmware_version_regex__init__.py
deleted file mode 100644
index b3d95a392..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/version_regex/test_command_stack_commands_list_firmware_version_regex__init__.py
+++ /dev/null
@@ -1,49 +0,0 @@
-from unittest.mock import patch, call
-import pytest
-from stack.commands.list.firmware.version_regex import Command
-
-class TestListFirmwareVersionRegexCommand:
- """A test case to hold the tests for the list firmware version_regex stacki Command class."""
-
- class CommandUnderTest(Command):
- """A class derived from the Command class under test used to override __init__.
-
- This allows easier instantiation for testing purposes by excluding the base Command
- class initialization code.
- """
- def __init__(self):
- pass
-
- @pytest.fixture
- def command(self):
- """Fixture to create and return a Command class instance for testing."""
- return self.CommandUnderTest()
-
- @patch.object(target = Command, attribute = "endOutput", autospec = True)
- @patch.object(target = Command, attribute = "addOutput", autospec = True)
- @patch.object(target = Command, attribute = "beginOutput", autospec = True)
- @patch.object(target = Command, attribute = "runPlugins", autospec = True)
- def test_run(self, mock_runPlugins, mock_beginOutput, mock_addOutput, mock_endOutput, command):
- """Test that run will run the plugins, gather the results, and output them as expected."""
- mock_header = ["name", "regex", "description"]
- mock_first_value = ("foo", ["bar", "baz"])
- mock_second_value = ("bag", ["fizz", "buzz"])
- mock_runPlugins.return_value = (
- (
- "basic",
- {
- "keys": mock_header,
- "values": [mock_first_value, mock_second_value],
- },
- ),
- )
-
- command.run("unused", "unused")
-
- mock_runPlugins.assert_called_once_with(command)
- mock_beginOutput.assert_called_once_with(command)
- assert [
- call(command, owner = mock_first_value[0], vals = mock_first_value[1]),
- call(command, owner = mock_second_value[0], vals = mock_second_value[1]),
- ] == mock_addOutput.mock_calls
- mock_endOutput.assert_called_once_with(command, header = mock_header)
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/version_regex/test_command_stack_commands_list_firmware_version_regex_plugin_basic.py b/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/version_regex/test_command_stack_commands_list_firmware_version_regex_plugin_basic.py
deleted file mode 100644
index fa5d72e11..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/list/firmware/version_regex/test_command_stack_commands_list_firmware_version_regex_plugin_basic.py
+++ /dev/null
@@ -1,41 +0,0 @@
-from unittest.mock import create_autospec, ANY
-import pytest
-from stack.commands import DatabaseConnection
-from stack.commands.list.firmware import Command
-from stack.commands.list.firmware.version_regex.plugin_basic import Plugin
-
-class TestListFirmwareVersionRegexBasicPlugin:
- """A test case for the list firmware version_regex basic plugin."""
-
- @pytest.fixture
- def basic_plugin(self):
- """A fixture that returns the plugin instance for use in tests.
-
- This sets up the required mocks needed to construct the plugin class.
- """
- mock_command = create_autospec(
- spec = Command,
- instance = True,
- )
- mock_command.db = create_autospec(
- spec = DatabaseConnection,
- spec_set = True,
- instance = True,
- )
- return Plugin(command = mock_command)
-
- def test_provides(self, basic_plugin):
- """Ensure that provides returns 'basic'."""
- assert basic_plugin.provides() == "basic"
-
- def test_run(self, basic_plugin):
- """Test that run queries the DB as expected."""
- basic_plugin.owner.db.select.return_value = [["foo", "bar", "baz"]]
- expected_results = {
- "keys": ["name", "regex", "description"],
- "values": [(row[0], row[1:]) for row in basic_plugin.owner.db.select.return_value],
- }
-
- assert expected_results == basic_plugin.run(args = "unused")
-
- basic_plugin.owner.db.select.assert_called_once_with(ANY)
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/list/host/firmware/mapping/test_command_stack_commands_list_host_firmware_mapping_plugin_basic.py b/test-framework/test-suites/unit/tests/command/stack/commands/list/host/firmware/mapping/test_command_stack_commands_list_host_firmware_mapping_plugin_basic.py
deleted file mode 100644
index a2ec90253..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/list/host/firmware/mapping/test_command_stack_commands_list_host_firmware_mapping_plugin_basic.py
+++ /dev/null
@@ -1,79 +0,0 @@
-from unittest.mock import create_autospec
-import pytest
-from stack.commands import DatabaseConnection
-from stack.commands.list.host.firmware.mapping import Command
-from stack.commands.list.host.firmware.mapping.plugin_basic import Plugin
-
-@pytest.fixture
-def basic_plugin():
- """A fixture that returns the plugin instance for use in tests.
-
- This sets up the required mocks needed to construct the plugin class.
- """
- mock_command = create_autospec(
- spec = Command,
- instance = True,
- )
- mock_command.db = create_autospec(
- spec = DatabaseConnection,
- spec_set = True,
- instance = True,
- )
- return Plugin(command = mock_command)
-
-def test_provides(basic_plugin):
- """Test that the provides returns the basic value."""
- assert basic_plugin.provides() == "basic"
-
-@pytest.mark.parametrize("hosts", ("", "backend-0-0", "backend-0-1", "backend-0-0 backend-0-1"))
-@pytest.mark.parametrize(
- "make, model, versions",
- (
- ("", "", ""),
- ("mellanox", "", ""),
- ("mellanox", "m7800", ""),
- ("mellanox", "m7800", "1.2.3"),
- ("dell", "", ""),
- ("dell", "x1052-software", ""),
- ("dell", "x1052-software", "1.2.3.4"),
- ),
-)
-@pytest.mark.parametrize("sort", ("nodes.Name", "firmware_make.name", "firmware_model.name", "firmware.version",))
-def test_get_firmware_mappings(
- hosts,
- make,
- model,
- versions,
- sort,
- basic_plugin,
-):
- """Test that get_firmware_mappings filters correctly based on provided arguments."""
- # Run the function.
- basic_plugin.get_firmware_mappings(
- hosts = hosts,
- versions = versions,
- make = make,
- model = model,
- sort = sort,
- )
-
- query_string = basic_plugin.owner.db.select.call_args[0][0]
- query_args = basic_plugin.owner.db.select.call_args[0][1]
- # Assert we find the expected where clause and query args.
- if hosts:
- assert "nodes.Name IN %s" in query_string
- assert hosts in query_args
-
- if make:
- assert "firmware_make.name=%s" in query_string
- assert make in query_args
-
- if model:
- assert "firmware_model.name=%s" in query_string
- assert model in query_args
-
- if versions:
- assert "firmware.version IN %s" in query_string
- assert versions in query_args
-
- assert sort in query_string
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/imp/test_command_stack_commands_remove_firmware_imp__init__.py b/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/imp/test_command_stack_commands_remove_firmware_imp__init__.py
deleted file mode 100644
index fa680c182..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/imp/test_command_stack_commands_remove_firmware_imp__init__.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from unittest.mock import patch
-import pytest
-from stack.commands.remove.firmware.imp import Command
-
-class TestRemoveFirmwareImpCommand:
- """A test case to hold the tests for the remove firmware imp stacki Command class."""
-
- class CommandUnderTest(Command):
- """A class derived from the Command class under test used to override __init__.
-
- This allows easier instantiation for testing purposes by excluding the base Command
- class initialization code.
- """
- def __init__(self):
- pass
-
- @pytest.fixture
- def command(self):
- """Fixture to create and return a Command class instance for testing."""
- return self.CommandUnderTest()
-
- @patch.object(target = Command, attribute = "runPlugins", autospec = True)
- def test_run(self, mock_runPlugins, command):
- """Test that run will run the plugins passing through the args."""
- mock_args = ["foo", "bar", "baz"]
-
- command.run(params = "unused", args = mock_args)
-
- mock_runPlugins.assert_called_once_with(command, args = mock_args)
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/imp/test_command_stack_commands_remove_firmware_imp_plugin_basic.py b/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/imp/test_command_stack_commands_remove_firmware_imp_plugin_basic.py
deleted file mode 100644
index 48baf57eb..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/imp/test_command_stack_commands_remove_firmware_imp_plugin_basic.py
+++ /dev/null
@@ -1,67 +0,0 @@
-from unittest.mock import create_autospec, ANY, patch
-import pytest
-from pymysql import IntegrityError
-from stack.commands import DatabaseConnection
-from stack.commands.remove.firmware import Command
-from stack.exception import ArgRequired, CommandError
-from stack.commands.remove.firmware.imp.plugin_basic import Plugin
-
-class TestRemoveFirmwareImpBasicPlugin:
- """A test case for the remove firmware make version_regex basic plugin."""
-
- @pytest.fixture
- def basic_plugin(self):
- """A fixture that returns the plugin instance for use in tests.
-
- This sets up the required mocks needed to construct the plugin class.
- """
- mock_command = create_autospec(
- spec = Command,
- instance = True,
- )
- mock_command.db = create_autospec(
- spec = DatabaseConnection,
- spec_set = True,
- instance = True,
- )
- return Plugin(command = mock_command)
-
- def test_provides(self, basic_plugin):
- """Ensure that provides returns 'basic'."""
- assert basic_plugin.provides() == "basic"
-
- @patch(target = "stack.commands.remove.firmware.imp.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.remove.firmware.imp.plugin_basic.unique_everseen", autospec = True)
- def test_run(self, mock_unique_everseen, mock_lowered, basic_plugin):
- """Test that run updates the database as expected when all arguments are valid."""
- mock_args = ["foo", "bar", "baz"]
- expected_args = tuple(mock_args)
- mock_unique_everseen.return_value = (arg for arg in mock_args)
-
- basic_plugin.run(args = mock_args)
-
- basic_plugin.owner.db.execute.assert_called_once_with(ANY, (expected_args,))
- mock_lowered.assert_called_once_with(mock_args)
- mock_unique_everseen.assert_called_once_with(mock_lowered.return_value)
- basic_plugin.owner.ensure_imps_exist.assert_called_once_with(imps = expected_args)
-
- @patch(target = "stack.commands.remove.firmware.imp.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.remove.firmware.imp.plugin_basic.unique_everseen", autospec = True)
- def test_run_imps_do_not_exist(self, mock_unique_everseen, mock_lowered, basic_plugin):
- """Test that run fails if ensure imps exist fails."""
- basic_plugin.owner.ensure_imps_exist.side_effect = CommandError(cmd = basic_plugin.owner, msg = "Test error")
-
- with pytest.raises(CommandError):
- basic_plugin.run(args = ["foo", "bar", "baz"])
-
- # Make sure the DB is not modified with bad arguments.
- basic_plugin.owner.db.execute.assert_not_called()
-
- @patch(target = "stack.commands.remove.firmware.imp.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.remove.firmware.imp.plugin_basic.unique_everseen", autospec = True)
- def test_run_integrity_error(self, mock_unique_everseen, mock_lowered, basic_plugin):
- """Test that run fails if there is an integrity error and turns it into a command error."""
- basic_plugin.owner.db.execute.side_effect = IntegrityError("Test error")
-
- with pytest.raises(CommandError):
- basic_plugin.run(args = ["foo", "bar", "baz"])
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/make/test_command_stack_commands_remove_firmware_make__init__.py b/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/make/test_command_stack_commands_remove_firmware_make__init__.py
deleted file mode 100644
index f666f814d..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/make/test_command_stack_commands_remove_firmware_make__init__.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from unittest.mock import patch
-import pytest
-from stack.commands.remove.firmware.make import Command
-
-class TestRemoveFirmwareMakeCommand:
- """A test case to hold the tests for the remove firmware make stacki Command class."""
-
- class CommandUnderTest(Command):
- """A class derived from the Command class under test used to override __init__.
-
- This allows easier instantiation for testing purposes by excluding the base Command
- class initialization code.
- """
- def __init__(self):
- pass
-
- @pytest.fixture
- def command(self):
- """Fixture to create and return a Command class instance for testing."""
- return self.CommandUnderTest()
-
- @patch.object(target = Command, attribute = "runPlugins", autospec = True)
- def test_run(self, mock_runPlugins, command):
- """Test that run will run the plugins passing through the args."""
- mock_args = ["foo", "bar", "baz"]
-
- command.run(params = "unused", args = mock_args)
-
- mock_runPlugins.assert_called_once_with(command, args = mock_args)
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/make/test_command_stack_commands_remove_firmware_make_plugin_basic.py b/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/make/test_command_stack_commands_remove_firmware_make_plugin_basic.py
deleted file mode 100644
index 63d26f6ab..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/make/test_command_stack_commands_remove_firmware_make_plugin_basic.py
+++ /dev/null
@@ -1,86 +0,0 @@
-from unittest.mock import create_autospec, ANY, patch, call
-import pytest
-from pymysql import IntegrityError
-from stack.commands import DatabaseConnection
-from stack.commands.remove.firmware import Command
-from stack.exception import ArgRequired, CommandError
-from stack.commands.remove.firmware.make.plugin_basic import Plugin
-
-class TestRemoveFirmwareMakeBasicPlugin:
- """A test case for the remove firmware make basic plugin."""
-
- @pytest.fixture
- def basic_plugin(self):
- """A fixture that returns the plugin instance for use in tests.
-
- This sets up the required mocks needed to construct the plugin class.
- """
- mock_command = create_autospec(
- spec = Command,
- instance = True,
- )
- mock_command.db = create_autospec(
- spec = DatabaseConnection,
- spec_set = True,
- instance = True,
- )
- return Plugin(command = mock_command)
-
- def test_provides(self, basic_plugin):
- """Ensure that provides returns 'basic'."""
- assert basic_plugin.provides() == "basic"
-
- def test_remove_related_models(self, basic_plugin):
- """Test that remove_related_models works as expected."""
- mock_makes = ["foo", "bar", "baz"]
- mock_models = [["fizz"], ["buzz"]]
- expected_models = [mock_model[0] for mock_model in mock_models]
- basic_plugin.owner.db.select.return_value = mock_models
-
- basic_plugin.remove_related_models(makes = mock_makes)
-
- assert [call(ANY, make) for make in mock_makes] == basic_plugin.owner.db.select.mock_calls
- assert [
- call(command = "remove.firmware.model", args = [*expected_models, f"make={make}"]) for make in mock_makes
- ] == basic_plugin.owner.call.mock_calls
-
- def test_remove_related_models_no_models(self, basic_plugin):
- """Test that remove_related_models works as expected when no models exist for the makes."""
- mock_makes = ["foo", "bar", "baz"]
- mock_models = []
- basic_plugin.owner.db.select.return_value = mock_models
-
- basic_plugin.remove_related_models(makes = mock_makes)
-
- assert [call(ANY, make) for make in mock_makes] == basic_plugin.owner.db.select.mock_calls
- basic_plugin.owner.call.assert_not_called()
-
- @patch.object(target = Plugin, attribute = "remove_related_models", autospec = True)
- @patch(target = "stack.commands.remove.firmware.make.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.remove.firmware.make.plugin_basic.unique_everseen", autospec = True)
- def test_run(self, mock_unique_everseen, mock_lowered, mock_remove_related_models, basic_plugin):
- """Test that run updates the database as expected when all arguments are valid."""
- mock_args = ["foo", "bar", "baz"]
- expected_args = tuple(mock_args)
- mock_unique_everseen.return_value = (arg for arg in mock_args)
-
- basic_plugin.run(args = mock_args)
-
- basic_plugin.owner.db.execute.assert_called_once_with(ANY, (expected_args,))
- mock_lowered.assert_called_once_with(mock_args)
- mock_unique_everseen.assert_called_once_with(mock_lowered.return_value)
- basic_plugin.owner.ensure_makes_exist.assert_called_once_with(makes = expected_args)
- mock_remove_related_models.assert_called_once_with(basic_plugin, makes = expected_args)
-
- @patch.object(target = Plugin, attribute = "remove_related_models", autospec = True)
- @patch(target = "stack.commands.remove.firmware.make.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.remove.firmware.make.plugin_basic.unique_everseen", autospec = True)
- def test_run_makes_do_not_exist(self, mock_unique_everseen, mock_lowered, mock_remove_related_models, basic_plugin):
- """Test that run fails if ensure makes exist fails."""
- basic_plugin.owner.ensure_makes_exist.side_effect = CommandError(cmd = basic_plugin.owner, msg = "Test error")
-
- with pytest.raises(CommandError):
- basic_plugin.run(args = ["foo", "bar", "baz"])
-
- # Make sure the DB is not modified with bad arguments.
- basic_plugin.owner.db.execute.assert_not_called()
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/make/version_regex/test_command_stack_commands_remove_firmware_make_version_regex__init__.py b/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/make/version_regex/test_command_stack_commands_remove_firmware_make_version_regex__init__.py
deleted file mode 100644
index d063733bb..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/make/version_regex/test_command_stack_commands_remove_firmware_make_version_regex__init__.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from unittest.mock import patch
-import pytest
-from stack.commands.remove.firmware.make.version_regex import Command
-
-class TestRemoveFirmwareMakeVersionRegexCommand:
- """A test case to hold the tests for the remove firmware make version_regex stacki Command class."""
-
- class CommandUnderTest(Command):
- """A class derived from the Command class under test used to override __init__.
-
- This allows easier instantiation for testing purposes by excluding the base Command
- class initialization code.
- """
- def __init__(self):
- pass
-
- @pytest.fixture
- def command(self):
- """Fixture to create and return a Command class instance for testing."""
- return self.CommandUnderTest()
-
- @patch.object(target = Command, attribute = "runPlugins", autospec = True)
- def test_run(self, mock_runPlugins, command):
- """Test that run will run the plugins passing through the args."""
- mock_args = ["foo", "bar", "baz"]
-
- command.run(params = "unused", args = mock_args)
-
- mock_runPlugins.assert_called_once_with(command, args = mock_args)
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/make/version_regex/test_command_stack_commands_remove_firmware_make_version_regex_plugin_basic.py b/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/make/version_regex/test_command_stack_commands_remove_firmware_make_version_regex_plugin_basic.py
deleted file mode 100644
index 0534cf58a..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/make/version_regex/test_command_stack_commands_remove_firmware_make_version_regex_plugin_basic.py
+++ /dev/null
@@ -1,67 +0,0 @@
-from unittest.mock import create_autospec, ANY, patch
-import pytest
-from stack.commands import DatabaseConnection
-from stack.commands.remove.firmware import Command
-from stack.exception import ArgRequired, CommandError
-from stack.commands.remove.firmware.make.version_regex.plugin_basic import Plugin
-
-class TestRemoveFirmwareMakeVersionRegexBasicPlugin:
- """A test case for the remove firmware make version_regex basic plugin."""
-
- @pytest.fixture
- def basic_plugin(self):
- """A fixture that returns the plugin instance for use in tests.
-
- This sets up the required mocks needed to construct the plugin class.
- """
- mock_command = create_autospec(
- spec = Command,
- instance = True,
- )
- mock_command.db = create_autospec(
- spec = DatabaseConnection,
- spec_set = True,
- instance = True,
- )
- return Plugin(command = mock_command)
-
- def test_provides(self, basic_plugin):
- """Ensure that provides returns 'basic'."""
- assert basic_plugin.provides() == "basic"
-
- @patch(target = "stack.commands.remove.firmware.make.version_regex.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.remove.firmware.make.version_regex.plugin_basic.unique_everseen", autospec = True)
- def test_run(self, mock_unique_everseen, mock_lowered, basic_plugin):
- """Test that run updates the database as expected when all arguments are valid."""
- mock_args = ["foo", "bar", "baz"]
- expected_args = tuple(mock_args)
- mock_unique_everseen.return_value = (arg for arg in mock_args)
-
- basic_plugin.run(args = mock_args)
-
- basic_plugin.owner.db.execute.assert_called_once_with(ANY, (expected_args,))
- mock_lowered.assert_called_once_with(mock_args)
- mock_unique_everseen.assert_called_once_with(mock_lowered.return_value)
- basic_plugin.owner.ensure_makes_exist.assert_called_once_with(makes = expected_args)
-
- @patch(target = "stack.commands.remove.firmware.make.version_regex.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.remove.firmware.make.version_regex.plugin_basic.unique_everseen", autospec = True)
- def test_run_missing_args(self, mock_unique_everseen, mock_lowered, basic_plugin):
- """Test that run fails if no args are passed."""
- with pytest.raises(ArgRequired):
- basic_plugin.run(args = [])
-
- # Make sure the DB is not modified with bad arguments.
- basic_plugin.owner.db.execute.assert_not_called()
-
- @patch(target = "stack.commands.remove.firmware.make.version_regex.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.remove.firmware.make.version_regex.plugin_basic.unique_everseen", autospec = True)
- def test_run_makes_do_not_exist(self, mock_unique_everseen, mock_lowered, basic_plugin):
- """Test that run fails if no ensure makes exist fails."""
- basic_plugin.owner.ensure_makes_exist.side_effect = CommandError(cmd = basic_plugin.owner, msg = "Test error")
-
- with pytest.raises(CommandError):
- basic_plugin.run(args = ["foo", "bar", "baz"])
-
- # Make sure the DB is not modified with bad arguments.
- basic_plugin.owner.db.execute.assert_not_called()
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/model/test_command_stack_commands_remove_firmware_model__init__.py b/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/model/test_command_stack_commands_remove_firmware_model__init__.py
deleted file mode 100644
index b00889409..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/model/test_command_stack_commands_remove_firmware_model__init__.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from unittest.mock import patch
-import pytest
-from stack.commands.remove.firmware.model import Command
-
-class TestRemoveFirmwareModelCommand:
- """A test case to hold the tests for the remove firmware model stacki Command class."""
-
- class CommandUnderTest(Command):
- """A class derived from the Command class under test used to override __init__.
-
- This allows easier instantiation for testing purposes by excluding the base Command
- class initialization code.
- """
- def __init__(self):
- pass
-
- @pytest.fixture
- def command(self):
- """Fixture to create and return a Command class instance for testing."""
- return self.CommandUnderTest()
-
- @patch.object(target = Command, attribute = "runPlugins", autospec = True)
- def test_run(self, mock_runPlugins, command):
- """Test that run will run the plugins passing through the args."""
- mock_args = ["foo", "bar", "baz"]
- mock_params = {"fizz": "buzz"}
-
- command.run(params = mock_params, args = mock_args)
-
- mock_runPlugins.assert_called_once_with(command, args = (mock_params, mock_args))
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/model/test_command_stack_commands_remove_firmware_model_plugin_basic.py b/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/model/test_command_stack_commands_remove_firmware_model_plugin_basic.py
deleted file mode 100644
index 9a1f532ef..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/model/test_command_stack_commands_remove_firmware_model_plugin_basic.py
+++ /dev/null
@@ -1,106 +0,0 @@
-from unittest.mock import create_autospec, ANY, patch, call
-import pytest
-from pymysql import IntegrityError
-from stack.commands import DatabaseConnection
-from stack.commands.remove.firmware import Command
-from stack.exception import ArgRequired, CommandError
-from stack.commands.remove.firmware.model.plugin_basic import Plugin
-
-class TestRemoveFirmwareModelBasicPlugin:
- """A test case for the remove firmware model basic plugin."""
-
- @pytest.fixture
- def basic_plugin(self):
- """A fixture that returns the plugin instance for use in tests.
-
- This sets up the required mocks needed to construct the plugin class.
- """
- mock_command = create_autospec(
- spec = Command,
- instance = True,
- )
- mock_command.db = create_autospec(
- spec = DatabaseConnection,
- spec_set = True,
- instance = True,
- )
- return Plugin(command = mock_command)
-
- def test_provides(self, basic_plugin):
- """Ensure that provides returns 'basic'."""
- assert basic_plugin.provides() == "basic"
-
- def test_remove_related_firmware(self, basic_plugin):
- """Test that remove_related_firmware works as expected."""
- mock_make = "bam!"
- mock_models = ["foo", "bar", "baz"]
- mock_firmwares = [["fizz"], ["buzz"]]
- expected_firmwares = [mock_firmware[0] for mock_firmware in mock_firmwares]
- basic_plugin.owner.db.select.return_value = mock_firmwares
-
- basic_plugin.remove_related_firmware(make = mock_make, models = mock_models)
-
- assert [call(ANY, (mock_make, model)) for model in mock_models] == basic_plugin.owner.db.select.mock_calls
- assert [
- call(
- command = "remove.firmware",
- args = [*expected_firmwares, f"make={mock_make}", f"model={model}"],
- )
- for model in mock_models
- ] == basic_plugin.owner.call.mock_calls
-
- def test_remove_related_firmware_no_firmware(self, basic_plugin):
- """Test that remove_related_firmware works as expected when there are no related firmwares."""
- mock_make = "bam!"
- mock_models = ["foo", "bar", "baz"]
- mock_firmwares = []
- basic_plugin.owner.db.select.return_value = mock_firmwares
-
- basic_plugin.remove_related_firmware(make = mock_make, models = mock_models)
-
- assert [call(ANY, (mock_make, model)) for model in mock_models] == basic_plugin.owner.db.select.mock_calls
- basic_plugin.owner.call.assert_not_called()
-
- @patch.object(target = Plugin, attribute = "remove_related_firmware", autospec = True)
- @patch(target = "stack.commands.remove.firmware.model.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.remove.firmware.model.plugin_basic.unique_everseen", autospec = True)
- def test_run(self, mock_unique_everseen, mock_lowered, mock_remove_related_firmware, basic_plugin):
- """Test that run updates the database as expected when all arguments are valid."""
- mock_args = ["foo", "bar", "baz"]
- expected_args = tuple(mock_args)
- mock_params = {"make": "fizz"}
- mock_unique_everseen.return_value = (arg for arg in mock_args)
- mock_lowered.return_value = mock_params.values()
-
- basic_plugin.run(args = (mock_params, mock_args))
-
- basic_plugin.owner.db.execute.assert_called_once_with(ANY, (expected_args, mock_params["make"]))
- assert [call(basic_plugin.owner.fillParams.return_value), call(mock_args)] == mock_lowered.mock_calls
- mock_unique_everseen.assert_called_once_with(mock_lowered.return_value)
- basic_plugin.owner.ensure_models_exist.assert_called_once_with(
- make = mock_params["make"],
- models = expected_args,
- )
- mock_remove_related_firmware.assert_called_once_with(
- basic_plugin,
- make = mock_params["make"],
- models = expected_args,
- )
-
- @patch.object(target = Plugin, attribute = "remove_related_firmware", autospec = True)
- @patch(target = "stack.commands.remove.firmware.model.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.remove.firmware.model.plugin_basic.unique_everseen", autospec = True)
- def test_run_models_do_not_exist(self, mock_unique_everseen, mock_lowered, mock_remove_related_firmware, basic_plugin):
- """Test that run fails if ensure models exist fails."""
- mock_args = ["foo", "bar", "baz"]
- expected_args = tuple(mock_args)
- mock_params = {"make": "fizz"}
- mock_unique_everseen.return_value = (arg for arg in mock_args)
- mock_lowered.return_value = mock_params.values()
- basic_plugin.owner.ensure_models_exist.side_effect = CommandError(cmd = basic_plugin.owner, msg = "Test error")
-
- with pytest.raises(CommandError):
- basic_plugin.run(args = (mock_params, mock_args))
-
- # model sure the DB is not modified with bad arguments.
- basic_plugin.owner.db.execute.assert_not_called()
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/model/version_regex/test_command_stack_commands_remove_firmware_model_version_regex__init__.py b/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/model/version_regex/test_command_stack_commands_remove_firmware_model_version_regex__init__.py
deleted file mode 100644
index a12f853ea..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/model/version_regex/test_command_stack_commands_remove_firmware_model_version_regex__init__.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from unittest.mock import patch
-import pytest
-from stack.commands.remove.firmware.model.version_regex import Command
-
-class TestRemoveFirmwareModelVersionRegexCommand:
- """A test case to hold the tests for the remove firmware model version_regex stacki Command class."""
-
- class CommandUnderTest(Command):
- """A class derived from the Command class under test used to override __init__.
-
- This allows easier instantiation for testing purposes by excluding the base Command
- class initialization code.
- """
- def __init__(self):
- pass
-
- @pytest.fixture
- def command(self):
- """Fixture to create and return a Command class instance for testing."""
- return self.CommandUnderTest()
-
- @patch.object(target = Command, attribute = "runPlugins", autospec = True)
- def test_run(self, mock_runPlugins, command):
- """Test that run will run the plugins passing through the args."""
- mock_args = ["foo", "bar", "baz"]
- mock_params = {"fizz": "buzz"}
-
- command.run(params = mock_params, args = mock_args)
-
- mock_runPlugins.assert_called_once_with(command, args = (mock_params, mock_args))
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/model/version_regex/test_command_stack_commands_remove_firmware_model_version_regex_plugin_basic.py b/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/model/version_regex/test_command_stack_commands_remove_firmware_model_version_regex_plugin_basic.py
deleted file mode 100644
index 4b7952706..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/model/version_regex/test_command_stack_commands_remove_firmware_model_version_regex_plugin_basic.py
+++ /dev/null
@@ -1,75 +0,0 @@
-from unittest.mock import create_autospec, ANY, patch, MagicMock, call
-import pytest
-from stack.commands import DatabaseConnection
-from stack.commands.remove.firmware import Command
-from stack.exception import ArgRequired, ParamRequired, ParamError, CommandError
-from stack.commands.remove.firmware.model.version_regex.plugin_basic import Plugin
-
-class TestRemoveFirmwareModelVersionRegexBasicPlugin:
- """A test case for the remove firmware model version_regex basic plugin."""
-
- @pytest.fixture
- def basic_plugin(self):
- """A fixture that returns the plugin instance for use in tests.
-
- This sets up the required mocks needed to construct the plugin class.
- """
- mock_command = create_autospec(
- spec = Command,
- instance = True,
- )
- mock_command.db = create_autospec(
- spec = DatabaseConnection,
- spec_set = True,
- instance = True,
- )
- return Plugin(command = mock_command)
-
- def test_provides(self, basic_plugin):
- """Ensure that provides returns "basic"."""
- assert basic_plugin.provides() == "basic"
-
- @patch(target = "stack.commands.remove.firmware.model.version_regex.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.remove.firmware.model.version_regex.plugin_basic.unique_everseen", autospec = True)
- def test_run(self, mock_unique_everseen, mock_lowered, basic_plugin):
- """Test that run updates the database as expected when all arguments are valid."""
- mock_args = ["foo", "bar", "baz"]
- mock_params = {"make": "fizz"}
- expected_args = tuple(mock_args)
- expected_param = mock_params["make"]
- mock_unique_everseen.return_value = (arg for arg in mock_args)
- # Since we don't care about the first return value, as it should be passed directly into unique_everseen(),
- # we just use a MagicMock so we can do equality testing later in assert_called_once_with().
- first_lowered_return = MagicMock()
- mock_lowered.side_effect = (
- first_lowered_return,
- (make for make in mock_params.values()),
- )
-
- basic_plugin.run(args = (mock_params, mock_args))
-
- # Ensure that the database update was performed
- basic_plugin.owner.db.execute.assert_called_once_with(ANY, (expected_args, expected_param))
- # Ensure that the expected calls happened for validating args and params.
- assert [call(mock_args), call(basic_plugin.owner.fillParams.return_value)] == mock_lowered.mock_calls
- mock_unique_everseen.assert_called_once_with(first_lowered_return)
- basic_plugin.owner.ensure_models_exist.assert_called_once_with(models = expected_args, make = expected_param)
-
- @patch(target = "stack.commands.remove.firmware.model.version_regex.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.remove.firmware.model.version_regex.plugin_basic.unique_everseen", autospec = True)
- def test_run_errors(
- self,
- mock_unique_everseen,
- mock_lowered,
- basic_plugin,
- ):
- """Test that run fails if the arguments do not validate."""
- # Set the appropriate mock function to fail.
- basic_plugin.owner.ensure_models_exist.side_effect = CommandError(cmd = basic_plugin.owner, msg = "Test failure")
- mock_lowered.return_value = ("foo",)
-
- with pytest.raises(CommandError):
- basic_plugin.run(args = ({"foo": "bar"}, ["baz"]))
-
- # Ensure that the database update was not performed due to argument and/or param errors
- basic_plugin.owner.db.execute.assert_not_called()
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/test_command_stack_commands_remove_firmware__init__.py b/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/test_command_stack_commands_remove_firmware__init__.py
deleted file mode 100644
index e2ee9a65f..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/test_command_stack_commands_remove_firmware__init__.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from unittest.mock import patch
-import pytest
-from stack.commands.remove.firmware import Command
-
-class TestRemoveFirmwareCommand:
- """A test case to hold the tests for the remove firmware stacki Command class."""
-
- class CommandUnderTest(Command):
- """A class derived from the Command class under test used to override __init__.
-
- This allows easier instantiation for testing purposes by excluding the base Command
- class initialization code.
- """
- def __init__(self):
- pass
-
- @pytest.fixture
- def command(self):
- """Fixture to create and return a Command class instance for testing."""
- return self.CommandUnderTest()
-
- @patch.object(target = Command, attribute = "runPlugins", autospec = True)
- def test_run(self, mock_runPlugins, command):
- """Test that run will run the plugins passing through the args."""
- mock_args = ["foo", "bar", "baz"]
- mock_params = {"make": "fizz", "model": "buzz"}
-
- command.run(params = mock_params, args = mock_args)
-
- mock_runPlugins.assert_called_once_with(command, args = (mock_params, mock_args))
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/test_command_stack_commands_remove_firmware_plugin_basic.py b/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/test_command_stack_commands_remove_firmware_plugin_basic.py
deleted file mode 100644
index d9ce31dbf..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/test_command_stack_commands_remove_firmware_plugin_basic.py
+++ /dev/null
@@ -1,185 +0,0 @@
-from unittest.mock import create_autospec, ANY, patch, call
-import pytest
-from pymysql import IntegrityError
-from stack.commands import DatabaseConnection
-from stack.commands.remove.firmware import Command
-from stack.exception import ArgRequired, CommandError
-from stack.commands.remove.firmware.plugin_basic import Plugin
-
-class TestRemoveFirmwareBasicPlugin:
- """A test case for the remove firmware make version_regex basic plugin."""
-
- @pytest.fixture
- def basic_plugin(self):
- """A fixture that returns the plugin instance for use in tests.
-
- This sets up the required mocks needed to construct the plugin class.
- """
- mock_command = create_autospec(
- spec = Command,
- instance = True,
- )
- mock_command.db = create_autospec(
- spec = DatabaseConnection,
- spec_set = True,
- instance = True,
- )
- return Plugin(command = mock_command)
-
- def test_provides(self, basic_plugin):
- """Ensure that provides returns 'basic'."""
- assert basic_plugin.provides() == "basic"
-
- @pytest.mark.parametrize(
- "make, model, versions",
- (
- ("", "", []),
- ("foo", "", []),
- ("foo", "bar", []),
- ("foo", "bar", ["fizz", "buzz"])
- ),
- )
- def test_validate_inputs(self, make, model, versions, basic_plugin):
- """Test that validate inputs works as expected when all inputs are valid."""
- basic_plugin.validate_inputs(make = make, model = model, versions = versions)
-
- if make:
- basic_plugin.owner.ensure_make_exists.assert_called_once_with(
- make = make,
- )
- else:
- basic_plugin.owner.ensure_make_exists.assert_not_called()
-
- if model:
- basic_plugin.owner.ensure_model_exists.assert_called_once_with(
- make = make,
- model = model,
- )
- else:
- basic_plugin.owner.ensure_model_exists.assert_not_called()
-
- if versions:
- basic_plugin.owner.ensure_firmwares_exist.assert_called_once_with(
- make = make,
- model = model,
- versions = versions,
- )
- else:
- basic_plugin.owner.ensure_firmwares_exist.assert_not_called()
-
- @pytest.mark.parametrize("failure_mock", ("ensure_make_exists", "ensure_model_exists", "ensure_firmwares_exist"))
- def test_validate_inputs_errors(self, failure_mock, basic_plugin):
- """Test that validate inputs works as expected when all inputs are valid."""
- mock_validation_functions = {
- "ensure_make_exists": basic_plugin.owner.ensure_make_exists,
- "ensure_model_exists": basic_plugin.owner.ensure_model_exists,
- "ensure_firmwares_exist": basic_plugin.owner.ensure_firmwares_exist,
- }
- mock_validation_functions[failure_mock].side_effect = CommandError(cmd = basic_plugin.owner, msg = "Test error")
-
- with pytest.raises(CommandError):
- basic_plugin.validate_inputs(make = "foo", model = "bar", versions = ["baz"])
-
- @pytest.mark.parametrize(
- "mock_make, mock_model, mock_versions, expected_query_params",
- (
- ("", "", [], []),
- ("foo", "", [], ["foo"]),
- ("foo", "bar", [], ["foo", "bar"]),
- ("foo", "bar", ["fizz", "buzz"], [["fizz", "buzz"], "foo", "bar"]),
- ),
- )
- def test_build_query(
- self,
- mock_make,
- mock_model,
- mock_versions,
- expected_query_params,
- basic_plugin,
- ):
- """Test that build query builds up the query as expected based on input arguments."""
- query, query_params = basic_plugin.build_query(
- make = mock_make,
- model = mock_model,
- versions = mock_versions,
- )
-
- assert expected_query_params == query_params
-
- @patch.object(target = Plugin, attribute = "build_query", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_inputs", autospec = True)
- @patch(target = "stack.commands.remove.firmware.plugin_basic.Path", autospec = True)
- @patch(target = "stack.commands.remove.firmware.plugin_basic.unique_everseen", autospec = True)
- @patch(target = "stack.commands.remove.firmware.plugin_basic.lowered", autospec = True)
- def test_run(
- self,
- mock_lowered,
- mock_unique_everseen,
- mock_path,
- mock_validate_inputs,
- mock_build_query,
- basic_plugin,
- ):
- """Test that run works as expected when the params and args are valid."""
- mock_args = ["foo", "bar", "baz"]
- mock_params = {"make": "fizz", "model": "buzz"}
- expected_versions = tuple(mock_args)
- mock_unique_everseen.return_value = expected_versions
- mock_lowered.return_value = mock_params.values()
- mock_query = "amockquery"
- mock_query_params = ["aparam", "anotherparam"]
- mock_build_query.return_value = (mock_query, mock_query_params)
- basic_plugin.owner.db.select.return_value = [["id", "path"]]
-
- basic_plugin.run(args = (mock_params, mock_args))
-
- assert [call(mock_args), call(basic_plugin.owner.fillParams.return_value)] == mock_lowered.mock_calls
- mock_unique_everseen.assert_called_once_with(mock_lowered.return_value)
- mock_validate_inputs.assert_called_once_with(
- basic_plugin,
- make = mock_params["make"],
- model = mock_params["model"],
- versions = expected_versions,
- )
- mock_build_query.assert_called_once_with(
- basic_plugin,
- make = mock_params["make"],
- model = mock_params["model"],
- versions = expected_versions,
- )
- basic_plugin.owner.db.select.assert_called_once_with(mock_query, mock_query_params)
- chained = call(basic_plugin.owner.db.select.return_value[0][1]).resolve(strict = True).unlink()
- assert chained.call_list() == mock_path.mock_calls
- basic_plugin.owner.db.execute.assert_called_once_with(
- ANY,
- basic_plugin.owner.db.select.return_value[0][0],
- )
-
- @patch.object(target = Plugin, attribute = "build_query", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_inputs", autospec = True)
- @patch(target = "stack.commands.remove.firmware.plugin_basic.Path", autospec = True)
- @patch(target = "stack.commands.remove.firmware.plugin_basic.unique_everseen", autospec = True)
- @patch(target = "stack.commands.remove.firmware.plugin_basic.lowered", autospec = True)
- def test_run_errors(
- self,
- mock_lowered,
- mock_unique_everseen,
- mock_path,
- mock_validate_inputs,
- mock_build_query,
- basic_plugin,
- ):
- """Test that run fails when params and/or args are invalid."""
- mock_args = ["foo", "bar", "baz"]
- mock_params = {"make": "fizz", "model": "buzz"}
- expected_versions = tuple(mock_args)
- mock_unique_everseen.return_value = expected_versions
- mock_lowered.return_value = mock_params.values()
- mock_query = "amockquery"
- mock_query_params = ["aparam", "anotherparam"]
- mock_build_query.return_value = (mock_query, mock_query_params)
- basic_plugin.owner.db.select.return_value = [["id", "path"]]
- mock_validate_inputs.side_effect = CommandError(cmd = basic_plugin.owner, msg = "Test error")
-
- with pytest.raises(CommandError):
- basic_plugin.run(args = (mock_params, mock_args))
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/version_regex/test_command_stack_commands_remove_firmware_version_regex__init__.py b/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/version_regex/test_command_stack_commands_remove_firmware_version_regex__init__.py
deleted file mode 100644
index 3585b4269..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/version_regex/test_command_stack_commands_remove_firmware_version_regex__init__.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from unittest.mock import patch
-import pytest
-from stack.commands.remove.firmware.version_regex import Command
-
-class TestRemoveFirmwareVersionRegexCommand:
- """A test case to hold the tests for the remove firmware version_regex stacki Command class."""
-
- class CommandUnderTest(Command):
- """A class derived from the Command class under test used to override __init__.
-
- This allows easier instantiation for testing purposes by excluding the base Command
- class initialization code.
- """
- def __init__(self):
- pass
-
- @pytest.fixture
- def command(self):
- """Fixture to create and return a Command class instance for testing."""
- return self.CommandUnderTest()
-
- @patch.object(target = Command, attribute = "runPlugins", autospec = True)
- def test_run(self, mock_runPlugins, command):
- """Test that run will run the plugins passing through the args."""
- mock_args = ["foo", "bar", "baz"]
-
- command.run(params = "unused", args = mock_args)
-
- mock_runPlugins.assert_called_once_with(command, args = mock_args)
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/version_regex/test_command_stack_commands_remove_firmware_version_regex_plugin_basic.py b/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/version_regex/test_command_stack_commands_remove_firmware_version_regex_plugin_basic.py
deleted file mode 100644
index 4040351a6..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/remove/firmware/version_regex/test_command_stack_commands_remove_firmware_version_regex_plugin_basic.py
+++ /dev/null
@@ -1,57 +0,0 @@
-from unittest.mock import create_autospec, ANY, patch
-import pytest
-from stack.commands import DatabaseConnection
-from stack.commands.remove.firmware import Command
-from stack.exception import ArgRequired, CommandError
-from stack.commands.remove.firmware.version_regex.plugin_basic import Plugin
-
-class TestRemoveFirmwareVersionRegexBasicPlugin:
- """A test case for the remove firmware version_regex basic plugin."""
-
- @pytest.fixture
- def basic_plugin(self):
- """A fixture that returns the plugin instance for use in tests.
-
- This sets up the required mocks needed to construct the plugin class.
- """
- mock_command = create_autospec(
- spec = Command,
- instance = True,
- )
- mock_command.db = create_autospec(
- spec = DatabaseConnection,
- spec_set = True,
- instance = True,
- )
- return Plugin(command = mock_command)
-
- def test_provides(self, basic_plugin):
- """Ensure that provides returns 'basic'."""
- assert basic_plugin.provides() == "basic"
-
- @patch(target = "stack.commands.remove.firmware.version_regex.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.remove.firmware.version_regex.plugin_basic.unique_everseen", autospec = True)
- def test_run(self, mock_unique_everseen, mock_lowered, basic_plugin):
- """Test that run updates the database as expected when all arguments are valid."""
- mock_args = ["foo", "bar", "baz"]
- expected_args = tuple(mock_args)
- mock_unique_everseen.return_value = (arg for arg in mock_args)
-
- basic_plugin.run(args = mock_args)
-
- basic_plugin.owner.db.execute.assert_called_once_with(ANY, (expected_args,))
- mock_lowered.assert_called_once_with(mock_args)
- mock_unique_everseen.assert_called_once_with(mock_lowered.return_value)
- basic_plugin.owner.ensure_version_regexes_exist.assert_called_once_with(names = expected_args)
-
- @patch(target = "stack.commands.remove.firmware.version_regex.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.remove.firmware.version_regex.plugin_basic.unique_everseen", autospec = True)
- def test_run_regexes_do_not_exist(self, mock_unique_everseen, mock_lowered, basic_plugin):
- """Test that run fails if no ensure_version_regexes_exist fails."""
- basic_plugin.owner.ensure_version_regexes_exist.side_effect = CommandError(cmd = basic_plugin.owner, msg = "Test error")
-
- with pytest.raises(CommandError):
- basic_plugin.run(args = ["foo", "bar", "baz"])
-
- # sure the DB is not modified with bad arguments.
- basic_plugin.owner.db.execute.assert_not_called()
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/remove/host/firmware/mapping/test_command_stack_commands_remove_host_firmware_mapping__init__.py b/test-framework/test-suites/unit/tests/command/stack/commands/remove/host/firmware/mapping/test_command_stack_commands_remove_host_firmware_mapping__init__.py
deleted file mode 100644
index 929665a89..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/remove/host/firmware/mapping/test_command_stack_commands_remove_host_firmware_mapping__init__.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from unittest.mock import patch
-import pytest
-from stack.commands.remove.host.firmware.mapping import Command
-
-class TestRemoveHostFirmwareMappingCommand:
- """A test case to hold the tests for the remove host firmware mapping stacki Command class."""
-
- class CommandUnderTest(Command):
- """A class derived from the Command class under test used to override __init__.
-
- This allows easier instantiation for testing purposes by excluding the base Command
- class initialization code.
- """
- def __init__(self):
- pass
-
- @pytest.fixture
- def command(self):
- """Fixture to create and return a Command class instance for testing."""
- return self.CommandUnderTest()
-
- @patch.object(target = Command, attribute = "runPlugins", autospec = True)
- def test_run(self, mock_runPlugins, command):
- """Test that run will run the plugins passing through the args."""
- mock_args = ["foo", "bar", "baz"]
- mock_params = {"fizz": "buzz"}
-
- command.run(params = mock_params, args = mock_args)
-
- mock_runPlugins.assert_called_once_with(command, args = (mock_params, mock_args))
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/remove/host/firmware/mapping/test_command_stack_commands_remove_host_firmware_mapping_plugin_basic.py b/test-framework/test-suites/unit/tests/command/stack/commands/remove/host/firmware/mapping/test_command_stack_commands_remove_host_firmware_mapping_plugin_basic.py
deleted file mode 100644
index 58fd78b31..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/remove/host/firmware/mapping/test_command_stack_commands_remove_host_firmware_mapping_plugin_basic.py
+++ /dev/null
@@ -1,351 +0,0 @@
-from unittest.mock import create_autospec, ANY, patch, call
-import pytest
-from stack.commands import DatabaseConnection
-from stack.commands.remove.host.firmware.mapping import Command
-from stack.exception import CommandError
-from stack.commands.remove.host.firmware.mapping.plugin_basic import Plugin
-
-class TestRemoveHostFirmwareMappingBasicPlugin:
- """A test case for the remove host firmware mapping basic plugin."""
-
- @pytest.fixture
- def basic_plugin(self):
- """A fixture that returns the plugin instance for use in tests.
-
- This sets up the required mocks needed to construct the plugin class.
- """
- mock_command = create_autospec(
- spec = Command,
- instance = True,
- )
- mock_command.db = create_autospec(
- spec = DatabaseConnection,
- spec_set = True,
- instance = True,
- )
- return Plugin(command = mock_command)
-
- def test_provides(self, basic_plugin):
- """Ensure that provides returns 'basic'."""
- assert basic_plugin.provides() == "basic"
-
- def test_validate_make(self, basic_plugin):
- """Ensure the make is validated if it exists."""
- mock_make = "foo"
-
- basic_plugin.validate_make(make = mock_make)
-
- basic_plugin.owner.ensure_make_exists.assert_called_once_with(
- make = mock_make,
- )
-
- def test_validate_make_not_provided(self, basic_plugin):
- """Ensure the make is not validated if not provided."""
- mock_make = ""
-
- basic_plugin.validate_make(make = mock_make)
-
- basic_plugin.owner.ensure_make_exists.assert_not_called()
-
- def test_validate_make_error(self, basic_plugin):
- """Ensure that validation fails when the make is invalid."""
- mock_make = "foo"
- basic_plugin.owner.ensure_make_exists.side_effect = CommandError(
- cmd = basic_plugin.owner,
- msg = "Test error",
- )
-
- with pytest.raises(CommandError):
- basic_plugin.validate_make(make = mock_make)
-
- def test_validate_model(self, basic_plugin):
- """Ensure the model is validated if it exists."""
- mock_make = "foo"
- mock_model = "bar"
-
- basic_plugin.validate_model(make = mock_make, model = mock_model)
-
- basic_plugin.owner.ensure_model_exists.assert_called_once_with(
- make = mock_make,
- model = mock_model,
- )
-
- def test_validate_model_not_provided(self, basic_plugin):
- """Ensure the model is not validated if not provided."""
- mock_make = "foo"
- mock_model = ""
-
- basic_plugin.validate_model(make = mock_make, model = mock_model)
-
- basic_plugin.owner.ensure_model_exists.assert_not_called()
-
- def test_validate_model_error(self, basic_plugin):
- """Ensure that validation fails when the model is invalid."""
- mock_make = "foo"
- mock_model = "bar"
- basic_plugin.owner.ensure_model_exists.side_effect = CommandError(
- cmd = basic_plugin.owner,
- msg = "Test error",
- )
-
- with pytest.raises(CommandError):
- basic_plugin.validate_model(make = mock_make, model = mock_model)
-
- @pytest.mark.parametrize(
- "hosts, versions, make, model",
- (
- (["foo"], ["bar"], "baz", "bag"),
- (["foo"], [], "baz", "bag"),
- (["foo"], [], "baz", ""),
- (["foo"], [], "", ""),
- ([], ["bar"], "baz", "bag"),
- ([], [], "baz", "bag"),
- ([], [], "baz", ""),
- ([], [], "", ""),
- )
- )
- def test_get_firmware_mappings_to_remove(self, hosts, versions, make, model, basic_plugin):
- """Test that get_firmware_mappings_to_remove works as expected for every valid argument combination."""
- test_inputs = {
- "hosts": hosts,
- "versions": versions,
- "make": make,
- "model": model,
- }
- basic_plugin.owner.db.select.return_value = [["1"]]
- expected_query_params = list(value for value in test_inputs.values() if value)
-
- assert [basic_plugin.owner.db.select.return_value[0][0]] == basic_plugin.get_firmware_mappings_to_remove(**test_inputs)
- basic_plugin.owner.db.select.assert_called_once_with(ANY, expected_query_params)
-
- @patch.object(target = Plugin, attribute = "get_firmware_mappings_to_remove", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_model", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_make", autospec = True)
- @patch(target = "stack.commands.remove.host.firmware.mapping.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.remove.host.firmware.mapping.plugin_basic.unique_everseen", autospec = True)
- def test_run(
- self,
- mock_unique_everseen,
- mock_lowered,
- mock_validate_make,
- mock_validate_model,
- mock_get_firmware_mappings_to_remove,
- basic_plugin,
- ):
- """Test that run works as expected when all params and args are provided and valid."""
- mock_args = ["foo", "bar"]
- expected_hosts = tuple(mock_args)
- mock_params = {"make": "fizz", "model": "buzz", "versions": "bazz, bang"}
- expected_versions = tuple(version.strip() for version in mock_params["versions"].split(",") if version.strip())
- mock_lowered.return_value = mock_params.values()
- mock_unique_everseen.side_effect = (
- mock_args,
- expected_versions,
- )
- basic_plugin.owner.getHosts.return_value = expected_hosts
- mock_get_firmware_mappings_to_remove.return_value = ["1", "2"]
-
- basic_plugin.run(args = (mock_params, mock_args))
-
- assert [call(mock_args), call(basic_plugin.owner.fillParams.return_value)] == mock_lowered.mock_calls
- mock_unique_everseen.assert_any_call(mock_lowered.return_value)
- # Check the generator expression passed to the second call of unique_everseen
- assert tuple(mock_unique_everseen.call_args_list[1][0][0]) == expected_versions
- basic_plugin.owner.getHosts.assert_called_once_with(args = expected_hosts)
- basic_plugin.owner.fillParams.assert_called_once_with(
- names = [
- ("make", ""),
- ("model", ""),
- ("versions", ""),
- ],
- params = mock_params,
- )
- mock_validate_make.assert_called_once_with(
- basic_plugin,
- make = mock_params["make"],
- )
- mock_validate_model.assert_called_once_with(
- basic_plugin,
- make = mock_params["make"],
- model = mock_params["model"],
- )
- basic_plugin.owner.ensure_firmwares_exist.assert_called_once_with(
- make = mock_params["make"],
- model = mock_params["model"],
- versions = expected_versions,
- )
- mock_get_firmware_mappings_to_remove.assert_called_once_with(
- basic_plugin,
- hosts = expected_hosts,
- make = mock_params["make"],
- model = mock_params["model"],
- versions = expected_versions,
- )
- basic_plugin.owner.db.execute.assert_called_once_with(
- ANY,
- (mock_get_firmware_mappings_to_remove.return_value,),
- )
-
- @patch.object(target = Plugin, attribute = "get_firmware_mappings_to_remove", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_model", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_make", autospec = True)
- @patch(target = "stack.commands.remove.host.firmware.mapping.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.remove.host.firmware.mapping.plugin_basic.unique_everseen", autospec = True)
- def test_run_no_hosts_or_versions(
- self,
- mock_unique_everseen,
- mock_lowered,
- mock_validate_make,
- mock_validate_model,
- mock_get_firmware_mappings_to_remove,
- basic_plugin,
- ):
- """Test that run works as expected when hosts and versions are not provided."""
- mock_args = []
- expected_hosts = tuple(mock_args)
- mock_params = {"make": "fizz", "model": "buzz", "versions": ""}
- mock_lowered.return_value = mock_params.values()
- mock_unique_everseen.return_value = mock_args
- mock_get_firmware_mappings_to_remove.return_value = ["1", "2"]
-
- basic_plugin.run(args = (mock_params, mock_args))
-
- assert [call(mock_args), call(basic_plugin.owner.fillParams.return_value)] == mock_lowered.mock_calls
- mock_unique_everseen.assert_called_once_with(mock_lowered.return_value)
- basic_plugin.owner.getHosts.assert_not_called()
- basic_plugin.owner.fillParams.assert_called_once_with(
- names = [
- ("make", ""),
- ("model", ""),
- ("versions", ""),
- ],
- params = mock_params,
- )
- mock_validate_make.assert_called_once_with(
- basic_plugin,
- make = mock_params["make"],
- )
- mock_validate_model.assert_called_once_with(
- basic_plugin,
- make = mock_params["make"],
- model = mock_params["model"],
- )
- basic_plugin.owner.ensure_firmwares_exist.assert_not_called()
- mock_get_firmware_mappings_to_remove.assert_called_once_with(
- basic_plugin,
- hosts = expected_hosts,
- make = mock_params["make"],
- model = mock_params["model"],
- versions = mock_params["versions"],
- )
- basic_plugin.owner.db.execute.assert_called_once_with(
- ANY,
- (mock_get_firmware_mappings_to_remove.return_value,),
- )
-
- @patch.object(target = Plugin, attribute = "get_firmware_mappings_to_remove", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_model", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_make", autospec = True)
- @patch(target = "stack.commands.remove.host.firmware.mapping.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.remove.host.firmware.mapping.plugin_basic.unique_everseen", autospec = True)
- def test_run_no_mappings_to_remove(
- self,
- mock_unique_everseen,
- mock_lowered,
- mock_validate_make,
- mock_validate_model,
- mock_get_firmware_mappings_to_remove,
- basic_plugin,
- ):
- """Test that run works as expected when there are no mappings to remove."""
- mock_args = ["foo", "bar"]
- expected_hosts = tuple(mock_args)
- mock_params = {"make": "fizz", "model": "buzz", "versions": "bazz, bang"}
- expected_versions = tuple(version.strip() for version in mock_params["versions"].split(",") if version.strip())
- mock_lowered.return_value = mock_params.values()
- mock_unique_everseen.side_effect = (
- mock_args,
- expected_versions,
- )
- basic_plugin.owner.getHosts.return_value = expected_hosts
- mock_get_firmware_mappings_to_remove.return_value = []
-
- basic_plugin.run(args = (mock_params, mock_args))
-
- assert [call(mock_args), call(basic_plugin.owner.fillParams.return_value)] == mock_lowered.mock_calls
- mock_unique_everseen.assert_any_call(mock_lowered.return_value)
- # Check the generator expression passed to the second call of unique_everseen
- assert tuple(mock_unique_everseen.call_args_list[1][0][0]) == expected_versions
- basic_plugin.owner.getHosts.assert_called_once_with(args = expected_hosts)
- basic_plugin.owner.fillParams.assert_called_once_with(
- names = [
- ("make", ""),
- ("model", ""),
- ("versions", ""),
- ],
- params = mock_params,
- )
- mock_validate_make.assert_called_once_with(
- basic_plugin,
- make = mock_params["make"],
- )
- mock_validate_model.assert_called_once_with(
- basic_plugin,
- make = mock_params["make"],
- model = mock_params["model"],
- )
- basic_plugin.owner.ensure_firmwares_exist.assert_called_once_with(
- make = mock_params["make"],
- model = mock_params["model"],
- versions = expected_versions,
- )
- mock_get_firmware_mappings_to_remove.assert_called_once_with(
- basic_plugin,
- hosts = expected_hosts,
- make = mock_params["make"],
- model = mock_params["model"],
- versions = expected_versions,
- )
- basic_plugin.owner.db.execute.assert_not_called()
-
- @pytest.mark.parametrize("failure_mock", ("validate_make", "validate_model", "ensure_firmwares_exist"))
- @patch.object(target = Plugin, attribute = "get_firmware_mappings_to_remove", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_model", autospec = True)
- @patch.object(target = Plugin, attribute = "validate_make", autospec = True)
- @patch(target = "stack.commands.remove.host.firmware.mapping.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.remove.host.firmware.mapping.plugin_basic.unique_everseen", autospec = True)
- def test_run_errors(
- self,
- mock_unique_everseen,
- mock_lowered,
- mock_validate_make,
- mock_validate_model,
- mock_get_firmware_mappings_to_remove,
- failure_mock,
- basic_plugin,
- ):
- """Test that run fails when the params or args are invalid."""
- mock_args = ["foo", "bar"]
- expected_hosts = tuple(mock_args)
- mock_params = {"make": "fizz", "model": "buzz", "versions": "bazz, bang"}
- expected_versions = tuple(version.strip() for version in mock_params["versions"].split(",") if version.strip())
- mock_lowered.return_value = mock_params.values()
- mock_unique_everseen.side_effect = (
- mock_args,
- expected_versions,
- )
- basic_plugin.owner.getHosts.return_value = expected_hosts
- mock_validation_functions = {
- "validate_make": mock_validate_make,
- "validate_model": mock_validate_model,
- "ensure_firmwares_exist": basic_plugin.owner.ensure_firmwares_exist,
- }
- mock_validation_functions[failure_mock].side_effect = CommandError(
- cmd = basic_plugin.owner,
- msg = "test error",
- )
-
- with pytest.raises(CommandError):
- basic_plugin.run(args = (mock_params, mock_args))
-
- basic_plugin.owner.db.execute.assert_not_called()
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/set/firmware/make/version_regex/test_command_stack_commands_set_firmware_make_version_regex__init__.py b/test-framework/test-suites/unit/tests/command/stack/commands/set/firmware/make/version_regex/test_command_stack_commands_set_firmware_make_version_regex__init__.py
deleted file mode 100644
index d7cf8659f..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/set/firmware/make/version_regex/test_command_stack_commands_set_firmware_make_version_regex__init__.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from unittest.mock import patch
-import pytest
-from stack.commands.set.firmware.make.version_regex import Command
-
-class TestSetFirmwareMakeVersionRegexCommand:
- """A test case to hold the tests for the set firmware make version_regex stacki Command class."""
-
- class CommandUnderTest(Command):
- """A class derived from the Command class under test used to override __init__.
-
- This allows easier instantiation for testing purposes by excluding the base Command
- class initialization code.
- """
- def __init__(self):
- pass
-
- @pytest.fixture
- def command(self):
- """Fixture to create and return a Command class instance for testing."""
- return self.CommandUnderTest()
-
- @patch.object(target = Command, attribute = "runPlugins", autospec = True)
- def test_run(self, mock_runPlugins, command):
- """Test that run will run the plugins passing through the args."""
- mock_params = {"fizz": "buzz"}
- mock_args = ["foo", "bar", "baz"]
-
- command.run(params = mock_params, args = mock_args)
-
- mock_runPlugins.assert_called_once_with(command, args = (mock_params, mock_args))
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/set/firmware/make/version_regex/test_command_stack_commands_set_firmware_make_version_regex_plugin_basic.py b/test-framework/test-suites/unit/tests/command/stack/commands/set/firmware/make/version_regex/test_command_stack_commands_set_firmware_make_version_regex_plugin_basic.py
deleted file mode 100644
index efc4a0532..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/set/firmware/make/version_regex/test_command_stack_commands_set_firmware_make_version_regex_plugin_basic.py
+++ /dev/null
@@ -1,77 +0,0 @@
-from unittest.mock import create_autospec, ANY, patch, call
-import pytest
-from stack.commands import DatabaseConnection
-from stack.commands.set.firmware.make.version_regex import Command
-from stack.exception import CommandError
-from stack.commands.set.firmware.make.version_regex.plugin_basic import Plugin
-
-class TestSetFirmwareMakeVersionRegexBasicPlugin:
- """A test case for the set firmware make version_regex basic plugin."""
-
- @pytest.fixture
- def basic_plugin(self):
- """A fixture that returns the plugin instance for use in tests.
-
- This sets up the required mocks needed to construct the plugin class.
- """
- mock_command = create_autospec(
- spec = Command,
- instance = True,
- )
- mock_command.db = create_autospec(
- spec = DatabaseConnection,
- spec_set = True,
- instance = True,
- )
- return Plugin(command = mock_command)
-
- def test_provides(self, basic_plugin):
- """Ensure that provides returns 'basic'."""
- assert basic_plugin.provides() == "basic"
-
- @patch(target = "stack.commands.set.firmware.make.version_regex.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.set.firmware.make.version_regex.plugin_basic.unique_everseen", autospec = True)
- def test_run(self, mock_unique_everseen, mock_lowered, basic_plugin):
- """Test that run updates the database as expected when all arguments are valid."""
- mock_args = ["foo", "bar", "baz"]
- mock_params = {"version_regex": "mock_name"}
- expected_args = tuple(mock_args)
- mock_unique_everseen.return_value = (arg for arg in mock_args)
- mock_lowered.return_value = mock_params.values()
-
- basic_plugin.run(args = (mock_params, mock_args))
-
- basic_plugin.owner.db.execute.assert_called_once_with(
- ANY,
- (basic_plugin.owner.get_version_regex_id.return_value, expected_args),
- )
- basic_plugin.owner.get_version_regex_id.assert_called_once_with(name = mock_params["version_regex"])
- basic_plugin.owner.fillParams.assert_called_once_with(
- names = [("version_regex", "")],
- params = mock_params,
- )
- assert [call(mock_args), call(basic_plugin.owner.fillParams.return_value)] == mock_lowered.mock_calls
- mock_unique_everseen.assert_called_once_with(mock_lowered.return_value)
- basic_plugin.owner.ensure_makes_exist.assert_called_once_with(makes = expected_args)
- basic_plugin.owner.ensure_version_regex_exists.assert_called_once_with(name = mock_params["version_regex"])
-
- @pytest.mark.parametrize("failure_mock", ("ensure_makes_exist", "ensure_version_regex_exists"))
- @patch(target = "stack.commands.set.firmware.make.version_regex.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.set.firmware.make.version_regex.plugin_basic.unique_everseen", autospec = True)
- def test_run_missing_args(self, mock_unique_everseen, mock_lowered, failure_mock, basic_plugin):
- """Test that run fails when any of the exist* functions fail."""
- mock_args = ["foo", "bar", "baz"]
- mock_params = {"version_regex": "mock_name"}
- mock_unique_everseen.return_value = (arg for arg in mock_args)
- mock_lowered.return_value = mock_params.values()
- mock_validation_functions = {
- "ensure_makes_exist": basic_plugin.owner.ensure_makes_exist,
- "ensure_version_regex_exists": basic_plugin.owner.ensure_version_regex_exists,
- }
- mock_validation_functions[failure_mock].side_effect = CommandError(cmd = basic_plugin.owner, msg = "Test error")
-
- with pytest.raises(CommandError):
- basic_plugin.run(args = (mock_params, mock_args))
-
- # Make sure the DB is not modified with bad arguments.
- basic_plugin.owner.db.execute.assert_not_called()
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/set/firmware/model/imp/test_command_stack_commands_set_firmware_model_imp__init__.py b/test-framework/test-suites/unit/tests/command/stack/commands/set/firmware/model/imp/test_command_stack_commands_set_firmware_model_imp__init__.py
deleted file mode 100644
index cd739bbbc..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/set/firmware/model/imp/test_command_stack_commands_set_firmware_model_imp__init__.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from unittest.mock import patch
-import pytest
-from stack.commands.set.firmware.model.imp import Command
-
-class TestSetFirmwareModelImpCommand:
- """A test case to hold the tests for the set firmware model imp stacki Command class."""
-
- class CommandUnderTest(Command):
- """A class derived from the Command class under test used to override __init__.
-
- This allows easier instantiation for testing purposes by excluding the base Command
- class initialization code.
- """
- def __init__(self):
- pass
-
- @pytest.fixture
- def command(self):
- """Fixture to create and return a Command class instance for testing."""
- return self.CommandUnderTest()
-
- @patch.object(target = Command, attribute = "runPlugins", autospec = True)
- def test_run(self, mock_runPlugins, command):
- """Test that run will run the plugins passing through the args."""
- mock_params = {"fizz": "buzz"}
- mock_args = ["foo", "bar", "baz"]
-
- command.run(params = mock_params, args = mock_args)
-
- mock_runPlugins.assert_called_once_with(command, args = (mock_params, mock_args))
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/set/firmware/model/imp/test_command_stack_commands_set_firmware_model_imp_plugin_basic.py b/test-framework/test-suites/unit/tests/command/stack/commands/set/firmware/model/imp/test_command_stack_commands_set_firmware_model_imp_plugin_basic.py
deleted file mode 100644
index 01794cfc0..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/set/firmware/model/imp/test_command_stack_commands_set_firmware_model_imp_plugin_basic.py
+++ /dev/null
@@ -1,77 +0,0 @@
-from unittest.mock import create_autospec, ANY, patch, call
-import pytest
-from stack.commands import DatabaseConnection
-from stack.commands.set.firmware.model.imp import Command
-from stack.exception import CommandError
-from stack.commands.set.firmware.model.imp.plugin_basic import Plugin
-
-class TestSetFirmwareModelImpBasicPlugin:
- """A test case for the set firmware model imp basic plugin."""
-
- @pytest.fixture
- def basic_plugin(self):
- """A fixture that returns the plugin instance for use in tests.
-
- This sets up the required mocks needed to construct the plugin class.
- """
- mock_command = create_autospec(
- spec = Command,
- instance = True,
- )
- mock_command.db = create_autospec(
- spec = DatabaseConnection,
- spec_set = True,
- instance = True,
- )
- return Plugin(command = mock_command)
-
- def test_provides(self, basic_plugin):
- """Ensure that provides returns 'basic'."""
- assert basic_plugin.provides() == "basic"
-
- @patch(target = "stack.commands.set.firmware.model.imp.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.set.firmware.model.imp.plugin_basic.unique_everseen", autospec = True)
- def test_run(self, mock_unique_everseen, mock_lowered, basic_plugin):
- """Test that run updates the database as expected when all arguments are valid."""
- mock_args = ["foo", "bar", "baz"]
- mock_params = { "imp": "mock_imp", "make": "mock_make"}
- expected_args = tuple(mock_args)
- mock_unique_everseen.return_value = (arg for arg in mock_args)
- mock_lowered.return_value = mock_params.values()
-
- basic_plugin.run(args = (mock_params, mock_args))
-
- basic_plugin.owner.db.execute.assert_called_once_with(
- ANY,
- (basic_plugin.owner.get_imp_id.return_value, mock_params["make"], expected_args),
- )
- basic_plugin.owner.get_imp_id.assert_called_once_with(imp = mock_params["imp"])
- basic_plugin.owner.fillParams.assert_called_once_with(
- names = [("imp", ""), ("make", "")],
- params = mock_params,
- )
- assert [call(mock_args), call(basic_plugin.owner.fillParams.return_value)] == mock_lowered.mock_calls
- mock_unique_everseen.assert_called_once_with(mock_lowered.return_value)
- basic_plugin.owner.ensure_models_exist.assert_called_once_with(make = mock_params["make"], models = expected_args)
- basic_plugin.owner.ensure_imp_exists.assert_called_once_with(imp = mock_params["imp"])
-
- @pytest.mark.parametrize("failure_mock", ("ensure_models_exist", "ensure_imp_exists"))
- @patch(target = "stack.commands.set.firmware.model.imp.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.set.firmware.model.imp.plugin_basic.unique_everseen", autospec = True)
- def test_run_missing_args(self, mock_unique_everseen, mock_lowered, failure_mock, basic_plugin):
- """Test that run fails when any of the exist* functions fail."""
- mock_args = ["foo", "bar", "baz"]
- mock_params = {"imp": "mock_imp", "make": "mock_make"}
- mock_unique_everseen.return_value = (arg for arg in mock_args)
- mock_lowered.return_value = mock_params.values()
- mock_validation_functions = {
- "ensure_models_exist": basic_plugin.owner.ensure_models_exist,
- "ensure_imp_exists": basic_plugin.owner.ensure_imp_exists,
- }
- mock_validation_functions[failure_mock].side_effect = CommandError(cmd = basic_plugin.owner, msg = "Test error")
-
- with pytest.raises(CommandError):
- basic_plugin.run(args = (mock_params, mock_args))
-
- # model sure the DB is not modified with bad arguments.
- basic_plugin.owner.db.execute.assert_not_called()
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/set/firmware/model/version_regex/test_command_stack_commands_set_firmware_model_version_regex__init__.py b/test-framework/test-suites/unit/tests/command/stack/commands/set/firmware/model/version_regex/test_command_stack_commands_set_firmware_model_version_regex__init__.py
deleted file mode 100644
index 8d7e5dea4..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/set/firmware/model/version_regex/test_command_stack_commands_set_firmware_model_version_regex__init__.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from unittest.mock import patch
-import pytest
-from stack.commands.set.firmware.model.version_regex import Command
-
-class TestSetFirmwareModelVersionRegexCommand:
- """A test case to hold the tests for the set firmware model version_regex stacki Command class."""
-
- class CommandUnderTest(Command):
- """A class derived from the Command class under test used to override __init__.
-
- This allows easier instantiation for testing purposes by excluding the base Command
- class initialization code.
- """
- def __init__(self):
- pass
-
- @pytest.fixture
- def command(self):
- """Fixture to create and return a Command class instance for testing."""
- return self.CommandUnderTest()
-
- @patch.object(target = Command, attribute = "runPlugins", autospec = True)
- def test_run(self, mock_runPlugins, command):
- """Test that run will run the plugins passing through the args."""
- mock_params = {"fizz": "buzz"}
- mock_args = ["foo", "bar", "baz"]
-
- command.run(params = mock_params, args = mock_args)
-
- mock_runPlugins.assert_called_once_with(command, args = (mock_params, mock_args))
diff --git a/test-framework/test-suites/unit/tests/command/stack/commands/set/firmware/model/version_regex/test_command_stack_commands_set_firmware_model_version_regex_plugin_basic.py b/test-framework/test-suites/unit/tests/command/stack/commands/set/firmware/model/version_regex/test_command_stack_commands_set_firmware_model_version_regex_plugin_basic.py
deleted file mode 100644
index b48cb1ff4..000000000
--- a/test-framework/test-suites/unit/tests/command/stack/commands/set/firmware/model/version_regex/test_command_stack_commands_set_firmware_model_version_regex_plugin_basic.py
+++ /dev/null
@@ -1,77 +0,0 @@
-from unittest.mock import create_autospec, ANY, patch, call
-import pytest
-from stack.commands import DatabaseConnection
-from stack.commands.set.firmware.model.version_regex import Command
-from stack.exception import CommandError
-from stack.commands.set.firmware.model.version_regex.plugin_basic import Plugin
-
-class TestSetFirmwareModelVersionRegexBasicPlugin:
- """A test case for the set firmware model version_regex basic plugin."""
-
- @pytest.fixture
- def basic_plugin(self):
- """A fixture that returns the plugin instance for use in tests.
-
- This sets up the required mocks needed to construct the plugin class.
- """
- mock_command = create_autospec(
- spec = Command,
- instance = True,
- )
- mock_command.db = create_autospec(
- spec = DatabaseConnection,
- spec_set = True,
- instance = True,
- )
- return Plugin(command = mock_command)
-
- def test_provides(self, basic_plugin):
- """Ensure that provides returns 'basic'."""
- assert basic_plugin.provides() == "basic"
-
- @patch(target = "stack.commands.set.firmware.model.version_regex.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.set.firmware.model.version_regex.plugin_basic.unique_everseen", autospec = True)
- def test_run(self, mock_unique_everseen, mock_lowered, basic_plugin):
- """Test that run updates the database as expected when all arguments are valid."""
- mock_args = ["foo", "bar", "baz"]
- mock_params = {"make": "mock_make", "version_regex": "mock_name"}
- expected_args = tuple(mock_args)
- mock_unique_everseen.return_value = (arg for arg in mock_args)
- mock_lowered.return_value = mock_params.values()
-
- basic_plugin.run(args = (mock_params, mock_args))
-
- basic_plugin.owner.db.execute.assert_called_once_with(
- ANY,
- (basic_plugin.owner.get_version_regex_id.return_value, expected_args, mock_params["make"]),
- )
- basic_plugin.owner.get_version_regex_id.assert_called_once_with(name = mock_params["version_regex"])
- basic_plugin.owner.fillParams.assert_called_once_with(
- names = [("make", ""), ("version_regex", "")],
- params = mock_params,
- )
- assert [call(mock_args), call(basic_plugin.owner.fillParams.return_value)] == mock_lowered.mock_calls
- mock_unique_everseen.assert_called_once_with(mock_lowered.return_value)
- basic_plugin.owner.ensure_models_exist.assert_called_once_with(make = mock_params["make"], models = expected_args)
- basic_plugin.owner.ensure_version_regex_exists.assert_called_once_with(name = mock_params["version_regex"])
-
- @pytest.mark.parametrize("failure_mock", ("ensure_models_exist", "ensure_version_regex_exists"))
- @patch(target = "stack.commands.set.firmware.model.version_regex.plugin_basic.lowered", autospec = True)
- @patch(target = "stack.commands.set.firmware.model.version_regex.plugin_basic.unique_everseen", autospec = True)
- def test_run_missing_args(self, mock_unique_everseen, mock_lowered, failure_mock, basic_plugin):
- """Test that run fails when any of the exist* functions fail."""
- mock_args = ["foo", "bar", "baz"]
- mock_params = {"make": "mock_make", "version_regex": "mock_name"}
- mock_unique_everseen.return_value = (arg for arg in mock_args)
- mock_lowered.return_value = mock_params.values()
- mock_validation_functions = {
- "ensure_models_exist": basic_plugin.owner.ensure_models_exist,
- "ensure_version_regex_exists": basic_plugin.owner.ensure_version_regex_exists,
- }
- mock_validation_functions[failure_mock].side_effect = CommandError(cmd = basic_plugin.owner, msg = "Test error")
-
- with pytest.raises(CommandError):
- basic_plugin.run(args = (mock_params, mock_args))
-
- # model sure the DB is not modified with bad arguments.
- basic_plugin.owner.db.execute.assert_not_called()
diff --git a/test-framework/test-suites/unit/tests/pylib/stack/test_pylib_stack_firmware.py b/test-framework/test-suites/unit/tests/pylib/stack/test_pylib_stack_firmware.py
deleted file mode 100644
index 1a4637a34..000000000
--- a/test-framework/test-suites/unit/tests/pylib/stack/test_pylib_stack_firmware.py
+++ /dev/null
@@ -1,238 +0,0 @@
-from unittest.mock import patch, call
-import hashlib
-import pytest
-import stack.download
-import stack.firmware
-
-class TestSupportedSchemes:
- """A test case for the schemes enum logic."""
-
- def test_pretty_string(self):
- """Ensure pretty_string works as expected."""
- assert ", ".join(
- stack.firmware.SUPPORTED_SCHEMES.__members__.keys()
- ) == stack.firmware.SUPPORTED_SCHEMES.pretty_string()
-
- @pytest.mark.parametrize("scheme", stack.firmware.SUPPORTED_SCHEMES)
- def test__str__(self, scheme):
- """Ensure the __str__ for each enum instance works as expected."""
- assert scheme.name == str(scheme)
-
-class TestFirmware:
- """A test case to hold the tests for the firmware utilities."""
-
- @pytest.mark.parametrize("hash_alg", stack.firmware.SUPPORTED_HASH_ALGS)
- def test_ensure_hash_alg_supported(self, hash_alg):
- """Test that ensure_hash_alg_supported works with all supported hash algs."""
- stack.firmware.ensure_hash_alg_supported(hash_alg = hash_alg)
-
- def test_ensure_hash_alg_supported_error(self):
- """Test that ensure_hash_alg_supported fails with an unsupported hash algorithm."""
- with pytest.raises(stack.firmware.FirmwareError):
- stack.firmware.ensure_hash_alg_supported(hash_alg = "foo")
-
- @pytest.mark.parametrize("hash_alg", stack.firmware.SUPPORTED_HASH_ALGS)
- @patch(target = "stack.firmware.ensure_hash_alg_supported", autospec = True)
- @patch(target = "stack.firmware.Path", autospec = True)
- def test_calculate_hash(self, mock_path, mock_ensure_hash_alg_supported, hash_alg):
- """Test that calculate_hash works for all supported hash types."""
- mock_path.return_value.read_bytes.return_value = b"bar"
-
- try:
- expected_hash = hashlib.new(
- name = hash_alg,
- data = mock_path.return_value.read_bytes.return_value
- ).hexdigest()
- except TypeError:
- # Need to handle shake_128 and shake_256 case where a digest length is required.
- expected_hash = hashlib.new(
- name = hash_alg,
- data = mock_path.return_value.read_bytes.return_value
- ).hexdigest(256)
-
- # Call providing the expected hash
- mock_file = "foo"
- assert expected_hash == stack.firmware.calculate_hash(
- file_path = mock_file,
- hash_alg = hash_alg,
- hash_value = expected_hash,
- )
- # Expect the hash to be validated
- mock_ensure_hash_alg_supported.assert_called_once_with(hash_alg = hash_alg)
- # Expect the path to be used to read the file
- mock_path.assert_called_once_with(mock_file)
- mock_path.return_value.read_bytes.assert_called_once_with()
-
- # Reset mocks for the next set of assertions
- mock_path.reset_mock()
- # Call without providing the expected hash
- assert expected_hash == stack.firmware.calculate_hash(
- file_path = mock_file,
- hash_alg = hash_alg,
- )
- # Expect the path to be used to read the file
- mock_path.assert_called_once_with(mock_file)
- mock_path.return_value.read_bytes.assert_called_once_with()
-
- @pytest.mark.parametrize("hash_alg", stack.firmware.SUPPORTED_HASH_ALGS)
- @patch(target = "stack.firmware.ensure_hash_alg_supported", autospec = True)
- @patch(target = "stack.firmware.Path", autospec = True)
- def test_calculate_hash_mismatched_provided_hash(self, mock_path, mock_ensure_hash_alg_supported, hash_alg):
- """Test that calculate_hash fails if the provided hash doesn't match the calculated one."""
- mock_path.return_value.read_bytes.return_value = b"bar"
-
- with pytest.raises(stack.firmware.FirmwareError):
- stack.firmware.calculate_hash(
- file_path = "foo",
- hash_alg = hash_alg,
- hash_value = "foo",
- )
-
- @patch(target = "stack.firmware.ensure_hash_alg_supported", autospec = True)
- @patch(target = "stack.firmware.Path", autospec = True)
- def test_calculate_hash_unsupported_hash(self, mock_path, mock_ensure_hash_alg_supported):
- """Test that calculate_hash fails if the provided hash_alg isn't supported."""
- mock_path.return_value.read_bytes.return_value = b"bar"
- mock_ensure_hash_alg_supported.side_effect = stack.firmware.FirmwareError("Test error")
-
- with pytest.raises(stack.firmware.FirmwareError):
- stack.firmware.calculate_hash(
- file_path = "foo",
- hash_alg = "foo",
- )
-
- @pytest.mark.parametrize("scheme", stack.firmware.SUPPORTED_SCHEMES)
- @patch(target = "uuid.uuid4", autospec = True)
- @patch(target = "stack.firmware.Path", autospec = True)
- @patch(target = "stack.firmware.BASE_PATH", autospec = True)
- @patch(target = "stack.download.fetch", autospec = True)
- def test_fetch_firmware(self, mock_fetch, mock_base_path, mock_path, mock_uuid4, scheme):
- """Test that fetch_firmware works as expected for each supported scheme."""
- mock_url = f"{scheme}://localhost/foo/bar"
- mock_make = "foo"
- mock_model = "bar"
- mock_username = "baz"
-
- result = stack.firmware.fetch_firmware(
- source = mock_url,
- make = mock_make,
- model = mock_model,
- username = mock_username,
- )
-
- # Make sure base_dir is used to properly build the target directory
- chained_calls = call.__truediv__(mock_make).__truediv__(mock_model).resolve().mkdir(parents = True, exist_ok = True)
- call_list = chained_calls.call_list()
- call_list.append(call.__truediv__().__truediv__().resolve().__truediv__(mock_uuid4.return_value.hex))
- assert all(mock_call in mock_base_path.mock_calls for mock_call in call_list)
- # Make sure the final file is built using the hex uuid4
- mock_base_path.__truediv__.return_value.__truediv__.return_value.resolve.return_value.__truediv__.assert_called_once_with(
- mock_uuid4.return_value.hex
- )
- mock_final_file = mock_base_path.__truediv__.return_value.__truediv__.return_value.resolve.return_value.__truediv__.return_value
- # Make sure the returned file is the final file
- assert mock_final_file == result
-
- # We make assertions based on the scheme in use.
- if scheme == stack.firmware.SUPPORTED_SCHEMES.file:
- # Ensure the file was constructed using the url path
- mock_path.assert_called_once_with("/foo/bar")
- mock_path.return_value.resolve.assert_called_once_with(strict = True)
- # Make sure the final file is written
- mock_final_file.write_bytes.assert_called_once_with(
- mock_path.return_value.resolve.return_value.read_bytes.return_value
- )
-
- elif scheme in (stack.firmware.SUPPORTED_SCHEMES.http, stack.firmware.SUPPORTED_SCHEMES.https):
- # Ensure the source was downloaded
- mock_fetch.assert_called_once_with(
- url = mock_url,
- file_path = mock_final_file,
- verbose = True,
- username = mock_username,
- )
-
- @pytest.mark.parametrize("scheme", stack.firmware.SUPPORTED_SCHEMES)
- @patch(target = "uuid.uuid4", autospec = True)
- @patch(target = "stack.firmware.Path", autospec = True)
- @patch(target = "stack.firmware.BASE_PATH", autospec = True)
- @patch(target = "stack.download.fetch", autospec = True)
- def test_fetch_firmware_errors(self, mock_fetch, mock_base_path, mock_path, mock_uuid4, scheme):
- """Test that fetch_firmware fails as expected for each supported scheme when fetching from the source fails."""
- mock_url = f"{scheme}://localhost/foo/bar"
- mock_make = "foo"
- mock_model = "bar"
-
- # Set up exceptions
- mock_path.return_value.resolve.side_effect = FileNotFoundError("Test error")
- mock_fetch.side_effect = stack.download.FetchError("Test error")
-
- with pytest.raises(stack.firmware.FirmwareError):
- stack.firmware.fetch_firmware(
- source = mock_url,
- make = mock_make,
- model = mock_model,
- )
-
- @patch(target = "uuid.uuid4", autospec = True)
- @patch(target = "stack.firmware.Path", autospec = True)
- @patch(target = "stack.firmware.BASE_PATH", autospec = True)
- @patch(target = "stack.download.fetch", autospec = True)
- def test_fetch_firmware_unsupoorted_scheme(self, mock_fetch, mock_base_path, mock_path, mock_uuid4):
- """Test that fetch_firmware fails when given an unsupported scheme."""
- mock_url = f"baz://localhost/foo/bar"
- mock_make = "foo"
- mock_model = "bar"
-
- with pytest.raises(stack.firmware.FirmwareError):
- stack.firmware.fetch_firmware(
- source = mock_url,
- make = mock_make,
- model = mock_model,
- )
-
- @patch(target = "stack.firmware.SUPPORTED_SCHEMES")
- @patch(target = "uuid.uuid4", autospec = True)
- @patch(target = "stack.firmware.Path", autospec = True)
- @patch(target = "stack.firmware.BASE_PATH", autospec = True)
- @patch(target = "stack.download.fetch", autospec = True)
- def test_fetch_firmware_forgotten_scheme(self, mock_fetch, mock_base_path, mock_path, mock_uuid4, mock_schemes):
- """Test that fetch_firmware fails when a case is not added to handle a supported scheme."""
- mock_url = f"baz://localhost/foo/bar"
- mock_make = "foo"
- mock_model = "bar"
- mock_schemes.__getitem__.return_value = "baz"
-
- with pytest.raises(RuntimeError):
- stack.firmware.fetch_firmware(
- source = mock_url,
- make = mock_make,
- model = mock_model,
- )
-
- @patch(target = "uuid.uuid4", autospec = True)
- @patch(target = "stack.firmware.Path", autospec = True)
- @patch(target = "stack.firmware.BASE_PATH", autospec = True)
- @patch(target = "stack.download.fetch", autospec = True)
- def test_fetch_firmware_local_path(self, mock_fetch, mock_base_path, mock_path, mock_uuid4):
- """Test that fetch_firmware works as expected when just a local path is provided."""
- mock_url = f"/foo/bar"
- mock_make = "foo"
- mock_model = "bar"
- mock_username = "baz"
- mock_final_file = mock_base_path.__truediv__.return_value.__truediv__.return_value.resolve.return_value.__truediv__.return_value
-
- stack.firmware.fetch_firmware(
- source = mock_url,
- make = mock_make,
- model = mock_model,
- username = mock_username,
- )
-
- # Ensure the file was constructed using the local path.
- mock_path.assert_called_once_with("/foo/bar")
- mock_path.return_value.resolve.assert_called_once_with(strict = True)
- # Make sure the final file is written
- mock_final_file.write_bytes.assert_called_once_with(
- mock_path.return_value.resolve.return_value.read_bytes.return_value
- )