1
0
mirror of https://github.com/gryf/boxpy.git synced 2026-02-02 06:05:47 +01:00

14 Commits
1.2 ... 1.4

Author SHA1 Message Date
276ddd8681 Added bash completion for start/stop commands. 2022-05-23 12:02:58 +02:00
0fb0d64db6 Added two new commands for starting and stopping VMs. 2022-05-23 12:02:28 +02:00
847279a990 Align case for command help messages. 2022-05-23 12:01:59 +02:00
38ed618b5b Added info if vm is running.
Also, added command for acpi shutdown.
2022-05-23 12:00:55 +02:00
1c39cd1985 Add ability to set different default username.
During cloud init user might want to change default user from the
distribution to something else. It will not be possible to ssh to such
machine using boxpy ssh command - only using ssh directly with the
correct user name would be possible. In this change there is added
possibility for setting username in boxpy_data section with desired
value to be used in ssh command.
2022-04-22 07:53:50 +02:00
ed25a0d208 Fix typo in message. 2022-04-22 07:53:04 +02:00
20120d898a Add error message in case of wrong config 2022-04-22 07:52:22 +02:00
e63d83fc7f Don't clutter current directory with checksum files.
In case of Centos, there is a need to get the checksum file first to
figure out the correct image filename, during that process checksum file
was left alone in the current directory. Place it in the temp dir in the
first place and than remove after we know the right image filename.
2022-02-08 20:39:03 +01:00
0093e32b74 Fix for rebuild command and calling vmdestroy function. 2021-11-04 09:26:38 +01:00
353d848072 Pass more than one machine to destroy command.
From now on, there is a possibility to pass more than one machine to be
destroyed.
2021-11-04 08:41:13 +01:00
4581ab0ed0 Specify from which Python version boxpy is supported. 2021-10-19 19:33:15 +02:00
deba0aa621 Narrow down proposed VMs to running only for ssh command. 2021-10-19 19:32:30 +02:00
6528813d6a Update examples for multinode setup 2021-10-19 19:31:18 +02:00
9699e61b35 Exit, if image checksum is wrong. 2021-10-03 19:08:15 +02:00
4 changed files with 208 additions and 64 deletions

View File

@@ -16,13 +16,13 @@ weird named options for ``vboxmanage`` ;P)
Requirements
------------
- Python 3.x
- Python >=3.7
- `pyyaml`_
- `requests`_
- Virtualbox (obviously)
- ``mkisofs`` or ``genisoimage`` command for generating iso image
- ``mkisofs`` or ``genisoimage`` command for generating ISO image
- ``wget`` command for fetching images
- ``sha256sum`` command for checksum check
- ``qemu-img`` from *qemu-utils* package command for converting between images

150
box.py
View File

@@ -18,7 +18,7 @@ import requests
import yaml
__version__ = "1.2"
__version__ = "1.3"
CACHE_DIR = os.environ.get('XDG_CACHE_HOME', os.path.expanduser('~/.cache'))
CLOUD_IMAGE = "ci.iso"
@@ -125,7 +125,7 @@ _boxpy() {
fi
fi
opts="create destroy rebuild info list completion ssh"
opts="create destroy rebuild info list completion ssh start stop"
if [[ ${cur} == "-q" || ${cur} == "-v" || ${COMP_CWORD} -eq 1 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
@@ -173,21 +173,36 @@ _boxpy() {
fi
;;
destroy|info)
info)
if [[ ${prev} == ${cmd} ]]; then
_vms_comp vms
fi
;;
destroy)
_vms_comp vms
_get_excluded_items "${COMPREPLY[@]}"
COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
;;
list)
items=(--long --running --run-by-boxpy)
_get_excluded_items "${items[@]}"
COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
;;
ssh)
if [[ ${prev} == ${cmd} ]]; then
_vms_comp runningvms
fi
;;
start)
if [[ ${prev} == ${cmd} ]]; then
_vms_comp vms
fi
;;
stop)
if [[ ${prev} == ${cmd} ]]; then
_vms_comp runningvms
fi
;;
esac
}
@@ -344,7 +359,7 @@ class FakeLogger:
class Config:
ATTRS = ('cpus', 'config', 'creator', 'disable_nested', 'disk_size',
'distro', 'forwarding', 'hostname', 'key', 'memory', 'name',
'port', 'version')
'port', 'version', 'username')
def __init__(self, args, vbox=None):
self.advanced = None
@@ -360,6 +375,7 @@ class Config:
self.name = args.name # this one is not stored anywhere
self.port = None # at least is not even tried to be retrieved
self.version = None
self.username = None
self._conf = {}
# set defaults stored in hard coded yaml
@@ -554,6 +570,7 @@ class VBoxManage:
self.name_or_uuid = name_or_uuid
self.vm_info = {}
self.uuid = None
self.running = False
def get_vm_base_path(self):
path = self._get_vm_config()
@@ -605,6 +622,9 @@ class VBoxManage:
if line.startswith('Config file:'):
self.vm_info['config_file'] = line.split('Config '
'file:')[1].strip()
if line.startswith('State:'):
self.running = line.split(':')[1].strip().startswith('running')
break
dom = xml.dom.minidom.parse(self.vm_info['config_file'])
@@ -648,6 +668,9 @@ class VBoxManage:
def poweroff(self):
Run(['vboxmanage', 'controlvm', self.name_or_uuid, 'poweroff'])
def acpipowerbutton(self):
Run(['vboxmanage', 'controlvm', self.name_or_uuid, 'acpipowerbutton'])
def vmlist(self, only_running=False, long_list=False, only_boxpy=False):
subcommand = 'runningvms' if only_running else 'vms'
machines = {}
@@ -1007,12 +1030,13 @@ class CentosStream(Image):
self._img_url = self.URL % (version, arch, self._img_fname)
def _get_image_name(self, version, arch):
Run(['wget', self._checksum_url, '-q', '-O', self._checksum_file])
fname = os.path.join(self._tmp, self._checksum_file)
Run(['wget', self._checksum_url, '-q', '-O', fname])
pat = re.compile(self.IMG % (version, arch))
images = []
with open(self._checksum_file) as fobj:
with open(fname) as fobj:
for line in fobj.read().strip().split('\n'):
line = line.strip()
if line.startswith('#'):
@@ -1021,6 +1045,7 @@ class CentosStream(Image):
if match and match.groups():
images.append(match.groups()[0])
Run(['rm', fname])
images.reverse()
if images:
return images[0]
@@ -1150,6 +1175,9 @@ def vmcreate(args, conf=None):
image = get_image_object(vbox, conf.version, image=conf.distro)
path_to_disk = image.convert_to_vdi(conf.name + '.vdi', conf.disk_size)
if not path_to_disk:
return 21
iso = IsoImage(conf)
path_to_iso = iso.get_generated_image()
if not path_to_iso:
@@ -1171,34 +1199,52 @@ def vmcreate(args, conf=None):
# than, let's try to see if boostraping process has finished
LOG.info('Waiting for cloud init to finish ', end='')
username = DISTROS[conf.distro]["username"]
cmd = ['ssh', '-o', 'StrictHostKeyChecking=no',
'-o', 'UserKnownHostsFile=/dev/null',
'-o', 'ConnectTimeout=2',
'-i', conf.ssh_key_path[:-4],
f'ssh://{DISTROS[conf.distro]["username"]}'
f'@localhost:{vbox.vm_info["port"]}', 'sudo cloud-init status']
f'ssh://{username}@localhost:{vbox.vm_info["port"]}',
'sudo cloud-init status']
try:
while True:
out = Run(cmd).stdout
LOG.debug('Out: %s', out)
out = Run(cmd)
LOG.debug('Out: %s', out.stdout)
if (not out) or ('status' in out and 'running' in out):
if (not out.stdout) or ('status' in out.stdout and
'running' in out.stdout):
LOG.info('.', end='')
sys.stdout.flush()
if 'Permission denied (publickey)' in out.stderr:
if conf.username and conf.username != username:
username = conf.username
vbox.setextradata('username', username)
cmd[9] = (f'ssh://{username}'
f'@localhost:{vbox.vm_info["port"]}')
continue
raise PermissionError(f'There is an issue with accessing '
f'VM with ssh for user {username}. '
f'Check output in debug mode.')
time.sleep(3)
continue
LOG.info(' done')
break
out = out.split(':')[1].strip()
out = out.stdout.split(':')[1].strip()
if out != 'done':
cmd = cmd[:-1]
cmd.append('cloud-init status -l')
LOG.warning('Cloud init finished with "%s" status:\n%s', out,
Run(cmd).stdout)
except PermissionError:
LOG.info('\n')
iso.cleanup()
image.cleanup()
vbox.destroy()
raise
except KeyboardInterrupt:
LOG.warning('\nIterrupted, cleaning up')
LOG.warning('\nInterrupted, cleaning up')
iso.cleanup()
image.cleanup()
vbox.destroy()
@@ -1210,22 +1256,35 @@ def vmcreate(args, conf=None):
# reread config to update fields
conf = Config(args, vbox)
username = DISTROS[conf.distro]["username"]
LOG.info('You can access your VM by issuing:')
LOG.info(f'ssh -p {conf.port} -i {conf.ssh_key_path[:-4]} '
f'{DISTROS[conf.distro]["username"]}@localhost')
if conf.username and conf.username != username:
LOG.info(f'ssh -p {conf.port} -i {conf.ssh_key_path[:-4]} '
f'{conf.username}@localhost')
else:
LOG.info(f'ssh -p {conf.port} -i {conf.ssh_key_path[:-4]} '
f'{username}@localhost')
LOG.info('or simply:')
LOG.info(f'boxpy ssh {conf.name}')
return 0
def vmdestroy(args):
vbox = VBoxManage(args.name)
if not vbox.get_vm_info():
LOG.fatal(f'Cannot remove VM "{args.name}" - it doesn\'t exists.')
return 18
if isinstance(args.name, list):
vm_names = args.name
else:
LOG.header('Removing VM: %s', args.name)
return VBoxManage(args.name).destroy()
vm_names = [args.name]
for name in vm_names:
vbox = VBoxManage(name)
if not vbox.get_vm_info():
LOG.fatal(f'Cannot remove VM "{name}" - it doesn\'t exists.')
return 18
LOG.header('Removing VM: %s', name)
res = VBoxManage(name).destroy()
if res:
return res
return 0
def vmlist(args):
@@ -1321,7 +1380,8 @@ def vmrebuild(args):
try:
conf = Config(args, vbox)
except BoxNotFound:
except BoxNotFound as ex:
LOG.fatal(f'Error with parsing config: {ex}')
return 8
except yaml.YAMLError:
LOG.fatal(f'Cannot read or parse file `{args.config}` as YAML '
@@ -1369,13 +1429,43 @@ def connect(args):
f'file.')
return 16
username = conf.username or DISTROS[conf.distro]["username"]
return Run(['ssh', '-o', 'StrictHostKeyChecking=no',
'-o', 'UserKnownHostsFile=/dev/null',
'-i', conf.ssh_key_path[:-4],
f'ssh://{DISTROS[conf.distro]["username"]}'
f'ssh://{username}'
f'@localhost:{conf.port}'], False).returncode
def _set_vmstate(name, state):
vbox = VBoxManage(name)
if not vbox.get_vm_info():
LOG.fatal(f'No machine has been found with a name `{name}`.')
return 20
if vbox.running and state == "start":
LOG.info(f'VM "{name}" is already running.')
return
if not vbox.running and state == "stop":
LOG.info(f'VM "{name}" is already stopped.')
return
if state == "start":
vbox.poweron()
else:
vbox.acpipowerbutton()
def vmstart(args):
_set_vmstate(args.name, 'start')
def vmstop(args):
_set_vmstate(args.name, 'stop')
def main():
parser = argparse.ArgumentParser(description="Automate deployment and "
"maintenance of VMs using cloud config,"
@@ -1428,7 +1518,7 @@ def main():
f"Default {DISTROS['ubuntu']['default_version']}")
destroy = subparsers.add_parser('destroy', help='destroy VM')
destroy.add_argument('name', help='name or UUID of the VM')
destroy.add_argument('name', nargs='+', help='name or UUID of the VM')
destroy.set_defaults(func=vmdestroy)
list_vms = subparsers.add_parser('list', help='list VMs')
@@ -1441,7 +1531,7 @@ def main():
help='show only running VMs')
list_vms.set_defaults(func=vmlist)
rebuild = subparsers.add_parser('rebuild', help='Rebuild VM, all options '
rebuild = subparsers.add_parser('rebuild', help='rebuild VM, all options '
'besides vm name are optional, and their '
'values will be taken from vm definition.')
rebuild.add_argument('name', help='name or UUID of the VM')
@@ -1470,13 +1560,21 @@ def main():
rebuild.add_argument('-v', '--version', help='distribution version')
rebuild.set_defaults(func=vmrebuild)
start = subparsers.add_parser('start', help='start VM')
start.add_argument('name', help='name or UUID of the VM')
start.set_defaults(func=vmstart)
stop = subparsers.add_parser('stop', help='stop VM')
stop.add_argument('name', help='name or UUID of the VM')
stop.set_defaults(func=vmstop)
completion = subparsers.add_parser('completion', help='generate shell '
'completion')
completion.add_argument('shell', choices=['bash'],
help="pick shell to generate completions for")
completion.set_defaults(func=shell_completion)
ssh = subparsers.add_parser('ssh', help='Connect to the machine via SSH')
ssh = subparsers.add_parser('ssh', help='connect to the machine via SSH')
ssh.add_argument('name', help='name or UUID of the VM')
ssh.set_defaults(func=connect)

View File

@@ -35,8 +35,6 @@ write_files:
[[local|localrc]]
disable_all_services
disable_service tls-proxy
# Cinder
disable_service c-api
disable_service c-bak
@@ -80,15 +78,20 @@ write_files:
enable_service n-cond
enable_service n-cpu
enable_service n-sch
# Neutron
enable_service neutron
enable_service neutron-tag-ports-during-bulk-creation
# Octavia
enable_service o-api
enable_service o-cw
enable_service o-da
enable_service o-hk
enable_service o-hm
enable_service octavia
# OVN
# Neutron ovn services
enable_service ovn-controller
enable_service ovn-northd
enable_service ovs-vswitchd
@@ -99,60 +102,83 @@ write_files:
enable_service placement-client
# Neutron services
enable_service q-agt
enable_service q-dhcp
enable_service q-l3
enable_service q-meta
enable_service q-ovn-metadata-agent
enable_service q-qos
enable_service q-svc
enable_service q-trunk
enable_service rabbit
# Swift
disable_service s-account
disable_service s-container
disable_service s-object
disable_service s-proxy
# Tempest
enable_service tempest
enable_service tls-proxy
# TLS
disable_service tls-proxy
# Vars
ADMIN_PASSWORD="secretadmin"
DATABASE_PASSWORD="secretdatabase"
ETCD_USE_RAMDISK="True"
KURYR_ENABLED_HANDLERS="vif,endpoints,service,namespace,pod_label,policy,kuryrnetworkpolicy,kuryrnetwork,kuryrport,kuryrloadbalancer"
KURYR_EP_DRIVER_OCTAVIA_PROVIDER="amphora"
KURYR_K8S_API_PORT="6443"
KURYR_K8S_CLOUD_PROVIDER="False"
KURYR_K8S_CONTAINERIZED_DEPLOYMENT="True"
KURYR_ENFORCE_SG_RULES="False"
KURYR_EP_DRIVER_OCTAVIA_PROVIDER="ovn"
KURYR_K8S_MULTI_WORKER_TESTS="True"
KURYR_K8S_OCTAVIA_MEMBER_MODE="L2"
KURYR_LB_ALGORITHM="SOURCE_IP_PORT"
KURYR_NEUTRON_DEFAULT_ROUTER="router1"
KURYR_SG_DRIVER="policy"
KURYR_SUBNET_DRIVER="namespace"
LOGFILE="/opt/stack/logs/devstacklog.txt"
LOG_COLOR="False"
ML2_L3_PLUGIN="router"
ML2_L3_PLUGIN="ovn-router,trunk,qos"
OCTAVIA_AMP_IMAGE_FILE="/tmp/test-only-amphora-x64-haproxy-ubuntu-bionic.qcow2"
OCTAVIA_AMP_IMAGE_NAME="test-only-amphora-x64-haproxy-ubuntu-bionic"
OCTAVIA_AMP_IMAGE_SIZE="3"
Q_AGENT="openvswitch"
Q_ML2_TENANT_NETWORK_TYPE="vxlan"
Q_ML2_PLUGIN_MECHANISM_DRIVERS="openvswitch,linuxbridge"
OVN_BRANCH="v20.06.2"
OVN_BUILD_FROM_SOURCE="True"
OVN_DBS_LOG_LEVEL="dbg"
OVN_L3_CREATE_PUBLIC_NETWORK="True"
VAR_RUN_PATH="/usr/local/var/run"
RABBIT_PASSWORD="secretrabbit"
RECLONE="no"
SERVICE_PASSWORD="secretservice"
SERVICE_TOKEN="password"
TEMPEST_PLUGINS="/opt/stack/kuryr-tempest-plugin"
USE_PYTHON3="True"
LIBS_FROM_GIT=cinder,devstack,devstack-gate,devstack-plugin-container,glance,keystone,kuryr-kubernetes,kuryr-tempest-plugin,neutron,nova,octavia,placement,python-octaviaclient,requirements,swift,tempest
LIBS_FROM_GIT=cinder,devstack,devstack-gate,devstack-plugin-container,glance,keystone,kuryr-kubernetes,kuryr-tempest-plugin,neutron,nova,octavia,octavia-tempest-plugin,ovn-octavia-provider,placement,python-octaviaclient,requirements,swift,tempest
TEMPEST_PLUGINS="/opt/stack/kuryr-tempest-plugin /opt/stack/octavia-tempest-plugin"
# enabled plugins
enable_plugin devstack-plugin-container https://opendev.org/openstack/devstack-plugin-container
enable_plugin kuryr-kubernetes https://github.com/gryf/kuryr-kubernetes
enable_plugin kuryr-kubernetes https://opendev.org/openstack/kuryr-kubernetes
enable_plugin kuryr-tempest-plugin https://opendev.org/openstack/kuryr-tempest-plugin
enable_plugin neutron https://opendev.org/openstack/neutron
enable_plugin octavia https://opendev.org/openstack/octavia
enable_plugin octavia-tempest-plugin https://opendev.org/openstack/octavia-tempest-plugin
enable_plugin ovn-octavia-provider https://opendev.org/openstack/ovn-octavia-provider
[[post-config|$OCTAVIA_CONF]]
[api_settings]
enabled_provider_drivers = amphora:'Octavia Amphora driver',ovn:'Octavia OVN driver'
runcmd:
- [apt, purge, '-y', python3-pyasn1-modules]
- [apt, purge, '-y', python3-simplejson]
- [su, -, ubuntu, -c, "git clone https://github.com/gryf/vmstrap"]
- [su, -, ubuntu, -c, "vmstrap/bootstrap.sh"]
- [rm, -fr, /home/ubuntu/vmstrap]
- [su, -, ubuntu, -c, "echo 'export HOST_IP=192.168.10.10' >> .bashrc"]
- [su, -, ubuntu, -c, "cp /tmp/local.conf /home/ubuntu/devstack/"]
- [systemctl, restart, systemd-networkd]
boxpy_data:
key: vm
cpus: 4

View File

@@ -35,8 +35,6 @@ write_files:
[[local|localrc]]
disable_all_services
disable_service tls-proxy
# Cinder
disable_service c-bak
disable_service c-vol
@@ -55,46 +53,66 @@ write_files:
enable_service kuryr-daemon
disable_service kuryr-kubernetes
# Nova
enable_service n-cpu
# OVN
# Neutron
enable_service neutron
enable_service ovn-controller
disable_service ovn-northd
enable_service ovn-octavia-provider
enable_service ovs-vswitchd
enable_service ovsdb-server
# Placement API
enable_service placement-client
# Neutron services
# Neutron services cd
enable_service q-ovn-metadata-agent
disable_service q-svc
# tempest
disable_service tempest
# tls
disable_service tls-proxy
# Vars
ADMIN_PASSWORD="secretadmin"
DATABASE_HOST="192.168.10.10"
DATABASE_PASSWORD="secretdatabase"
ENABLE_CHASSIS_AS_GW="False"
GLANCE_HOSTPORT="192.168.10.10:9292"
ML2_L3_PLUGIN="router"
# turn on ovn-provider
KURYR_ENFORCE_SG_RULES="False"
KURYR_EP_DRIVER_OCTAVIA_PROVIDER="ovn"
KURYR_K8S_OCTAVIA_MEMBER_MODE="L2"
KURYR_LB_ALGORITHM="SOURCE_IP_PORT"
KURYR_NEUTRON_DEFAULT_ROUTER="router1"
VAR_RUN_PATH="/usr/local/var/run"
KURYR_ENABLED_HANDLERS="vif,endpoints,service,namespace,pod_label,policy,kuryrnetworkpolicy,kuryrnetwork,kuryrport,kuryrloadbalancer"
KURYR_FORCE_IMAGE_BUILD="True"
KURYR_EP_DRIVER_OCTAVIA_PROVIDER="amphora"
KURYR_K8S_API_PORT="6443"
KURYR_K8S_CLOUD_PROVIDER="False"
KURYR_K8S_CONTAINERIZED_DEPLOYMENT="True"
KURYR_SG_DRIVER="policy"
KURYR_SUBNET_DRIVER="namespace"
OVN_BRANCH="v20.06.2"
OVN_BUILD_FROM_SOURCE="True"
OVN_DBS_LOG_LEVEL="dbg"
OVN_L3_CREATE_PUBLIC_NETWORK="True"
LIBVIRT_TYPE="qemu"
LOGFILE="/opt/stack/logs/devstacklog.txt"
LOG_COLOR="False"
Q_AGENT="openvswitch"
Q_ML2_TENANT_NETWORK_TYPE="vxlan"
Q_ML2_PLUGIN_MECHANISM_DRIVERS="openvswitch,linuxbridge"
Q_HOST="192.168.10.10"
RABBIT_HOST="192.168.10.10"
RABBIT_PASSWORD="secretrabbit"
RECLONE="no"
SERVICE_HOST="192.168.10.10"
SERVICE_PASSWORD="secretservice"
SERVICE_TOKEN="password"
TEMPEST_PLUGINS="/opt/stack/kuryr-tempest-plugin"
USE_PYTHON3="True"
LIBS_FROM_GIT=cinder,devstack,devstack-gate,devstack-plugin-container,glance,keystone,kuryr-kubernetes,kuryr-tempest-plugin,neutron,nova,octavia,placement,python-octaviaclient,requirements,swift,tempest
@@ -102,11 +120,14 @@ write_files:
enable_plugin devstack-plugin-container https://opendev.org/openstack/devstack-plugin-container
enable_plugin kuryr-kubernetes https://opendev.org/openstack/kuryr
runcmd:
- [apt, purge, '-y', python3-pyasn1-modules]
- [apt, purge, '-y', python3-simplejson]
- [su, -, ubuntu, -c, "git clone https://github.com/gryf/vmstrap"]
- [su, -, ubuntu, -c, "vmstrap/bootstrap.sh"]
- [rm, -fr, /home/ubuntu/vmstrap]
- [su, -, ubuntu, -c, "echo 'export HOST_IP=192.168.10.11' >> .bashrc"]
- [su, -, ubuntu, -c, "cp /tmp/local.conf /home/ubuntu/devstack/"]
- [systemctl, restart, systemd-networkd]
boxpy_data:
key: vm
cpus: 4
@@ -114,4 +135,3 @@ boxpy_data:
disk_size: 50GB
advanced:
nic2: intnet
port: 2223