| # -*- coding: utf-8 -*- |
| # Copyright 2017 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """cros moblabvm: Interact with moblab VM setups. |
| |
| A typical moblab VM setup involves: |
| - A cros VM running a moblab image. |
| - A cros VM running another board image, to act as a sub-DUT for moblab. |
| - Networking to allow these VMs to interact. |
| - Special virtual disk used by the moblab VM for bootstrapping and for result |
| collection; seeded with some configuration files. |
| - [not implemented yet] Special image staging onto the moblab VM, so that it |
| can be used to provision the sub-DUT VM. |
| |
| See README.moblab_vm.md in this folder for usage examples. |
| """ |
| |
| from __future__ import print_function |
| |
| import os |
| import sys |
| |
| from chromite.cli import command |
| from chromite.lib import cros_build_lib |
| from chromite.lib import cros_logging as logging |
| from chromite.lib import moblab_vm |
| from chromite.lib import osutils |
| |
| |
| assert sys.version_info >= (3, 6), 'This module requires Python 3.6+' |
| |
| |
| @command.CommandDecorator('moblabvm') |
| class MoblabvmCommand(command.CliCommand): |
| """Manage a moblab VM setup.""" |
| |
| EPILOG = u""" |
| A typical moblabvm session looks so: |
| $ cros moblabvm --workspace /moblabvm/workspace create /moblab/image/ \ |
| --dut-image-dir /link/image/ |
| $ cros moblabvm --workspace /moblabvm/workspace start |
| ... |
| $ cros moblabvm --workspace /moblabvm/workspace describe |
| ... |
| moblabvm started. SSH port: 66443 |
| ... |
| |
| # Do something with the setup. Perhpas run a test. |
| $ test_that 127.0.0.1:66443 moblab_SmokeSuite |
| |
| # Can start/stop as needed: |
| $ cros moblabvm --workspace /moblabvm/workspace stop |
| # or, directly |
| $ cros moblabvm --workspace /moblabvm/workspace start --restart |
| |
| # When done, teardown everything. |
| $ cros moblabvm --workspace /moblabvm/workspace destroy |
| """ |
| |
| @classmethod |
| def AddParser(cls, parser): |
| """Add moblabvm specific subcommands and options.""" |
| super(MoblabvmCommand, cls).AddParser(parser) |
| parser.add_argument( |
| '--workspace', |
| required=True, |
| help='Workspace directory used as the workspace for this moblabvm. ' |
| 'Must be used consistently across moblabvm calls', |
| ) |
| subparsers = parser.add_subparsers(title='moblabvm subcommands', |
| dest='moblabvm_command') |
| |
| create_parser = _AddSubparser(parser, subparsers, 'create', |
| 'Create a new moblabvm setup.') |
| create_parser.add_argument( |
| 'moblab_image_dir', |
| help='Path to the directory containing moblab image. NOTE: This ' |
| 'directory must contain all the files output by build_image.', |
| ) |
| create_parser.add_argument( |
| '--dut-image-dir', |
| help='Path to the directory containing DUT image. NOTE: This directory ' |
| 'must contain all the files output by build_image.', |
| ) |
| create_parser.add_argument( |
| '--source-vm-images', |
| action='store_true', |
| default=False, |
| help='Copy already created VM images from source folders. ' |
| 'Default behaviour is to convert the test images to VM images.', |
| ) |
| create_parser.add_argument( |
| '--clean', |
| action='store_true', |
| default=False, |
| help='Destroy any previously setup moblabvm from the workspace. ' |
| 'Default is False.', |
| ) |
| |
| start_parser = _AddSubparser(parser, subparsers, 'start', |
| 'Launch an existing moblabvm setup.') |
| start_parser.add_argument( |
| '--restart', |
| action='store_true', |
| default=False, |
| help="Stop the VMs if they're already running. Default is False.", |
| ) |
| |
| _AddSubparser(parser, subparsers, 'stop', |
| 'Stop a running moblabvm setup.') |
| _AddSubparser(parser, subparsers, 'destroy', |
| 'Teardown a moblabvm setup.') |
| _AddSubparser(parser, subparsers, 'describe', |
| 'Describe a running instance of the moblabvm setup. ' |
| 'Provides useful information for using the VMs.') |
| |
| def Run(self): |
| """The main handler of this CLI.""" |
| self.options.Freeze() |
| cmd = self.options.moblabvm_command |
| if cmd == 'create': |
| # Allow the workspace to not exist. This makes the CLI uniform across the |
| # 'cros moblabvm' commands, without burdening the user with a mkdir before |
| # 'create' |
| osutils.SafeMakedirsNonRoot(self.options.workspace) |
| |
| if not os.path.isdir(self.options.workspace): |
| cros_build_lib.Die('Workspace directory %s does not exist!', |
| self.options.workspace) |
| |
| vm = moblab_vm.MoblabVm(self.options.workspace) |
| if cmd == 'create': |
| if self.options.clean: |
| vm.Destroy() |
| vm.Create(self.options.moblab_image_dir, self.options.dut_image_dir, |
| create_vm_images=not self.options.source_vm_images) |
| elif cmd == 'start': |
| if self.options.restart: |
| vm.Stop() |
| vm.Start() |
| _Describe(vm) |
| elif cmd == 'stop': |
| vm.Stop() |
| elif cmd == 'destroy': |
| vm.Destroy() |
| elif cmd == 'describe': |
| _Describe(vm) |
| else: |
| assert False, 'Unrecognized command %s!' % cmd |
| |
| |
| def _AddSubparser(parser, subparsers, name, description): |
| """Adds a subparser to the given parser, with common options. |
| |
| Forwards some options from the parser to the new subparser to ensure |
| consistent formatting of output etc. |
| |
| Args: |
| parser: The parent parser for this subparser. |
| subparsers: The subparsers group to add this subparser to. |
| name: Name of the new sub-command. |
| description: Description to be used for the sub-command. |
| |
| Returns: |
| The new subparser. |
| """ |
| return subparsers.add_parser( |
| name, |
| description=description, |
| caching=parser.caching, |
| formatter_class=parser.formatter_class, |
| ) |
| |
| |
| def _Describe(vm): |
| """Prints some information relevant to using the VMs.""" |
| if not vm.initialized: |
| logging.notice('No MoblabVM setup found. You need to create one.') |
| logging.notice('See "cros moblabvm --help"') |
| return |
| |
| if not vm.running: |
| logging.notice('MoblabVm is not running.') |
| return |
| |
| logging.notice('MoblabVm is running.') |
| logging.notice('Moblab VM information:') |
| logging.notice(' SSH Port to connect from host: %s', vm.moblab_ssh_port) |
| # TODO(pprabhu) Pull out VNC and AFE port forwarding from cros_vm_lib.sh so |
| # that it can be reported better, and so that we don't need to special case |
| # moblab in cros_vm_lib anymore. |
| logging.notice(' MAC address of moblab-internal network in the VM: %s', |
| vm.moblab_internal_mac) |
| |
| if not vm.dut_running: |
| logging.notice('There is no vm-DUT attached to moblab.') |
| return |
| logging.notice('sub-DUT information:') |
| logging.notice(' SSH Port to connect from host: %s', vm.dut_ssh_port) |
| logging.notice(' MAC address of moblab-internal network in the VM: %s', |
| vm.dut_internal_mac) |