Skip to content

Commit 44ac13c

Browse files
committed
Bugfix: Broken update operations for cores and ram
The bugfix for the #62 issue. - Fixed node resize for cores and ram. - More graceful treatment of errors handling for root volume creating - New `get_volume` method for the driver Signed-off-by: Anton Kremenetsky <anton.kremenetsky@gmail.com>
1 parent a4e0320 commit 44ac13c

4 files changed

Lines changed: 101 additions & 9 deletions

File tree

genesis_core/node/machine/pool/driver/base.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
# under the License.
1616

1717
import abc
18+
import uuid as sys_uuid
1819
import typing as tp
1920

2021
from genesis_core.node.dm import models
@@ -57,6 +58,12 @@ def list_volumes(
5758
) -> tp.Iterable[models.MachineVolume]:
5859
"""Return volume list from data plane."""
5960

61+
@abc.abstractmethod
62+
def get_volume(
63+
self, machine: sys_uuid.UUID, uuid: sys_uuid.UUID
64+
) -> models.MachineVolume:
65+
"""Get the machine volume by uuid."""
66+
6067
@abc.abstractmethod
6168
def set_machine_cores(self, machine: models.Machine, cores: int) -> None:
6269
"""Set machine cores."""
@@ -111,6 +118,11 @@ def list_volumes(
111118
"""Return volume list from data plane."""
112119
return []
113120

121+
def get_volume(
122+
self, machine: sys_uuid.UUID, uuid: sys_uuid.UUID
123+
) -> models.MachineVolume:
124+
"""Get the machine volume by uuid."""
125+
114126
def set_machine_cores(self, machine: models.Machine, cores: int) -> None:
115127
"""Set machine cores."""
116128

genesis_core/node/machine/pool/driver/exceptions.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,8 @@ class MachineAlreadyExistsError(exceptions.GCException):
2626
class VolumeAlreadyExistsError(exceptions.GCException):
2727
__template__ = "The volume {volume} already exists."
2828
volume: sys_uuid.UUID
29+
30+
31+
class VolumeNotFoundError(exceptions.GCException):
32+
__template__ = "The volume {volume} not found."
33+
volume: sys_uuid.UUID

genesis_core/node/machine/pool/driver/libvirt.py

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
# under the License.
1616
from __future__ import annotations
1717

18-
import json
1918
import logging
2019
import time
2120
import typing as tp
@@ -24,11 +23,13 @@
2423
import contextlib as ctxlib
2524

2625
import libvirt
26+
import netaddr
2727

2828
from genesis_core.node.dm import models
2929
from genesis_core.common import constants as c
3030
from genesis_core.node import constants as nc
3131
from genesis_core.node.machine.pool.driver import base
32+
from genesis_core.node.machine.pool.driver import exceptions as pool_exc
3233

3334
ImageFormatType = tp.Literal["raw", "qcow2"]
3435
NetworkType = 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:

genesis_core/node/machine/service.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,8 @@ def _create_volumes(
156156
try:
157157
driver.create_volume(v)
158158
except pool_exceptions.VolumeAlreadyExistsError:
159-
# Do nothing the volume is already created
160-
pass
159+
# Copy the necessary fields
160+
v.path = driver.get_volume(v.machine, v.uuid).path
161161

162162
def _actualize_machine(
163163
self,
@@ -220,7 +220,7 @@ def _actualize_pool(
220220
self._actualize_pool_state(pool, actual_machines.values())
221221

222222
# Delete any machines that are not in the target list
223-
for uuid in set(actual_machines.keys()) - set(target_machines.keys()):
223+
for uuid in actual_machines.keys() - target_machines.keys():
224224
machine = actual_machines[uuid]
225225
try:
226226
driver.delete_machine(machine)
@@ -232,7 +232,7 @@ def _actualize_pool(
232232
)
233233

234234
# Create any machines that are not in the actual list
235-
for uuid in set(target_machines.keys()) - set(actual_machines.keys()):
235+
for uuid in target_machines.keys() - actual_machines.keys():
236236
machine = target_machines[uuid]
237237
# Skip machines that are not ready. They are building and
238238
# will be ready a little bit later.
@@ -278,7 +278,7 @@ def _actualize_pool(
278278
)
279279

280280
# Actualize any machines that are in both lists
281-
for uuid in set(target_machines.keys()) & set(actual_machines.keys()):
281+
for uuid in target_machines.keys() & actual_machines.keys():
282282
target_machine = target_machines[uuid]
283283
actual_machine = actual_machines[uuid]
284284
# Skip machines that are not ready. They are building and

0 commit comments

Comments
 (0)