kcli 99.0.202510241005__py3-none-any.whl → 99.0.202601080644__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kcli
3
- Version: 99.0.202510241005
3
+ Version: 99.0.202601080644
4
4
  Summary: Provisioner/Manager for Libvirt/Vsphere/Aws/Gcp/Hcloud/Kubevirt/Ovirt/Openstack/IBM Cloud and containers
5
5
  Home-page: http://github.com/karmab/kcli
6
6
  Author: Karim Boumedhel
@@ -1,17 +1,17 @@
1
- kcli-99.0.202510241005.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
1
+ kcli-99.0.202601080644.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
2
2
  kvirt/__init__.py,sha256=eoZ6GfifbqhMLNzjlqRDVil-yyBkOmVN9ujSgJWNBlY,15
3
3
  kvirt/baseconfig.py,sha256=QG_AHI1_HAgHwIdZSm1BXkpn9pW35kTMUkFOb7UE_wY,83513
4
4
  kvirt/bottle.py,sha256=K24aCQYSylDpkxmLYGygSpAJ4MlMqMk85AMpbCWA43c,175729
5
5
  kvirt/cli.py,sha256=D1lMERW5zuH6H7vzJHIX2UIdcpv16V2CaV-Ear3UTC8,265483
6
- kvirt/config.py,sha256=ikO03beELPo1o08-Aa-S1OgxdufvTDXMsaY828R7NgI,184294
6
+ kvirt/config.py,sha256=Vw_9kP7IYRk4kiY-95Ku98TWdt6ZVSGNMGFSNspoYhk,184633
7
7
  kvirt/containerconfig.py,sha256=qbvOWwFlyU2F-4rZn9PLj97wDnSBIwAYA6Xlxr13OOc,2329
8
- kvirt/defaults.py,sha256=n55lnshkM6oXJPcolMvCo8Tjz9xdBGO8nGI1ejQJzvc,12212
8
+ kvirt/defaults.py,sha256=vZFFUI_hegVf2W2Zle0fTcSQNhcStJGWXtrBXTYE22E,12401
9
9
  kvirt/ekstoken.py,sha256=MjK_tcLFl2VKxqgTSB4ve_kybZw5dBj2H6BezGGtBpM,1475
10
10
  kvirt/examples.py,sha256=KTiMRjXHpwoEg5fPgndDADa15LKV79aT6Ty2ihu8wI4,28876
11
11
  kvirt/gketoken.py,sha256=up6cyslRTsoOHu9xT8fa0chES6RWExIu0IzXQY0IxQs,934
12
12
  kvirt/keywords.yaml,sha256=HVg-130j21WWuDrSvaGI98xLwLDiMwUsU3oAnRUm2Ls,10145
13
13
  kvirt/klist.py,sha256=TT6j6_gHUgSIkTsrDnTvNMdgrdpgOUJZbJp9xPyq5ns,3395
14
- kvirt/kmcp.py,sha256=Wyfx2BmRryphCl29Bs37EL4suVwpoorb5IJunbty3HU,26172
14
+ kvirt/kmcp.py,sha256=1zKdMGkcNjNr8detyKaUmiSyCRxg3kr-bfmrJ6aSaSc,29612
15
15
  kvirt/cluster/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  kvirt/cluster/sampleprovider.py,sha256=6K-rh95Os9yMOTzGp9afiQeWhDE2Y2KEc0TjnpFY3xs,181
17
17
  kvirt/cluster/aks/__init__.py,sha256=I-e-MwzLEaOtuS_xPdY4rIO_Wey2M9o9-VX5ETgxlQM,8967
@@ -81,7 +81,7 @@ kvirt/cluster/kubeadm/disconnected.yml,sha256=ntIU2kPXJfDJiW9b2aip4QdmricDTUxzZ6
81
81
  kvirt/cluster/kubeadm/join.sh,sha256=JIkMmRDlCzi-MshamKizHUiJCSzshvESjo9inN47ItA,267
82
82
  kvirt/cluster/kubeadm/kcli_default.yml,sha256=4vO3t2kGHkzmoU4hILyBum08Yxh4a8n7aO785B4zflU,2449
83
83
  kvirt/cluster/kubeadm/keepalived.conf,sha256=OwQ_PozqBMzAULPjjJ2TbAHK3n9BE5gyCqkqZ0SFmic,588
84
- kvirt/cluster/kubeadm/keepalived.sh,sha256=QMLjKiVtOa4S4-zBEnXWOoN8a6-FhNxKbSgovw-koOg,424
84
+ kvirt/cluster/kubeadm/keepalived.sh,sha256=D4oRKdrjHPERIFc3N8y75vWoDyLeNj1xk1WCVFEpm8Y,470
85
85
  kvirt/cluster/kubeadm/nfs.sh,sha256=g_8yPIpKedLwiXJ9FkMC2R6pCANSMX8SVWk-WDML1vY,1769
86
86
  kvirt/cluster/kubeadm/nfs.yml,sha256=js0KtTYqI1dtnc018wBXefyVPsCVn_sSneShMCJWUe4,226
87
87
  kvirt/cluster/kubeadm/pre_el.sh,sha256=5x1yZ_05EAJVE2TYLm_4beNX-Cc57QNUFuqpbS6tN78,2008
@@ -146,7 +146,7 @@ kvirt/cluster/kubeadm/disconnected/scripts/05_web.sh,sha256=Rx8HcXlZUS4BAGZaz9Xz
146
146
  kvirt/cluster/kubeadm/disconnected/scripts/deploy.sh,sha256=rN9whVEGPzAc-3WwQjLF0tSb976bsXv8ol1lSCD709s,593
147
147
  kvirt/cluster/kubernetes/__init__.py,sha256=3-Y_L5eSKmPjorrpjyhyaKT-eyB9KmbF80y2zdONCls,9287
148
148
  kvirt/cluster/microshift/__init__.py,sha256=4yzu0ZNyczYomhxy23vWgVJBr88y4OYmkSq915NA_OY,4319
149
- kvirt/cluster/microshift/kcli_default.yml,sha256=-zFf6SfiXDZihhjaNmuuiFlKfuVKUpadyMhTIwPoIdg,635
149
+ kvirt/cluster/microshift/kcli_default.yml,sha256=lEgp7QcerTmRc0V-rsGBNj0AVAbjadd3atNmBRpZFEE,635
150
150
  kvirt/cluster/microshift/kcli_plan.yml,sha256=DKqw0smtF0eBRYegUbXFgEH1RYihRRq-P9-_T3cZsJg,571
151
151
  kvirt/cluster/microshift/scripts/00_sslip.sh,sha256=sD2d5PwNGEw5hEr65O8DGJK282cy-TLG_aLWRXLnueE,262
152
152
  kvirt/cluster/microshift/scripts/01_clients.sh,sha256=wsgvmssSfGrH4W9RjcInwkUWr7pORN3JXI8avaq1Fj0,1100
@@ -164,7 +164,7 @@ kvirt/cluster/openshift/99-autologin.yaml,sha256=qkOEEfsuH4S-ptZ6sWY-GnaKzBM_EYt
164
164
  kvirt/cluster/openshift/99-bootstrap-deletion-2.yaml,sha256=dq-jBNqBfuPH0HCXsql3ZP6gOawapYzWr7_TRqMSRZo,616
165
165
  kvirt/cluster/openshift/99-bootstrap-deletion.yaml,sha256=7AdtAtSsrI0OeuNe68eY8QkAEgYJEeClhNxQnl3hVGI,1237
166
166
  kvirt/cluster/openshift/99-chrony.yaml,sha256=NxNx35-nrEBdT83Rw0y8Pl-C4prknGtiGbT6EGkchkM,529
167
- kvirt/cluster/openshift/99-kcli-forcedns,sha256=meMfjHRDP8d0clIkZk6HAMnF615IQZWd3aPps_s4ELc,961
167
+ kvirt/cluster/openshift/99-kcli-forcedns,sha256=WKWiT4De-N9sC5DWSlZ1flWSC9NuNNWHOAJVL5GEHWg,800
168
168
  kvirt/cluster/openshift/99-kcli-forcedns-ibm,sha256=F65o8Au2PmfapeXdS-t-CTURkrVmOvlAA-6xdgsd0bc,476
169
169
  kvirt/cluster/openshift/99-kubevirt-fix.yaml,sha256=A8hyix2InQAkZeD1UEjWS0jCd_Ih8e_2rDKuUaz3Gug,854
170
170
  kvirt/cluster/openshift/99-metal3-fake-machine.yaml,sha256=Bm1h00oD0PACNHk1QQJf-kfHQHD7f4VOkGRCQ9rfSUI,250
@@ -176,7 +176,7 @@ kvirt/cluster/openshift/99-ovn.yaml,sha256=cdEJWIVdFsaYLWtp5_v3TSI0sFTzYZvo2Euaj
176
176
  kvirt/cluster/openshift/99-prega-catalog.yaml,sha256=kRRCkq7joALdpeZlOL4zXvtZod6XRnk_mBgE7rTWxLA,313
177
177
  kvirt/cluster/openshift/99-sno.yaml,sha256=JFxdhvChu1o29fOzDyfuU-jtcfcLXd7G7YAt8y9HGU8,488
178
178
  kvirt/cluster/openshift/Corefile,sha256=lSF1OmirH2vyxO9qNItWzYLp-sLYF4z5liAX6LjTRkw,792
179
- kvirt/cluster/openshift/__init__.py,sha256=MxtsiusWmbz64NV4nfn5jbP2Ui_PdBR06X2Nrdg1u6M,96573
179
+ kvirt/cluster/openshift/__init__.py,sha256=AcNBFIcxSyo9eYSsjZFUtPLw0nzr9ZzfdtG09fZY7Kg,97298
180
180
  kvirt/cluster/openshift/autoapprovercron.yml,sha256=vR2IRwjNpu4rQDy3H_EDSt3Is0o7A7CIo_zbqcEv8ww,1324
181
181
  kvirt/cluster/openshift/autorules.yml,sha256=j0WEdUDUDbQrnMGEZXFRe3vNz2JQLNaDr_EjaucgAu8,985
182
182
  kvirt/cluster/openshift/bgp-vip.sh,sha256=e70UJTxb7H_mWK1Zi7ofHV2ISqWZYfpOG0tArtn8Dvo,459
@@ -265,7 +265,7 @@ kvirt/cluster/openshift/apps/odf-operator/kcli_default.yml,sha256=E_T0swimoydU9Z
265
265
  kvirt/cluster/openshift/apps/odf-operator/nad_cluster.yml,sha256=JwiKY5mtLG_oEQZfu_9G0lwnbmJsWe4UHzztnmBLRuI,364
266
266
  kvirt/cluster/openshift/apps/odf-operator/nad_public.yml,sha256=UqZXcXfOpbpbSBiWYHfFgKnTsXiHbCDX-DGMpsy82fE,362
267
267
  kvirt/cluster/openshift/apps/odf-operator/post.sh,sha256=oh_5uyRBCIFXWj6D9MDANEMIJmnbCY_NEHc4d033Jt0,745
268
- kvirt/cluster/openshift/apps/odf-operator/pre.sh,sha256=o3cToaC84-NGmnT3vZcdiaK75EGKLGWKJgBv5kAmwgU,755
268
+ kvirt/cluster/openshift/apps/odf-operator/pre.sh,sha256=ZhBugAuWHM3mv5J5IHXRKYYolGjjHWytkFME-I-Nq1w,789
269
269
  kvirt/cluster/openshift/apps/serverless-operator/cr.yml,sha256=7afGUDFUYzGpsd5LeYYR_5ylMI5O2pd8g68CoHdSmJo,363
270
270
  kvirt/cluster/openshift/apps/serverless-operator/kcli_default.yml,sha256=9UrEQnGv9vNLsyXYetR_VGCylGPnYFsvF4oxnRKCd8E,157
271
271
  kvirt/cluster/openshift/apps/serverless-operator/post.sh,sha256=t4vvARtQKie9WY1dsjFVCT4sf_4xsJu31qgdmB1O_HE,278
@@ -285,11 +285,11 @@ kvirt/cluster/openshift/customisation/99-iptables.yaml,sha256=Ky_607MS_UEuTrRW65
285
285
  kvirt/cluster/openshift/customisation/99-monitoring.yaml,sha256=baO_U_-tLG2w_QmBsnarLe58m3v3KW2CWoFg0CsdR5Y,184
286
286
  kvirt/cluster/openshift/customisation/99-registry.yaml,sha256=dgzvUvHbuboyQDgyjpIpZn3I_rSPZG56YmwnIAJ9wzs,289
287
287
  kvirt/cluster/openshift/disconnected/haproxy.cfg,sha256=UJLex4syWzKSmg4KUrskCCO-OV28igksgAv8-EVOWi0,1364
288
- kvirt/cluster/openshift/disconnected/mirror-config.yaml,sha256=bjcPl3uadIEx7JaWHWkV_Xek3upLxZRI0_G5SGdOg7k,5745
288
+ kvirt/cluster/openshift/disconnected/mirror-config.yaml,sha256=ImEEsA-gnECwXiLFDXhCNrByG_mj_4WRUXHjY4dBqA0,5723
289
289
  kvirt/cluster/openshift/disconnected/registry.service,sha256=Mc0KDlmifNt_SbTGVUj3QLtKZs6LPIk-GOtVjZkyQHY,211
290
290
  kvirt/cluster/openshift/disconnected/bin/sync_image.sh,sha256=U2Q28q3aO8B4PHN8AzFg_rJZZAnLPWCaZfJX_3yQvLs,328
291
- kvirt/cluster/openshift/disconnected/scripts/01_packages.sh,sha256=OGx16d343Q_HIkBu0kGjbwMCSnlLwheROAeHgtXuwbE,394
292
- kvirt/cluster/openshift/disconnected/scripts/02_registry.sh,sha256=tVaue72Tr71jVzTdcgyam0CqhMUQs-rqEuuJLJ6SYAU,2084
291
+ kvirt/cluster/openshift/disconnected/scripts/01_packages.sh,sha256=sjGySdrNZofXseslvvKzTSVRBtaRGQxMKpROIrLJfWI,404
292
+ kvirt/cluster/openshift/disconnected/scripts/02_registry.sh,sha256=EEBfy3NrLuuQ6NCjnWEoBEvIRL0xC-NmTUp-wQJdVmg,2099
293
293
  kvirt/cluster/openshift/disconnected/scripts/03_mirror.sh,sha256=F_a_ZWS-ulBO2wQOqSrfcPHFMa_4_GjBwf_bBspfMbo,3472
294
294
  kvirt/cluster/openshift/disconnected/scripts/04_web.sh,sha256=CYwycblPOu5f4hdAe83ZTde2ZeWkGb8CnNPR95SKy_U,197
295
295
  kvirt/cluster/openshift/disconnected/scripts/deploy.sh,sha256=fTRiycD9xQQAPTExH3kD3jqmwha_rA_nBEdjeRnIXmk,491
@@ -315,7 +315,7 @@ kvirt/cluster/rke2/kcli_default.yml,sha256=CgX1rl9bGwts6on784Uy8zUb-XkldzTZJ990P
315
315
  kvirt/cluster/rke2/keepalived.conf,sha256=Sr_aHMyb5LNoWW3rtsvsRP0HurRiwfx9AS3LMZyuZHc,588
316
316
  kvirt/cluster/rke2/keepalived.sh,sha256=QMLjKiVtOa4S4-zBEnXWOoN8a6-FhNxKbSgovw-koOg,424
317
317
  kvirt/cluster/rke2/workers.yml,sha256=YTnQ9j0xqCeyaT7Kcbl_vXYrL6ps74nIMljUKb2vah4,636
318
- kvirt/common/__init__.py,sha256=hpGgrgk2FrrqxpL54ww1DFvJKs8VcwNfQvfBwp1hNFY,112614
318
+ kvirt/common/__init__.py,sha256=07myuqh8f7FOmHz1T6uesWgUaWHZA-1ZzyGGvANpCEc,112907
319
319
  kvirt/common/autoscale.yaml.j2,sha256=fCKt-ooHIr0oeAIZWQLlIcVJD0msW5A7BkNyVbO6Dj0,1406
320
320
  kvirt/common/fake_kubeconfig.json,sha256=XxFI3MSxrYbA6-4U8W5vSVFxrQmOeTOxAVzc7e1ngNc,8945
321
321
  kvirt/common/ignition.j2,sha256=eZN_uM9itflZCvrakYU25HFdQLdLxqZAN9pEzYmEDCE,669
@@ -356,7 +356,7 @@ kvirt/extra_keywords/all.yaml,sha256=MtG_qciWtQwtlFE6o28xd7GExm8iD24Xz71nBZRbbvw
356
356
  kvirt/extra_keywords/aws.yaml,sha256=QnoMc1_Hrxm8LX7M40EzFwD2l-4bmvb2_7LtB2ubsUM,60
357
357
  kvirt/extra_keywords/gcp.yaml,sha256=1DxpN-zQd3m2nOHqny10WXKHDzKbhDrUPqUZCYQJRlw,301
358
358
  kvirt/extra_keywords/kubevirt.yaml,sha256=2R7BJAJigQv9KGSEuRVzpWWMoehAcIV4KEP4_lclaDo,568
359
- kvirt/extra_keywords/kvm.yaml,sha256=vWYLKYls8ylPGz6of6MRxVK8A7t74OUdDqNfkS0hIvE,977
359
+ kvirt/extra_keywords/kvm.yaml,sha256=8oODiEo2S5tEa3zR-VaF9wDoUtYyU8tquLto52Z01Wc,1028
360
360
  kvirt/extra_keywords/proxmox.yaml,sha256=p3EdyOhWpLYdsbNe-Tb_1PqO76YbDN9hiSTZmwwNIcc,176
361
361
  kvirt/extra_keywords/vsphere.yml,sha256=YifpEVseWQWaJ7mBsmYYB-KE3aCK5xiztQOs9gW6SQg,334
362
362
  kvirt/internalplans/__init__.py,sha256=mYT93wrouDx2RY_c9iqv4ECuRule05fWDhsj-2eZUf8,1642
@@ -390,12 +390,12 @@ kvirt/providers/fake/__init__.py,sha256=TYJe0iwO0Y7QrksKx4E63-Lxgc6WYNcjAHAq_Lwh
390
390
  kvirt/providers/gcp/__init__.py,sha256=_8qln7pO0_3MSmZ1P4X-THlTbbwFuR_a4wVCCeL0WSI,102731
391
391
  kvirt/providers/gcp/gcp-hack.service,sha256=gG35O6KTLlGqIMwrSBqb_id-h3SWm_us_yLkLfQelOw,175
392
392
  kvirt/providers/gcp/gcp-hack.sh,sha256=kH051zNphRtJe-eVi8CX28_mxP6ID1f3pSTDzvE9XzE,220
393
- kvirt/providers/hcloud/__init__.py,sha256=CPf6L2pEMIGnAVVdt4fczjj2tlJmSkL0eSLoqC9-biE,22728
393
+ kvirt/providers/hcloud/__init__.py,sha256=AlR8_vSZvKJfDLbPnYtkVS3X7i-l_j7_ii7I15zqODY,23708
394
394
  kvirt/providers/ibm/__init__.py,sha256=OTGDRCwvvOZCTNGD_QXx52-9_tsBzA7Y079RAo9XL9M,66129
395
- kvirt/providers/kubevirt/__init__.py,sha256=x65aC-YxsjIVNuUxkvBcbvvMJbm8za7XNg2huogel1I,94583
396
- kvirt/providers/kvm/__init__.py,sha256=p1pTEUXOW1n5Qcj4qfQduGCVXYGd_PyhDuMVPknWMXs,195388
395
+ kvirt/providers/kubevirt/__init__.py,sha256=NSnx2RwtRQSrrhpqLK8Fb_ye9YgJdMbkO-_wBFd7DZg,94602
396
+ kvirt/providers/kvm/__init__.py,sha256=6AKRejZpYp9lBqN6w3v8-12X7E-ANIl32Xd0OL14Pl0,195669
397
397
  kvirt/providers/kvm/helpers.py,sha256=t2CLczaU-gWPXL6iglK2Mlu-85ErH59nkdSA50v18lM,1965
398
- kvirt/providers/openstack/__init__.py,sha256=T8gGmgzF0JlzsHUHEKCOUGHW5KWfFPXig6lOvYeGeUU,60117
398
+ kvirt/providers/openstack/__init__.py,sha256=-2ZiCQGBdRps6B30Ct2JdN-IZHRfHpRqmy6jado0QOM,60989
399
399
  kvirt/providers/ovirt/__init__.py,sha256=2qVEM3JsFnNm2gvTLRNdru5GZBwnQ7AzYSXyPdDGlT8,64383
400
400
  kvirt/providers/proxmox/__init__.py,sha256=5IUzNbPXPOMhYCNQkvGgxfo-OdHHdWg9JW3wdYo3nco,46960
401
401
  kvirt/providers/vsphere/__init__.py,sha256=g-twblPaKCCgdujThLp9QJbjNusxIQNS39l9E5BpVSg,86248
@@ -403,7 +403,7 @@ kvirt/providers/vsphere/helpers.py,sha256=eJnwVGdWArTurH5ckzScvLThNJaMWeakdxBbDT
403
403
  kvirt/providers/vsphere/tagging.py,sha256=7RqFIUETSzHAjS0F_8-j145-_8lca7aDUsCxs4DhQVQ,4893
404
404
  kvirt/providers/web/__init__.py,sha256=_9fv0_x5VomFrMZqTsMLHkIRkvujmFgTxggtRctvMD0,22053
405
405
  kvirt/version/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
406
- kvirt/version/git,sha256=kxK9FrbraOmb9QCAANMOPZoCvdyItpFRo5PbnfVO0OE,19
406
+ kvirt/version/git,sha256=WgkadiGg9-33RFgc6KHjELSQWLUtPXBrokGF8jw87fY,19
407
407
  kvirt/web/__init__.py,sha256=QW-FVaQGYogXwgV2g4f0J9s0QqUI6qIyX1ruRaiI8Yk,46658
408
408
  kvirt/web/main.py,sha256=DIb6tW3ce7wCc83kD9HBCG4ALJuLItp9uaP6tzdsm_Y,261
409
409
  kvirt/web/static/css/bootstrap-notify.css,sha256=ibUTW-jDj-F8d1T1KZ4DOujRmVTFfvMKL9y14QgEaPQ,408
@@ -645,8 +645,8 @@ kvirt/web/templates/vmprofiles.html,sha256=T65Cv7povhBtD2dyuWkWJE6byaA9EYtLSk5gt
645
645
  kvirt/web/templates/vmprofilestable.html,sha256=-8viNOqSvN_yzimENxq7Q0187zbflOn8p8zpgx0Vyek,1420
646
646
  kvirt/web/templates/vms.html,sha256=k2ptKeiLFdpzgfbk6xi__sduu0UALYXXrnrmDEmAfaQ,470
647
647
  kvirt/web/templates/vmstable.html,sha256=ikuYkIm_ocvGIBXFyJt1-tBjwQWCyqkgC08leoXgR54,4894
648
- kcli-99.0.202510241005.dist-info/METADATA,sha256=uW2jM6T_t6x5imtsP88PJUQy3sds7WYrBEp1AFwAogk,4645
649
- kcli-99.0.202510241005.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
650
- kcli-99.0.202510241005.dist-info/entry_points.txt,sha256=i2SGYxlDWWeD6Nn0QBRT2kZ7uA4pmvVU5CunMeQ3zfA,207
651
- kcli-99.0.202510241005.dist-info/top_level.txt,sha256=FwVTxtMIWdr8QBk5mTfd0f_GheFlRR_vn4zl6p5N57s,6
652
- kcli-99.0.202510241005.dist-info/RECORD,,
648
+ kcli-99.0.202601080644.dist-info/METADATA,sha256=AnADRLBJX-2OAwO_QCy3Ms_zX8BZwm1W7wXV9gF1PYo,4645
649
+ kcli-99.0.202601080644.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
650
+ kcli-99.0.202601080644.dist-info/entry_points.txt,sha256=e5EB5e2acrwl3_sYA0GrjE42UMcMfiijMJqXb6TZRmQ,245
651
+ kcli-99.0.202601080644.dist-info/top_level.txt,sha256=FwVTxtMIWdr8QBk5mTfd0f_GheFlRR_vn4zl6p5N57s,6
652
+ kcli-99.0.202601080644.dist-info/RECORD,,
@@ -2,7 +2,8 @@
2
2
  ekstoken = kvirt.ekstoken:cli
3
3
  gketoken = kvirt.gketoken:cli
4
4
  kcli = kvirt.cli:cli
5
+ kcliagent = kvirt.agent.agent:main
6
+ kclimcp = kvirt.kmcp:main
5
7
  klist.py = kvirt.klist:main
6
- kmcp = kvirt.kmcp:main
7
8
  ksushy = kvirt.ksushy.main:run
8
9
  kweb = kvirt.web.main:run
@@ -2,7 +2,7 @@ PKGMGR="{{ 'apt-get' if ubuntu else 'dnf' }}"
2
2
  $PKGMGR -y install keepalived
3
3
  NETMASK=$(ip -o -f inet addr show | awk '/scope global/ {print $4}' | head -1 | cut -d'/' -f2)
4
4
  sed -i "s/NETMASK/$NETMASK/" /root/keepalived.conf
5
- NIC=$(find /sys/class/net -type l -not -lname '*virtual*' -printf '%f\n' | head -1)
5
+ NIC=$(awk '/default/ {for(i=1;i<=NF;i++) if($i=="dev") {print $(i+1); exit}}' <(ip route show default; ip -6 route show default))
6
6
  sed -i "s/NIC/$NIC/" /root/keepalived.conf
7
7
  cp /root/keepalived.conf /etc/keepalived
8
8
  systemctl enable --now keepalived
@@ -5,7 +5,7 @@ info: |
5
5
  cluster: mymicroshift
6
6
  domain: karmalabs.corp
7
7
  version: stable
8
- tag: '4.16'
8
+ tag: '4.20'
9
9
  image: rhel9
10
10
  network: default
11
11
  memory: 4096
@@ -1,21 +1,18 @@
1
1
  #!/bin/bash
2
2
 
3
- which crictl >/dev/null 2>&1
3
+ CLUSTER={{ cluster }}
4
+ DOMAIN={{ domain }}
5
+
6
+ [ "$(hostname -s)" == "{{ cluster }}-bootstrap" ] || which crictl >/dev/null 2>&1
4
7
  if [ "$?" != "0" ] ; then
5
8
  exit 0
6
9
  fi
7
10
 
8
- if [ "$2" = "dhcp4-change" ] || [ "$2" = "dhcp6-change" ] || [ "$2" = "up" ] || [ "$2" = "connectivity-change" ]; then
11
+ if [ "$(grep '# kcli' /etc/resolv.conf)" == "" ] ; then
9
12
  sleep 2
10
13
  NIC={{ "$(ip -6 r | grep -v lo | head -1 | grep -oP '(?<=dev )[^ ]*')" if ipv6 else "$(ip r | grep default | head -1 | grep -oP '(?<=dev )[^ ]*')" }}
11
14
  IP={{ "$(ip -o -f inet6 addr show $NIC | head -1 | grep -oP '(?<=inet6 )[^ ]*' | cut -d '/' -f 1)" if ipv6 else "$(ip -o -f inet addr show $NIC | head -1 | grep -oP '(?<=inet )[^ ]*' | cut -d '/' -f 1)" }}
12
- if [ "$IP" != "" ] ; then
13
- grep -q $IP /etc/resolv.conf
14
- if [ "$?" != "0" ] ; then
15
- sed -i 's/{{ cluster }}.{{ domain }}//' /etc/resolv.conf
16
- sed -i 's/search /search {{ cluster }}.{{ domain }} /' /etc/resolv.conf
17
- sed -i "/nameserver.* #coredns/d" /etc/resolv.conf
18
- sed -i "0,/nameserver/s/nameserver/nameserver $IP #coredns\n&/" /etc/resolv.conf
19
- fi
20
- fi
15
+ sed -i "s/$CLUSTER.$DOMAIN//" /etc/resolv.conf
16
+ sed -i "s/search /search $CLUSTER.$DOMAIN /" /etc/resolv.conf
17
+ sed -i "0,/nameserver/s/nameserver/nameserver $IP # kcli\n&/" /etc/resolv.conf
21
18
  fi
@@ -11,6 +11,7 @@ from kvirt.common import ssh, scp, _ssh_credentials, get_ssh_pub_key
11
11
  from kvirt.common import start_baremetal_hosts_with_iso, update_baremetal_hosts, get_new_vip, process_postscripts
12
12
  from kvirt.defaults import LOCAL_OPENSHIFT_APPS, OPENSHIFT_TAG
13
13
  import os
14
+ import platform
14
15
  import re
15
16
  from random import choice
16
17
  from shutil import copy2, move, rmtree, which
@@ -336,16 +337,18 @@ def get_downstream_installer(version='stable', macosx=False, tag=None, debug=Fal
336
337
 
337
338
 
338
339
  def get_okd_installer(tag, version='stable', debug=False):
339
- if version == 'stable' and str(tag).count('.') == 1:
340
- tag = f'quay.io/okd/scos-release:{tag}.0-okd-scos.1'
341
- elif 'quay.io' not in str(tag) and 'registry.ci.openshift.org' not in str(tag):
342
- if version == 'candidate':
343
- url = "https://amd64.origin.releases.ci.openshift.org/api/v1/releasestream/4-scos-next/latest"
344
- elif version in ['ci', 'nightly']:
345
- url = f"https://amd64.origin.releases.ci.openshift.org/api/v1/releasestream/{tag}.0-0.okd-scos/latest"
346
- else:
347
- url = "https://amd64.origin.releases.ci.openshift.org/api/v1/releasestream/4-scos-stable/latest"
340
+ if version == 'candidate':
341
+ url = "https://amd64.origin.releases.ci.openshift.org/api/v1/releasestream/4-scos-next/latest"
342
+ tag = json.loads(urlopen(url).read())['pullSpec']
343
+ elif version in ['ci', 'nightly']:
344
+ url = f"https://amd64.origin.releases.ci.openshift.org/api/v1/releasestream/{tag}.0-0.okd-scos/latest"
348
345
  tag = json.loads(urlopen(url).read())['pullSpec']
346
+ else:
347
+ url = 'https://quay.io/api/v1/repository/okd/scos-release/tag'
348
+ for t in json.loads(urlopen(url).read())["tags"]:
349
+ if tag in t["name"] and ".ec." not in t["name"]:
350
+ tag = f'quay.io/okd/scos-release:{t["name"]}'
351
+ break
349
352
  cmd = f"oc adm release extract --command=openshift-install --to . {tag}"
350
353
  cmd += "; chmod 700 openshift-install"
351
354
  pprint(f'Downloading openshift-install {tag} in current directory')
@@ -786,7 +789,7 @@ def create(config, plandir, cluster, overrides, dnsconfig=None):
786
789
  if os.path.exists('coreos-installer'):
787
790
  pprint("Removing old coreos-installer")
788
791
  os.remove('coreos-installer')
789
- if version not in ['ci', 'candidate', 'nightly', 'stable']:
792
+ if version not in ['ci', 'candidate', 'latest', 'nightly', 'stable']:
790
793
  return {'result': 'failure', 'reason': f"Incorrect version {version}"}
791
794
  else:
792
795
  pprint(f"Using {version} version")
@@ -938,13 +941,19 @@ def create(config, plandir, cluster, overrides, dnsconfig=None):
938
941
  tag = f'registry.ci.openshift.org/{basetag}/release:{tag}'
939
942
  which_openshift = which('openshift-install')
940
943
  openshift_dir = os.path.dirname(which_openshift) if which_openshift is not None else '.'
941
- if which_openshift is not None and not has_internet():
942
- pprint("Using existing openshift-install found in your PATH")
943
- warning("Not checking version")
944
- elif okd:
945
- run = get_okd_installer(tag, version=version)
946
- elif not same_release_images(version=version, tag=tag, pull_secret=pull_secret, path=openshift_dir):
947
- if version in ['ci', 'nightly'] or '/' in str(tag):
944
+ if which_openshift is None:
945
+ download = True
946
+ elif not has_internet:
947
+ pprint("Using existing openshift-install found in your PATH and skipping version check")
948
+ download = False
949
+ else:
950
+ download = not same_release_images(version=version, tag=tag, pull_secret=pull_secret, path=openshift_dir)
951
+ if download:
952
+ pprint("Redownloading openshift-install as found version doesn't match requirements")
953
+ if download:
954
+ if okd:
955
+ run = get_okd_installer(tag, version=version)
956
+ elif version in ['ci', 'nightly'] or '/' in str(tag):
948
957
  nightly = version == 'nightly'
949
958
  run = get_ci_installer(pull_secret, tag=tag, nightly=nightly)
950
959
  elif version in ['candidate', 'stable', 'latest']:
@@ -954,10 +963,6 @@ def create(config, plandir, cluster, overrides, dnsconfig=None):
954
963
  if run != 0:
955
964
  return {'result': 'failure', 'reason': "Couldn't download openshift-install"}
956
965
  pprint("Move downloaded openshift-install somewhere in your PATH if you want to reuse it")
957
- elif which_openshift is not None:
958
- pprint("Using existing openshift-install found in your PATH")
959
- else:
960
- pprint("Reusing matching openshift-install")
961
966
  os.environ["PATH"] = f'{os.getcwd()}:{os.environ["PATH"]}'
962
967
  INSTALLER_VERSION = get_installer_version()
963
968
  pprint(f"Using installer version {INSTALLER_VERSION}")
@@ -980,6 +985,10 @@ def create(config, plandir, cluster, overrides, dnsconfig=None):
980
985
  cacmd = f"openssl s_client -showcerts -connect {disconnected_url} </dev/null 2>/dev/null|"
981
986
  cacmd += "openssl x509 -outform PEM"
982
987
  data['ca'] = os.popen(cacmd).read()
988
+ kcli_images = ["curl:multi", "origin-coredns:multi", "haproxy:multi", "origin-keepalived-ipfailover:multi",
989
+ "mdns-publisher:multi", "kubectl:multi"]
990
+ kcli_images = '\n'.join([f'quay.io/karmab/{image}' for image in kcli_images])
991
+ warning(f"Make sure to push the following images in your registry: {kcli_images}")
983
992
  if sno:
984
993
  pass
985
994
  elif image is None:
@@ -1020,6 +1029,10 @@ def create(config, plandir, cluster, overrides, dnsconfig=None):
1020
1029
  if not images:
1021
1030
  msg = f"Missing {image}. Indicate correct image in your parameters file..."
1022
1031
  return {'result': 'failure', 'reason': msg}
1032
+ if provider in virt_providers and platform.machine() != arch:
1033
+ pprint(f"Forcing release image to {arch}")
1034
+ base_url = 'quay.io/okd/scos-release' if okd else 'quay.io/openshift-release-dev'
1035
+ os.environ['OPENSHIFT_INSTALL_RELEASE_IMAGE_OVERRIDE'] = f'{base_url}:{INSTALLER_VERSION}-{arch}'
1023
1036
  overrides['image'] = image
1024
1037
  static_networking_ctlplane, static_networking_worker = False, False
1025
1038
  macentries = []
@@ -1,3 +1,4 @@
1
+ {{ "storagecluster" | wait_crd }}
1
2
  {% if odf_nodes %}
2
3
  {% set nodes = odf_nodes %}
3
4
  {% elif localstorage_nodes is defined %}
@@ -13,18 +13,15 @@ mirror:
13
13
  platform:
14
14
  graph: false
15
15
  {% if version in ['ci', 'nightly'] %}
16
- {% set release = 'origin/release-scos:scos-%s' % tag if okd else 'registry.ci.openshift.org/ocp/release:%s' % tag %}
16
+ {% set release = 'origin/release-scos:scos-%s' % tag if okd else 'ocp/release:%s' % tag %}
17
17
  release: registry.ci.openshift.org/{{ release }}
18
18
  {% else %}
19
19
  channels:
20
20
  - name: {{ disconnected_channel or ('candidate' if 'rc' in tag else 'stable') }}-{{ release }}
21
- {% if tag|count('.') > 1 %}
22
- minVersion: {{ (disconnected_extra_release or tag)|min_ocp_version(tag) }}
23
- maxVersion: {{ (disconnected_extra_release or tag)|max_ocp_version(tag) }}
24
- {% endif %}
21
+ minVersion: {{ (disconnected_extra_release or openshift_version)|min_ocp_version(openshift_version) }}
22
+ maxVersion: {{ (disconnected_extra_release or openshift_version)|max_ocp_version(openshift_version) }}
25
23
  {% endif %}
26
24
  architectures:
27
- # - multi
28
25
  - amd64
29
26
  additionalImages:
30
27
  {% for image in kcli_images + extra_images|default([]) %}
@@ -1,9 +1,9 @@
1
1
  if [ "$(which dnf)" != "" ] ; then
2
2
  echo "fastestmirror=1" >> /etc/dnf/dnf.conf
3
- dnf -y install podman httpd httpd-tools jq skopeo libseccomp-devel haproxy
3
+ dnf -y install podman httpd httpd-tools jq skopeo libseccomp-devel haproxy runc
4
4
  else
5
5
  apt-get update
6
- apt-get -y install podman apache2 apache2-utils jq skopeo haproxy
6
+ apt-get -y install podman apache2 apache2-utils jq skopeo haproxy runc
7
7
  fi
8
8
 
9
9
  curl https://mirror.openshift.com/pub/openshift-v4/x86_64/clients/ocp/stable/openshift-client-linux.tar.gz | tar zxf - oc
@@ -45,6 +45,6 @@ else
45
45
  update-ca-certificates
46
46
  fi
47
47
  htpasswd -bBc /opt/registry/auth/htpasswd $REGISTRY_USER $REGISTRY_PASSWORD
48
- podman create --name registry --net host --security-opt label=disable -v /opt/registry/data:/var/lib/registry:z -v /opt/registry/auth:/auth:z -v /opt/registry/conf/config.yml:/etc/docker/registry/config.yml -e "REGISTRY_AUTH=htpasswd" -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry" -e "REGISTRY_HTTP_SECRET=ALongRandomSecretForRegistry" -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd -v /opt/registry/certs:/certs:z -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key $REGISTRY_IMAGE
48
+ podman create --name registry --net host --runtime=runc --security-opt label=disable -v /opt/registry/data:/var/lib/registry:z -v /opt/registry/auth:/auth:z -v /opt/registry/conf/config.yml:/etc/docker/registry/config.yml -e "REGISTRY_AUTH=htpasswd" -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry" -e "REGISTRY_HTTP_SECRET=ALongRandomSecretForRegistry" -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd -v /opt/registry/certs:/certs:z -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key $REGISTRY_IMAGE
49
49
  [ "$?" == "0" ] || !!
50
50
  systemctl enable --now registry
kvirt/common/__init__.py CHANGED
@@ -30,6 +30,8 @@ from tempfile import TemporaryDirectory
30
30
  from time import sleep
31
31
  import yaml
32
32
 
33
+ _original_construct_mapping = yaml.SafeLoader.construct_mapping
34
+
33
35
 
34
36
  class NoAliasDumper(yaml.SafeDumper):
35
37
  def ignore_aliases(self, data):
@@ -1582,7 +1584,7 @@ def is_debian9(image):
1582
1584
 
1583
1585
 
1584
1586
  def is_debian_new(image):
1585
- return 'debian10' in image.lower() or 'debian12' in image.lower()
1587
+ return 'debian' in image.lower() and image.lower() != 'debian9' and image.lower() != 'debian8'
1586
1588
 
1587
1589
 
1588
1590
  def is_ubuntu(image):
@@ -2522,7 +2524,11 @@ def plan_constructor(loader, node, deep=False):
2522
2524
  for key_node, value_node in node.value:
2523
2525
  key = loader.construct_object(key_node, deep=deep)
2524
2526
  value = loader.construct_object(value_node, deep=deep)
2525
- if isinstance(value, dict):
2527
+ if key == "vmrules":
2528
+ value = [_original_construct_mapping(loader, item_node, deep=deep) for item_node in value_node.value]
2529
+ mapping[key] = value
2530
+ continue
2531
+ elif isinstance(value, dict):
2526
2532
  _type = value.get('type', 'vm')
2527
2533
  if key == 'parameters':
2528
2534
  mapping[key] = value
kvirt/config.py CHANGED
@@ -135,7 +135,7 @@ class Kconfig(Kbaseconfig):
135
135
  except Exception as e:
136
136
  exception = e if debug else None
137
137
  dependency_error('hcloud', exception)
138
- k = Khcloud(api_key=apikey, location=location)
138
+ k = Khcloud(api_key=apikey, location=location, debug=debug)
139
139
  elif self.type == 'azure':
140
140
  try:
141
141
  from kvirt.providers.azure import Kazure
@@ -1067,8 +1067,11 @@ class Kconfig(Kbaseconfig):
1067
1067
  if index < len(disks):
1068
1068
  disk = disks[index]
1069
1069
  currentdisksize = currentdisk['size']
1070
- disksize = disk.get('size', 10) if isinstance(disk, dict) else int(disk)
1071
- if disksize > currentdisksize:
1070
+ disksize = disk.get('size', 10) if isinstance(disk, dict) else int(disk) if disk.isdigit() else None
1071
+ if disksize is None:
1072
+ warning(f"Invalid Disk {index} value {disk} in {name}")
1073
+ break
1074
+ elif disksize > currentdisksize:
1072
1075
  if currentvm.get('status') != 'down':
1073
1076
  warning(f"Cant resize Disk {index} in {name} while VM is up")
1074
1077
  break
@@ -2553,6 +2556,8 @@ class Kconfig(Kbaseconfig):
2553
2556
  def create_kube(self, cluster, kubetype, overrides={}):
2554
2557
  cwd = os.getcwd()
2555
2558
  cluster = overrides.get('cluster') or cluster or f"my{kubetype}"
2559
+ if 'domain' in overrides and overrides['domain'] == 'local':
2560
+ return {'result': 'failure', 'reason': 'Invalid domain .local'}
2556
2561
  if self.type == 'web' and self.k.localkube:
2557
2562
  return self.k.create_kube(cluster, kubetype, overrides)
2558
2563
  ippool = overrides.get('ippool') or overrides.get('confpool')
kvirt/defaults.py CHANGED
@@ -56,11 +56,13 @@ IMAGES = {'almalinux8': f'{ALMA}/8/cloud/x86_64/images/AlmaLinux-8-GenericCloud-
56
56
  'debian10': f'{DEBIAN}/openstack/current-10/debian-10-openstack-amd64.qcow2',
57
57
  'debian11': f'{DEBIAN}/cloud/bullseye/latest/debian-11-generic-amd64.qcow2',
58
58
  'debian12': f'{DEBIAN}/cloud/bookworm/latest/debian-12-generic-amd64.qcow2',
59
+ 'debian13': f'{DEBIAN}/cloud/trixie/latest/debian-13-generic-amd64.qcow2',
59
60
  'fcos': 'https://builds.coreos.fedoraproject.org/streams/stable.json',
60
61
  'fedora39': f'{FEDORA}/39/Cloud/x86_64/images/Fedora-Cloud-Base-39-1.5.x86_64.qcow2',
61
62
  'fedora40': f'{FEDORA}/40/Cloud/x86_64/images/Fedora-Cloud-Base-Generic.x86_64-40-1.14.qcow2',
62
63
  'fedora41': f'{FEDORA}/41/Cloud/x86_64/images/Fedora-Cloud-Base-Generic-41-1.4.x86_64.qcow2',
63
64
  'fedora42': f'{FEDORA}/42/Cloud/x86_64/images/Fedora-Cloud-Base-Generic-42-1.1.x86_64.qcow2',
65
+ 'fedora43': f'{FEDORA}/43/Cloud/x86_64/images/Fedora-Cloud-Base-Generic-43-1.6.x86_64.qcow2',
64
66
  'fedoralatest': 'https://alt.fedoraproject.org/cloud',
65
67
  'freebsd122': f'{BSD}/freebsd/12.2/freebsd-12.2.qcow2',
66
68
  'freebsd130': f'{BSD}/freebsd/13.0/freebsd-13.0-zfs.qcow2',
@@ -2,6 +2,7 @@ arch: Architecture
2
2
  emulator: Custom emulator
3
3
  extra_iso: Extra Iso to plug to the node
4
4
  floppy: Floppy to plug to the node
5
+ fwzone: firewall zone where to put a given network
5
6
  hugepages: hugepages flag
6
7
  hugepages_1gb: 1gb hugepages flag
7
8
  hugepages_2mb: 2mb hugepages flag
kvirt/kmcp.py CHANGED
@@ -9,6 +9,10 @@ from typing import Optional, List, Dict, Any
9
9
  from urllib.request import urlopen
10
10
 
11
11
 
12
+ def handle_none(values):
13
+ return [None if e is not None and isinstance(e, str) and 'none' in e.lower() else e for e in values]
14
+
15
+
12
16
  def _parse_vms_list(_list, overrides={}):
13
17
  if isinstance(_list, str):
14
18
  print(_list)
@@ -62,6 +66,7 @@ def create_kube(context: Context,
62
66
  disks: List[Dict] = [], nets: List[Dict] = [], sno_vm: bool = False,
63
67
  client: str = None, debug: bool = False, region: str = None,
64
68
  zone: str = None, namespace: str = None, overrides: Dict[str, Any] = {}) -> Dict[str, Any]:
69
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
65
70
  """Create cluster"""
66
71
  if disks:
67
72
  overrides['disk_size'] = disks[0]['size'] if isinstance(disks[0], dict) else disks[0]
@@ -109,6 +114,7 @@ def create_network(context: Context,
109
114
  client: str = None, debug: bool = False, region: str = None,
110
115
  zone: str = None, namespace: str = None, overrides: dict = {}) -> dict:
111
116
  """Create network"""
117
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
112
118
  nodhcp = not dhcp if dhcp is not None else nodhcp
113
119
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
114
120
  k = config.k
@@ -128,6 +134,7 @@ def create_plan(context: Context,
128
134
  client: str = None, debug: bool = False, region: str = None,
129
135
  zone: str = None, namespace: str = None, overrides: dict = {}) -> dict:
130
136
  """Create plan"""
137
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
131
138
  # if container_mode():
132
139
  # inputfile = f"/workdir/{inputfile}"
133
140
  if 'minimum_version' in overrides:
@@ -159,6 +166,7 @@ def create_pool(context: Context,
159
166
  client: str = None, debug: bool = False, region: str = None,
160
167
  zone: str = None, namespace: str = None) -> dict:
161
168
  """Create pool"""
169
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
162
170
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
163
171
  k = config.k
164
172
  if path is None:
@@ -173,6 +181,7 @@ def create_vm(context: Context,
173
181
  client: str = None, debug: bool = False, region: str = None,
174
182
  zone: str = None, namespace: str = None, overrides: dict = {}) -> dict:
175
183
  """Create vm"""
184
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
176
185
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
177
186
  return config.create_vm(name, profile, overrides=overrides)
178
187
 
@@ -183,6 +192,7 @@ def delete_kube(context: Context,
183
192
  client: str = None, debug: bool = False, region: str = None,
184
193
  zone: str = None, namespace: str = None, overrides: dict = {}):
185
194
  """Delete cluster"""
195
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
186
196
  if client is not None:
187
197
  overrides['client'] = client
188
198
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
@@ -197,6 +207,7 @@ def delete_network(context: Context,
197
207
  client: str = None, debug: bool = False, region: str = None,
198
208
  zone: str = None, namespace: str = None) -> Dict[str, Any]:
199
209
  """Delete network"""
210
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
200
211
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
201
212
  k = config.k
202
213
  for name in names:
@@ -212,6 +223,7 @@ def delete_plan(context: Context,
212
223
  client: str = None, debug: bool = False, region: str = None,
213
224
  zone: str = None, namespace: str = None) -> Dict[str, Any]:
214
225
  """Delete plan"""
226
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
215
227
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
216
228
  plans = [p[0] for p in config.list_plans()] if allplans else plans
217
229
  for plan in plans:
@@ -227,6 +239,7 @@ def delete_pool(context: Context,
227
239
  client: str = None, debug: bool = False, region: str = None,
228
240
  zone: str = None, namespace: str = None) -> dict:
229
241
  """Delete pool"""
242
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
230
243
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
231
244
  k = config.k
232
245
  return k.delete_pool(name=pool, full=full)
@@ -238,6 +251,7 @@ def delete_vm(context: Context,
238
251
  client: str = None, debug: bool = False, region: str = None,
239
252
  zone: str = None, namespace: str = None) -> dict:
240
253
  """Delete vm"""
254
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
241
255
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
242
256
  return config.k.delete(vm)
243
257
 
@@ -248,6 +262,7 @@ def info_vm(context: Context,
248
262
  client: str = None, debug: bool = False, region: str = None,
249
263
  zone: str = None, namespace: str = None) -> dict:
250
264
  """Get info of vm"""
265
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
251
266
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
252
267
  return config.k.info(name)
253
268
 
@@ -256,6 +271,8 @@ def info_vm(context: Context,
256
271
  def list_clients(context: Context,
257
272
  client: str = None, debug: bool = False) -> list:
258
273
  """List clients"""
274
+ if client is not None and client.lower() == 'none':
275
+ client = None
259
276
  clientstable = ["Client", "Type", "Enabled", "Current"]
260
277
  baseconfig = Kbaseconfig(client=client, debug=debug)
261
278
  for client in sorted(baseconfig.clients):
@@ -273,6 +290,7 @@ def list_clusters(context: Context,
273
290
  client: str = None, debug: bool = False, region: str = None,
274
291
  zone: str = None, namespace: str = None) -> list:
275
292
  """List clusters"""
293
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
276
294
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
277
295
  return config.k.list_kubes()
278
296
 
@@ -282,6 +300,7 @@ def list_images(context: Context,
282
300
  client: str = None, debug: bool = False, region: str = None,
283
301
  zone: str = None, namespace: str = None) -> list:
284
302
  """List images"""
303
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
285
304
  return Kconfig(client=client).k.volumes()
286
305
 
287
306
 
@@ -290,6 +309,7 @@ def list_networks(context: Context,
290
309
  client: str = None, debug: bool = False, region: str = None,
291
310
  zone: str = None, namespace: str = None) -> dict:
292
311
  """List networks"""
312
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
293
313
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
294
314
  return config.k.list_networks()
295
315
 
@@ -299,6 +319,7 @@ def list_pools(context: Context,
299
319
  client: str = None, debug: bool = False, region: str = None,
300
320
  zone: str = None, namespace: str = None) -> list:
301
321
  """List pools"""
322
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
302
323
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
303
324
  return config.k.list_pools()
304
325
 
@@ -308,6 +329,7 @@ def list_vms(context: Context,
308
329
  client: str = None, debug: bool = False, region: str = None,
309
330
  zone: str = None, namespace: str = None) -> list:
310
331
  """List vms"""
332
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
311
333
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
312
334
  return config.k.list()
313
335
 
@@ -318,6 +340,7 @@ def scale_kube(context: Context,
318
340
  client: str = None, debug: bool = False, region: str = None,
319
341
  zone: str = None, namespace: str = None, overrides: dict = {}) -> dict:
320
342
  """Scale cluster"""
343
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
321
344
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
322
345
  if ctlplanes is not None:
323
346
  overrides['ctlplanes'] = ctlplanes
@@ -332,6 +355,7 @@ def start_plan(context: Context,
332
355
  client: str = None, debug: bool = False, region: str = None,
333
356
  zone: str = None, namespace: str = None) -> Dict[str, Any]:
334
357
  """Start plan"""
358
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
335
359
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
336
360
  for plan in plans:
337
361
  result = config.start_plan(plan)
@@ -346,6 +370,7 @@ def start_vm(context: Context,
346
370
  client: str = None, debug: bool = False, region: str = None,
347
371
  zone: str = None, namespace: str = None) -> dict:
348
372
  """Start vm"""
373
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
349
374
  return Kconfig(client).k.start(name)
350
375
 
351
376
 
@@ -355,6 +380,7 @@ def stop_plan(context: Context,
355
380
  client: str = None, debug: bool = False, region: str = None,
356
381
  zone: str = None, namespace: str = None) -> Dict[str, Any]:
357
382
  """Stop plan"""
383
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
358
384
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
359
385
  for plan in plans:
360
386
  result = config.stop_plan(plan, soft=soft)
@@ -369,6 +395,7 @@ def stop_vm(context: Context,
369
395
  client: str = None, debug: bool = False, region: str = None,
370
396
  zone: str = None, namespace: str = None) -> dict:
371
397
  """Stop vm"""
398
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
372
399
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
373
400
  return config.k.stop(name)
374
401
 
@@ -379,6 +406,7 @@ def create_bucket(context: Context,
379
406
  client: str = None, debug: bool = False, region: str = None,
380
407
  zone: str = None, namespace: str = None):
381
408
  """Create bucket"""
409
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
382
410
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
383
411
  k = config.k
384
412
  for bucket in buckets:
@@ -391,6 +419,7 @@ def create_bucketfile(context: Context,
391
419
  client: str = None, debug: bool = False, region: str = None,
392
420
  zone: str = None, namespace: str = None) -> str:
393
421
  """Create bucketfile in bucket"""
422
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
394
423
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
395
424
  k = config.k
396
425
  return k.upload_to_bucket(bucket, path, temp_url=temp, public=public)
@@ -402,6 +431,7 @@ def create_dns(context: Context,
402
431
  client: str = None, debug: bool = False, region: str = None,
403
432
  zone: str = None, namespace: str = None):
404
433
  """Create dns entry"""
434
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
405
435
  if alias is None:
406
436
  alias = []
407
437
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
@@ -419,6 +449,7 @@ def create_lb(context: Context,
419
449
  client: str = None, debug: bool = False, region: str = None,
420
450
  zone: str = None, namespace: str = None):
421
451
  """Create load balancer"""
452
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
422
453
  if name is None:
423
454
  name = get_random_name().replace('_', '-')
424
455
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
@@ -432,6 +463,7 @@ def create_securitygroup(context: Context,
432
463
  client: str = None, debug: bool = False, region: str = None,
433
464
  zone: str = None, namespace: str = None, overrides: dict = {}) -> dict:
434
465
  """Create security group"""
466
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
435
467
  securitygroup = securitygroup
436
468
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
437
469
  k = config.k
@@ -444,6 +476,7 @@ def delete_bucket(context: Context,
444
476
  client: str = None, debug: bool = False, region: str = None,
445
477
  zone: str = None, namespace: str = None):
446
478
  """Delete bucket"""
479
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
447
480
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
448
481
  k = config.k
449
482
  for bucket in buckets:
@@ -456,6 +489,7 @@ def delete_bucketfile(context: Context,
456
489
  client: str = None, debug: bool = False, region: str = None,
457
490
  zone: str = None, namespace: str = None):
458
491
  """Delete bucketfile from bucket"""
492
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
459
493
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
460
494
  k = config.k
461
495
  k.delete_from_bucket(bucket, path)
@@ -467,6 +501,7 @@ def delete_dns(context: Context,
467
501
  client: str = None, debug: bool = False, region: str = None,
468
502
  zone: str = None, namespace: str = None):
469
503
  """Delete dns entry"""
504
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
470
505
  domain = domain or net
471
506
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
472
507
  k = config.k
@@ -479,6 +514,7 @@ def delete_lb(context: Context,
479
514
  names: list = [], client: str = None, debug: bool = False, region: str = None,
480
515
  zone: str = None, namespace: str = None):
481
516
  """Delete load balancer"""
517
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
482
518
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
483
519
  for name in names:
484
520
  config.delete_loadbalancer(name)
@@ -490,6 +526,7 @@ def delete_securitygroup(context: Context,
490
526
  client: str = None, debug: bool = False, region: str = None,
491
527
  zone: str = None, namespace: str = None):
492
528
  """Delete security group"""
529
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
493
530
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
494
531
  k = config.k
495
532
  for securitygroup in securitygroups:
@@ -502,6 +539,7 @@ def download_bucketfile(context: Context,
502
539
  client: str = None, debug: bool = False, region: str = None,
503
540
  zone: str = None, namespace: str = None):
504
541
  """Download bucketfile from bucket"""
542
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
505
543
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
506
544
  k = config.k
507
545
  k.download_from_bucket(bucket, path)
@@ -513,6 +551,7 @@ def list_bucketfiles(context: Context,
513
551
  client: str = None, debug: bool = False, region: str = None,
514
552
  zone: str = None, namespace: str = None) -> list:
515
553
  """List bucketfiles of bucket"""
554
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
516
555
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
517
556
  k = config.k
518
557
  return k.list_bucketfiles(bucket)
@@ -523,6 +562,7 @@ def list_buckets(context: Context,
523
562
  client: str = None, debug: bool = False, region: str = None,
524
563
  zone: str = None, namespace: str = None) -> list:
525
564
  """List buckets"""
565
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
526
566
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
527
567
  k = config.k
528
568
  return k.list_buckets()
@@ -534,6 +574,7 @@ def list_dns_entries(context: Context,
534
574
  client: str = None, debug: bool = False, region: str = None,
535
575
  zone: str = None, namespace: str = None) -> list:
536
576
  """List dns entries"""
577
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
537
578
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
538
579
  if domain is None:
539
580
  return config.k.list_dns_zones()
@@ -546,6 +587,7 @@ def list_lbs(context: Context,
546
587
  client: str = None, debug: bool = False, region: str = None,
547
588
  zone: str = None, namespace: str = None) -> list:
548
589
  """List load balancers"""
590
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
549
591
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
550
592
  return config.list_loadbalancers()
551
593
 
@@ -556,6 +598,7 @@ def list_securitygroups(context: Context,
556
598
  client: str = None, debug: bool = False, region: str = None,
557
599
  zone: str = None, namespace: str = None) -> list:
558
600
  """List security groups"""
601
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
559
602
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
560
603
  k = config.k
561
604
  return k.list_security_groups(network=network)
@@ -567,6 +610,7 @@ def update_securitygroup(context: Context,
567
610
  client: str = None, debug: bool = False, region: str = None,
568
611
  zone: str = None, namespace: str = None, overrides: dict = {}):
569
612
  """Update security group"""
613
+ client, region, zone, namespace = handle_none([client, region, zone, namespace])
570
614
  config = Kconfig(client=client, debug=debug, region=region, zone=zone, namespace=namespace)
571
615
  result = config.k.update_security_group(name=securitygroup, overrides=overrides)
572
616
  common.handle_response(result, securitygroup, element='SecurityGroup', action='updated')
@@ -15,10 +15,11 @@ import os
15
15
 
16
16
 
17
17
  class Khcloud():
18
- def __init__(self, api_key=None, location=None):
18
+ def __init__(self, api_key=None, location=None, debug=False):
19
19
  self.conn = Client(token=api_key)
20
20
  self.location = self.conn.locations.get_by_name(location)
21
21
  self.machine_flavor_cache = {}
22
+ self.debug = debug
22
23
  return
23
24
 
24
25
  def create(self, name, virttype=None, profile='', flavor=None, plan='kvirt', cpumodel='host-model', cpuflags=[],
@@ -37,22 +38,6 @@ class Khcloud():
37
38
  if len(disks) > 0:
38
39
  disks = disks[1:]
39
40
 
40
- volumeresponses = []
41
- for index, disk in enumerate(disks):
42
- if isinstance(disk, int):
43
- disksize = disk
44
- elif isinstance(disk, str) and disk.isdigit():
45
- disksize = int(disk)
46
- elif isinstance(disk, dict):
47
- disksize = disk.get('size', '10')
48
-
49
- diskname = f"{name}-disk{index}"
50
- volumeresponse = self.conn.volumes.get_by_name(diskname)
51
- if volumeresponse is None:
52
- volumeresponse = self.conn.volumes.create(disksize, diskname, location=self.location,
53
- labels={"kcli-managed": "volume"})
54
- volumeresponses.append(volumeresponse)
55
-
56
41
  if not keys:
57
42
  publickeyfile = get_ssh_pub_key()
58
43
  if publickeyfile is not None:
@@ -113,40 +98,67 @@ class Khcloud():
113
98
 
114
99
  placement_group = response.placement_group
115
100
 
101
+ # If "location_options" is specified use it, otherwise "location", otherwise use self.location
102
+ location_options = overrides.get("location_options", [overrides.get("location", self.location.name)])
116
103
  flavor_options = overrides.get("flavor_options", [flavor] if flavor is not None else [])
117
104
 
118
- for idx, flavor_option in enumerate(flavor_options):
119
- servertype = self.conn.server_types.get_by_name(flavor_option)
120
- if "snapshot_id" in overrides:
121
- hetzner_image = self.conn.images.get_by_id(overrides.get("snapshot_id"))
122
- else:
123
- hetzner_image = self.conn.images.get_by_name_and_architecture(image, servertype.architecture)
124
-
125
- try:
126
- created_vm = self.conn.servers.create(
127
- name=name,
128
- server_type=servertype,
129
- image=hetzner_image,
130
- start_after_create=False,
131
- user_data=userdata,
132
- volumes=[],
133
- ssh_keys=hetzner_ssh_keys,
134
- location=self.location,
135
- public_net=ServerCreatePublicNetwork(enable_ipv4=False, enable_ipv6=False),
136
- labels=labels,
137
- placement_group=placement_group
138
- )
139
-
140
- created_vm.action.wait_until_finished(300)
141
- break
142
- except APIException as e:
143
- if e.code == "resource_unavailable":
144
- msg = f"Could not get server of type '{flavor_option}' in location '{self.location.name}'"
145
- if len(flavor_options) > (idx + 1):
146
- warning(f"{msg}' trying the next configured flavor option.")
105
+ volumeresponses = []
106
+ for location_option in location_options:
107
+ location = self.conn.locations.get_by_name(location_option)
108
+ created_vm = None
109
+ for idx, flavor_option in enumerate(flavor_options):
110
+ if self.debug:
111
+ pprint(f"Trying to create VM {name} in location {location.name} with flavor {flavor_option}")
112
+ servertype = self.conn.server_types.get_by_name(flavor_option)
113
+ if "snapshot_id" in overrides:
114
+ hetzner_image = self.conn.images.get_by_id(overrides.get("snapshot_id"))
115
+ else:
116
+ hetzner_image = self.conn.images.get_by_name_and_architecture(image, servertype.architecture)
117
+
118
+ try:
119
+ created_vm = self.conn.servers.create(
120
+ name=name,
121
+ server_type=servertype,
122
+ image=hetzner_image,
123
+ start_after_create=False,
124
+ user_data=userdata,
125
+ volumes=[],
126
+ ssh_keys=hetzner_ssh_keys,
127
+ location=location,
128
+ public_net=ServerCreatePublicNetwork(enable_ipv4=False, enable_ipv6=False),
129
+ labels=labels,
130
+ placement_group=placement_group
131
+ )
132
+
133
+ for index, disk in enumerate(disks):
134
+ if isinstance(disk, int):
135
+ disksize = disk
136
+ elif isinstance(disk, str) and disk.isdigit():
137
+ disksize = int(disk)
138
+ elif isinstance(disk, dict):
139
+ disksize = disk.get('size', '10')
140
+
141
+ diskname = f"{name}-disk{index}"
142
+ volumeresponse = self.conn.volumes.get_by_name(diskname)
143
+ if volumeresponse is None:
144
+ volumeresponse = self.conn.volumes.create(disksize, diskname, location=location,
145
+ labels={"kcli-managed": "volume"})
146
+ volumeresponses.append(volumeresponse)
147
+
148
+ created_vm.action.wait_until_finished(300)
149
+ break
150
+ except APIException as e:
151
+ if e.code == "resource_unavailable":
152
+ msg = f"Could not get server of type '{flavor_option}' in location '{location.name}'"
153
+ if len(flavor_options) > (idx + 1):
154
+ warning(f"{msg}, trying the next configured flavor option.")
155
+ else:
156
+ warning(msg)
157
+ error("No more flavors available to try. Define more flavors in 'flavor_options'.")
147
158
  else:
148
- warning(msg)
149
- error("No more flavors available to try. Define more flavors in 'flavor_options'.")
159
+ return {'result': 'failure', 'reason': json.dumps(e)}
160
+ if created_vm is not None:
161
+ break
150
162
 
151
163
  created_vm = created_vm.server
152
164
 
@@ -533,7 +545,7 @@ class Khcloud():
533
545
  for disk in alldisks:
534
546
  if self.debug:
535
547
  print(disk)
536
- disks[disk.name] = {'pool': "", 'path': self.location}
548
+ disks[disk.name] = {'pool': "", 'path': disk.location.name}
537
549
  return disks
538
550
 
539
551
  def list_buckets(self):
@@ -751,6 +751,7 @@ class Kubevirt():
751
751
  from websocket import create_connection
752
752
  except:
753
753
  error("You need to install websocket-client package")
754
+ return
754
755
  kubectl = self.kubectl
755
756
  namespace = self.namespace
756
757
  vm = _get_resource(kubectl, 'vm', name, namespace, debug=self.debug)
@@ -1203,6 +1203,7 @@ class Kvirt(object):
1203
1203
  <source><address domain='0x%s' bus='0x%s' slot='0x%s' function='0x%s'/></source>
1204
1204
  </hostdev>""" % (newdomain, newbus, newslot, newfunction)
1205
1205
  hostdevxml += newhostdev
1206
+ pmuxml = "<pmu state='on'/>" if overrides.get('pmu', False) else ''
1206
1207
  rngxml = ""
1207
1208
  if rng:
1208
1209
  rngxml = """<rng model='virtio'>
@@ -1304,6 +1305,7 @@ class Kvirt(object):
1304
1305
  <bootmenu enable="yes" timeout="60"/>
1305
1306
  </os>
1306
1307
  <features>
1308
+ {pmuxml}
1307
1309
  {smmxml}
1308
1310
  {ioapicxml}
1309
1311
  {acpixml}
@@ -1344,7 +1346,7 @@ class Kvirt(object):
1344
1346
  isoxml=isoxml, extraisoxml=extraisoxml, floppyxml=floppyxml, displayxml=displayxml,
1345
1347
  serialxml=serialxml, sharedxml=sharedxml, guestxml=guestxml, videoxml=videoxml,
1346
1348
  hostdevxml=hostdevxml, rngxml=rngxml, tpmxml=tpmxml, cpuxml=cpuxml, qemuextraxml=qemuextraxml,
1347
- ioapicxml=ioapicxml, acpixml=acpixml, iommuxml=iommuxml, iommumemxml=iommumemxml,
1349
+ ioapicxml=ioapicxml, acpixml=acpixml, pmuxml=pmuxml, iommuxml=iommuxml, iommumemxml=iommumemxml,
1348
1350
  iommufeaturesxml=iommufeaturesxml, iommudevicexml=iommudevicexml, controllerxml=controllerxml,
1349
1351
  clockxml=clockxml)
1350
1352
  if self.debug:
@@ -1449,7 +1451,8 @@ class Kvirt(object):
1449
1451
  xml = vm.XMLDesc(0)
1450
1452
  root = ET.fromstring(xml)
1451
1453
  for _os in list(root.iter('os')):
1452
- if list(_os.iter('boot'))[0].get('dev') == 'cdrom':
1454
+ boot = _os.iter('boot')
1455
+ if boot is not None and list(boot)[0].get('dev') == 'cdrom':
1453
1456
  return {'result': 'success'}
1454
1457
  newxml = ET.tostring(root).decode("utf-8")
1455
1458
  newxml = newxml.replace('dev="cdrom"', 'dev="TEMP"')
@@ -1960,7 +1963,7 @@ class Kvirt(object):
1960
1963
  if ips and 'ip' not in yamlinfo:
1961
1964
  ip4s = [i for i in ips if ':' not in i]
1962
1965
  ip6s = [i for i in ips if i not in ip4s]
1963
- yamlinfo['ip'] = ip4s[0] if ip4s else ip6s[0]
1966
+ yamlinfo['ip'] = ip4s[0] if ip4s else ip6s[-1]
1964
1967
  if len(all_ips) > 1:
1965
1968
  yamlinfo['ips'] = all_ips
1966
1969
  pcidevices = []
@@ -2692,7 +2695,6 @@ class Kvirt(object):
2692
2695
  else:
2693
2696
  vm.setVcpus(numcpus)
2694
2697
  return {'result': 'success'}
2695
- warning("Note it will only be effective upon next start")
2696
2698
  cpunode.text = str(numcpus)
2697
2699
  newxml = ET.tostring(root).decode("utf-8")
2698
2700
  conn.defineXML(newxml)
@@ -3537,7 +3539,11 @@ class Kvirt(object):
3537
3539
  domainxml = f"<domain name='{name}' localOnly='{localdomain}'/>"
3538
3540
  if len(name) < 16:
3539
3541
  bridgename = name if name != 'default' else 'virbr0'
3540
- bridgexml = f"<bridge name='{bridgename}' stp='on' delay='0'/>"
3542
+ if 'fwzone' in overrides:
3543
+ fwzone = overrides.get('fwzone')
3544
+ bridgexml = f"<bridge name='{bridgename}' zone='{fwzone}' stp='on' delay='0'/>"
3545
+ else:
3546
+ bridgexml = f"<bridge name='{bridgename}' stp='on' delay='0'/>"
3541
3547
  else:
3542
3548
  return {'result': 'failure', 'reason': f"network {name} is more than 16 characters"}
3543
3549
  prefix = cidr.split('/')[1]
@@ -217,8 +217,18 @@ class Kopenstack(object):
217
217
  security_groups=securitygroups)
218
218
  tenant_id = instance.tenant_id
219
219
  if need_floating:
220
- floating_ips = [f['id'] for f in neutron.list_floatingips()['floatingips']
221
- if f['port_id'] is None]
220
+ available_floating_ips = [f for f in neutron.list_floatingips()['floatingips'] if f['port_id'] is None ]
221
+ if self.external_network is not None:
222
+ networks = neutron.list_networks(name=self.external_network)['networks']
223
+ if networks:
224
+ network_id = networks[0]['id']
225
+ if available_floating_ips:
226
+ floating_ips = [f['id'] for f in available_floating_ips if f['floating_network_id'] == network_id]
227
+ else:
228
+ error("Network " + self.external_network + "seems not to be a valid external network")
229
+ else:
230
+ if available_floating_ips:
231
+ floating_ips = [f['id'] for f in available_floating_ips]
222
232
  if not floating_ips:
223
233
  network_id = None
224
234
  if self.external_network is not None:
@@ -880,8 +890,12 @@ class Kopenstack(object):
880
890
 
881
891
  def create_network(self, name, cidr=None, dhcp=True, nat=True, domain=None, plan='kvirt', overrides={}):
882
892
  if nat:
883
- externalnets = [n for n in self.neutron.list_networks()['networks'] if n['router:external']]
884
- externalnet_id = externalnets[0]['id'] if externalnets else None
893
+ if self.external_network is not None:
894
+ external_networks = self.neutron.list_networks(name=self.external_network)['networks']
895
+ externalnet_id = external_networks[0]['id']
896
+ else:
897
+ externalnets = [n for n in self.neutron.list_networks()['networks'] if n['router:external']]
898
+ externalnet_id = externalnets[0]['id'] if externalnets else None
885
899
  routers = [router for router in self.neutron.list_routers()['routers'] if router['name'] == 'kvirt']
886
900
  router_id = routers[0]['id'] if routers else None
887
901
  try:
kvirt/version/git CHANGED
@@ -1 +1 @@
1
- 1ceba51 2025/10/24
1
+ aeff98c 2026/01/08