def try_connect(ssh): """ Recursive function to enable multiple connection attempts """ try: ssh.connect(self.ssh_server, port=int(self.ssh_port), username=self.ssh_user, password=self.ssh_password) STREAM.success(" -> Connection established") except Exception as err: STREAM.warning(" -> Fail (%s)" % err) if "ecdsakey" in str(err): STREAM.warning("ECDSAKey error, try to fix.") Popen('ssh-keygen -f %s -R "[%s]:%s"' % (os.path.join( os.path.expanduser("~"), ".ssh/known_hosts"), self.ssh_server, self.ssh_port), shell=True, stdout=PIPE, stderr=PIPE).communicate() if self.connect_tries > 20: raise paramiko.ssh_exception.SSHException( "Connection retries limit exceed!") self.connect_tries += 1 STREAM.info(" -> Connection retry %s:" % self.connect_tries) sleep(15) try_connect(ssh)
def find_vm_files(self): """ Method to find VirtualMachine files location """ try: vbox_path = getattr(self, "openstack_vbox_catalog") except AttributeError: if os.path.exists(os.path.join(os.path.expanduser("~"), "VirtualBox VMs")): vbox_path = os.path.join(os.path.expanduser("~"), "VirtualBox VMs") elif os.path.exists(os.path.join(os.path.expanduser("~"), "virtualbox")): vbox_path = os.path.join(os.path.expanduser("~"), "virtualbox") else: STREAM.error(" -> Virtual box catalog not found!") STREAM.warning(" -> You may specify it directly by adding 'openstack_vbox_catalog' attribute" " in your configuration file") return None STREAM.debug(" -> VirtualBox directory: %s" % vbox_path) vm_path = None for paths, dirs, files in os.walk(vbox_path): if self.vm_name in dirs: vm_path = os.path.join(paths, self.vm_name) return vm_path if vm_path is None: STREAM.error("VirtualMachine directory(%s) not found in the VirtualBox catalog directory tree(%s)!\n" "Make sure that the VirtualMachine name you specify in parameter(vm_name) exists" " in the directory tree (%s)" % (self.vm_name, vbox_path, vbox_path)) return None
def check_keyword(keyword_name): try: STREAM.debug(" -> Check for keyword:") keyword = importlib.import_module("vmaker.keywords.%s" % keyword_name) STREAM.debug(" %s" % keyword) STREAM.debug(" -> Check for a class <Keyword>:") cls = getattr(keyword, "Keyword") STREAM.debug(" %s" % cls) STREAM.debug(" -> Check for entrypoint <main>:") entry = getattr(cls, "main") STREAM.debug(" %s" % entry) STREAM.debug(" -> Check for REQUIRED_CONFIG_ATTRS:") entry = getattr(cls, "REQUIRED_CONFIG_ATTRS") STREAM.debug(" %s" % entry) STREAM.success(_aligner(" -> Checking and loading keyword <%s>" % keyword_name, "OK")) except ImportError as err: STREAM.warning(_aligner(" -> Checking and loading keyword <%s>" % keyword_name, "FAILED")) STREAM.critical(" -> %s" % err) sys.exit() except AttributeError as err: STREAM.warning(_aligner(" -> Checking and loading keyword <%s>" % keyword_name, "FAILED")) STREAM.critical(" -> %s" % err) sys.exit() finally: sleep(0.1)
def cache_image(self, nova, depth=3): """ Method to cache image on one random node. """ server = self.create_instance(nova) STREAM.debug(" -> Created instance: %s" % server) # if recursion will not breaked, whatever keyword will be terminated by vmaker timeout. while True: sleep(2) status = self.get_instance_status(nova, server.id) STREAM.debug(" -> Creation status: %s" % status) if status == "ACTIVE": self.delete_instance(nova, server) STREAM.success(" -> Image has been cached.") break elif status == "ERROR": self.delete_instance(nova, server) STREAM.warning(" -> Unexpected error while launch instance") if depth == 0: break STREAM.warning(" -> Trying to cache image again.") self.cache_image(nova, depth=depth - 1) break
def generate_default_config(config_file): template = """; You can create vm objects and assign them any actions. ; Specify preffered section name. [debian9-x86-template] ; Mandatory keys. ; Key specifies, which type of object will be created (vm, group, alias). type = vm ; Key specifies Keywords which will be performed for this VirtualMachine actions = port_forwarding, vbox_start, execute_command, vbox_stop ; Variable keys ; Key specifies to which group this object belongs. group = linux ; You may specify email to receive notifications about Keyword's errors. ;alert = [email protected] ; That description will be shown in subject of the email ;alert_description = install curl in vm ; Attributes needed for the correct work of a Keyword's ; name of the virtual machine in VirtualBox. vm_name = debian9-x86-template ; Command which will be executed in VirtualMachine by Keyword "execute_command" execute_command = apt-get install -y clamav [fedora27-amd64] type = vm ; actions will be inherited from group group = linux vm_name = fedora27-amd64 execute_command = dnf install -y clamav [freebsd10-amd64] type = vm group = linux vm_name = freebsd10-amd64 execute_command = pkg install -y clamav ; You can create groups and combine it with other objects. ; Groups support attribute inheritance (groups attributes have a lower priority than vm attributes). ; Specify name of the group. [linux] ; Mandatory key. type = group ; Key specifies keywords which will be performed for the group of VirtualMachines. actions = port_forwarding, vbox_start, execute_command, vbox_stop ; You can specify a timeout for each Keyword after which the process will be terminated (ex: <Keyword_name>_timeout) execute_command_timeout = 10 ; You can combine some Keywords in one action, named alias. [linux_aliases] type = alias ; By default aliases extends to all objects, but you can assign aliases at specific group ;group = linux reboot = vbox_stop, vbox_start """ STREAM.info("==> Generating default configuration file...") if os.path.exists(config_file): STREAM.warning(" -> File %s already exists!" % config_file) STREAM.warning(" -> Do you want to overwrite it? (y/n): ") answers = ["y", "n"] while 1: choice = raw_input().lower() if choice in answers: break STREAM.error("Choose y or n! : ") if choice == answers[0]: with open(config_file, "w") as config: config.write(template) STREAM.success(" -> Generated %s" % config_file) else: STREAM.notice(" -> Cancelled by user.") sys.exit() else: with open(config_file, "w") as config: config.write(template) STREAM.success(" -> Generated %s" % config_file)
def check_attributes_dependencies(self): STREAM.info("==> Checking for keywords required attributes.") for vm in self.config_sequence: # Set of required attributes for all Keywords used in the VirtualMachine req_args = set() STREAM.debug("==> VirtualMachine: %s" % vm) for action in self.config[vm].actions: try: # List of required attributes for the Keyword to work req_attr = self.loaded_keywords[ action].REQUIRED_CONFIG_ATTRS # Add required attributes of current action to summary set req_args = set(req_args) | set(req_attr) except KeyError: # If action not in executions section, check for aliases if action not in self.executions.keys(): # Check aliases actions for required attributes try: aliases = self.config[vm].aliases[action] # Intercept if VirtualMachine have no aliases except KeyError as key: STREAM.error( "The keyword (%s) you use in the configuration file does not exist or is not enabled." % key) STREAM.warning( "You can't use this keyword until you turn it on in .vmaker.ini" ) sys.exit(1) # Intercept if VirtualMachine have no aliases except AttributeError: STREAM.error( "The keyword (u'%s') you use in the configuration file does not exist or is not enabled." % action) STREAM.warning( "You can't use this keyword until you turn it on in .vmaker.ini" ) sys.exit(1) for act in aliases: req_attr = self.loaded_keywords[ act].REQUIRED_CONFIG_ATTRS req_args = set(req_args) | set(req_attr) vm_attrs = [ name for name in dir(self.config[vm]) if not name.startswith('__') ] req_args = set(req_args) vm_attrs = set(vm_attrs) STREAM.debug(" -> [%s] Required attributes for actions: %s" % (vm, req_args)) STREAM.debug(" -> [%s] VirtualMachines attributes: %s" % (vm, vm_attrs)) # Attributes comparison result = req_args - vm_attrs if len(result) > 0: STREAM.error( " -> Section <%s> missed required attributes %s." % (vm, list(result))) STREAM.error( " -> This causes problems in the operation of some keywords. Check your user configuration file." ) sys.exit() STREAM.success(" -> All attributes are present.")
def export_vm_configuration(self): """ Method to export VirtualMachine configuration from Virtual Vox """ STREAM.info("==> Checking if vm exists...") vms = Popen("vboxmanage list vms |awk '{print $1}'", shell=True, stdout=PIPE, stderr=PIPE).communicate() vms = vms[0] if not self.vm_name in vms: STREAM.error( " -> VirtualMachine doesn't exist!\nMake sure that the VirtualMachine" " you specified in the parameter(vm_name) are exists.") return False STREAM.success(" -> Exists: True") STREAM.info("==> Exporting configuration...") STREAM.debug(" -> vagrant catalog directory: %s" % self.vagrant_catalog) if not os.path.exists(self.vagrant_catalog): STREAM.error( " -> Vagrant catalog (%s) does not exist!\nMake sure that the catalog" " you specified in the parameter(vagrant_catalog) are exists." % self.vagrant_catalog) STREAM.warning(" -> Export in vagrant, passed.") return False self.work_dir = os.path.join(self.vagrant_catalog, self.vm_name) self.tmp_dir = os.path.join(self.vagrant_catalog, self.vm_name, "tmp") try: os.makedirs(self.tmp_dir) except OSError as errno: if "Errno 17" in str(errno): STREAM.debug( "==> Temporary directory detected, cleaning before start..." ) shutil.rmtree(self.tmp_dir) STREAM.debug(" -> Removed: %s" % self.tmp_dir) os.makedirs(self.tmp_dir) else: STREAM.error(errno) return False result = Popen( 'VBoxManage export %s --output %s' % (self.vm_name, os.path.join(self.tmp_dir, self.vm_name + ".ovf")), shell=True, stdout=PIPE, stderr=PIPE).communicate() if len(result[1]) > 0: if "0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%" not in result[ 1]: raise Exception(result[1]) diskname = "" for fil in os.listdir(self.tmp_dir): if fil.endswith(".vmdk"): diskname = fil os.rename(os.path.join(self.tmp_dir, fil), os.path.join(self.tmp_dir, "box-disk.vmdk")) elif fil.endswith(".ovf"): os.rename(os.path.join(self.tmp_dir, fil), os.path.join(self.tmp_dir, "box.ovf")) with open(os.path.join(self.tmp_dir, "box.ovf"), "r") as ovf: ovf_file = ovf.read() with open(os.path.join(self.tmp_dir, "box.ovf"), "w") as ovf: ovf.write(ovf_file.replace(diskname, "box-disk.vmdk")) return True