1515# under the License.
1616from __future__ import annotations
1717
18- import json
1918import logging
2019import time
2120import typing as tp
2423import contextlib as ctxlib
2524
2625import libvirt
26+ import netaddr
2727
2828from genesis_core .node .dm import models
2929from genesis_core .common import constants as c
3030from genesis_core .node import constants as nc
3131from genesis_core .node .machine .pool .driver import base
32+ from genesis_core .node .machine .pool .driver import exceptions as pool_exc
3233
3334ImageFormatType = tp .Literal ["raw" , "qcow2" ]
3435NetworkType = tp .Literal ["bridge" , "network" ]
@@ -474,6 +475,38 @@ def _vir_volume2machine_volume(
474475 project_id = c .SERVICE_PROJECT_ID ,
475476 )
476477
478+ def _list_interfaces (self , machine : models .Machine ) -> list [models .Port ]:
479+ """List all interfaces of the machine."""
480+ # TODO(akremenetsky): The `Port` model is used to represent
481+ # an interface. We need more appropriate model.
482+ ports = []
483+
484+ with ctxlib .closing (self ._connect ()) as cn :
485+ domain = cn .lookupByUUIDString (str (machine .uuid ))
486+ domain_xml = minidom .parseString (domain .XMLDesc ())
487+
488+ for iface in domain_xml .getElementsByTagName ("interface" ):
489+ mac_tags = iface .getElementsByTagName ("mac" )
490+ if len (mac_tags ) != 1 or not mac_tags [0 ].getAttribute (
491+ "address"
492+ ):
493+ LOG .error ("Unable to detect MAC address for %s" , iface )
494+ continue
495+
496+ mac = mac_tags [0 ].getAttribute ("address" )
497+ ports .append (
498+ models .Port (
499+ uuid = sys_uuid .UUID (
500+ "00000000-0000-0000-0000-000000000000"
501+ ),
502+ machine = machine .uuid ,
503+ mac = mac ,
504+ project_id = c .SERVICE_PROJECT_ID ,
505+ )
506+ )
507+
508+ return ports
509+
477510 def list_volumes (
478511 self , machine : models .Machine
479512 ) -> tp .Iterable [models .MachineVolume ]:
@@ -490,6 +523,40 @@ def list_volumes(
490523 LOG .debug ("Volumes: %s" , result )
491524 return result
492525
526+ def get_volume (
527+ self , machine : sys_uuid .UUID , uuid : sys_uuid .UUID
528+ ) -> models .MachineVolume :
529+ target_volume = models .MachineVolume (
530+ uuid = uuid ,
531+ machine = machine ,
532+ # These fields don't make sense in this case, just placeholders
533+ size = 1 ,
534+ node = sys_uuid .uuid4 (),
535+ project_id = c .SERVICE_PROJECT_ID ,
536+ )
537+ name = self ._form_vir_volume_name (target_volume )
538+
539+ """Get the machine volume by uuid."""
540+ with ctxlib .closing (self ._connect ()) as cn :
541+ storage_pool = cn .storagePoolLookupByName (self ._spec .storage_pool )
542+
543+ # We don't know which format is used for the volume so we try them all
544+ for fmt in tp .get_args (ImageFormatType ):
545+ name_with_format = f"{ name } .{ fmt } "
546+ try :
547+ volume = storage_pool .storageVolLookupByName (
548+ name_with_format
549+ )
550+ break
551+ except libvirt .libvirtError as e :
552+ if e .get_error_code () == libvirt .VIR_ERR_NO_STORAGE_VOL :
553+ continue
554+ raise
555+ else :
556+ raise pool_exc .VolumeNotFoundError (volume = uuid )
557+
558+ return self ._vir_volume2machine_volume (volume )
559+
493560 def create_volume (
494561 self , volume : models .MachineVolume
495562 ) -> models .MachineVolume :
@@ -499,7 +566,13 @@ def create_volume(
499566 volume_xml = XMLLibvirtVolume .xml_from_base_template (
500567 storage_pool , name , volume .size << 30
501568 )
502- virt_volume = storage_pool .createXML (volume_xml )
569+
570+ try :
571+ virt_volume = storage_pool .createXML (volume_xml )
572+ except libvirt .libvirtError as e :
573+ if e .get_error_code () == libvirt .VIR_ERR_STORAGE_VOL_EXIST :
574+ raise pool_exc .VolumeAlreadyExistsError (volume = volume .uuid )
575+ raise
503576
504577 # TODO(akremenetsky): We shouldn't change the original object
505578 volume .path = virt_volume .path ()
@@ -632,22 +705,24 @@ def delete_machine(
632705
633706 def set_machine_cores (self , machine : models .Machine , cores : int ) -> None :
634707 """Set machine cores."""
708+ ports = self ._list_interfaces (machine )
635709 volumes = self .list_volumes (machine )
636710 self .delete_machine (machine , delete_volumes = False )
637711
638712 machine .cores = cores
639- self .create_machine (machine , volumes = volumes )
713+ self .create_machine (machine , volumes = volumes , ports = ports )
640714 LOG .debug (
641715 "The domain %s was updated with cores %s" , machine .uuid , cores
642716 )
643717
644718 def set_machine_ram (self , machine : models .Machine , ram : int ) -> None :
645719 """Set machine ram."""
720+ ports = self ._list_interfaces (machine )
646721 volumes = self .list_volumes (machine )
647722 self .delete_machine (machine , delete_volumes = False )
648723
649724 machine .ram = ram
650- self .create_machine (machine , volumes = volumes )
725+ self .create_machine (machine , volumes = volumes , ports = ports )
651726 LOG .debug ("The domain %s was updated with ram %s" , machine .uuid , ram )
652727
653728 def reset_machine (self , machine : models .Machine ) -> None :
0 commit comments