def _export_deployment_manifest(self, deployment, deployment_dir): deployment_dir.mkdir(parents=True, exist_ok=True) deployment_manifest_file = deployment_dir / UvnDefaults["registry"][ "deployment_file"] db_args = self.identity_db.get_export_args() yml(deployment, to_file=deployment_manifest_file, **db_args) # Generate a human-readable summary report_file = deployment_dir / UvnDefaults["registry"][ "deployment_report"] deployment_summary = UvnDeploymentSummary(self, deployment) render(deployment_summary, "human", to_file=report_file) logger.activity("generated deployment summary: {}", report_file)
def start(self, cfg_name=None, cfg=None): # Generate configuration if not cfg_name: if not self.cfg_name: raise ValueError("no configuration name specified") cfg_name = self.cfg_name if not cfg: if not self._cfg: raise RuntimeError("no configuration object specified") cfg = self._cfg render(cfg, "rs-config", to_file=self.cfg_file) self.cfg_name = cfg_name self._cfg = cfg threading.Thread.start(self)
def reload(self): if self.dnsmasq_enabled: logger.trace("[dnsmasq] reloading") self._generate_dnsmasq_config( db_only=True, upstream_servers=self.upstream_servers, noupstream=self.noupstream, except_interfaces=self.except_interfaces, localhost_only=self.localhost_only) db_dir = pathlib.Path(UvnDefaults["nameserver"]["db_dir"]) db_file = db_dir / UvnDefaults["nameserver"]["db_file"] render(self, "dnsmasq-db", to_file=db_file) # dnsmasq_reload() self.dnsmaq_monitor.trigger()
def render(self, base_dir): base_dir = pathlib.Path(base_dir) for cell_name, cfg in self.configs.items(): cfg_file = base_dir / UvnDefaults["registry"]["vpn"][ "particles"]["particle_cfg_fmt"].format( cfg.registry_address, cfg.cell_name, self.name) qr_file = base_dir / UvnDefaults["registry"][ "vpn"]["particles"]["particle_qr_fmt"].format( cfg.registry_address, cfg.cell_name, self.name) render(cfg, "wireguard-cfg", to_file=cfg_file) qr.encode_file(cfg_file, qr_file, format="png") logger.debug("generated particle: {} -> {}", self.name, cfg.cell_name) manifest_file = base_dir / UvnDefaults["particle"]["manifest"] render(self, "manifest", to_file=manifest_file, manifest=True)
def _create_particles_connection(self): wg_config = render(self.cell.particles_vpn, "wireguard-cfg") wgi = WireGuardInterface(self.cell.particles_vpn.interface, self.cell.particles_vpn.addr_local, self.cell.particles_vpn.network.prefixlen, wg_config, keep=self.keep) return wgi
def _create_participant(self, cls, extra): logger.debug("agent participant: {}", cls) cfg_dir = self._basedir / UvnDefaults["dds"]["dir"] profile_file = cfg_dir / UvnDefaults["dds"]["profile_file"] with StaticData.dds_profile_file().open() as profile_file_tmplt: render({ "registry_address": self.registry.address, "cell_name": self.registry.deployed_cell.id.name if self.registry.packaged else self.agent_id(), "deployment_id": self.registry.latest_deployment.id if self.registry.latest_deployment else UvnDefaults["registry"]["deployment_bootstrap"] }, profile_file_tmplt.read(), to_file=profile_file, inline=True) return cls(self._basedir, self.registry, listener=self, profile_file=str(profile_file), **extra)
def _create_root_connection(self): wg_config = render(self.cell.registry_vpn, "wireguard-cfg") wgi = WireGuardInterface( self.cell.registry_vpn.interface, self.cell.registry_vpn.cell_ip, UvnDefaults["registry"]["vpn"]["registry"]["netmask"], wg_config, keep=self.keep, allowed_ips={ ipaddress.ip_network("{}/{}".format( UvnDefaults["registry"]["vpn"]["registry"]["base_ip"], UvnDefaults["registry"]["vpn"]["registry"]["netmask"])) }) return wgi
def _create_router_connections(self): port_config = CellRouterPortWireguardConfig(self.registry, self.cell) wg_config = render(port_config, "wireguard-cfg") wgi = WireGuardInterface( self.cell.router_port.interface, self.cell.router_port.addr_local, UvnDefaults["registry"]["vpn"]["router"]["netmask"], wg_config, keep=self.keep, allowed_ips={ ipaddress.ip_network(a) for a in UvnDefaults["registry"]["vpn"]["router"] ["allowed_ips"] }) return [wgi]
def _generate_dnsmasq_config( self, db_only=False, upstream_servers=UvnDefaults["nameserver"]["upstream_servers"], localhost_only=False, except_interfaces=[], noupstream=False): db_dir = self._basedir / UvnDefaults["nameserver"]["db_dir"] hosts_dir = self._basedir / UvnDefaults["nameserver"]["hosts_dir"] hosts_file = hosts_dir / UvnDefaults["nameserver"]["hosts_file"] db_file = db_dir / UvnDefaults["nameserver"]["db_file"] conf_file = UvnDefaults["nameserver"]["conf_file"] logger.trace( "[dnsmasq][generating] configuration: conf_file={}, hosts_file={}", conf_file, hosts_file) db_dir.mkdir(parents=True, exist_ok=True) # render(self, "dnsmasq-db", to_file=db_file) render(self, "dnsmasq-hosts", to_file=hosts_file, export_all=True) if not db_only: render(self, "dnsmasq-conf", to_file=conf_file, context={ "except_interfaces": except_interfaces, "upstream_servers": upstream_servers if not noupstream else [], "hosts_dir": hosts_dir, "hosts_file": hosts_file, "db_dir": db_dir, "db_file": db_file, "local_only": localhost_only, "noupstream": noupstream }) resolv_conf = pathlib.Path("/etc/resolv.conf") resolv_conf_bkp = pathlib.Path("/etc/resolv.conf.uno.bkp") if not resolv_conf_bkp.exists(): shutil.copy2(str(resolv_conf), str(resolv_conf_bkp)) else: logger.warning("not overwritten: {}", resolv_conf_bkp) render(self, "resolv-conf", to_file=resolv_conf, context={"noupstream": noupstream}) self.except_interfaces = list(except_interfaces) self.upstream_servers = list(upstream_servers) self.noupstream = noupstream self.localhost_only = localhost_only
def _create_backbone(self): wg_backbone = [] if self.cell_cfg is not None: for b in self.cell_cfg.backbone: wg_config = render(b, "wireguard-cfg") wgi = WireGuardInterface( b.interface, b.addr_local, UvnDefaults["registry"]["vpn"]["backbone2"]["netmask"], wg_config, keep=self.keep, allowed_ips={ ipaddress.ip_network(a) for a in UvnDefaults["registry"]["vpn"]["router"] ["allowed_ips"] }) wg_backbone.append(wgi) return wg_backbone
def _initialize_runner_context(tmp_dir, basedir, dockerfile, container_arch, connext_helper, keep=False, copy_uno=False, copy_uvn=False, build_wheel=False, dev=False, connext=False): logger.activity("[context] initializing: {}", tmp_dir) context_data = [] extra_args = {} if copy_uno and not dev: repo_dir = DockerController._clone_uno(tmp_dir, keep=keep, dev=dev) context_data.append(repo_dir.name) if copy_uvn: uvn_dir = DockerController._clone_uvn(tmp_dir, basedir, keep=keep) context_data.append(uvn_dir.name) # Retrieve a pre-built wheel file to install connextdds-py connextdds_wheel = None if not build_wheel: try: connextdds_wheel = StaticData.connextdds_wheel(container_arch) except Exception as e: logger.warning( "no prebuilt connextdds-py wheel found for architecture {}" ) if not connextdds_wheel or build_wheel: connextdds_wheel = connext_helper.py.build(container_arch) dds_whl_path = tmp_dir / connextdds_wheel.name connextdds_wheel.copy_to(dds_whl_path) context_data.append(dds_whl_path.name) extra_args["CONNEXTDDS_WHEEL"] = connextdds_wheel.name if connext: # Copy Connext DDS libraries and routing service dds_path = tmp_dir / UvnDefaults["dds"]["home"] connextdds_arc = UvnDefaults["dds"]["connext"]["arch"][ container_arch] connext_helper.copy_to(dds_path, archs=[connextdds_arc]) context_data.append(dds_path.name) # Instantiate Dockerfile dockerfile_path = tmp_dir / "Dockerfile" base_image = None if container_arch == "x86_64": if sys.version_info.minor == 6: base_image = "ubuntu:18.04" elif sys.version_info.minor == 8: base_image = "ubuntu:20.04" elif container_arch == "armv7l": if sys.version_info.minor == 7: base_image = "balenalib/raspberry-pi-debian:latest" else: raise ValueError( f"unsupported container architecture: {container_arch}") if not base_image: raise RuntimeError( f"Python 3.{sys.version_info.minor} not supported in {container_arch} uno containers yet" ) dockerfile_tmplt = Dockerfile(base_image=base_image, dev=dev, ndds=False, rpi_extra=container_arch == "armv7l") render(dockerfile_tmplt, "Dockerfile", to_file=dockerfile_path) context_data.append("Dockerfile") # Instantiate entrypoint script entrypoint_path = tmp_dir / "entrypoint.sh" with entrypoint_path.open("w") as output: entrypoint_stream = StaticData.script("entrypoint.sh", binary=False) shutil.copyfileobj(entrypoint_stream, output) context_data.append("entrypoint.sh") # Create a tar containing the custom build context context_tar = tmp_dir / UvnDefaults["docker"]["context"]["tar"] cmd_args = ["tar", "cvf", context_tar] cmd_args.extend(context_data) exec_command( cmd_args, cwd=tmp_dir, fail_msg="failed to archive build context: {}".format(context_tar)) return context_tar, extra_args
def _quagga_start(self, cfg): # Make sure services are stopped self._quagga_stop() try: pid_zebra = self._basedir / UvnDefaults["router"]["zebra"]["pid"] pid_ospfd = self._basedir / UvnDefaults["router"]["ospfd"]["pid"] conf_zebra = self._basedir / UvnDefaults["router"]["zebra"]["conf"] conf_ospfd = self._basedir / UvnDefaults["router"]["ospfd"]["conf"] # Generate configuration files render(cfg, "ospfd.conf", to_file=conf_ospfd, context={"basedir": self._basedir}) render(cfg, "zebra.conf", to_file=conf_zebra, context={"basedir": self._basedir}) # Create log directories ospfd_log = self._basedir / UvnDefaults["router"]["ospfd"]["log"] ospfd_log.parent.mkdir(exist_ok=True, parents=True) zebra_log = self._basedir / UvnDefaults["router"]["zebra"]["log"] zebra_log.parent.mkdir(exist_ok=True, parents=True) # Create directory for socket file zebra_socket = self._basedir / UvnDefaults["router"]["zebra"][ "socket"] zebra_socket.parent.mkdir(exist_ok=True, parents=True) # Create directory for vty sockets vty_dir = pathlib.Path(UvnDefaults["router"]["vty"]["dir"]) vty_dir.mkdir(exist_ok=True, parents=True) # Create config file for vtysh vtysh_conf = pathlib.Path(UvnDefaults["router"]["vty"]["conf"]) render(cfg, "vtysh.conf", to_file=vtysh_conf) q_user = UvnDefaults["router"]["user"] q_group = UvnDefaults["router"]["group"] # Change permissions of file and directories accessed by daemons chown(f"{q_user}:{q_group}", zebra_socket.parent, vty_dir) for f in [ospfd_log, zebra_log, pid_ospfd, pid_zebra]: touch(str(f)) chown(f"{q_user}:{q_group}", str(f)) self._zebra = QuaggaThread("zebra", conf_zebra, pid_zebra, zebra_socket, q_user, q_group) self._ospfd = QuaggaThread("ospfd", conf_ospfd, pid_ospfd, zebra_socket, q_user, q_group) self._zebra.start() # Wait a few seconds for zebra to start and create its socket time.sleep(UvnDefaults["router"]["start_wait"]) self._ospfd.start() time.sleep(UvnDefaults["router"]["start_wait"]) self._started = True except Exception as e: logger.exception(e) logger.error("failed to start quagga daemons") # Try to reset state to stopped self.stop() raise e return self._zebra, self._ospfd