def read_resource(self, ctx: HandlerContext, resource: Config) -> None: if resource.facts: return vyos = self.get_connection(ctx, resource.id.version, resource) current = self.get_config_dict(ctx, resource, vyos) cfg = current for key in resource.node.split(" "): if isinstance(cfg, str): cfg = {cfg: {}} break elif key in cfg: cfg = cfg[key] else: raise ResourcePurged() ctx.debug( "Comparing desired with current", desired=resource.config, current=cfg, node=resource.node, raw_current=current, ) current_cfg = self._dict_to_path(resource.node, cfg) ctx.debug("Current paths", path=current_cfg) resource.config = current_cfg
def _execute_command(self, ctx: HandlerContext, vyos, command, terminator, timeout=10): """Patch for wonky behavior of vymgmt, after exit it can no longer use the unique prompt""" conn = vyos._Router__conn conn.sendline(command) i = conn.expect([terminator, TIMEOUT], timeout=timeout) output = conn.before if isinstance(output, bytes): output = output.decode("utf-8") if not i == 0: ctx.debug("got raw result %(result)s", result=conn.before.decode(), cmd=command) raise vymgmt.VyOSError("Connection timed out") if not conn.prompt(): ctx.debug("got raw result %(result)s", result=conn.before.decode(), cmd=command) raise vymgmt.VyOSError("Connection timed out") return output
def create_resource(self, ctx: HandlerContext, resource: Config) -> None: if resource.facts: return ctx.debug("Creating resource, invalidating cache") self._invalidate_cache(resource) self._execute(ctx, resource, delete=False) ctx.set_created()
def update_resource(self, ctx: HandlerContext, changes: dict, resource: Config) -> None: if resource.facts: return ctx.debug("Updating resource, invalidating cache") self._invalidate_cache(resource) self._execute(ctx, resource, delete=True) ctx.set_updated()
def create_resource(self, ctx: HandlerContext, resource: Config) -> None: vyos = self.get_connection(ctx, resource.id.version, resource) # try old command first, new one hangs due to insufficient entropy cmd = "generate vpn rsa-key bits 2048 random /dev/urandom" result = vyos.run_op_mode_command(cmd) if "Invalid command:" in result: cmd = "generate vpn rsa-key bits 2048" result = vyos.run_op_mode_command(cmd) ctx.debug("got result %(result)s", result=result, cmd=cmd) assert "has been generated" in result
def _execute(self, ctx: handler.HandlerContext, events: dict, cache: AgentCache) -> (bool, bool): """ :param ctx The context to use during execution of this deploy :param events Possible events that are available for this resource :param cache The cache instance to use :return (success, send_event) Return whether the execution was successful and whether a change event should be sent to provides of this resource. """ ctx.debug("Start deploy %(deploy_id)s of resource %(resource_id)s", deploy_id=self.gid, resource_id=self.resource_id) provider = None try: provider = handler.Commander.get_provider(cache, self.scheduler.agent, self.resource) provider.set_cache(cache) except Exception: if provider is not None: provider.close() cache.close_version(self.resource.id.version) ctx.set_status(const.ResourceState.unavailable) ctx.exception("Unable to find a handler for %(resource_id)s", resource_id=str(self.resource.id)) return False, False yield self.scheduler.agent.thread_pool.submit(provider.execute, ctx, self.resource) send_event = (hasattr(self.resource, "send_event") and self.resource.send_event) if ctx.status is not const.ResourceState.deployed: provider.close() cache.close_version(self.resource.id.version) return False, send_event if len(events) > 0 and provider.can_process_events(): ctx.info( "Sending events to %(resource_id)s because of modified dependencies", resource_id=str(self.resource.id)) yield self.scheduler.agent.thread_pool.submit( provider.process_events, ctx, self.resource, events) provider.close() cache.close_version(self.resource_id.version) return True, send_event
def facts(self, ctx: HandlerContext, resource: Config) -> None: vyos = self.get_connection(ctx, resource.id.version, resource) orig = current = self.get_config_dict(ctx, resource, vyos) path = resource.node.split(" ") for el in path: if el in current: current = current[el] else: ctx.debug("No value found", error=current, path=path, orig=orig) return {} return {"value": current}
def delete_resource(self, ctx: HandlerContext, resource: Config) -> None: if resource.facts: return ctx.debug("Deleting resource, invalidating cache") self._invalidate_cache(resource) vyos = self.get_connection(ctx, resource.id.version, resource) vyos.configure() vyos.delete(resource.node) vyos.commit() if resource.save: vyos.save() vyos.exit(force=True) ctx.set_purged()
def facts(self, ctx: HandlerContext, resource: IpFact) -> None: # example output # vyos@vyos:~$ show interfaces # Codes: S - State, L - Link, u - Up, D - Down, A - Admin Down # Interface IP Address S/L Description # --------- ---------- --- ----------- # eth0 10.0.0.7/24 u/u # eth1 10.1.0.15/24 u/u # lo 127.0.0.1/8 u/u # ::1/128 try: vyos = self.get_connection(ctx, resource.id.version, resource) cmd = "show interfaces" interface = resource.interface result = vyos.run_op_mode_command(cmd).replace("\r", "") ctx.debug("got result %(result)s", result=result, cmd=cmd) parsed_lines = [ self.parse_line(line) for line in result.split("\n") ] parsed_lines = [line for line in parsed_lines if line is not None] # find right lines ips = itertools.dropwhile(lambda x: x[0] != interface, parsed_lines) ips = list( itertools.takewhile(lambda x: x[0] == interface or not x[0], ips)) ctx.debug("got ips %(ips)s", ips=ips) ips = [ip[1] for ip in ips] if not ips: return {} if len(ips) == 1: return {"ip_address": ips[0]} else: ips = sorted(ips) out = {"ip_address": ips[0]} for i, addr in enumerate(ips): out[f"ip_address_{i}"] = addr return out finally: self.post(ctx, resource)
def get_pubkey(self, ctx: HandlerContext, resource: Config) -> str: vyos = self.get_connection(ctx, resource.id.version, resource) cmd = "TERM=ansi show vpn ike rsa-keys" try: result = re.sub( "\x1b\\[[0-9]?[a-zA-Z]", "", vyos.run_op_mode_command(cmd).replace("\r", ""), ) except vymgmt.router.VyOSError: ctx.debug( "got raw raw result %(result)s", result=vyos._Router__conn.before.decode("utf-8"), cmd=cmd, ) raise ctx.debug("got raw result %(result)s", result=result, cmd=cmd) marker = "Local public key (/config/ipsec.d/rsa-keys/localhost.key):" if marker in result: idx = result.find(marker) result = result[idx + len(marker):] if "====" in result: idx = result.find("====") result = result[:idx] ctx.debug("got result %(result)s", result=result, cmd=cmd) result = result.strip() else: raise ResourcePurged() return result
def _execute(self, ctx: HandlerContext, resource: Config, delete: bool) -> None: commands = [x for x in resource.config.split("\n") if len(x) > 0] vyos = self.get_connection(ctx, resource.id.version, resource) try: vyos.configure() if delete and not resource.never_delete: ctx.debug("Deleting %(node)s", node=resource.node) vyos.delete(resource.node) for cmd in commands: ctx.debug("Setting %(cmd)s", cmd=cmd) if delete and resource.never_delete: try: vyos.delete(cmd) except vymgmt.ConfigError: pass vyos.set(cmd) vyos.commit() if resource.save: vyos.save() vyos.exit(force=True) except vymgmt.router.VyOSError: ctx.debug( "got raw raw result %(result)s", result=vyos._Router__conn.before.decode("utf-8"), cmd=cmd, ) raise