aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNeil Williams <neil.williams@linaro.org>2018-07-18 07:08:37 +0100
committerNeil Williams <neil.williams@linaro.org>2018-07-18 15:22:11 +0100
commitd874e845c1ebed0bb3e7b5e169992ffa7927264c (patch)
tree0ee8f6d60d34e992ecaafc57c1858580a0cff88b
parent1d660d9bd2f5b84cd91d05d9382b5cd41d749642 (diff)
LAVA-1381 CLI to copy an existing device
Change-Id: Ic98160b15f1300fb7607e9096ee2bbea698c330e
-rw-r--r--doc/v2/first-devices.rst7
-rw-r--r--lava_server/management/commands/devices.py103
-rw-r--r--man/lava-server.rst13
3 files changed, 123 insertions, 0 deletions
diff --git a/doc/v2/first-devices.rst b/doc/v2/first-devices.rst
index 16c04d277..cc441dbbb 100644
--- a/doc/v2/first-devices.rst
+++ b/doc/v2/first-devices.rst
@@ -212,6 +212,13 @@ running a health check immediately. Alternatively, specify the
lava-server manage devices add --offline --device-type qemu --worker <worker> qemu01
+It is also possible to copy an existing device as a new device with a new hostname.
+
+.. code-block:: none
+
+ # copy existing qemu01 to a new qem02
+ lava-server manage devices copy qemu01 qemu02 --worker <worker> --offline
+
See ``lava-server manage help devices`` for more options.
Adding a dictionary to the first QEMU device
diff --git a/lava_server/management/commands/devices.py b/lava_server/management/commands/devices.py
index 04814dc86..0bf0232c3 100644
--- a/lava_server/management/commands/devices.py
+++ b/lava_server/management/commands/devices.py
@@ -18,6 +18,7 @@
# along
# with this program; if not, see <http://www.gnu.org/licenses>.
+import os
import contextlib
import csv
@@ -96,6 +97,24 @@ class Command(BaseCommand):
owner.add_argument("--group",
help="Name of the group with ownership of the device")
+ # "copy" sub-command
+ add_parser = sub.add_parser("copy", help="Copy an existing device as a new hostname")
+ add_parser.add_argument(
+ "original", help="Hostname of the existing device")
+ add_parser.add_argument(
+ "target", help="Hostname of the device to create")
+ add_parser.add_argument(
+ "--offline", action="store_false", dest="online", default=True,
+ help="Create the device offline (online by default)")
+ add_parser.add_argument(
+ "--private", action="store_false", dest="public", default=True,
+ help="Make the device private (public by default)")
+ add_parser.add_argument(
+ "--worker", required=True, help="The name of the worker (required)")
+ add_parser.add_argument(
+ "--copy-with-tags", action="store_true", dest='copytags', default=False,
+ help="Set all the tags of the original device on the target device")
+
# "details" sub-command
details_parser = sub.add_parser("details", help="Details about a device")
details_parser.add_argument("hostname",
@@ -147,6 +166,8 @@ class Command(BaseCommand):
""" Forward to the right sub-handler """
if options["sub_command"] == "add":
self.handle_add(options)
+ elif options["sub_command"] == "copy":
+ self.handle_copy(options)
elif options["sub_command"] == "details":
self.handle_details(options["hostname"])
elif options["sub_command"] == "list":
@@ -233,6 +254,88 @@ class Command(BaseCommand):
self._assign(options['group'], device, group=True, owner=True)
device.save()
+ def handle_copy(self, options):
+ original = options['original']
+ target = options['target']
+ worker_name = options['worker']
+ public = options['public']
+ online = options['online']
+ tags = options['copytags']
+
+ try:
+ from_device = Device.objects.get(hostname=original)
+ except Device.DoesNotExist:
+ raise CommandError("Original device '%s' does NOT exist!" % original)
+
+ with contextlib.suppress(Device.DoesNotExist):
+ Device.objects.get(hostname=target)
+ raise CommandError("Target device '%s' already exists" % target)
+
+ origin = from_device.load_configuration()
+ if not origin:
+ raise CommandError("Device dictionary does not exist for %s" % original)
+
+ if online:
+ target_dict = os.path.join(Device.CONFIG_PATH, "%s.jinja2" % target)
+ if not os.path.exists(target_dict):
+ raise CommandError(
+ "Refusing to copy %s to new device %s with health 'Good' -"
+ " no device dictionary exists for target device, yet. "
+ "Use --offline or copy %s.jinja2 to "
+ "/etc/lava-server/dispatcher-config/devices/ and try again." % (original, target, target))
+
+ try:
+ worker = Worker.objects.get(hostname=worker_name)
+ except Worker.DoesNotExist:
+ raise CommandError("Unable to find worker '%s'" % worker_name)
+
+ health = Device.HEALTH_GOOD if online else Device.HEALTH_MAINTENANCE
+ description = from_device.description
+ device_type = from_device.device_type
+ from_tags = None
+ if tags:
+ from_tags = from_device.tags.all()
+
+ physical_owner = None
+ physical_group = None
+ owner = None
+ group = None
+
+ if from_device.physical_owner:
+ physical_owner = from_device.physical_owner
+ elif from_device.physical_group:
+ physical_group = from_device.physical_group
+
+ if from_device.owner:
+ owner = from_device.owner
+ elif from_device.group:
+ group = from_device.group
+
+ with transaction.atomic():
+ device = Device.objects.create(
+ hostname=target, device_type=device_type, description=description,
+ state=Device.STATE_IDLE, health=health, worker_host=worker, is_public=public)
+
+ if from_tags is not None:
+ for tag in from_tags:
+ device.tags.add(tag)
+
+ if physical_owner:
+ self._assign(physical_owner, device, user=True, physical=True)
+ elif physical_group:
+ self._assign(physical_group, device, group=True, physical=True)
+
+ if owner:
+ self._assign(owner, device, user=True, owner=True)
+ elif group:
+ self._assign(group, device, group=True, owner=True)
+
+ device.save()
+
+ destiny = device.load_configuration()
+ if not destiny:
+ print("Reminder: device dictionary does not yet exist for %s" % target)
+
def handle_details(self, hostname):
""" Print device details """
try:
diff --git a/man/lava-server.rst b/man/lava-server.rst
index 16ef6e3cb..3447c706b 100644
--- a/man/lava-server.rst
+++ b/man/lava-server.rst
@@ -102,6 +102,19 @@ lava_scheduler_app
--worker WORKER The name of the worker
--tags TAG1 TAG2 List of tags for the device
+ copy Copy an existing device as a new device
+
+ optional arguments:
+ -h, --help show this help message and exit
+ --original ORIGINAL Hostname of the existing device
+ --target TARGET Hostname of the device to create
+ --offline Create the device offline (online by default)
+ --private Make the device private (public by default)
+ --worker WORKER The name of the worker
+ --copy-with-tags Set all the tags of the original device on the target
+ device
+
+
details Details about a device
positional arguments: