Пример #1
0
 async def _prepare_bootstrap(self) -> None:
     assert self._state
     if self._state.stage == NodeStageEnum.BOOTSTRAPPING:
         raise NodeCantBootstrapError("node bootstrapping")
     elif self._state.stage > NodeStageEnum.NONE:
         raise NodeCantBootstrapError("node can't be bootstrapped")
     elif self._init_stage < NodeInitStage.PRESTART:
         raise NodeNotStartedError()
Пример #2
0
    async def deploy(self, params: DeployParamsModel) -> None:

        assert self._state
        if self._init_stage < NodeInitStage.AVAILABLE:
            raise NodeNotStartedError()

        if self.deployment_state.error:
            raise NodeCantDeployError("node is in error state")

        logger.debug("deploy > unsubscribe inventory updates")
        if self._inventory_sub:
            self.gstate.inventory.unsubscribe(self._inventory_sub)

        # check parameters
        if not params.ntpaddr or len(params.ntpaddr) == 0:
            raise NodeCantDeployError("missing ntp server address")
        if not params.hostname or len(params.hostname) == 0:
            raise NodeCantDeployError("missing hostname parameter")

        disk_solution = Disks.gen_solution(self.gstate)
        if not disk_solution.possible:
            raise NodeCantDeployError("no possible deployment solution found")
        assert disk_solution.systemdisk is not None

        disks = DeploymentDisksConfig(system=disk_solution.systemdisk.path)
        disks.storage = [
            d.path for d in disk_solution.storage if d.path is not None
        ]
        logger.debug(f"mgr > deploy > disks: {disks}")

        # set hostname in memory; we'll write it later
        self._state.hostname = params.hostname
        self._token = self._generate_token()
        assert self._state.address

        logger.info("deploy node")
        await self._deployment.deploy(
            DeploymentConfig(
                hostname=params.hostname,
                address=self._state.address,
                token=self._token,
                ntp_addr=params.ntpaddr,
                disks=disks,
            ),
            self._post_bootstrap_finisher,
            self._finish_deployment,
        )
        await self._save_token()
        await self._save_ntp_addr(params.ntpaddr)

        admin_user = UserModel(username="******", password="******")
        admin_user.hash_password()
        user_mgr = UserMgr(self.gstate.store)
        await user_mgr.put(admin_user)
Пример #3
0
    async def join(
        self, leader_address: str, token: str, params: JoinParamsModel
    ) -> bool:
        logger.debug(f"join > with leader {leader_address}, token: {token}")

        if self._init_stage < NodeInitStage.AVAILABLE:
            raise NodeNotStartedError()
        elif self._init_stage > NodeInitStage.AVAILABLE:
            raise NodeCantJoinError()

        if not params.hostname or len(params.hostname) == 0:
            raise NodeError("Hostname parameter not provided.")

        assert self._state
        assert self._state.address

        # set in-memory hostname state, write it later
        self._state.hostname = params.hostname

        disk_solution = Disks.gen_solution(self.gstate)
        if not disk_solution.possible:
            raise NodeCantJoinError("No disk deployment solution found.")
        assert disk_solution.systemdisk is not None

        disks = DeploymentDisksConfig(system=disk_solution.systemdisk.path)
        disks.storage = [
            d.path for d in disk_solution.storage if d.path is not None
        ]

        try:
            res: bool = await self._deployment.join(
                leader_address,
                token,
                self._state.uuid,
                params.hostname,
                self._state.address,
                disks,
            )

            if not res:
                return False

        except Exception as e:
            # propagate exceptions
            raise e

        self._token = token
        await self._save_state()
        await self._node_start()
        return True
Пример #4
0
 def connmgr(self) -> ConnMgr:
     if not self._connmgr:
         raise NodeNotStartedError()
     elif self._shutting_down:
         raise NodeShuttingDownError()
     return self._connmgr
Пример #5
0
 def uuid(self) -> UUID:
     if self._state is None:
         raise NodeNotStartedError()
     return self._state.uuid
Пример #6
0
    async def join(self, leader_address: str, token: str) -> bool:
        logger.debug(f"join > with leader {leader_address}, token: {token}")

        if self._init_stage == NodeInitStage.NONE:
            raise NodeNotStartedError()
        elif self._init_stage > NodeInitStage.PRESTART:
            raise NodeCantJoinError()

        assert self._state
        assert self._state.hostname
        assert self._state.address

        if self._state.stage == NodeStageEnum.BOOTSTRAPPING:
            raise NodeBootstrappingError()
        elif self._state.stage == NodeStageEnum.BOOTSTRAPPED:
            raise NodeHasBeenDeployedError()
        elif self._state.stage == NodeStageEnum.JOINING:
            raise NodeAlreadyJoiningError()
        elif self._state.stage == NodeStageEnum.READY:
            raise NodeHasJoinedError()
        assert self._state.stage == NodeStageEnum.NONE
        assert self._state.role == NodeRoleEnum.NONE

        uri: str = f"ws://{leader_address}/api/nodes/ws"
        conn = await self._connmgr.connect(uri)
        logger.debug(f"join > conn: {conn}")

        joinmsg = JoinMessageModel(uuid=self._state.uuid,
                                   hostname=self._state.hostname,
                                   address=self._state.address,
                                   token=token)
        msg = MessageModel(type=MessageTypeEnum.JOIN, data=joinmsg.dict())
        await conn.send(msg)

        self._state.stage = NodeStageEnum.JOINING
        self._save_state()

        reply: MessageModel = await conn.receive()
        logger.debug(f"join > recv: {reply}")
        if reply.type == MessageTypeEnum.ERROR:
            errmsg = ErrorMessageModel.parse_obj(reply.data)
            logger.error(f"join > error: {errmsg.what}")
            await conn.close()
            self._state.stage = NodeStageEnum.NONE
            self._save_state()
            return False

        assert reply.type == MessageTypeEnum.WELCOME
        welcome = WelcomeMessageModel.parse_obj(reply.data)
        assert welcome.pubkey

        authorized_keys: Path = Path("/root/.ssh/authorized_keys")
        if not authorized_keys.parent.exists():
            authorized_keys.parent.mkdir(0o700)
        with authorized_keys.open("a") as fd:
            fd.writelines([welcome.pubkey])
            logger.debug(f"join > wrote pubkey to {authorized_keys}")

        readymsg = ReadyToAddMessageModel()
        await conn.send(
            MessageModel(type=MessageTypeEnum.READY_TO_ADD, data=readymsg))
        await conn.close()

        self._state.stage = NodeStageEnum.READY
        self._state.role = NodeRoleEnum.FOLLOWER
        self._save_state()

        self._token = token
        self._save_token(should_exist=False)

        self._node_start()
        return True