Exemple #1
0
async def get_cluster_view(client: AdminAPI) -> ClusterView:
    """
    Returns ClusterView object
    """
    (nodes_config_resp, nodes_state_resp,
     maintenances_resp) = await asyncio.gather(
         admin_api.get_nodes_config(client),
         admin_api.get_nodes_state(client),
         admin_api.get_maintenances(client),
         return_exceptions=True,
     )

    if isinstance(maintenances_resp, NotSupported):
        # This exception can be raised from cluster which does not support
        # MaintenanceManager yet
        maintenances = []
    elif isinstance(maintenances_resp, Exception):
        raise maintenances_resp
    else:
        # pyre-fixme[16]: `BaseException` has no attribute `maintenances`.
        maintenances = maintenances_resp.maintenances

    if isinstance(nodes_config_resp, Exception):
        raise nodes_config_resp

    if isinstance(nodes_state_resp, Exception):
        raise nodes_state_resp

    return ClusterView(
        # pyre-fixme[16]: `BaseException` has no attribute `nodes`.
        nodes_config=nodes_config_resp.nodes,
        # pyre-fixme[16]: `BaseException` has no attribute `states`.
        nodes_state=nodes_state_resp.states,
        maintenances=maintenances,
    )
Exemple #2
0
    def _combine(
        cv: ClusterView,
        shards: Optional[List[str]] = None,
        node_names: Optional[List[str]] = None,
        node_indexes: Optional[List[int]] = None,
    ) -> Tuple[ShardID, ...]:

        shards = list(shards or [])
        node_names = list(node_names or [])
        node_indexes = list(node_indexes or [])

        shard_ids = parse_shards(shards)
        for nn in node_names:
            shard_ids.add(ShardID(node=cv.get_node_id(node_name=nn), shard_index=-1))
        for ni in node_indexes:
            shard_ids.add(ShardID(node=NodeID(node_index=ni), shard_index=-1))
        shard_ids_expanded = cv.expand_shards(shard_ids)

        return shard_ids_expanded
Exemple #3
0
                def one_node(mv: MaintenanceView, cv: ClusterView,
                             ni: int) -> str:
                    def shards_table(mv: MaintenanceView, cv: ClusterView,
                                     ni: int) -> str:
                        headers = [
                            "SHARD INDEX",
                            "CURRENT STATE",
                            "TARGET STATE",
                            "MAINTENANCE STATUS",
                            "LAST UPDATED",
                        ]
                        tbl = []
                        for shard in mv.get_shards_by_node_index(ni):
                            target_state = mv.shard_target_state
                            shard_state = mv.get_shard_state(shard)
                            cur_op_state = shard_state.current_operational_state
                            current_state = colored(
                                cur_op_state.name,
                                _color_shard_op_state(
                                    cur_op_state,
                                    # pyre-ignore
                                    mv.shard_target_state,
                                ),
                            )

                            mnt_status = mv.get_shard_maintenance_status(shard)
                            maintenance_status = colored(
                                mnt_status.name, _color(mnt_status))
                            last_updated_at = mv.get_shard_last_updated_at(
                                shard)
                            if last_updated_at:
                                last_updated = (
                                    f"{last_updated_at} "
                                    f"({naturaltime(last_updated_at)})")
                            else:
                                last_updated = "-"

                            tbl.append([
                                shard.shard_index,
                                current_state,
                                # pyre-ignore
                                target_state.name,
                                maintenance_status,
                                last_updated,
                            ])
                        return tabulate(tbl, headers=headers, tablefmt="plain")

                    nv = cv.get_node_view(node_index=ni)
                    return "N{node_index} ({node_name}):\n{shards_table}".format(
                        node_index=nv.node_index,
                        node_name=nv.node_name,
                        shards_table=indent(shards_table(mv, cv, ni), "  "),
                    )
Exemple #4
0
        def sequencers(mv: MaintenanceView, cv: ClusterView) -> Optional[str]:
            if not mv.affects_sequencers:
                return None

            headers = [
                "NODE INDEX",
                "NODE NAME",
                "LOCATION",
                "TARGET STATE",
                "CURRENT STATE",
                "MAINTENANCE STATUS",
                "LAST UPDATED",
            ]
            tbl = []
            for n in mv.sequencer_nodes:
                nv = cv.get_node_view(node_index=n.node_index)
                node_index = nv.node_index
                node_name = nv.node_name
                location = nv.location
                # pyre-ignore
                target_state = mv.sequencer_target_state.name
                mnt_status = mv.get_sequencer_maintenance_status(n)
                current_state = colored(
                    # pyre-ignore
                    nv.sequencer_state.state.name,
                    _color(mnt_status),
                )
                maintenance_status = colored(mnt_status.name,
                                             _color(mnt_status))

                last_updated_at = mv.get_sequencer_last_updated_at(n)
                if last_updated_at:
                    last_updated = f"{last_updated_at} ({naturaltime(last_updated_at)})"
                else:
                    last_updated = "-"

                tbl.append([
                    node_index,
                    node_name,
                    location,
                    target_state,
                    current_state,
                    maintenance_status,
                    last_updated,
                ])
            return "Sequencer Maintenances:\n{}".format(
                indent(tabulate(tbl, headers=headers, tablefmt="plain"),
                       prefix="  "))
Exemple #5
0
            def aggregated(mv: MaintenanceView, cv: ClusterView) -> str:
                headers = [
                    "NODE INDEX",
                    "NODE NAME",
                    "LOCATION",
                    "TARGET STATE",
                    "CURRENT STATE",
                    "MAINTENANCE STATUS",
                    "LAST UPDATED",
                ]
                tbl = []
                for ni in mv.affected_storage_node_indexes:
                    nv = cv.get_node_view(node_index=ni)
                    node_index = ni
                    node_name = nv.node_name
                    location = nv.location
                    target_state = (
                        # pyre-ignore
                        f"{mv.shard_target_state.name}"
                        f"({len(mv.get_shards_by_node_index(ni))})")

                    # current_state
                    chunks = []
                    for cur_op_state, num in sorted(
                            Counter(
                                (mv.get_shard_state(
                                    shard).current_operational_state
                                 for shard in mv.get_shards_by_node_index(ni)
                                 )).items(),
                            key=lambda x: x[0].name,
                    ):
                        chunks.append(
                            colored(
                                f"{cur_op_state.name}({num})",
                                _color_shard_op_state(
                                    cur_op_state,
                                    # pyre-ignore
                                    mv.shard_target_state,
                                ),
                            ))

                    current_state = ",".join(chunks)

                    # maintenance status
                    mnt_statuses = [
                        mv.get_shard_maintenance_status(shard)
                        for shard in mv.get_shards_by_node_index(ni)
                    ]
                    chunks = []
                    for mnt_status, num in sorted(
                            Counter(mnt_statuses).items(),
                            key=lambda x: x[0].name):
                        chunks.append(
                            colored(f"{mnt_status.name}({num})",
                                    _color(mnt_status)))

                    maintenance_status = ",".join(chunks)

                    last_updated_at_time = min((
                        ShardMaintenanceProgress.from_thrift(
                            # pyre-ignore
                            ss.maintenance).last_updated_at
                        for ss in nv.shard_states if ss.maintenance))
                    last_updated_at = (
                        f"{last_updated_at_time} ({naturaltime(last_updated_at_time)})"
                    )

                    tbl.append([
                        node_index,
                        node_name,
                        location,
                        target_state,
                        current_state,
                        maintenance_status,
                        last_updated_at,
                    ])
                return tabulate(tabular_data=tbl,
                                headers=headers,
                                tablefmt="plain")
Exemple #6
0
    def _validate(
        self,
        cv: ClusterView,
        ncs: List[NodeConfig],
        nss: List[NodeState],
        mnts: Tuple[MaintenanceDefinition, ...],
    ):
        nis = sorted(nc.node_index for nc in ncs)
        ni_to_nc = {nc.node_index: nc for nc in ncs}
        ni_to_ns = {ns.node_index: ns for ns in nss}
        ni_to_mnts: Dict[int,
                         List[MaintenanceDefinition]] = {ni: []
                                                         for ni in nis}
        for mnt in mnts:
            mnt_nis = set()
            for s in mnt.shards:
                assert s.node.node_index is not None
                mnt_nis.add(s.node.node_index)
            for n in mnt.sequencer_nodes:
                assert n.node_index is not None
                mnt_nis.add(n.node_index)
            for ni in mnt_nis:
                ni_to_mnts[ni].append(mnt)

        self.assertEqual(sorted(cv.get_all_node_indexes()),
                         sorted(ni_to_nc.keys()))

        self.assertEqual(
            sorted(cv.get_all_node_views(),
                   key=operator.attrgetter("node_index")),
            sorted(
                (NodeView(
                    node_config=ni_to_nc[ni],
                    node_state=ni_to_ns[ni],
                    maintenances=tuple(ni_to_mnts[ni]),
                ) for ni in ni_to_nc.keys()),
                key=operator.attrgetter("node_index"),
            ),
        )

        self.assertEqual(sorted(cv.get_all_node_names()),
                         sorted(nc.name for nc in ncs))

        self.assertEqual(sorted(cv.get_all_maintenance_ids()),
                         sorted(mnt.group_id for mnt in mnts))

        self.assertEqual(
            sorted(cv.get_all_maintenances(),
                   key=operator.attrgetter("group_id")),
            sorted(mnts, key=operator.attrgetter("group_id")),
        )

        for ni in nis:
            nn = ni_to_nc[ni].name
            nc = ni_to_nc[ni]
            ns = ni_to_ns[ni]
            mnts = tuple(ni_to_mnts[ni])
            nv = NodeView(node_config=ni_to_nc[ni],
                          node_state=ni_to_ns[ni],
                          maintenances=mnts)

            self.assertEqual(cv.get_node_view_by_node_index(ni), nv)
            self.assertEqual(cv.get_node_name_by_node_index(ni), nn)
            self.assertEqual(cv.get_node_config_by_node_index(ni), nc)
            self.assertEqual(cv.get_node_state_by_node_index(ni), ns)
            self.assertEqual(cv.get_maintenances_by_node_index(ni), mnts)

            self.assertEqual(cv.get_node_view_by_node_name(nn), nv)
            self.assertEqual(cv.get_node_index_by_node_name(nn), ni)
            self.assertEqual(cv.get_node_config_by_node_name(nn), nc)
            self.assertEqual(cv.get_node_state_by_node_name(nn), ns)
            self.assertEqual(cv.get_maintenances_by_node_name(nn), mnts)

            self.assertEqual(cv.get_node_view(node_name=nn), nv)
            self.assertEqual(cv.get_node_index(node_name=nn), ni)
            self.assertEqual(cv.get_node_config(node_name=nn), nc)
            self.assertEqual(cv.get_node_state(node_name=nn), ns)
            self.assertEqual(cv.get_maintenances(node_name=nn), mnts)

            self.assertEqual(cv.get_node_view(node_index=ni), nv)
            self.assertEqual(cv.get_node_name(node_index=ni), nn)
            self.assertEqual(cv.get_node_config(node_index=ni), nc)
            self.assertEqual(cv.get_node_state(node_index=ni), ns)
            self.assertEqual(cv.get_maintenances(node_index=ni), mnts)

        with self.assertRaises(ValueError):
            cv.get_node_view(None, None)

        with self.assertRaises(ValueError):
            cv.get_node_config(None, None)

        with self.assertRaises(ValueError):
            cv.get_node_state(None, None)

        with self.assertRaises(ValueError):
            cv.get_maintenances(None, None)

        # mismatch node_index and node_name
        if len(nis) > 1:
            nn = ni_to_nc[nis[0]].name
            ni = nis[1]
            with self.assertRaises(ValueError):
                cv.get_node_view(ni, nn)

            with self.assertRaises(ValueError):
                cv.get_node_config(ni, nn)

            with self.assertRaises(ValueError):
                cv.get_node_state(ni, nn)

            with self.assertRaises(ValueError):
                cv.get_maintenances(ni, nn)

        # non-existent node_index
        with self.assertRaises(KeyError):
            cv.get_node_view(node_index=max(nis) + 1)

        # non-existent node_name
        with self.assertRaises(KeyError):
            nns = {nc.name for nc in ncs}
            while True:
                nn = gen_word()
                if nn not in nns:
                    break
            cv.get_node_view(node_name=nn)

        for mnt in mnts:
            assert mnt.group_id is not None
            self.assertEqual(cv.get_maintenance_by_id(mnt.group_id), mnt)
    def _validate(
        self,
        cv: ClusterView,
        ncs: List[NodeConfig],
        nss: List[NodeState],
        mnts: Tuple[MaintenanceDefinition, ...],
    ) -> None:
        nis = sorted(nc.node_index for nc in ncs)
        ni_to_nc = {nc.node_index: nc for nc in ncs}
        ni_to_ns = {ns.node_index: ns for ns in nss}
        ni_to_mnts: Dict[int,
                         List[MaintenanceDefinition]] = {ni: []
                                                         for ni in nis}
        for mnt in mnts:
            mnt_nis = set()
            for s in mnt.shards:
                assert s.node.node_index is not None
                mnt_nis.add(s.node.node_index)
            for n in mnt.sequencer_nodes:
                assert n.node_index is not None
                mnt_nis.add(n.node_index)
            for ni in mnt_nis:
                ni_to_mnts[ni].append(mnt)

        self.assertEqual(sorted(cv.get_all_node_indexes()),
                         sorted(ni_to_nc.keys()))

        self.assertEqual(
            sorted(cv.get_all_node_views(),
                   key=operator.attrgetter("node_index")),
            sorted(
                (NodeView(
                    node_config=ni_to_nc[ni],
                    node_state=ni_to_ns[ni],
                    maintenances=tuple(ni_to_mnts[ni]),
                ) for ni in ni_to_nc.keys()),
                key=operator.attrgetter("node_index"),
            ),
        )

        self.assertEqual(sorted(cv.get_all_node_names()),
                         sorted(nc.name for nc in ncs))

        self.assertEqual(
            sorted(cv.get_all_maintenance_ids()),
            # pyre-fixme[6]: Expected `Iterable[Variable[_LT (bound to
            #  _SupportsLessThan)]]` for 1st param but got
            #  `Generator[typing.Optional[str], None, None]`.
            sorted(mnt.group_id for mnt in mnts),
        )

        self.assertEqual(
            sorted(cv.get_all_maintenances(),
                   key=operator.attrgetter("group_id")),
            sorted(mnts, key=operator.attrgetter("group_id")),
        )

        for ni in nis:
            nn = ni_to_nc[ni].name
            nc = ni_to_nc[ni]
            ns = ni_to_ns[ni]
            node_mnts = tuple(ni_to_mnts[ni])
            nv = NodeView(
                node_config=ni_to_nc[ni],
                node_state=ni_to_ns[ni],
                maintenances=node_mnts,
            )

            self.assertEqual(cv.get_node_view_by_node_index(ni), nv)
            self.assertEqual(cv.get_node_name_by_node_index(ni), nn)
            self.assertEqual(cv.get_node_config_by_node_index(ni), nc)
            self.assertEqual(cv.get_node_state_by_node_index(ni), ns)
            self.assertEqual(cv.get_node_maintenances_by_node_index(ni),
                             node_mnts)

            self.assertEqual(cv.get_node_view_by_node_name(nn), nv)
            self.assertEqual(cv.get_node_index_by_node_name(nn), ni)
            self.assertEqual(cv.get_node_config_by_node_name(nn), nc)
            self.assertEqual(cv.get_node_state_by_node_name(nn), ns)
            self.assertEqual(cv.get_node_maintenances_by_node_name(nn),
                             node_mnts)

            self.assertEqual(cv.get_node_view(node_name=nn), nv)
            self.assertEqual(cv.get_node_index(node_name=nn), ni)
            self.assertEqual(cv.get_node_config(node_name=nn), nc)
            self.assertEqual(cv.get_node_state(node_name=nn), ns)
            self.assertEqual(cv.get_node_maintenances(node_name=nn), node_mnts)

            self.assertEqual(cv.get_node_view(node_index=ni), nv)
            self.assertEqual(cv.get_node_name(node_index=ni), nn)
            self.assertEqual(cv.get_node_config(node_index=ni), nc)
            self.assertEqual(cv.get_node_state(node_index=ni), ns)
            self.assertEqual(cv.get_node_maintenances(node_index=ni),
                             node_mnts)

        with self.assertRaises(ValueError):
            cv.get_node_view(None, None)

        with self.assertRaises(ValueError):
            cv.get_node_config(None, None)

        with self.assertRaises(ValueError):
            cv.get_node_state(None, None)

        with self.assertRaises(ValueError):
            cv.get_node_maintenances(None, None)

        # mismatch node_index and node_name
        if len(nis) > 1:
            nn = ni_to_nc[nis[0]].name
            ni = nis[1]
            with self.assertRaises(ValueError):
                cv.get_node_view(ni, nn)

            with self.assertRaises(ValueError):
                cv.get_node_config(ni, nn)

            with self.assertRaises(ValueError):
                cv.get_node_state(ni, nn)

            with self.assertRaises(ValueError):
                cv.get_node_maintenances(ni, nn)

        # non-existent node_index
        with self.assertRaises(NodeNotFoundError):
            cv.get_node_view(node_index=max(nis) + 1)

        # non-existent node_name
        with self.assertRaises(NodeNotFoundError):
            nns = {nc.name for nc in ncs}
            nn: str = ""
            while True:
                nn = gen_word()
                if nn not in nns:
                    break
            cv.get_node_view(node_name=nn)

        for mnt in mnts:
            group_id: str = mnt.group_id or ""
            self.assertEqual(cv.get_maintenance_by_id(group_id), mnt)
            self.assertTupleEqual(
                cv.get_node_indexes_by_maintenance_id(group_id),
                tuple(
                    sorted(
                        set({
                            n.node_index
                            for n in mnt.sequencer_nodes
                            if n.node_index is not None
                        }).union({
                            s.node.node_index
                            for s in mnt.shards
                            if s.node.node_index is not None
                        }))),
            )
            self.assertEqual(group_id,
                             cv.get_maintenance_view_by_id(group_id).group_id)

        self.assertListEqual(
            # pyre-fixme[6]: Expected `Iterable[Variable[_LT (bound to
            #  _SupportsLessThan)]]` for 1st param but got
            #  `Generator[typing.Optional[str], None, None]`.
            sorted(m.group_id for m in mnts),
            sorted(mv.group_id for mv in cv.get_all_maintenance_views()),
        )

        # expand_shards
        self.assertEqual(
            cv.expand_shards(shards=[
                ShardID(node=NodeID(node_index=nis[0]), shard_index=0)
            ]),
            (ShardID(
                node=NodeID(
                    node_index=ni_to_nc[nis[0]].node_index,
                    name=ni_to_nc[nis[0]].name,
                    address=ni_to_nc[nis[0]].data_address,
                ),
                shard_index=0,
            ), ),
        )
        num_shards: int = getattr(ni_to_nc[nis[0]], "storage",
                                  StorageConfig()).num_shards
        self.assertEqual(
            len(
                cv.expand_shards(shards=[
                    ShardID(node=NodeID(node_index=nis[0]),
                            shard_index=ALL_SHARDS)
                ])),
            num_shards,
        )
        self.assertEqual(
            len(
                cv.expand_shards(shards=[
                    ShardID(node=NodeID(node_index=nis[0]),
                            shard_index=ALL_SHARDS),
                    ShardID(node=NodeID(node_index=nis[0]),
                            shard_index=ALL_SHARDS),
                    ShardID(node=NodeID(node_index=nis[1]),
                            shard_index=ALL_SHARDS),
                ])),
            getattr(ni_to_nc[nis[0]], "storage", StorageConfig()).num_shards +
            getattr(ni_to_nc[nis[1]], "storage", StorageConfig()).num_shards,
        )
        self.assertEqual(
            len(
                cv.expand_shards(
                    shards=[
                        ShardID(node=NodeID(node_index=nis[0]),
                                shard_index=ALL_SHARDS),
                        ShardID(node=NodeID(node_index=nis[1]), shard_index=0),
                    ],
                    node_ids=[NodeID(node_index=0)],
                )),
            getattr(ni_to_nc[nis[0]], "storage", StorageConfig()).num_shards +
            1,
        )

        # normalize_node_id
        self.assertEqual(
            cv.normalize_node_id(NodeID(node_index=nis[0])),
            NodeID(
                node_index=nis[0],
                address=ni_to_nc[nis[0]].data_address,
                name=ni_to_nc[nis[0]].name,
            ),
        )
        self.assertEqual(
            cv.normalize_node_id(NodeID(name=ni_to_nc[nis[0]].name)),
            NodeID(
                node_index=nis[0],
                address=ni_to_nc[nis[0]].data_address,
                name=ni_to_nc[nis[0]].name,
            ),
        )

        # get_node_id
        self.assertEqual(cv.get_node_id(node_index=0).node_index, 0)

        # search_maintenances
        storages_node_views = [
            nv for nv in cv.get_all_node_views() if nv.is_storage
        ]
        sequencers_node_views = [
            nv for nv in cv.get_all_node_views() if nv.is_sequencer
        ]
        self.assertEqual(len(cv.search_maintenances()), len(mnts))
        self.assertEqual(len(cv.search_maintenances(node_ids=[])), 0)
        self.assertEqual(
            len(
                cv.search_maintenances(sequencer_nodes=[
                    storages_node_views[3].node_id,
                    sequencers_node_views[4].node_id,
                ])),
            1,
        )
        self.assertEqual(
            len(
                cv.search_maintenances(node_ids=[
                    storages_node_views[3].node_id,
                    sequencers_node_views[4].node_id,
                ])),
            1,
        )
        self.assertEqual(
            len(
                cv.search_maintenances(shards=[
                    ShardID(node=storages_node_views[1].node_id, shard_index=1)
                ])),
            1,
        )

        # shard_target_state
        self.assertEqual(
            len(
                cv.search_maintenances(
                    shard_target_state=ShardOperationalState.MAY_DISAPPEAR)),
            3,
        )
        self.assertEqual(
            len(
                cv.search_maintenances(
                    shard_target_state=ShardOperationalState.DRAINED)),
            0,
        )

        # sequencer_target_state
        self.assertEqual(
            len(
                cv.search_maintenances(
                    sequencer_target_state=SequencingState.ENABLED)),
            0,
        )
        self.assertEqual(
            len(
                cv.search_maintenances(
                    sequencer_target_state=SequencingState.DISABLED)),
            3,
        )

        self.assertEqual(len(cv.search_maintenances(user="******")), 1)
        self.assertEqual(len(cv.search_maintenances(reason="whatever")), 1)

        self.assertEqual(len(cv.search_maintenances(skip_safety_checks=True)),
                         0)
        self.assertEqual(len(cv.search_maintenances(skip_safety_checks=False)),
                         4)

        self.assertEqual(
            len(cv.search_maintenances(force_restore_rebuilding=True)), 0)
        self.assertEqual(
            len(cv.search_maintenances(force_restore_rebuilding=False)), 4)

        self.assertEqual(
            len(cv.search_maintenances(allow_passive_drains=True)), 0)
        self.assertEqual(
            len(cv.search_maintenances(allow_passive_drains=False)), 4)

        self.assertEqual(
            len(cv.search_maintenances(group_id=mnts[0].group_id)), 1)

        self.assertEqual(
            len(
                cv.search_maintenances(
                    progress=MaintenanceProgress.IN_PROGRESS)), 4)