Example #1
0
 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)
Example #2
0
 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)
Example #3
0
 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()
Example #4
0
 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)
Example #5
0
 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
Example #6
0
 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)
Example #7
0
 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
Example #8
0
 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]
Example #9
0
    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
Example #10
0
 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
Example #11
0
    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
Example #12
0
    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