blob: faa0a378e0e30db571643f5a41eecfec91d104e0 [file] [log] [blame]
# -*- 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)