def list(
        cls,
        crd_api: CustomObjectsApi,
        cluster_deployment: "ClusterDeployment",
    ) -> List["Agent"]:
        resources = crd_api.list_namespaced_custom_object(
            group=CRD_API_GROUP,
            version=CRD_API_VERSION,
            plural=cls._plural,
            namespace=cluster_deployment.ref.namespace,
        )
        assigned_agents = []
        for item in resources.get("items", []):
            assigned_cluster_ref = ObjectReference(
                name=item["spec"]["clusterDeploymentName"]["name"],
                namespace=item["spec"]["clusterDeploymentName"]["namespace"],
            )
            if assigned_cluster_ref == cluster_deployment.ref:
                assigned_agents.append(
                    cls(
                        kube_api_client=cluster_deployment.crd_api.api_client,
                        name=item["metadata"]["name"],
                        namespace=item["metadata"]["namespace"],
                    ))

        return assigned_agents
Beispiel #2
0
    def list(
        cls,
        crd_api: CustomObjectsApi,
        cluster_deployment,
        agents_namespace=None,
    ) -> List["Agent"]:
        agents_namespace = agents_namespace or cluster_deployment.ref.namespace
        resources = crd_api.list_namespaced_custom_object(
            group=consts.CRD_API_GROUP,
            version=consts.CRD_API_VERSION,
            plural=cls._plural,
            namespace=agents_namespace,
        )
        assigned_agents = []
        for item in resources.get("items", []):
            if item["spec"].get("clusterDeploymentName") is None:
                # Unbound late-binding agent, not part of the given cluster_deployment
                continue

            assigned_cluster_ref = ObjectReference(
                name=item["spec"]["clusterDeploymentName"]["name"],
                namespace=item["spec"]["clusterDeploymentName"]["namespace"],
            )
            if assigned_cluster_ref == cluster_deployment.ref:
                assigned_agents.append(
                    cls(
                        kube_api_client=cluster_deployment.crd_api.api_client,
                        name=item["metadata"]["name"],
                        namespace=item["metadata"]["namespace"],
                    )
                )

        return assigned_agents
def _patch_and_delete_stubborn_custom_resources(  # type: ignore
    group: str,
    version: str,
    plural: str,
    namespace: str,
    status_element: str,
    logger: kopf.Logger,
    use_async=True,
    **_: Any,
):
    logger.info(f"_patch_and_delete_stubborn_custom_resources for {plural}.{group} in namespace {namespace}")
    co = CustomObjectsApi()
    resp = co.list_namespaced_custom_object(group=group, version=version, plural=plural, namespace=namespace)
    failed_res = [
        item.get("metadata").get("name")
        for item in resp["items"]
        if item.get("status", {}).get(status_element) in ["Failed", "Completed", "InProgress"]
    ]
    for item in failed_res:
        try:
            logger.info(f"Patching item {item} in {plural}.{group}")
            patch = json.loads("""{"metadata":{"finalizers":[]}}""")
            co.patch_namespaced_custom_object(
                group=group, version=version, plural=plural, namespace=namespace, name=item, body=patch
            )
            logger.info(f"Deleting item {item} in {plural}.{group}")
            co.delete_namespaced_custom_object(
                group=group,
                version=version,
                plural=plural,
                namespace=namespace,
                name=item,
            )
        except ApiException as e:
            logger.warn("Trying to patch and delete failed: %s\n" % e)
Beispiel #4
0
def get_custom_object_name(api: client.CustomObjectsApi,
                           name: str,
                           group: str,
                           version: str,
                           plural: str,
                           namespace: str = 'default') -> str:
    """Get the name generated by kubeflow for tf/pyt jobs.

    :param api: The k8s api to interact with tf/pyt jobs.
    :type api: client.CustomObjectsAPI
    :param name: The name that odin gives the task
    :type name: str
    :param group: The group name of the people creating the custom resource (kubeflow.org)
    :param version: The version of the api to use. (defaults to v1beta2 for kubeflow objects)
    :param plural: The name used by the custom objects in the api. TFJob -> tfjobs, PytorchJob -> pytorchjob
    :param namespace: The namespace of the job.

    :returns: The name that kubeflow gives the job.
    """
    jobs = api.list_namespaced_custom_object(group, version, namespace,
                                             plural)['items']
    job = [j for j in jobs if j['metadata']['generateName'] == name]
    if job:
        return job[0]['metadata']['name']
    return None
Beispiel #5
0
    def list(
        cls,
        crd_api: CustomObjectsApi,
        cluster_deployment: 'ClusterDeployment',
    ) -> List['Agent']:
        resources = crd_api.list_namespaced_custom_object(
            group=CRD_API_GROUP,
            version=CRD_API_VERSION,
            plural=cls._plural,
            namespace=cluster_deployment.ref.namespace,
        )
        assigned_agents = []
        for item in resources.get('items', []):
            assigned_cluster_ref = ObjectReference(
                name=item['spec']['clusterDeploymentName']['name'],
                namespace=item['spec']['clusterDeploymentName']['namespace'],
            )
            if assigned_cluster_ref == cluster_deployment.ref:
                assigned_agents.append(
                    cls(
                        kube_api_client=cluster_deployment.crd_api.api_client,
                        name=item['metadata']['name'],
                        namespace=item['metadata']['namespace'],
                    ))

        return assigned_agents
Beispiel #6
0
    def list(cls, crd_api: CustomObjectsApi,
             namespace) -> List["ClusterDeployment"]:
        resources = crd_api.list_namespaced_custom_object(
            group=consts.HIVE_API_GROUP,
            version=consts.HIVE_API_VERSION,
            plural=cls._plural,
            namespace=namespace,
        )

        return resources
    def list(cls, crd_api: CustomObjectsApi,
             namespace) -> List["AgentClusterInstall"]:
        resources = crd_api.list_namespaced_custom_object(
            group=consts.CRD_AGENT_INSTALL_GROUP,
            version=consts.CRD_AGENT_INSTALL_VERSION,
            plural=cls._plural,
            namespace=namespace,
        )

        return resources
    def get_pod_stats(self, tenant, vnf_uuid):

        cust = CustomObjectsApi()
        data = ['metrics.k8s.io', 'v1beta1', tenant, 'pods']
        try:
            if vnf_uuid:
                data.append(self.vnfs[vnf_uuid].name)
                return cust.get_namespaced_custom_object(*data)
            else:
                return cust.list_namespaced_custom_object(*data)
        except ApiException as ex:
            raise KeyError(ex)
Beispiel #9
0
def ensure_custom_object(api: client.CustomObjectsApi, custom_object, group,
                         plural, version, namespace, name):
    if len(
            api.list_namespaced_custom_object(
                namespace=namespace,
                field_selector=f'metadata.name={name}',
                group=group,
                plural=plural,
                version=version)['items']) == 0:
        logger.info(f'creating custom object: {namespace}/{name}')
        api.create_namespaced_custom_object(body=custom_object,
                                            namespace=namespace,
                                            group=group,
                                            plural=plural,
                                            version=version)
    else:
        logger.info(f'custom object exists: {namespace}/{name}')
Beispiel #10
0
def destroy_custom_object(api: client.CustomObjectsApi, group, plural, version,
                          namespace, name):
    if len(
            api.list_namespaced_custom_object(
                namespace=namespace,
                field_selector=f'metadata.name={name}',
                group=group,
                plural=plural,
                version=version)['items']) == 1:
        logger.info(f'destroying custom object: {namespace}/{name}')
        api.delete_namespaced_custom_object(namespace=namespace,
                                            group=group,
                                            plural=plural,
                                            version=version,
                                            name=name,
                                            body=V1DeleteOptions())
    else:
        logger.info(
            f'cannot find custom object to destroy: {namespace}/{name}')
Beispiel #11
0
class InfraEnv(BaseCustomResource):
    """
    InfraEnv is used to generate cluster iso.
    Image is automatically generated on CRD deployment, after InfraEnv is
    reconciled. Image download url will be exposed in the status.
    """

    _plural = "infraenvs"

    def __init__(
        self,
        kube_api_client: ApiClient,
        name: str,
        namespace: str = consts.DEFAULT_NAMESPACE,
    ):
        super().__init__(name, namespace)
        self.crd_api = CustomObjectsApi(kube_api_client)

    def create_from_yaml(self, yaml_data: dict) -> None:
        self.crd_api.create_namespaced_custom_object(
            group=consts.CRD_API_GROUP,
            version=consts.CRD_API_VERSION,
            plural=self._plural,
            body=yaml_data,
            namespace=self.ref.namespace,
        )

        log.info("created infraEnv %s: %s", self.ref, pformat(yaml_data))

    def create(
        self,
        cluster_deployment: Optional[ClusterDeployment],
        secret: Secret,
        proxy: Optional[Proxy] = None,
        ignition_config_override: Optional[str] = None,
        nmstate_label: Optional[str] = None,
        ssh_pub_key: Optional[str] = None,
        **kwargs,
    ) -> None:
        body = {
            "apiVersion": f"{consts.CRD_API_GROUP}/{consts.CRD_API_VERSION}",
            "kind": "InfraEnv",
            "metadata": self.ref.as_dict(),
            "spec": {
                "pullSecretRef": secret.ref.as_dict(),
                "nmStateConfigLabelSelector": {
                    "matchLabels": {
                        f"{consts.CRD_API_GROUP}/selector-nmstate-config-name":
                        nmstate_label or ""
                    }
                },
                "ignitionConfigOverride": ignition_config_override or "",
            },
        }

        # Late-binding infra-envs don't have a clusterRef in the beginning
        if cluster_deployment is not None:
            body["spec"]["clusterRef"] = cluster_deployment.ref.as_dict()

        spec = body["spec"]
        if proxy:
            spec["proxy"] = proxy.as_dict()
        if ssh_pub_key:
            spec["sshAuthorizedKey"] = ssh_pub_key

        spec.update(kwargs)
        self.crd_api.create_namespaced_custom_object(
            group=consts.CRD_API_GROUP,
            version=consts.CRD_API_VERSION,
            plural=self._plural,
            body=body,
            namespace=self.ref.namespace,
        )

        log.info("created infraEnv %s: %s", self.ref, pformat(body))

    def patch(
        self,
        cluster_deployment: Optional[ClusterDeployment],
        secret: Optional[Secret],
        proxy: Optional[Proxy] = None,
        ignition_config_override: Optional[str] = None,
        nmstate_label: Optional[str] = None,
        ssh_pub_key: Optional[str] = None,
        **kwargs,
    ) -> None:
        body = {"spec": kwargs}

        spec = body["spec"]
        if cluster_deployment:
            spec["clusterRef"] = cluster_deployment.ref.as_dict()

        if secret:
            spec["pullSecretRef"] = secret.ref.as_dict()

        if proxy:
            spec["proxy"] = proxy.as_dict()

        if nmstate_label:
            spec["nmStateConfigLabelSelector"] = {
                "matchLabels": {
                    f"{consts.CRD_API_GROUP}/selector-nmstate-config-name":
                    nmstate_label,
                }
            }

        if ignition_config_override:
            spec["ignitionConfigOverride"] = ignition_config_override

        if ssh_pub_key:
            spec["sshAuthorizedKey"] = ssh_pub_key

        self.crd_api.patch_namespaced_custom_object(
            group=consts.CRD_API_GROUP,
            version=consts.CRD_API_VERSION,
            plural=self._plural,
            name=self.ref.name,
            namespace=self.ref.namespace,
            body=body,
        )

        log.info("patching infraEnv %s: %s", self.ref, pformat(body))

    def get(self) -> dict:
        return self.crd_api.get_namespaced_custom_object(
            group=consts.CRD_API_GROUP,
            version=consts.CRD_API_VERSION,
            plural=self._plural,
            name=self.ref.name,
            namespace=self.ref.namespace,
        )

    def delete(self) -> None:
        self.crd_api.delete_namespaced_custom_object(
            group=consts.CRD_API_GROUP,
            version=consts.CRD_API_VERSION,
            plural=self._plural,
            name=self.ref.name,
            namespace=self.ref.namespace,
        )

        log.info("deleted infraEnv %s", self.ref)

    def status(
        self,
        timeout: Union[int, float] = consts.DEFAULT_WAIT_FOR_CRD_STATUS_TIMEOUT
    ) -> dict:
        """
        Status is a section in the CRD that is created after registration to
        assisted service and it defines the observed state of InfraEnv.
        Since the status key is created only after resource is processed by the
        controller in the service, it might take a few seconds before appears.
        """
        def _attempt_to_get_status() -> dict:
            return self.get()["status"]

        return waiting.wait(
            _attempt_to_get_status,
            sleep_seconds=0.5,
            timeout_seconds=timeout,
            waiting_for=f"infraEnv {self.ref} status",
            expected_exceptions=KeyError,
        )

    def get_iso_download_url(
        self,
        timeout: Union[int, float] = consts.DEFAULT_WAIT_FOR_ISO_URL_TIMEOUT,
    ):
        def _attempt_to_get_image_url() -> str:
            return self.get()["status"]["isoDownloadURL"]

        return waiting.wait(
            _attempt_to_get_image_url,
            sleep_seconds=3,
            timeout_seconds=timeout,
            waiting_for="image to be created",
            expected_exceptions=KeyError,
        )

    def get_cluster_id(self):
        iso_download_url = self.get_iso_download_url()
        return ISO_URL_PATTERN.match(iso_download_url).group("cluster_id")

    @classmethod
    def deploy_default_infraenv(
        cls,
        kube_api_client: ApiClient,
        name: str,
        namespace: str,
        pull_secret: str,
        ignore_conflict: bool = True,
        cluster_deployment: Optional[ClusterDeployment] = None,
        secret: Optional[Secret] = None,
        proxy: Optional[Proxy] = None,
        ignition_config_override: Optional[str] = None,
        **kwargs,
    ) -> "InfraEnv":

        infra_env = InfraEnv(kube_api_client, name, namespace)
        try:
            if "filepath" in kwargs:
                infra_env._create_infraenv_from_yaml_file(
                    filepath=kwargs["filepath"], )
            else:
                infra_env._create_infraenv_from_attrs(
                    kube_api_client=kube_api_client,
                    name=name,
                    ignore_conflict=ignore_conflict,
                    pull_secret=pull_secret,
                    cluster_deployment=cluster_deployment,
                    secret=secret,
                    proxy=proxy,
                    ignition_config_override=ignition_config_override,
                    **kwargs,
                )
        except ApiException as e:
            if not (e.reason == "Conflict" and ignore_conflict):
                raise

        # wait until install-env will have status (i.e until resource will be
        # processed in assisted-service).
        infra_env.status()

        return infra_env

    def _create_infraenv_from_yaml_file(
        self,
        filepath: str,
    ) -> None:
        with open(filepath) as fp:
            yaml_data = yaml.safe_load(fp)

        self.create_from_yaml(yaml_data)

    def _create_infraenv_from_attrs(
        self,
        kube_api_client: ApiClient,
        cluster_deployment: ClusterDeployment,
        pull_secret: str,
        secret: Optional[Secret] = None,
        proxy: Optional[Proxy] = None,
        ignition_config_override: Optional[str] = None,
        **kwargs,
    ) -> None:
        if not secret:
            secret = deploy_default_secret(
                kube_api_client=kube_api_client,
                name=cluster_deployment.ref.name,
                namespace=self._reference.namespace,
                pull_secret=pull_secret,
            )
        self.create(
            cluster_deployment=cluster_deployment,
            secret=secret,
            proxy=proxy,
            ignition_config_override=ignition_config_override,
            **kwargs,
        )

    def list_agents(self) -> List[Agent]:
        all_agents = self.crd_api.list_namespaced_custom_object(
            group=consts.CRD_API_GROUP,
            version=consts.CRD_API_VERSION,
            plural=Agent._plural,
            namespace=self.ref.namespace,
        ).get("items", [])

        return [
            Agent(
                kube_api_client=self.crd_api.api_client,
                name=agent["metadata"]["name"],
                namespace=agent["metadata"]["namespace"],
            ) for agent in all_agents if agent["metadata"]["labels"].get(
                "infraenvs.agent-install.openshift.io") == self.ref.name
        ]

    def wait_for_agents(
        self,
        num_agents: int = 1,
        timeout: Union[int, float] = consts.DEFAULT_WAIT_FOR_AGENTS_TIMEOUT,
    ) -> List[Agent]:
        def _wait_for_sufficient_agents_number() -> List[Agent]:
            agents = self.list_agents()
            return agents if len(agents) == num_agents else []

        return waiting.wait(
            _wait_for_sufficient_agents_number,
            sleep_seconds=0.5,
            timeout_seconds=timeout,
            waiting_for=f"cluster {self.ref} to have {num_agents} agents",
        )
    quantity, unit = __split_quantity_and_unit_from_string(usage_string)
    return CPUQuantity(quantity, unit)


def __get_memory_usage_from_string(usage_string: str) -> MemoryQuantity:
    quantity, unit = __split_quantity_and_unit_from_string(usage_string)
    return MemoryQuantity(quantity, unit)


def __get_pods_metrics_from_response(results: dict) -> Iterable[PodMetrics]:
    for pod in results["items"]:
        pod_name = pod["metadata"]["name"]
        total_cpu = CPUQuantity()
        total_memory = MemoryQuantity()
        containers = pod["containers"]
        for container in containers:
            total_cpu += __get_cpu_usage_from_string(container["usage"]["cpu"])
            total_memory += __get_memory_usage_from_string(
                container["usage"]["memory"])
        yield PodMetrics(pod_name, total_memory, total_cpu, len(containers))


if __name__ == '__main__':
    load_kube_config()
    custom_api = CustomObjectsApi()
    results = custom_api.list_namespaced_custom_object('metrics.k8s.io',
                                                       'v1beta1', sys.argv[1],
                                                       'pods')
    for pod in __get_pods_metrics_from_response(results):
        print(pod)