Skip to content

Commit bada08c

Browse files
committed
lib/vm.py: add vdis related function
Add a list of vdis object to a VM Add get_dom0_vm in host.py Add function to connect/disconnect a VDI from a VM Signed-off-by: Damien Thenot <[email protected]>
1 parent 1d1e036 commit bada08c

File tree

5 files changed

+81
-9
lines changed

5 files changed

+81
-9
lines changed

lib/basevm.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import logging
22

3-
from typing import TYPE_CHECKING, Any, Literal, Optional, overload
3+
from typing import TYPE_CHECKING, Any, List, Literal, Optional, overload
44

55
if TYPE_CHECKING:
66
import lib.host
@@ -59,7 +59,7 @@ def name(self) -> str:
5959
def _disk_list(self):
6060
raise NotImplementedError()
6161

62-
def vdi_uuids(self, sr_uuid=None):
62+
def vdi_uuids(self, sr_uuid: Optional[str] = None) -> List[str]:
6363
output = self._disk_list()
6464
if output == '':
6565
return []

lib/host.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ def __init__(self, pool: 'lib.pool.Pool', hostname_or_ip):
6767
self.uuid = self.inventory['INSTALLATION_UUID']
6868
self.xcp_version = version.parse(self.inventory['PRODUCT_VERSION'])
6969
self.xcp_version_short = f"{self.xcp_version.major}.{self.xcp_version.minor}"
70+
self._dom0: Optional[VM] = None
7071

7172
def __str__(self):
7273
return self.hostname_or_ip
@@ -711,3 +712,20 @@ def enable_hsts_header(self):
711712
def disable_hsts_header(self):
712713
self.ssh(['rm', '-f', f'{XAPI_CONF_DIR}/00-XCP-ng-tests-enable-hsts-header.conf'])
713714
self.restart_toolstack(verify=True)
715+
716+
def get_dom0_uuid(self):
717+
return self.inventory["CONTROL_DOMAIN_UUID"]
718+
719+
def get_dom0_VM(self) -> VM:
720+
if not self._dom0:
721+
self._dom0 = VM(self.get_dom0_uuid(), self)
722+
return self._dom0
723+
724+
def get_sr_from_vdi_uuid(self, vdi_uuid: str) -> Optional[SR]:
725+
sr_uuid = self.xe("vdi-param-get", {
726+
"param-name": "sr-uuid",
727+
"uuid": vdi_uuid,
728+
})
729+
if not sr_uuid:
730+
return None
731+
return SR(sr_uuid, self.pool)

lib/sr.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
import logging
22
import time
3-
from typing import Optional
43

54
import lib.commands as commands
6-
from lib.common import prefix_object_name, safe_split, strtobool, wait_for, wait_for_not
5+
from lib.common import (
6+
prefix_object_name,
7+
safe_split,
8+
strtobool,
9+
wait_for,
10+
wait_for_not,
11+
)
712
from lib.vdi import VDI
813

14+
from typing import Optional
15+
916
class SR:
1017
def __init__(self, uuid, pool):
1118
self.uuid = uuid

lib/vdi.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,7 @@ def __init__(self, uuid, *, host=None, sr=None):
2525
# TODO: use a different approach when migration is possible
2626
if sr is None:
2727
assert host
28-
sr_uuid = host.pool.get_vdi_sr_uuid(uuid)
29-
# avoid circular import
30-
# FIXME should get it from Host instead
31-
from lib.sr import SR
32-
self.sr = SR(sr_uuid, host.pool)
28+
self.sr = host.get_sr_from_vdi_uuid(self.uuid)
3329
else:
3430
self.sr = sr
3531

lib/vm.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
)
2020
from lib.snapshot import Snapshot
2121
from lib.vbd import VBD
22+
from lib.vdi import VDI
2223
from lib.vif import VIF
2324

2425
from typing import TYPE_CHECKING, List, Literal, Optional, Union, overload
@@ -33,6 +34,15 @@ def __init__(self, uuid: str, host: 'Host'):
3334
self.previous_host = None # previous host when migrated or being migrated
3435
self.is_windows = self.param_get('platform', 'device_id', accept_unknown_key=True) == '0002'
3536
self.is_uefi = self.param_get('HVM-boot-params', 'firmware', accept_unknown_key=True) == 'uefi'
37+
try:
38+
self.vdis = [VDI(vdi_uuid, host=host) for vdi_uuid in self.vdi_uuids()]
39+
except commands.SSHCommandFailed as e:
40+
# Doesn't work with Dom0 since `vm-disk-list` doesn't work on it so we create empty list
41+
if e.stdout == "Error: No matching VMs found":
42+
logging.info("Couldn't get disks list. We are Dom0. Continuing...")
43+
self.vdis = []
44+
else:
45+
raise
3646

3747
def power_state(self) -> str:
3848
return self.param_get('power-state')
@@ -287,6 +297,47 @@ def checkpoint(self) -> Snapshot:
287297
'new-name-label': 'Checkpoint of %s' % self.uuid}),
288298
self.host)
289299

300+
def connect_vdi(self, vdi: VDI, device: str = "autodetect") -> str:
301+
logging.info(f">> Plugging VDI {vdi.uuid} on VM {self.uuid}")
302+
vbd_uuid = self.host.xe("vbd-create", {
303+
"vdi-uuid": vdi.uuid,
304+
"vm-uuid": self.uuid,
305+
"device": device,
306+
})
307+
try:
308+
self.host.xe("vbd-plug", {"uuid": vbd_uuid})
309+
except commands.SSHCommandFailed:
310+
self.host.xe("vbd-destroy", {"uuid": vbd_uuid})
311+
raise
312+
313+
self.vdis.append(vdi)
314+
315+
return vbd_uuid
316+
317+
def disconnect_vdi(self, vdi: VDI):
318+
logging.info(f"<< Unplugging VDI {vdi.uuid} from VM {self.uuid}")
319+
assert vdi in self.vdis, "VDI {vdi.uuid} not in VM {self.uuid} VDI list"
320+
vbd_uuid = self.host.xe("vbd-list", {
321+
"vdi-uuid": vdi.uuid,
322+
"vm-uuid": self.uuid
323+
}, minimal=True)
324+
try:
325+
self.host.xe("vbd-unplug", {"uuid": vbd_uuid})
326+
except commands.SSHCommandFailed as e:
327+
if e.stdout == f"The device is not currently attached\ndevice: {vbd_uuid}":
328+
logging.info(f"VBD {vbd_uuid} already unplugged")
329+
else:
330+
raise
331+
self.host.xe("vbd-destroy", {"uuid": vbd_uuid})
332+
self.vdis.remove(vdi)
333+
334+
def destroy_vdi(self, vdi_uuid: str) -> None:
335+
for vdi in self.vdis:
336+
if vdi.uuid == vdi_uuid:
337+
self.vdis.remove(vdi)
338+
super().destroy_vdi(vdi_uuid)
339+
break
340+
290341
def vifs(self):
291342
_vifs = []
292343
for vif_uuid in safe_split(self.host.xe('vif-list', {'vm-uuid': self.uuid}, minimal=True)):

0 commit comments

Comments
 (0)