Skip to content

Commit 353391f

Browse files
committed
Bootstrap new installation with boot and main networks
Adaptation for the bootstrap command to run a new installation with two network. - Boot network. It's required to perform provisioning process of new node. Also it's a private isolate network allows to transfer a private keys for agents. - Main network. It's a base network for communication. Signed-off-by: Anton Kremenetsky <anton.kremenetsky@gmail.com>
1 parent 949c54d commit 353391f

6 files changed

Lines changed: 259 additions & 115 deletions

File tree

genesis_devtools/cmd/cli.py

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
BOOTSTRAP_TAG = "bootstrap"
4646
LaunchModeType = tp.Literal["core", "element", "custom"]
4747
GC_CIDR = ipaddress.IPv4Network("10.20.0.0/22")
48+
GC_BOOT_CIDR = ipaddress.IPv4Network("10.30.0.0/24")
4849

4950

5051
@click.group(invoke_without_command=True)
@@ -498,14 +499,32 @@ def push_cmd(
498499
@click.option(
499500
"--cidr",
500501
default=GC_CIDR,
501-
help="Network CIDR",
502+
help="The main network CIDR",
502503
show_default=True,
503504
type=ipaddress.IPv4Network,
504505
)
505506
@click.option(
506507
"--bridge",
507508
default=None,
508-
help="Name of the linux bridge, it will be created if not set.",
509+
help=(
510+
"Name of the linux bridge for the main network, "
511+
"it will be created if not set."
512+
),
513+
)
514+
@click.option(
515+
"--boot-cidr",
516+
default=GC_BOOT_CIDR,
517+
help="The bootstrap network CIDR",
518+
show_default=True,
519+
type=ipaddress.IPv4Network,
520+
)
521+
@click.option(
522+
"--boot-bridge",
523+
default=None,
524+
help=(
525+
"Name of the linux bridge for the bootstrap network, "
526+
"it will be created if not set."
527+
),
509528
)
510529
@click.option(
511530
"-f",
@@ -607,6 +626,8 @@ def bootstrap_cmd(
607626
stand_spec: str | None,
608627
cidr: ipaddress.IPv4Network,
609628
bridge: str | None,
629+
boot_cidr: ipaddress.IPv4Network,
630+
boot_bridge: str | None,
610631
force: bool,
611632
no_wait: bool,
612633
use_image_inplace: bool,
@@ -676,6 +697,8 @@ def bootstrap_cmd(
676697
stand_spec=stand_spec,
677698
cidr=cidr,
678699
bridge=bridge,
700+
boot_cidr=boot_cidr,
701+
boot_bridge=boot_bridge,
679702
force=force,
680703
use_image_inplace=use_image_inplace,
681704
repository=repository,
@@ -1305,6 +1328,8 @@ def _bootstrap_core(
13051328
stand_spec: tp.Dict[str, tp.Any] | None,
13061329
cidr: ipaddress.IPv4Network,
13071330
bridge: str | None,
1331+
boot_cidr: ipaddress.IPv4Network,
1332+
boot_bridge: str | None,
13081333
force: bool,
13091334
use_image_inplace: bool,
13101335
repository: str,
@@ -1314,11 +1339,17 @@ def _bootstrap_core(
13141339
logger.info("Starting genesis bootstrap in 'core' mode")
13151340

13161341
net_name = utils.installation_net_name(name)
1317-
default_stand_network = stand_models.Network(
1342+
default_stand_main_network = stand_models.Network(
13181343
name=bridge if bridge else net_name,
13191344
cidr=cidr,
13201345
managed_network=False if bridge else True,
13211346
)
1347+
boot_net_name = utils.installation_boot_net_name(name)
1348+
default_stand_boot_network = stand_models.Network(
1349+
name=boot_bridge if boot_bridge else boot_net_name,
1350+
cidr=boot_cidr,
1351+
managed_network=False if boot_bridge else True,
1352+
)
13221353

13231354
# Single bootstrap stand
13241355
if stand_spec is None:
@@ -1330,14 +1361,18 @@ def _bootstrap_core(
13301361
use_image_inplace=use_image_inplace,
13311362
cores=cores,
13321363
memory=memory,
1333-
network=default_stand_network,
1364+
network=default_stand_main_network,
1365+
boot_network=default_stand_boot_network,
13341366
bootstrap_name=bootstrap_domain_name,
13351367
hypervisors=hypervisors,
13361368
)
13371369
else:
13381370
dev_stand = stand_models.Stand.from_spec(stand_spec)
13391371
if dev_stand.network.is_dummy:
1340-
dev_stand.network = default_stand_network
1372+
dev_stand.network = default_stand_main_network
1373+
1374+
if dev_stand.boot_network.is_dummy:
1375+
dev_stand.boot_network = default_stand_boot_network
13411376

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

1371-
infra.create_stand(dev_stand, repository=repository)
1372-
logger.info("Launched genesis installation")
1406+
try:
1407+
infra.create_stand(dev_stand, repository=repository)
1408+
logger.info("Launched genesis installation")
1409+
except Exception:
1410+
infra.delete_stand(dev_stand)
1411+
logger.error(f"Failed to launch genesis installation {dev_stand.name}")
1412+
raise
13731413

13741414
cidr = dev_stand.network.cidr
13751415
logger.important(

genesis_devtools/infra/driver/libvirt.py

Lines changed: 59 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,24 @@ def _extract_net_from_bootstrap(
7070
name=name, cidr=cidr, managed_network=managed_network, dhcp=dhcp
7171
)
7272

73+
def _extract_boot_net_from_bootstrap(
74+
self, bootstrap: minidom.Document
75+
) -> models.Network:
76+
try:
77+
net = bootstrap.getElementsByTagName(vc.GENESIS_META_BOOT_NET_TAG)[
78+
0
79+
]
80+
except Exception:
81+
return models.Network.dummy()
82+
83+
name = net.firstChild.nodeValue
84+
cidr = ipaddress.IPv4Network(net.getAttribute("cidr"))
85+
managed_network = bool(int(net.getAttribute("managed_network")))
86+
dhcp = bool(int(net.getAttribute("dhcp")))
87+
return models.Network(
88+
name=name, cidr=cidr, managed_network=managed_network, dhcp=dhcp
89+
)
90+
7391
def _domain2bootstrap(self, domain: minidom.Document) -> models.Bootstrap:
7492
node = self._domain2node(domain)
7593
return models.Bootstrap.from_node(node)
@@ -111,6 +129,9 @@ def list_stands(self) -> tp.List[models.Stand]:
111129
if node_type == "bootstrap":
112130
stand.bootstraps.append(self._domain2bootstrap(domain))
113131
stand.network = self._extract_net_from_bootstrap(domain)
132+
stand.boot_network = self._extract_boot_net_from_bootstrap(
133+
domain
134+
)
114135
elif node_type == "baremetal":
115136
stand.baremetals.append(self._domain2node(domain))
116137
else:
@@ -136,19 +157,35 @@ def create_stand(
136157
):
137158
raise ValueError(f"Some domain in stand {stand} already exists")
138159

139-
if stand.network.managed_network and libvirt.has_net(stand.network):
140-
raise ValueError(f"Network {stand.network} already exists")
160+
if stand.network.managed_network and libvirt.has_net(
161+
stand.network.name
162+
):
163+
raise ValueError(f"Network {stand.network.name} already exists")
164+
165+
if stand.boot_network.managed_network and libvirt.has_net(
166+
stand.boot_network.name
167+
):
168+
raise ValueError(
169+
f"Network {stand.boot_network.name} already exists"
170+
)
141171

172+
# Main network for ordinary communication
142173
if stand.network.managed_network:
143174
libvirt.create_nat_network(
144175
name=stand.network.name,
145176
cidr=stand.network.cidr,
146177
dhcp_enabled=stand.network.dhcp,
147178
)
148179

180+
# Isolated network for bootstrap procedure
181+
if stand.boot_network.managed_network:
182+
libvirt.create_isolated_network(
183+
name=stand.boot_network.name,
184+
)
185+
149186
# Prepare config drive for the bootstrap node
150187
spec = {
151-
"schema_version": 1,
188+
"schema_version": 2,
152189
"stand": dataclasses.asdict(stand),
153190
**extra_data,
154191
}
@@ -181,6 +218,17 @@ def create_stand(
181218
"dhcp": int(stand.network.dhcp),
182219
},
183220
),
221+
self._tag(
222+
vc.GENESIS_META_BOOT_NET_TAG,
223+
stand.boot_network.name,
224+
{
225+
"cidr": str(stand.boot_network.cidr),
226+
"managed_network": int(
227+
stand.boot_network.managed_network
228+
),
229+
"dhcp": int(stand.boot_network.dhcp),
230+
},
231+
),
184232
)
185233

186234
libvirt.create_domain(
@@ -190,10 +238,7 @@ def create_stand(
190238
cores=bootstrap.cores,
191239
memory=bootstrap.memory,
192240
disks=bootstrap.disks,
193-
network=stand.network.name,
194-
net_type=(
195-
"network" if stand.network.managed_network else "bridge"
196-
),
241+
networks=(stand.network, stand.boot_network),
197242
meta_tags=tags,
198243
config_drive=config_drive_path,
199244
)
@@ -214,10 +259,8 @@ def create_stand(
214259
use_image_inplace=node.use_image_inplace,
215260
cores=node.cores,
216261
memory=node.memory,
217-
network=stand.network.name,
218-
net_type=(
219-
"network" if stand.network.managed_network else "bridge"
220-
),
262+
# Put new node to the boot network
263+
networks=(stand.boot_network,),
221264
meta_tags=tags,
222265
disks=node.disks,
223266
boot="network",
@@ -226,7 +269,9 @@ def create_stand(
226269
def delete_stand(self, stand: models.Stand) -> None:
227270
"""Delete the stand."""
228271
for node in itertools.chain(stand.bootstraps, stand.baremetals):
229-
libvirt.destroy_domain(node.name)
272+
if libvirt.has_domain(node.name):
273+
libvirt.destroy_domain(node.name)
230274

231-
if stand.network.managed_network:
232-
libvirt.destroy_net(stand.network.name)
275+
for net in (stand.network.name, stand.boot_network.name):
276+
if libvirt.has_net(net):
277+
libvirt.destroy_net(net)

genesis_devtools/infra/libvirt/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@
2323
GENESIS_META_MEM_TAG = "genesis:mem"
2424
GENESIS_META_IMAGE_TAG = "genesis:image"
2525
GENESIS_META_NET_TAG = "genesis:network"
26+
GENESIS_META_BOOT_NET_TAG = "genesis:boot_network"
2627

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

0 commit comments

Comments
 (0)