Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 47 additions & 7 deletions genesis_devtools/cmd/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
BOOTSTRAP_TAG = "bootstrap"
LaunchModeType = tp.Literal["core", "element", "custom"]
GC_CIDR = ipaddress.IPv4Network("10.20.0.0/22")
GC_BOOT_CIDR = ipaddress.IPv4Network("10.30.0.0/24")


@click.group(invoke_without_command=True)
Expand Down Expand Up @@ -498,14 +499,32 @@ def push_cmd(
@click.option(
"--cidr",
default=GC_CIDR,
help="Network CIDR",
help="The main network CIDR",
show_default=True,
type=ipaddress.IPv4Network,
)
@click.option(
"--bridge",
default=None,
help="Name of the linux bridge, it will be created if not set.",
help=(
"Name of the linux bridge for the main network, "
"it will be created if not set."
),
)
@click.option(
"--boot-cidr",
default=GC_BOOT_CIDR,
help="The bootstrap network CIDR",
show_default=True,
type=ipaddress.IPv4Network,
)
@click.option(
"--boot-bridge",
default=None,
help=(
"Name of the linux bridge for the bootstrap network, "
"it will be created if not set."
),
)
@click.option(
"-f",
Expand Down Expand Up @@ -607,6 +626,8 @@ def bootstrap_cmd(
stand_spec: str | None,
cidr: ipaddress.IPv4Network,
bridge: str | None,
boot_cidr: ipaddress.IPv4Network,
boot_bridge: str | None,
force: bool,
no_wait: bool,
use_image_inplace: bool,
Expand Down Expand Up @@ -676,6 +697,8 @@ def bootstrap_cmd(
stand_spec=stand_spec,
cidr=cidr,
bridge=bridge,
boot_cidr=boot_cidr,
boot_bridge=boot_bridge,
force=force,
use_image_inplace=use_image_inplace,
repository=repository,
Expand Down Expand Up @@ -1305,6 +1328,8 @@ def _bootstrap_core(
stand_spec: tp.Dict[str, tp.Any] | None,
cidr: ipaddress.IPv4Network,
bridge: str | None,
boot_cidr: ipaddress.IPv4Network,
boot_bridge: str | None,
force: bool,
use_image_inplace: bool,
repository: str,
Expand All @@ -1314,11 +1339,17 @@ def _bootstrap_core(
logger.info("Starting genesis bootstrap in 'core' mode")

net_name = utils.installation_net_name(name)
default_stand_network = stand_models.Network(
default_stand_main_network = stand_models.Network(
name=bridge if bridge else net_name,
cidr=cidr,
managed_network=False if bridge else True,
)
boot_net_name = utils.installation_boot_net_name(name)
default_stand_boot_network = stand_models.Network(
name=boot_bridge if boot_bridge else boot_net_name,
cidr=boot_cidr,
managed_network=False if boot_bridge else True,
)

# Single bootstrap stand
if stand_spec is None:
Expand All @@ -1330,14 +1361,18 @@ def _bootstrap_core(
use_image_inplace=use_image_inplace,
cores=cores,
memory=memory,
network=default_stand_network,
network=default_stand_main_network,
boot_network=default_stand_boot_network,
bootstrap_name=bootstrap_domain_name,
hypervisors=hypervisors,
)
else:
dev_stand = stand_models.Stand.from_spec(stand_spec)
if dev_stand.network.is_dummy:
dev_stand.network = default_stand_network
dev_stand.network = default_stand_main_network

if dev_stand.boot_network.is_dummy:
dev_stand.boot_network = default_stand_boot_network

# Assign the image to bootstraps if it wasn't specified
# in the specification.
Expand Down Expand Up @@ -1368,8 +1403,13 @@ def _bootstrap_core(
infra.delete_stand(stand)
logger.info(f"Destroyed old genesis installation: {dev_stand.name}")

infra.create_stand(dev_stand, repository=repository)
logger.info("Launched genesis installation")
try:
infra.create_stand(dev_stand, repository=repository)
logger.info("Launched genesis installation")
except Exception:
infra.delete_stand(dev_stand)
logger.error(f"Failed to launch genesis installation {dev_stand.name}")
raise

cidr = dev_stand.network.cidr
logger.important(
Expand Down
73 changes: 59 additions & 14 deletions genesis_devtools/infra/driver/libvirt.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,24 @@ def _extract_net_from_bootstrap(
name=name, cidr=cidr, managed_network=managed_network, dhcp=dhcp
)

def _extract_boot_net_from_bootstrap(
self, bootstrap: minidom.Document
) -> models.Network:
try:
net = bootstrap.getElementsByTagName(vc.GENESIS_META_BOOT_NET_TAG)[
0
]
except Exception:
return models.Network.dummy()

name = net.firstChild.nodeValue
cidr = ipaddress.IPv4Network(net.getAttribute("cidr"))
managed_network = bool(int(net.getAttribute("managed_network")))
dhcp = bool(int(net.getAttribute("dhcp")))
return models.Network(
name=name, cidr=cidr, managed_network=managed_network, dhcp=dhcp
)

def _domain2bootstrap(self, domain: minidom.Document) -> models.Bootstrap:
node = self._domain2node(domain)
return models.Bootstrap.from_node(node)
Expand Down Expand Up @@ -111,6 +129,9 @@ def list_stands(self) -> tp.List[models.Stand]:
if node_type == "bootstrap":
stand.bootstraps.append(self._domain2bootstrap(domain))
stand.network = self._extract_net_from_bootstrap(domain)
stand.boot_network = self._extract_boot_net_from_bootstrap(
domain
)
elif node_type == "baremetal":
stand.baremetals.append(self._domain2node(domain))
else:
Expand All @@ -136,19 +157,35 @@ def create_stand(
):
raise ValueError(f"Some domain in stand {stand} already exists")

if stand.network.managed_network and libvirt.has_net(stand.network):
raise ValueError(f"Network {stand.network} already exists")
if stand.network.managed_network and libvirt.has_net(
stand.network.name
):
raise ValueError(f"Network {stand.network.name} already exists")

if stand.boot_network.managed_network and libvirt.has_net(
stand.boot_network.name
):
raise ValueError(
f"Network {stand.boot_network.name} already exists"
)

# Main network for ordinary communication
if stand.network.managed_network:
libvirt.create_nat_network(
name=stand.network.name,
cidr=stand.network.cidr,
dhcp_enabled=stand.network.dhcp,
)

# Isolated network for bootstrap procedure
if stand.boot_network.managed_network:
libvirt.create_isolated_network(
name=stand.boot_network.name,
)

# Prepare config drive for the bootstrap node
spec = {
"schema_version": 1,
"schema_version": 2,
"stand": dataclasses.asdict(stand),
**extra_data,
}
Expand Down Expand Up @@ -181,6 +218,17 @@ def create_stand(
"dhcp": int(stand.network.dhcp),
},
),
self._tag(
vc.GENESIS_META_BOOT_NET_TAG,
stand.boot_network.name,
{
"cidr": str(stand.boot_network.cidr),
"managed_network": int(
stand.boot_network.managed_network
),
"dhcp": int(stand.boot_network.dhcp),
},
),
)

libvirt.create_domain(
Expand All @@ -190,10 +238,7 @@ def create_stand(
cores=bootstrap.cores,
memory=bootstrap.memory,
disks=bootstrap.disks,
network=stand.network.name,
net_type=(
"network" if stand.network.managed_network else "bridge"
),
networks=(stand.network, stand.boot_network),
meta_tags=tags,
config_drive=config_drive_path,
)
Expand All @@ -214,10 +259,8 @@ def create_stand(
use_image_inplace=node.use_image_inplace,
cores=node.cores,
memory=node.memory,
network=stand.network.name,
net_type=(
"network" if stand.network.managed_network else "bridge"
),
# Put new node to the boot network
networks=(stand.boot_network,),
meta_tags=tags,
disks=node.disks,
boot="network",
Expand All @@ -226,7 +269,9 @@ def create_stand(
def delete_stand(self, stand: models.Stand) -> None:
"""Delete the stand."""
for node in itertools.chain(stand.bootstraps, stand.baremetals):
libvirt.destroy_domain(node.name)
if libvirt.has_domain(node.name):
libvirt.destroy_domain(node.name)

if stand.network.managed_network:
libvirt.destroy_net(stand.network.name)
for net in (stand.network.name, stand.boot_network.name):
if libvirt.has_net(net):
libvirt.destroy_net(net)
1 change: 1 addition & 0 deletions genesis_devtools/infra/libvirt/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@
GENESIS_META_MEM_TAG = "genesis:mem"
GENESIS_META_IMAGE_TAG = "genesis:image"
GENESIS_META_NET_TAG = "genesis:network"
GENESIS_META_BOOT_NET_TAG = "genesis:boot_network"

BootMode = tp.Literal["hd", "network"]
Loading