diff --git a/deployer/deployer.py b/deployer/deployer.py index 02bcd4d..64ef85a 100644 --- a/deployer/deployer.py +++ b/deployer/deployer.py @@ -49,10 +49,10 @@ def _parse_networks(self): return False for nw_conf in nw_configs: - name = nw_conf["name"] nw = Network(nw_conf) if nw is None: return False + name = nw.name_ self.Topology().AddNetwork(name, nw) return True @@ -74,10 +74,10 @@ def _parse_vms(self): return False for vm_conf in vm_configs: - name = vm_conf["name"] vm = VirtualMachine(vm_conf) if vm is None: return False + name = vm.name_ self.Topology().AddVm(name, vm) return True diff --git a/deployer/network.py b/deployer/network.py index d5f1a09..3af5191 100644 --- a/deployer/network.py +++ b/deployer/network.py @@ -60,6 +60,9 @@ def _has_valid_network_fields(nw): @staticmethod def _validate_network_conf(nw): + if G.NO_NETWORK: + return True + if not Network._has_valid_network_fields(nw): return False diff --git a/deployer/tests/test_virtual_machines.py b/deployer/tests/test_virtual_machines.py index bd7872e..fa8923d 100644 --- a/deployer/tests/test_virtual_machines.py +++ b/deployer/tests/test_virtual_machines.py @@ -4,12 +4,15 @@ import deployer import deployer.topology +import deployer.globals as G +from deployer.deployer import Deployer from deployer.network import Network from deployer.virtual_machine import VirtualMachine class TestVirtualMachine(unittest.TestCase): def setUp(self): + self.deployer_ = Deployer({}) self.vm_ = None self.iso_nw_ = Network({ "name": "iso1", @@ -278,3 +281,57 @@ def test_add_ce_virtual_machine(self): "/var/lib/libvirt/images/vm1/cloud-init.iso") self.assertEqual(str(self.vm_.libvirt_vm_base), "/var/lib/libvirt/images/vm1") + + def test_add_custom_virtual_machine(self): + del self.vm_conf_["flavor"] + self.vm_conf_["disk"] = "300G" + self.vm_conf_["ram"] = 24576 + self.vm_conf_["vcpus"] = 12 + self.vm_ = VirtualMachine(self.vm_conf_) + self.mock_mkdir.assert_called_once_with(parents=True, exist_ok=True) + self.assertEqual(self.vm_.name_, "vm1") + self.assertEqual(self.vm_.vnc_port_, 5900) + self.assertEqual(self.vm_.flavor_, "") + self.assertEqual(self.vm_.vcpus_, 12) + self.assertEqual(self.vm_.ram_, 24576) + self.assertEqual(self.vm_.root_disk_sz_, "300G") + self.assertEqual(self.vm_.networks_, { + "mgmt1": self.mgmt_conf_, + "nat1": self.nat_conf_, + "iso1": self.iso_conf_ + }) + self.assertEqual(str(self.vm_.user_data_cfg_), + "/var/lib/libvirt/images/vm1/user-data.cfg") + self.assertEqual(str(self.vm_.network_data_cfg_), + "/var/lib/libvirt/images/vm1/nw-data.cfg") + self.assertEqual(str(self.vm_.root_disk_), + "/var/lib/libvirt/images/vm1/root_disk.qcow2") + self.assertEqual(str(self.vm_.cloud_init_iso_), + "/var/lib/libvirt/images/vm1/cloud-init.iso") + self.assertEqual(str(self.vm_.libvirt_vm_base), + "/var/lib/libvirt/images/vm1") + + @mock.patch('pathlib.Path.is_file', return_value=True) + def test_add_virtual_machine_valid_base_image(self, mock_is_file): + self.vm_conf_["base_image"] = "/path/to/base_image" + self.deployer_.config_['vms'] = [self.vm_conf_] + ok = self.deployer_._parse_vms() + self.vm_ = self.deployer_.topology_.vms_[self.vm_conf_["name"]] + self.assertEqual(self.vm_.base_image_, "/path/to/base_image") + self.assertTrue(ok) + + @mock.patch('pathlib.Path.is_file', return_value=False) + def test_add_virtual_machine_invalid_base_image(self, mock_is_file): + self.vm_conf_["base_image"] = "/path/to/base_image" + self.deployer_.config_['vms'] = [self.vm_conf_] + ok = self.deployer_._parse_vms() + self.vm_ = self.deployer_.topology_.vms_[self.vm_conf_["name"]] + self.assertFalse(ok) + del self.deployer_.topology_.vms_[self.vm_conf_["name"]] + + def test_add_virtual_machine_default_base_image(self): + self.deployer_.config_['vms'] = [self.vm_conf_] + ok = self.deployer_._parse_vms() + self.vm_ = self.deployer_.topology_.vms_[self.vm_conf_["name"]] + self.assertEqual(self.vm_.base_image_, G.UBUNTU_TEMPLATE) + self.assertTrue(ok) diff --git a/deployer/virtual_machine.py b/deployer/virtual_machine.py index c1fb0b5..75fd4d7 100644 --- a/deployer/virtual_machine.py +++ b/deployer/virtual_machine.py @@ -2,6 +2,7 @@ import logging import yaml import ipaddress +from pathlib import Path import deployer.globals as G from deployer.globals import LIBVIRT_IMAGES, VM_FLAVORS @@ -21,6 +22,7 @@ class VirtualMachine: vcpus_ = 8 ram_ = 16384 root_disk_sz_ = "80G" + base_image_ = "" topology_ = Topology() def __new__(cls, conf): @@ -31,7 +33,8 @@ def __new__(cls, conf): def __init__(self, conf): self.name_ = conf["name"] - self.flavor_ = conf["flavor"] + self.base_image_ = conf.get("base_image", G.OS_IMAGE_TEMPLATE) + self.flavor_ = conf.get("flavor", "") self.vnc_port_ = int(conf["vnc_port"]) self.libvirt_vm_base = LIBVIRT_IMAGES.joinpath(self.name_) self.user_data_cfg_ = self.libvirt_vm_base.joinpath("user-data.cfg") @@ -72,6 +75,15 @@ def _has_valid_vm_fields(vm): logging.critical("'name' is required key in vm config") return False + base_image = vm.get("base_image", None) + if base_image: + if not isinstance(base_image, str): + logging.critical("'base_image' must be a string") + return False + if not Path(base_image).is_file(): + logging.critical(f"'base_image' file does not exist: {base_image}") + return False + networks = vm.get("networks", None) if not networks or not isinstance(networks, dict): logging.critical("'networks' is required key in vm config") @@ -155,6 +167,9 @@ def _validate_vm_network_config(nws): @staticmethod def _validate_vm_config(vm): + if G.NO_VM: + return True + if not VirtualMachine._has_valid_vm_fields(vm): return False @@ -324,7 +339,7 @@ def __generate_iso_config(self, ip4, ip6): # end __generate_iso_config def __create_root_disk(self): - cmd = f"sudo qemu-img convert -f qcow2 -O qcow2 {G.OS_IMAGE_TEMPLATE} {self.root_disk_}" + cmd = f"sudo qemu-img convert -f qcow2 -O qcow2 {self.base_image_} {self.root_disk_}" ExecuteCommand(cmd) cmd = f"sudo qemu-img resize {self.root_disk_} {self.root_disk_sz_}"