Example #1
0
    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
Example #2
0
    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
Example #3
0
 def create_resource(self, ctx: HandlerContext,
                     resource: PurgeableResource) -> None:
     self._io.mkdir(resource.path)
     mode = str(resource.permissions)
     self._io.chmod(resource.path, mode)
     self._io.chown(resource.path, resource.owner, resource.group)
     ctx.set_created()
Example #4
0
    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()
Example #5
0
    def read_resource(self, ctx: HandlerContext, resource: AgentConfig) -> None:
        agent_config = self._get_map()
        ctx.set("map", agent_config)

        if resource.agentname not in agent_config:
            raise ResourcePurged()

        resource.uri = agent_config[resource.agentname]
Example #6
0
 def update_resource(self, ctx: HandlerContext, changes: dict,
                     resource: PurgeableResource) -> None:
     if "permissions" in changes:
         mode = str(resource.permissions)
         self._io.chmod(resource.path, mode)
     if "owner" in changes or "group" in changes:
         self._io.chown(resource.path, resource.owner, resource.group)
     ctx.set_updated()
Example #7
0
    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()
Example #8
0
 def post(self, ctx: HandlerContext, resource: Config) -> None:
     if self.connection:
         try:
             # Vyos cannot logout before exiting configuration mode
             self.connection.exit(force=True)
             self.connection.logout()
             self.connection = None
         except:  # noqa: E722
             ctx.exception("Failed to close connection")
Example #9
0
 def deploy(
     self,
     ctx: HandlerContext,
     resource: Resource,
     requires: Dict[ResourceIdStr, const.ResourceState],
 ) -> None:
     if self.skip(resource.id.agent_name, resource.key):
         raise SkipResource()
     elif self.fail(resource.id.agent_name, resource.key):
         raise Exception()
     elif resource.set_state_to_deployed:
         ctx.set_status(const.ResourceState.deployed)
Example #10
0
    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
Example #11
0
    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()
Example #12
0
 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}
Example #13
0
    def create_resource(self, ctx: HandlerContext, resource: PurgeableResource) -> None:
        if self._io.file_exists(resource.path):
            raise Exception(
                f"Cannot create file {resource.path}, because it already exists."
            )

        data = self.get_file(resource.hash)
        if hash_file(data) != resource.hash:
            raise Exception(
                "File hash was %s expected %s" % (resource.hash, hash_file(data))
            )

        self._io.put(resource.path, data)
        self._io.chmod(resource.path, str(resource.permissions))
        self._io.chown(resource.path, resource.owner, resource.group)
        ctx.set_created()
Example #14
0
    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)
Example #15
0
    def update_resource(self, ctx: HandlerContext, changes: dict,
                        resource: PurgeableResource) -> None:
        if not self._io.file_exists(resource.path):
            raise Exception(
                f"Cannot update file {resource.path} because it doesn't exist")

        if "hash" in changes:
            data = self.get_file(resource.hash)
            if hash_file(data) != resource.hash:
                raise Exception("File hash was %s expected %s" %
                                (resource.hash, hash_file(data)))
            self._io.put(resource.path, data)

        if "permissions" in changes:
            self._io.chmod(resource.path, str(resource.permissions))

        if "owner" in changes or "group" in changes:
            self._io.chown(resource.path, resource.owner, resource.group)
        ctx.set_updated()
Example #16
0
    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
Example #17
0
def test_CRUD_handler_purged_response(purged_desired, purged_actual, excn,
                                      create, delete, updated, caplog):
    """
    purged_actual and excn are conceptually equivalent, this test case serves to prove that they are in fact, equivalent
    """
    caplog.set_level(logging.DEBUG)

    class DummyCrud(CRUDHandler):
        def __init__(self):
            self.updated = False
            self.created = False
            self.deleted = False

        def read_resource(self, ctx: HandlerContext,
                          resource: resources.PurgeableResource) -> None:
            resource.purged = purged_actual
            if updated:
                resource.value = "b"
            if excn:
                raise ResourcePurged()

        def update_resource(self, ctx: HandlerContext, changes: dict,
                            resource: resources.PurgeableResource) -> None:
            self.updated = True

        def create_resource(self, ctx: HandlerContext,
                            resource: resources.PurgeableResource) -> None:
            self.created = True

        def delete_resource(self, ctx: HandlerContext,
                            resource: resources.PurgeableResource) -> None:
            self.deleted = True

    @resource("aa::Aa", "aa", "aa")
    class TestResource(PurgeableResource):
        fields = ("value", )

    res = TestResource(Id("aa::Aa", "aa", "aa", "aa", 1))
    res.purged = purged_desired
    res.value = "a"

    ctx = HandlerContext(res, False)

    handler = DummyCrud()
    handler.execute(ctx, res, False)

    assert handler.updated == ((not (create or delete)) and updated
                               and not purged_desired)
    assert handler.created == create
    assert handler.deleted == delete
    no_error_in_logs(caplog)
    log_contains(caplog, "inmanta.agent.handler", logging.DEBUG,
                 "resource aa::Aa[aa,aa=aa],v=1: Calling read_resource")
Example #18
0
    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
Example #19
0
    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
Example #20
0
 def update_resource(self, ctx: HandlerContext, changes: dict,
                     resource: AgentConfig) -> None:
     agent_config = ctx.get("map")
     agent_config[resource.agentname] = resource.uri
     self._set_map(agent_config)
Example #21
0
 def delete_resource(self, ctx: HandlerContext,
                     resource: AgentConfig) -> None:
     agent_config = ctx.get("map")
     del agent_config[resource.agentname]
     self._set_map(agent_config)
Example #22
0
 def _do_set_fact(self, ctx: HandlerContext,
                  resource: PurgeableResource) -> None:
     ctx.set_fact(fact_id=resource.key, value=resource.value)
Example #23
0
def test_context_changes():
    """Test registering changes in the handler context"""
    resource = PurgeableResource(
        Id.parse_id("std::File[agent,path=/test],v=1"))
    ctx = HandlerContext(resource)

    # use attribute change attributes
    ctx.update_changes(
        {"value": AttributeStateChange(current="a", desired="b")})
    assert len(ctx.changes) == 1

    # use dict
    ctx.update_changes({"value": dict(current="a", desired="b")})
    assert len(ctx.changes) == 1
    assert isinstance(ctx.changes["value"], AttributeStateChange)

    # use dict with empty string
    ctx.update_changes({"value": dict(current="", desired="value")})
    assert len(ctx.changes) == 1
    assert ctx.changes["value"].current == ""
    assert ctx.changes["value"].desired == "value"

    # use tuple
    ctx.update_changes({"value": ("a", "b")})
    assert len(ctx.changes) == 1
    assert isinstance(ctx.changes["value"], AttributeStateChange)

    # use wrong arguments
    with pytest.raises(InvalidOperation):
        ctx.update_changes({"value": ("a", "b", 3)})

    with pytest.raises(InvalidOperation):
        ctx.update_changes({"value": ["a", "b"]})

    with pytest.raises(InvalidOperation):
        ctx.update_changes({"value": "test"})
Example #24
0
 def delete_resource(self, ctx: HandlerContext, resource: PurgeableResource) -> None:
     self._io.remove(resource.target)
     ctx.set_purged()
Example #25
0
 def update_resource(
     self, ctx: HandlerContext, changes: dict, resource: PurgeableResource
 ) -> None:
     self._io.remove(resource.target)
     self._io.symlink(resource.source, resource.target)
     ctx.set_updated()
Example #26
0
 def create_resource(self, ctx: HandlerContext, resource: PurgeableResource) -> None:
     self._io.symlink(resource.source, resource.target)
     ctx.set_created()
Example #27
0
 def delete_resource(self, ctx: HandlerContext, resource: PurgeableResource) -> None:
     self._io.rmdir(resource.path)
     ctx.set_purged()
Example #28
0
 def delete_resource(self, ctx: HandlerContext, resource: PurgeableResource) -> None:
     if self._io.file_exists(resource.path):
         self._io.remove(resource.path)
         ctx.set_purged()
Example #29
0
 def update_resource(self, ctx: HandlerContext, changes: dict,
                     resource: ResourceResource) -> None:
     a = self.run_sync(self.test_sync)
     assert a == 5
     ctx.set_updated()
Example #30
0
 def delete_resource(self, ctx: HandlerContext,
                     resource: ResourceResource) -> None:
     ctx.set_purged()