1
0
mirror of https://github.com/gryf/boxpy.git synced 2026-02-01 21:45:46 +01:00

11 Commits
1.4 ... 1.6

Author SHA1 Message Date
f0282874f8 Added information regarding url for the dl image 2023-02-12 11:45:50 +01:00
4db0b422b8 Added missing fname from Centos class constructor 2023-02-12 11:45:19 +01:00
c3ee529d95 Make 22.04 default version for ubuntu. 2022-12-24 12:29:49 +01:00
cdcb7ffdce Added implementation for custom image. 2022-11-17 20:34:13 +01:00
9658a9ef36 Add commandline options for providing custom image.
Two new commandline options are added: image and default-user. When
image parameter has been add there are implications that:

- default-user is also provided by commandline - regardless it is
  already present in yaml config
- distro parameter is ignored
- custom username, which might be provided by yaml file will become
  default-user if absent.

All of that is the consequence, that by providing custom qcow2 image
there is no easy way to determine what operating system is passed by,
therefore it is purely declarative way of creating VM with such image.
2022-11-17 20:27:58 +01:00
706dfe8688 Decrease memory/disk size of default vm 2022-11-17 19:22:16 +01:00
8252e189cc Fix issue with condition for extra data 2022-11-16 09:11:27 +01:00
e6d4d8ab7a Fix minor issue with pattern for centos image 2022-11-16 09:10:49 +01:00
b7b4ba5cbc Readme update 2022-11-15 20:33:10 +01:00
47766b6cd9 Added ability to point to local qcow2 image.
Instead of downloading image from the network, there is a way to point
out cloud user and image within yaml configuration.
2022-11-15 20:27:14 +01:00
55cb8d5e30 Better messagingn in conf/modules vbox issues 2022-10-16 08:52:55 +02:00
6 changed files with 153 additions and 73 deletions

View File

@@ -55,7 +55,7 @@ or simply link it somewhere in the path:
$ chmod +x ~/bin/boxpy
and now you can issue some command. For example, to spin up a VM with Ubuntu
18.04 with one CPU, 2GB of memory and 10GB of disk:
18.04 with one CPU, 1GB of memory and 6GB of disk:
.. code:: shell-session
@@ -102,6 +102,10 @@ Currently, following commands are available:
All of the commands have a range of options, and can be examined by using
``--help`` option.
YAML Configuration
------------------
What is more interesting though, is the fact, that you can pass your own
`cloud-init`_ yaml file, so that VM can be provisioned in easy way.
@@ -203,6 +207,19 @@ configuration additional NIC for virtual machine, i.e:
advanced:
nic2: intnet
To select image from local file system, it is possible to set one by providing
it under ``boxpy_data.image`` key:
.. code:: yaml
…
boxpy_data:
image: /path/to/the/qcow2/image
default_user: cloud-user
Note, that default_user is also needed to be provided, as there is no guess,
what is the default username for cloud-init configured within provided image.
License
-------

92
box.py
View File

@@ -46,9 +46,9 @@ ssh:
emit_keys_to_console: false
boxpy_data:
cpus: 1
disk_size: 10240
disk_size: 6144
key: ~/.ssh/id_rsa
memory: 2048
memory: 1024
'''
COMPLETIONS = {'bash': '''\
_boxpy() {
@@ -138,8 +138,9 @@ _boxpy() {
fi
;;
create|rebuild)
items=(--cpus --disable-nested --disk-size --distro --forwarding
--key --memory --hostname --port --config --version --type)
items=(--cpus --disable-nested --disk-size --default-user --distro
--forwarding --image --key --memory --hostname --port --config
--version --type)
if [[ ${prev} == ${cmd} ]]; then
if [[ ${cmd} = "rebuild" ]]; then
_vms_comp vms
@@ -267,6 +268,10 @@ class BoxVBoxFailure(BoxError):
pass
class BoxConfError(BoxError):
pass
class FakeLogger:
"""
print based "logger" class. I like to use 'end' parameter of print
@@ -358,18 +363,20 @@ class FakeLogger:
class Config:
ATTRS = ('cpus', 'config', 'creator', 'disable_nested', 'disk_size',
'distro', 'forwarding', 'hostname', 'key', 'memory', 'name',
'port', 'version', 'username')
'distro', 'default_user', 'forwarding', 'hostname', 'image',
'key', 'memory', 'name', 'port', 'version', 'username')
def __init__(self, args, vbox=None):
self.advanced = None
self.distro = None
self.default_user = None
self.cpus = None
self.creator = None
self.disable_nested = 'False'
self.disk_size = None
self.forwarding = {}
self.hostname = None
self.image = None
self.key = None
self.memory = None
self.name = args.name # this one is not stored anywhere
@@ -412,6 +419,10 @@ class Config:
continue
setattr(self, attr, str(val))
# sort out case, where there is image/default-user provided
if self.image:
self._update_distros_with_custom_image()
# set distribution and version if not specified by user
if not self.distro:
self.distro = 'ubuntu'
@@ -498,7 +509,7 @@ class Config:
self.ssh_key_path = os.path.join(os.path.expanduser("~/.ssh"),
self.ssh_key_path)
if not os.path.exists(self.ssh_key_path):
raise BoxNotFound(f'Cannot find ssh public key: {self.key}')
raise BoxConfError(f'Cannot find ssh public key: {self.key}')
def _set_defaults(self):
conf = yaml.safe_load(USER_DATA)
@@ -553,6 +564,18 @@ class Config:
self._conf = conf
def _update_distros_with_custom_image(self):
self.image = os.path.abspath(self.image)
self.distro = 'custom'
if not self.username:
self.username = self.default_user
DISTROS['custom'] = {'username': self.default_user,
'realname': 'custom os',
'img_class': CustomImage,
'amd64': 'x86_64',
'image': self.image,
'default_version': '0'}
def _update(self, source, update):
for key, val in update.items():
if isinstance(val, collections.abc.Mapping):
@@ -720,6 +743,12 @@ class VBoxManage:
LOG.fatal('Failed to create VM:\n%s', out.stderr)
return None
if out.stdout.startswith('WARNING:'):
LOG.fatal('Created crippled VM:\n%s\nFix the issue with '
'VirtualBox, remove the dead VM and start over.',
out.stdout)
return None
for line in out.stdout.split('\n'):
if line.startswith('UUID:'):
self.uuid = line.split('UUID:')[1].strip()
@@ -886,10 +915,10 @@ class Image:
URL = ""
IMG = ""
def __init__(self, vbox, version, arch, release):
def __init__(self, vbox, version, arch, release, fname=None):
self.vbox = vbox
self._tmp = tempfile.mkdtemp(prefix='boxpy_')
self._img_fname = None
self._img_fname = fname
def convert_to_vdi(self, disk_img, size):
LOG.info('Converting and resizing "%s", new size: %s', disk_img, size)
@@ -949,7 +978,8 @@ class Image:
return True
fname = os.path.join(CACHE_DIR, self._img_fname)
LOG.header('Downloading image %s', self._img_fname)
LOG.header('Downloading image %s from %s', self._img_fname,
self._img_url)
Run(['wget', '-q', self._img_url, '-O', fname])
if not self._checksum():
@@ -968,7 +998,7 @@ class Ubuntu(Image):
URL = "https://cloud-images.ubuntu.com/releases/%s/release/%s"
IMG = "ubuntu-%s-server-cloudimg-%s.img"
def __init__(self, vbox, version, arch, release):
def __init__(self, vbox, version, arch, release, fname=None):
super().__init__(vbox, version, arch, release)
self._img_fname = self.IMG % (version, arch)
self._img_url = self.URL % (version, self._img_fname)
@@ -993,7 +1023,7 @@ class Fedora(Image):
IMG = "Fedora-Cloud-Base-%s-%s.%s.qcow2"
CHKS = "Fedora-Cloud-%s-%s-%s-CHECKSUM"
def __init__(self, vbox, version, arch, release):
def __init__(self, vbox, version, arch, release, fname=None):
super().__init__(vbox, version, arch, release)
self._img_fname = self.IMG % (version, release, arch)
self._img_url = self.URL % (version, arch, self._img_fname)
@@ -1016,10 +1046,10 @@ class Fedora(Image):
class CentosStream(Image):
URL = "https://cloud.centos.org/centos/%s-stream/%s/images/%s"
IMG = '.*(CentOS-Stream-GenericCloud-%s-[0-9]+\.[0-9].%s.qcow2).*'
IMG = '.*(CentOS-Stream-GenericCloud-%s-[0-9]+.[0-9].%s.qcow2).*'
CHKS = "CHECKSUM"
def __init__(self, vbox, version, arch, release):
def __init__(self, vbox, version, arch, release, fname=None):
super().__init__(vbox, version, arch, release)
self._checksum_file = '%s-centos-stream-%s-%s' % (self.CHKS, version,
arch)
@@ -1064,11 +1094,18 @@ class CentosStream(Image):
return expected_sum
class CustomImage(Image):
def _download_image(self):
# just use provided image
return True
DISTROS = {'ubuntu': {'username': 'ubuntu',
'realname': 'ubuntu',
'img_class': Ubuntu,
'amd64': 'amd64',
'default_version': '20.04'},
'default_version': '22.04'},
'fedora': {'username': 'fedora',
'realname': 'fedora',
'img_class': Fedora,
@@ -1086,7 +1123,7 @@ def get_image_object(vbox, version, image='ubuntu', arch='amd64'):
if image == 'fedora':
release = FEDORA_RELEASE_MAP[version]
return DISTROS[image]['img_class'](vbox, version, DISTROS[image]['amd64'],
release)
release, DISTROS[image].get('image'))
class IsoImage:
@@ -1137,7 +1174,8 @@ def vmcreate(args, conf=None):
if not conf:
try:
conf = Config(args)
except BoxNotFound:
except BoxConfError as err:
LOG.fatal(f'Configuration error: {err.args[0]}.')
return 7
except yaml.YAMLError:
LOG.fatal(f'Cannot read or parse file `{args.config}` as YAML '
@@ -1161,7 +1199,9 @@ def vmcreate(args, conf=None):
if not vbox.create_controller('SATA', 'sata'):
return 4
for key in ('distro', 'hostname', 'key', 'version'):
for key in ('distro', 'hostname', 'key', 'version', 'image', 'username'):
if getattr(conf, key) is None:
continue
if not vbox.setextradata(key, getattr(conf, key)):
return 5
@@ -1493,10 +1533,16 @@ def main():
help="Alternative user-data template filepath")
create.add_argument('-d', '--distro', help="Image name. 'ubuntu' is "
"default")
create.add_argument('-e', '--default-user', help="Default cloud-init user "
"to be used with custom image (--image param). "
"Without image it will make no effect.")
create.add_argument('-f', '--forwarding', action='append', help="expose "
"port from VM to the host. It should be in format "
"'hostport:vmport'. this option can be used multiple "
"times for multiple ports.")
create.add_argument('-i', '--image', help="custom qcow2 image filepath. "
"Note, that it requires to provide --default-user as "
"well.")
create.add_argument('-k', '--key', help="SSH key to be add to the config "
"drive. Default ~/.ssh/id_rsa")
create.add_argument('-m', '--memory', help="amount of memory in "
@@ -1538,10 +1584,16 @@ def main():
rebuild.add_argument('-c', '--config',
help="Alternative user-data template filepath")
rebuild.add_argument('-d', '--distro', help="Image name.")
rebuild.add_argument('-e', '--default-user', help="Default cloud-init "
"user to be used with custom image (--image param). "
"Without image it will make no effect.")
rebuild.add_argument('-f', '--forwarding', action='append', help="expose "
"port from VM to the host. It should be in format "
"'hostport:vmport'. this option can be used multiple "
"times for multiple ports.")
rebuild.add_argument('-i', '--image', help="custom qcow2 image filepath. "
"Note, that it requires to provide --default-user as "
"well.")
rebuild.add_argument('-k', '--key',
help='SSH key to be add to the config drive')
rebuild.add_argument('-m', '--memory', help='amount of memory in '
@@ -1584,6 +1636,10 @@ def main():
args = parser.parse_args()
if 'image' in args and 'default_user' not in args:
parser.error('Parameter --image requires --default-user')
return 22
LOG.set_verbose(args.verbose, args.quiet)
if 'func' not in args and args.version:

View File

@@ -29,7 +29,7 @@ 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"]
- [su, -, ubuntu, -c, "vmstrap/bootstrap.sh -c"]
- [rm, -fr, /home/ubuntu/vmstrap]
- [su, -, ubuntu, -c, "cp /tmp/local.conf /home/ubuntu/devstack/"]
- [su, -, ubuntu, -c, "echo 'export HOST_IP=10.0.2.15' >> .bashrc"]

View File

@@ -174,7 +174,7 @@ 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"]
- [su, -, ubuntu, -c, "vmstrap/bootstrap.sh -c"]
- [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/"]
@@ -186,3 +186,4 @@ boxpy_data:
disk_size: 50GB
advanced:
nic2: intnet
version: 20.04

View File

@@ -123,7 +123,7 @@ 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"]
- [su, -, ubuntu, -c, "vmstrap/bootstrap.sh -c"]
- [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/"]
@@ -135,3 +135,4 @@ boxpy_data:
disk_size: 50GB
advanced:
nic2: intnet
version: 20.04

View File

@@ -1,5 +1,6 @@
packages:
- build-essential
- exuberant-ctags
- gettext
- libfontconfig1-dev
- libgif-dev
@@ -21,12 +22,16 @@ packages:
- libxrender-dev
- libxt-dev
- make
- mc
- sharutils
- silversearcher-ag
- tmux
- vim-nox
- xinit
runcmd:
- [su, -, ubuntu, -c, "git clone https://github.com/gryf/wmaker -b experimental"]
- [su, -, ubuntu, -c, "git clone https://github.com/gryf/vmstrap"]
- [su, -, ubuntu, -c, "vmstrap/bootstrap.sh"]
- [su, -, ubuntu, -c, "vmstrap/bootstrap.sh -c"]
- [rm, -fr, /home/ubuntu/vmstrap]
boxpy_data:
key: vm