diff --git a/kvirt/cli.py b/kvirt/cli.py index 5e34e52e2..ac6890a71 100755 --- a/kvirt/cli.py +++ b/kvirt/cli.py @@ -453,7 +453,7 @@ def download_image(args): image = args.image overrides = handle_parameters(args.param, args.paramfile) name = overrides.get('name') - cmd = overrides.get('image') + cmds = overrides.get('cmds') url = overrides.get('url') if image is None: if url is not None: @@ -477,7 +477,7 @@ def download_image(args): kvm_openstack = not overrides.get('qemu', False) config = Kconfig(client=args.client, debug=args.debug, region=args.region, zone=args.zone, namespace=args.namespace) pool = overrides.get('pool') or config.pool - result = config.download_image(pool=pool, image=image, cmd=cmd, url=url, size=size, arch=arch, + result = config.download_image(pool=pool, image=image, cmds=cmds, url=url, size=size, arch=arch, kvm_openstack=kvm_openstack, rhcos_installer=rhcos_installer, name=name) sys.exit(0 if result['result'] == 'success' else 1) diff --git a/kvirt/config.py b/kvirt/config.py index fbefc2691..7637151e9 100644 --- a/kvirt/config.py +++ b/kvirt/config.py @@ -1860,8 +1860,8 @@ def plan(self, plan, ansible=False, url=None, path=None, container=False, inputf else: if isinstance(imageurl, str) and imageurl == "None": imageurl = None - cmd = imageprofile.get('cmd') - self.download_image(pool=pool, image=image, cmd=cmd, url=imageurl, size=imagesize) + cmds = imageprofile.get('cmds', []) + self.download_image(pool=pool, image=image, cmds=cmds, url=imageurl, size=imagesize) if bucketentries and not onlyassets and self.type in ['aws', 'azure', 'gcp', 'openstack']: pprint("Deploying Bucket Entries...") for entry in bucketentries: @@ -2388,7 +2388,7 @@ def plan(self, plan, ansible=False, url=None, path=None, container=False, inputf overrides.get('tempkeydir').cleanup() return returndata - def download_image(self, pool=None, image=None, url=None, cmd=None, size=None, arch='x86_64', + def download_image(self, pool=None, image=None, url=None, cmds=[], size=None, arch='x86_64', kvm_openstack=True, rhcos_commit=None, rhcos_installer=False, name=None): k = self.k if pool is None: @@ -2434,8 +2434,8 @@ def download_image(self, pool=None, image=None, url=None, cmd=None, size=None, a if url.strip() == '': error("Missing proper url.Leaving...") return {'result': 'failure', 'reason': "Missing image"} - if cmd is None and image != '' and image in IMAGESCOMMANDS: - cmd = IMAGESCOMMANDS[image] + if not cmds and image != '' and image in IMAGESCOMMANDS: + cmds = [IMAGESCOMMANDS[image]] pprint(f"Grabbing image {image} from url {url}") need_iso = 'api/assisted-images/images' in url shortname = os.path.basename(url).split('?')[0] @@ -2443,15 +2443,15 @@ def download_image(self, pool=None, image=None, url=None, cmd=None, size=None, a image = f'boot-{shortname}.iso' try: convert = '.raw.' in url - result = k.add_image(url, pool, cmd=cmd, name=image, size=size, convert=convert) + result = k.add_image(url, pool, cmds=cmds, name=name or image, size=size, convert=convert) except Exception as e: error(f"Got {e}") - error(f"Please run kcli delete image --yes {shortname}") + error(f"Please run kcli delete image --yes {name}") return {'result': 'failure', 'reason': "User interruption"} found = 'found' in result if found: return {'result': 'success'} - common.handle_response(result, image, element='Image', action='Added') + common.handle_response(result, name or image, element='Image', action='Added') if result['result'] != 'success': return {'result': 'failure', 'reason': result['reason']} return {'result': 'success'} diff --git a/kvirt/examples.py b/kvirt/examples.py index 578a1d4d9..b285c2763 100644 --- a/kvirt/examples.py +++ b/kvirt/examples.py @@ -417,8 +417,8 @@ # Download specific arch $ kcli download image centos9stream -P arch=aarch64 -# Execute command after download -$ kcli download image centos9stream -P cmd='echo welcome here > /etc/motd' +# Execute commands after download +$ kcli download image centos9stream -P cmds=['echo welcome here > /etc/motd'] # Download with specific name $ kcli download image centos9stream -P name=centos9 diff --git a/kvirt/providers/aws/__init__.py b/kvirt/providers/aws/__init__.py index eb539611b..4ae41d7ac 100644 --- a/kvirt/providers/aws/__init__.py +++ b/kvirt/providers/aws/__init__.py @@ -1073,7 +1073,7 @@ def delete_image(self, image, pool=None): except: return {'result': 'failure', 'reason': f"Image {image} not found"} - def add_image(self, url, pool, short=None, cmd=None, name=None, size=None, convert=False): + def add_image(self, url, pool, short=None, cmds=[], name=None, size=None, convert=False): print("not implemented") return {'result': 'success'} diff --git a/kvirt/providers/azure/__init__.py b/kvirt/providers/azure/__init__.py index ffb76a0ed..475de0ba1 100644 --- a/kvirt/providers/azure/__init__.py +++ b/kvirt/providers/azure/__init__.py @@ -789,7 +789,7 @@ def delete_image(self, image, pool=None): else: return {'result': 'failure', 'reason': f'Image {image} not found'} - def add_image(self, url, pool, short=None, cmd=None, name=None, size=None, convert=False): + def add_image(self, url, pool, short=None, cmds=[], name=None, size=None, convert=False): bucket = None # if 'blob.core.windows.net' not in url: if self.storage_account not in url: diff --git a/kvirt/providers/fake/__init__.py b/kvirt/providers/fake/__init__.py index 88bb60d55..ff7acf50a 100644 --- a/kvirt/providers/fake/__init__.py +++ b/kvirt/providers/fake/__init__.py @@ -9,7 +9,7 @@ def __init__(self): def volumes(self, iso=True): return glob.glob('*.iso') - def add_image(self, url, pool, cmd=None, name=None, size=None, convert=False): + def add_image(self, url, pool, cmds=[], name=None, size=None, convert=False): os.system("curl -Lk %s > %s" % (url, os.path.basename(url))) def delete_image(self, image, pool=None): diff --git a/kvirt/providers/gcp/__init__.py b/kvirt/providers/gcp/__init__.py index 8ea0e49c3..63df018b2 100644 --- a/kvirt/providers/gcp/__init__.py +++ b/kvirt/providers/gcp/__init__.py @@ -1081,7 +1081,7 @@ def delete_image(self, image, pool=None): except: return {'result': 'failure', 'reason': f'Image {image} not found'} - def add_image(self, url, pool, short=None, cmd=None, name=None, size=None, convert=False): + def add_image(self, url, pool, short=None, cmds=[], name=None, size=None, convert=False): conn = self.conn project = self.project shortimage = os.path.basename(url).split('?')[0].replace('.tar.gz', '').replace('.', '-').replace('-', '.') diff --git a/kvirt/providers/ibm/__init__.py b/kvirt/providers/ibm/__init__.py index c75e6edc6..a46e25584 100644 --- a/kvirt/providers/ibm/__init__.py +++ b/kvirt/providers/ibm/__init__.py @@ -743,7 +743,7 @@ def delete_image(self, image, pool=None): return {'result': 'failure', 'reason': f'Unable to delete image. Hit {e}'} return {'result': 'success'} - def add_image(self, url, pool, short=None, cmd=None, name=None, size=None, convert=False): + def add_image(self, url, pool, short=None, cmds=[], name=None, size=None, convert=False): cos_id = self.cos_resource_instance_id.split(':')[7] identity_client = IamIdentityV1(authenticator=self.authenticator) api_key_detail = identity_client.get_api_keys_details(iam_api_key=self.iam_api_key).get_result() diff --git a/kvirt/providers/kubevirt/__init__.py b/kvirt/providers/kubevirt/__init__.py index 96cefb9ad..d52cedead 100644 --- a/kvirt/providers/kubevirt/__init__.py +++ b/kvirt/providers/kubevirt/__init__.py @@ -1271,7 +1271,7 @@ def delete_image(self, image, pool=None): return {'result': 'success'} return {'result': 'failure', 'reason': f'image {image} not found'} - def add_image(self, url, pool, short=None, cmd=None, name=None, size=None, convert=False): + def add_image(self, url, pool, short=None, cmds=[], name=None, size=None, convert=False): if size is None: size = _base_image_size(url) warning(f"Setting size of image to {size}G. This will be the size of primary disks using this") diff --git a/kvirt/providers/kvm/__init__.py b/kvirt/providers/kvm/__init__.py index ab5d5c9ac..261459554 100644 --- a/kvirt/providers/kvm/__init__.py +++ b/kvirt/providers/kvm/__init__.py @@ -3209,7 +3209,7 @@ def delete_image(self, image, pool=None): continue return {'result': 'failure', 'reason': f'Image {image} not found'} - def add_image(self, url, pool, cmd=None, name=None, size=None, convert=False): + def add_image(self, url, pool, cmds=[], name=None, size=None, convert=False): poolname = pool shortimage = os.path.basename(url).split('?')[0] need_uncompress = any(shortimage.endswith(suffix) for suffix in ['.gz', '.xz', '.bz2', '.zst']) @@ -3270,17 +3270,19 @@ def add_image(self, url, pool, cmd=None, name=None, size=None, convert=False): uncompresscmd = 'ssh %s -p %s %s@%s "%s %s %s/%s"' % (self.identitycommand, self.port, self.user, self.host, executable, flag, poolpath, full_name) os.system(uncompresscmd) - if cmd is not None: + if cmds: if self.host == 'localhost' or self.host == '127.0.0.1': if which('virt-customize') is not None: - cmd = f"virt-customize -a {poolpath}/{name} --run-command '{cmd}'" - os.system(cmd) + for cmd in cmds: + cmd = f"virt-customize -a {poolpath}/{name} --run-command '{cmd}'" + os.system(cmd) elif self.protocol == 'ssh': - cmd = 'ssh %s -p %s %s@%s "virt-customize -a %s/%s --run-command \'%s\'"' % (self.identitycommand, - self.port, self.user, - self.host, poolpath, - name, cmd) - os.system(cmd) + for cmd in cmds: + cmd = 'ssh %s -p %s %s@%s "virt-customize -a %s/%s --run-command \'%s\'"' % (self.identitycommand, + self.port, self.user, + self.host, poolpath, + name, cmd) + os.system(cmd) if convert: name = name.replace('.raw', '') cmd = f"qemu-img convert -O qcow2 {poolpath}/{name}.raw {poolpath}/{name}" diff --git a/kvirt/providers/openstack/__init__.py b/kvirt/providers/openstack/__init__.py index b64782484..e36c1ff46 100644 --- a/kvirt/providers/openstack/__init__.py +++ b/kvirt/providers/openstack/__init__.py @@ -810,7 +810,7 @@ def delete_image(self, image, pool=None): return {'result': 'success'} return {'result': 'failure', 'reason': f"Image {image} not found"} - def add_image(self, url, pool, short=None, cmd=None, name=None, size=None, convert=False): + def add_image(self, url, pool, short=None, cmds=[], name=None, size=None, convert=False): downloaded = False shortimage = os.path.basename(url).split('?')[0] if name is not None and name.endswith('iso'): @@ -842,15 +842,16 @@ def add_image(self, url, pool, short=None, cmd=None, name=None, size=None, conve shortimage = shortimage.replace('.gz', '').replace('.xz', '').replace('.bz2', '').replace('.zst', '') image_path = image_path.replace('.gz', '').replace('.xz', '').replace('.bz2', '').replace('.zst', '') disk_format = 'iso' if shortimage.endswith('iso') else 'qcow2' - if cmd is not None: - pprint(f"Running {cmd} on {image_path}") - if disk_format == 'iso': - if image_path not in cmd: - cmd += f" {image_path}" - os.system(cmd) - elif which('virt-customize') is not None: - cmd = f"virt-customize -a {image_path} --run-command '{cmd}'" - os.system(cmd) + if cmds: + for cmd in cmds: + pprint(f"Running {cmd} on {image_path}") + if disk_format == 'iso': + if image_path not in cmd: + cmd += f" {image_path}" + os.system(cmd) + elif which('virt-customize') is not None: + cmd = f"virt-customize -a {image_path} --run-command '{cmd}'" + os.system(cmd) glanceimage = self.glance.images.create(name=shortimage, disk_format=disk_format, container_format='bare') self.glance.images.upload(glanceimage.id, open(image_path, 'rb')) if downloaded: diff --git a/kvirt/providers/ovirt/__init__.py b/kvirt/providers/ovirt/__init__.py index 81597a24c..7fbeaf166 100644 --- a/kvirt/providers/ovirt/__init__.py +++ b/kvirt/providers/ovirt/__init__.py @@ -996,7 +996,7 @@ def delete_image(self, image, pool=None): return {'result': 'success'} return {'result': 'failure', 'reason': f"Image {image} not found"} - def add_image(self, url, pool, short=None, cmd=None, name=None, size=None, convert=False): + def add_image(self, url, pool, short=None, cmds=[], name=None, size=None, convert=False): downloaded = False shortimage = os.path.basename(url).split('?')[0] iso = True if shortimage.endswith('.iso') or name.endswith('.iso') else False diff --git a/kvirt/providers/packet/__init__.py b/kvirt/providers/packet/__init__.py index 27bc95a2e..91c15e110 100644 --- a/kvirt/providers/packet/__init__.py +++ b/kvirt/providers/packet/__init__.py @@ -576,7 +576,7 @@ def delete_image(self, image, pool=None): print("not implemented") return {'result': 'success'} - def add_image(self, url, pool, short=None, cmd=None, name=None, size=None, convert=False): + def add_image(self, url, pool, short=None, cmds=[], name=None, size=None, convert=False): return {'result': 'failure', 'reason': "not implemented"} def _create_network(self, name, facility, vlan=None, vxlan=None): diff --git a/kvirt/providers/proxmox/__init__.py b/kvirt/providers/proxmox/__init__.py index f62c742c5..6895ea8af 100644 --- a/kvirt/providers/proxmox/__init__.py +++ b/kvirt/providers/proxmox/__init__.py @@ -186,7 +186,7 @@ def _check_storage(self, pool, node=None): ret = s return ret - def add_image(self, url, pool, cmd=None, name=None, size=None, convert=False): + def add_image(self, url, pool, cmds=[], name=None, size=None, convert=False): if self.imagepool: pool = self.imagepool diff --git a/kvirt/providers/sampleprovider.py b/kvirt/providers/sampleprovider.py index 472ee5b3b..8c541765e 100644 --- a/kvirt/providers/sampleprovider.py +++ b/kvirt/providers/sampleprovider.py @@ -183,7 +183,7 @@ def delete_image(self, image, pool=None): print("not implemented") return {'result': 'success'} - def add_image(self, url, pool, short=None, cmd=None, name=None, size=None, convert=False): + def add_image(self, url, pool, short=None, cmds=[], name=None, size=None, convert=False): print("not implemented") return {'result': 'success'} diff --git a/kvirt/providers/vsphere/__init__.py b/kvirt/providers/vsphere/__init__.py index d79f9e630..f9584f4b6 100644 --- a/kvirt/providers/vsphere/__init__.py +++ b/kvirt/providers/vsphere/__init__.py @@ -1268,7 +1268,7 @@ def delete_network(self, name=None, cidr=None, force=False): def vm_ports(self, name): return ['default'] - def add_image(self, url, pool, short=None, cmd=None, name=None, size=None, convert=False): + def add_image(self, url, pool, short=None, cmds=[], name=None, size=None, convert=False): downloaded = False si = self.si rootFolder = self.rootFolder @@ -1351,6 +1351,10 @@ def add_image(self, url, pool, short=None, cmd=None, name=None, size=None, conve extension = os.path.splitext(shortimage)[1].replace('.', '') vmdk_file = shortimage.replace(extension, 'vmdk') vmdk_path = f"/tmp/{vmdk_file}" + if cmds and shortimage.endswith('qcow2') and which('virt-customize') is not None: + for cmd in cmds: + cmd = f"virt-customize -a /tmp/{shortimage} --run-command '{cmd}'" + os.system(cmd) if not os.path.exists(vmdk_path): pprint("Converting qcow2 file to vmdk") os.popen(f"qemu-img convert -O vmdk -o subformat=streamOptimized /tmp/{shortimage} {vmdk_path}").read() diff --git a/kvirt/providers/web/__init__.py b/kvirt/providers/web/__init__.py index 0aca66157..9819885ba 100644 --- a/kvirt/providers/web/__init__.py +++ b/kvirt/providers/web/__init__.py @@ -289,13 +289,13 @@ def delete_image(self, image, pool=None): response = json.loads(urlopen(request, context=self.context).read()) return response - def add_image(self, url, pool, short=None, cmd=None, name=None, size=None, convert=False): + def add_image(self, url, pool, short=None, cmds=[], name=None, size=None, convert=False): image_url = f"{self.base}/images" data = {'url': url, 'pool': pool, 'convert': convert} if name is not None: data['image'] = name - if cmd is not None: - data['cmd'] = cmd + if cmds: + data['cmds'] = cmds if size is not None: data['size'] = size data = json.dumps(data).encode('utf-8')