From 1a9dcb5b8f598ee348f23eafecc2d92ef09c8122 Mon Sep 17 00:00:00 2001 From: "Zhang Rong(Jon)" Date: Mon, 16 May 2022 15:55:20 +0800 Subject: [PATCH] Enhance: Enable O2 DMS by exposing k8s API endpoint 1. Add profile query parameter for IMS and DMS API, if the parameter exists, return k8s profile information 2. Add an API of the download k8s config file Issue-ID: INF-264 Signed-off-by: Zhang Rong(Jon) Change-Id: I5cba1290b5b1b7103cf6f5f88f5998ef1dc8b8fd --- Dockerfile | 4 +- Dockerfile.localtest | 26 ++++----- charts/templates/deployment.yaml | 4 +- docker-compose.yml | 2 +- o2dms/api/dms_dto.py | 11 ++++ o2dms/api/dms_lcm_view.py | 105 ++++++++++++++++++++++++++++++++--- o2dms/api/dms_route.py | 31 ++++++++++- o2ims/adapter/orm.py | 1 + o2ims/domain/ocloud.py | 11 +++- o2ims/service/auditor/dms_handler.py | 42 ++++++++++++-- o2ims/views/ocloud_dto.py | 92 +++++++++++++++++++----------- o2ims/views/ocloud_route.py | 12 ++-- o2ims/views/ocloud_view.py | 81 ++++++++++++++++++++++++++- 13 files changed, 346 insertions(+), 76 deletions(-) diff --git a/Dockerfile b/Dockerfile index dc0adcb..84ebb52 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.10-slim-buster -RUN apt-get update; apt-get install -y git gcc +RUN apt-get update && apt-get install -y git gcc procps vim curl # in case git repo is not accessable # RUN mkdir -p /cgtsclient @@ -44,8 +44,6 @@ COPY configs/ /etc/o2/ COPY tests/ /tests/ -RUN apt-get install -y procps vim curl - RUN curl -O https://get.helm.sh/helm-v3.3.1-linux-amd64.tar.gz; RUN tar -zxvf helm-v3.3.1-linux-amd64.tar.gz; cp linux-amd64/helm /usr/local/bin diff --git a/Dockerfile.localtest b/Dockerfile.localtest index b047113..3a3a7ea 100644 --- a/Dockerfile.localtest +++ b/Dockerfile.localtest @@ -1,25 +1,21 @@ FROM python:3.10-slim-buster -RUN apt-get update; apt-get install -y git gcc +RUN apt-get update && apt-get install -y git gcc \ + vim curl procps # in case git repo is not accessable -RUN mkdir -p /cgtsclient +RUN mkdir -p /cgtsclient && mkdir -p /distcloud-client COPY temp/config /cgtsclient/ -RUN pip install -e cgtsclient/sysinv/cgts-client/cgts-client/ - -RUN mkdir -p /distcloud-client COPY temp/distcloud-client /distcloud-client/ -RUN pip install -e /distcloud-client/distributedcloud-client -# in case git repo is not accessable +RUN pip install -e cgtsclient/sysinv/cgts-client/cgts-client/ \ + && pip install -e /distcloud-client/distributedcloud-client +# in case git repo is not accessable -COPY requirements.txt /tmp/ -COPY constraints.txt /tmp/ - -RUN pip install -r /tmp/requirements.txt -c /tmp/constraints.txt +COPY requirements.txt constraints.txt requirements-test.txt /tmp/ -COPY requirements-test.txt /tmp/ -RUN pip install -r /tmp/requirements-test.txt +RUN pip install -r /tmp/requirements.txt -c /tmp/constraints.txt \ + && pip install -r /tmp/requirements-test.txt RUN mkdir -p /src @@ -45,9 +41,9 @@ COPY configs/ /etc/o2/ # RUN pip install -e /src COPY tests/ /tests/ -RUN apt-get install -y procps vim +#RUN apt-get install -y procps vim -RUN apt-get install -y curl +#RUN apt-get install -y curl RUN curl -O https://get.helm.sh/helm-v3.3.1-linux-amd64.tar.gz; RUN tar -zxvf helm-v3.3.1-linux-amd64.tar.gz; cp linux-amd64/helm /usr/local/bin diff --git a/charts/templates/deployment.yaml b/charts/templates/deployment.yaml index a25f326..0f0b433 100644 --- a/charts/templates/deployment.yaml +++ b/charts/templates/deployment.yaml @@ -109,8 +109,8 @@ spec: ports: - containerPort: 80 env: - - name: API_HOST - value: api + - name: API_HOST_EXTERNAL_FLOATING + value: {{ .Values.ocloud.API_HOST_EXTERNAL_FLOATING }} - name: DB_HOST value: postgres - name: DB_PASSWORD diff --git a/docker-compose.yml b/docker-compose.yml index 962d36d..ad3d857 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -42,7 +42,7 @@ services: environment: - DB_HOST=postgres - DB_PASSWORD=o2ims123 - - API_HOST=api + - API_HOST_EXTERNAL_FLOATING=${API_HOST_EXTERNAL_FLOATING} - REDIS_HOST=redis - PYTHONDONTWRITEBYTECODE=1 - FLASK_APP=/o2app/entrypoints/flask_application.py diff --git a/o2dms/api/dms_dto.py b/o2dms/api/dms_dto.py index 8b305e3..ae699f4 100644 --- a/o2dms/api/dms_dto.py +++ b/o2dms/api/dms_dto.py @@ -19,6 +19,16 @@ logger = o2logging.get_logger(__name__) class DmsDTO: + profile = api_dms_lcm_v1.model("DMSGetDtoProfile", { + 'cluster_api_endpoint': fields.String( + attributes='cluster_api_endpoint'), + 'cluster_ca_cert': fields.String(attributes='cluster_ca_cert'), + 'admin_user': fields.String(attributes='admin_user'), + 'admin_client_cert': fields.String(attributes='admin_client_cert'), + 'admin_client_key': fields.String(attributes='admin_client_key'), + 'kube_config_file': fields.String(attributes='kube_config_file') + }) + dms_get = api_dms_lcm_v1.model( "DmsGetDto", { @@ -30,6 +40,7 @@ class DmsDTO: 'supportedLocations': fields.String, 'capabilities': fields.String, 'capacity': fields.String, + 'profile': fields.Nested(profile, False, True), } ) diff --git a/o2dms/api/dms_lcm_view.py b/o2dms/api/dms_lcm_view.py index 3a634e4..208636a 100644 --- a/o2dms/api/dms_lcm_view.py +++ b/o2dms/api/dms_lcm_view.py @@ -12,23 +12,110 @@ # See the License for the specific language governing permissions and # limitations under the License. -from sqlalchemy import select +import string +import random +import yaml +from datetime import datetime + +# from sqlalchemy import select from o2common.service import unit_of_work -from o2ims.adapter.orm import deploymentmanager +# from o2ims.adapter.orm import deploymentmanager from o2common.helper import o2logging +from o2common.config import config logger = o2logging.get_logger(__name__) def deployment_managers(uow: unit_of_work.AbstractUnitOfWork): + # with uow: + # res = uow.session.execute(select(deploymentmanager)) + # return [dict(r) for r in res] with uow: - res = uow.session.execute(select(deploymentmanager)) - return [dict(r) for r in res] + li = uow.deployment_managers.list() + return [r.serialize() for r in li] def deployment_manager_one(deploymentManagerId: str, - uow: unit_of_work.AbstractUnitOfWork): + uow: unit_of_work.AbstractUnitOfWork, + profile: str = 'params'): + # with uow: + # res = uow.session.execute(select(deploymentmanager).where( + # deploymentmanager.c.deploymentManagerId == deploymentManagerId)) + # first = res.first() + # return None if first is None else dict(first) + # with uow: + # first = uow.deployment_managers.get(deploymentManagerId) + # return first.serialize() if first is not None else None with uow: - res = uow.session.execute(select(deploymentmanager).where( - deploymentmanager.c.deploymentManagerId == deploymentManagerId)) - first = res.first() - return None if first is None else dict(first) + first = uow.deployment_managers.get(deploymentManagerId) + if first is None: + return first + result = first.serialize() + + if "params" == profile: + pass + elif "file" == profile and result.hasattr("profile"): + p = result.pop("profile", None) + result["profile"] = _gen_kube_config(deploymentManagerId, p) + else: + result.pop("profile", None) + + return result + + +def _gen_kube_config(dmId: str, kubeconfig: dict) -> dict: + + # KUBECONFIG environment variable + # reference: + # https://kubernetes.io/docs/tasks/access-application-cluster/configure-access-multiple-clusters/ + data = { + 'apiVersion': 'v1', + 'clusters': [ + { + 'cluster': { + 'server': + kubeconfig.pop('cluster_api_endpoint', None), + 'certificate-authority-data': + kubeconfig.pop('cluster_ca_cert', None), + }, + 'name': 'inf-cluster' + }], + 'contexts': [ + { + 'context': { + 'cluster': 'inf-cluster', + 'user': 'kubernetes-admin' + }, + 'name': 'kubernetes-admin@inf-cluster' + } + ], + 'current-context': 'kubernetes-admin@inf-cluster', + 'kind': 'Config', + 'preferences': {}, + 'users': [ + { + 'name': kubeconfig.pop('admin_user', None), + 'user': { + 'client-certificate-data': + kubeconfig.pop('admin_client_cert', None), + 'client-key-data': + kubeconfig.pop('admin_client_key', None), + } + }] + } + + # Generate a random key for tmp kube config file + letters = string.ascii_uppercase + random_key = ''.join(random.choice(letters) for i in range(10)) + + # Get datetime of now as tag of the tmp file + current_time = datetime.now().strftime("%Y%m%d%H%M%S") + tmp_file_name = random_key + "_" + current_time + + # write down the yaml file of kubectl into tmp folder + with open('/tmp/kubeconfig_' + tmp_file_name, 'w') as file: + yaml.dump(data, file) + + kubeconfig["kube_config_file"] = config.get_api_url() + \ + config.get_o2dms_api_base() + "/" + dmId + "/download/" + tmp_file_name + + return kubeconfig diff --git a/o2dms/api/dms_route.py b/o2dms/api/dms_route.py index 480d965..c92bdc1 100644 --- a/o2dms/api/dms_route.py +++ b/o2dms/api/dms_route.py @@ -13,7 +13,9 @@ # limitations under the License. # from flask import jsonify -from flask_restx import Resource +from os.path import exists +from flask import send_file +from flask_restx import Resource, reqparse from o2dms.api.dms_dto import DmsDTO from o2dms.api import dms_lcm_view @@ -31,6 +33,8 @@ def configure_api_route(): # ---------- DeploymentManagers ---------- # @api_dms_lcm_v1.route("/") @api_dms_lcm_v1.param('deploymentManagerID', 'ID of the deployment manager') +@api_dms_lcm_v1.param('profile', 'DMS profile', + location='args') @api_dms_lcm_v1.response(404, 'Deployment manager not found') class DmsGetRouter(Resource): @@ -43,9 +47,32 @@ class DmsGetRouter(Resource): deploymentManagerID )) bus = MessageBus.get_instance() + + parser = reqparse.RequestParser() + parser.add_argument('profile', location='args') + args = parser.parse_args() + result = dms_lcm_view.deployment_manager_one( - deploymentManagerID, bus.uow) + deploymentManagerID, bus.uow, args.profile) if result is not None: return result api_dms_lcm_v1.abort(404, "Deployment manager {} doesn't exist".format( deploymentManagerID)) + + +@api_dms_lcm_v1.route("//download/") +@api_dms_lcm_v1.param('deploymentManagerID', + 'ID of the deployment manager') +@api_dms_lcm_v1.param('filename', + 'profile filename') +@api_dms_lcm_v1.response(404, 'profile not found') +class DeploymentManagerGetFileRouter(Resource): + def get(self, deploymentManagerID, filename): + path = "/tmp/kubeconfig_" + filename + + if exists(path): + return send_file(path, as_attachment=True) + api_dms_lcm_v1.abort( + 404, + "Deployment manager {}'s Kube config file doesn't exist". + format(deploymentManagerID)) diff --git a/o2ims/adapter/orm.py b/o2ims/adapter/orm.py index 45ec4d8..cb3a694 100644 --- a/o2ims/adapter/orm.py +++ b/o2ims/adapter/orm.py @@ -132,6 +132,7 @@ deploymentmanager = Table( Column("supportedLocations", String(255)), Column("capabilities", String(255)), Column("capacity", String(255)), + Column("profile", Text()) # Column("extensions", String(1024)) ) diff --git a/o2ims/domain/ocloud.py b/o2ims/domain/ocloud.py index 1775aa2..8547f5d 100644 --- a/o2ims/domain/ocloud.py +++ b/o2ims/domain/ocloud.py @@ -27,7 +27,7 @@ class DeploymentManager(AgRoot, Serializer): def __init__(self, id: str, name: str, ocloudid: str, dmsendpoint: str, description: str = '', supportedLocations: str = '', capabilities: str = '', - capacity: str = '') -> None: + capacity: str = '', profile: str = '') -> None: super().__init__() self.deploymentManagerId = id self.version_number = 0 @@ -38,8 +38,17 @@ class DeploymentManager(AgRoot, Serializer): self.supportedLocations = supportedLocations self.capabilities = capabilities self.capacity = capacity + self.profile = profile self.extensions = [] + def serialize(self): + d = Serializer.serialize(self) + + if 'profile' in d and d['profile'] != '': + d['profile'] = json.loads(d['profile']) + + return d + class ResourcePool(AgRoot, Serializer): def __init__(self, id: str, name: str, location: str, diff --git a/o2ims/service/auditor/dms_handler.py b/o2ims/service/auditor/dms_handler.py index 9ef0a77..89811c6 100644 --- a/o2ims/service/auditor/dms_handler.py +++ b/o2ims/service/auditor/dms_handler.py @@ -15,6 +15,9 @@ # pylint: disable=unused-argument from __future__ import annotations +import base64 +import json + from o2ims.domain.stx_object import StxGenericModel # from dataclasses import asdict # from typing import List, Dict, Callable, Type @@ -43,7 +46,7 @@ def update_dms( with uow: dms = uow.deployment_managers.get(stxobj.id) if not dms: - logger.info("add dms:" + stxobj.name + logger.info("add dms: " + stxobj.name + " update_at: " + str(stxobj.updatetime) + " id: " + str(stxobj.id) + " hash: " + str(stxobj.hash)) @@ -56,7 +59,7 @@ def update_dms( else: localmodel = dms if is_outdated(localmodel, stxobj): - logger.info("update a dms:" + stxobj.name + logger.info("update a dms: " + stxobj.name + " update_at: " + str(stxobj.updatetime) + " id: " + str(stxobj.id) + " hash: " + str(stxobj.hash)) @@ -83,9 +86,10 @@ def create_by(stxobj: StxGenericModel, parentid: str) -> DeploymentManager: supportedLocations = '' capabilities = '' capacity = '' + profile = _convert_content(stxobj.content) localmodel = DeploymentManager( stxobj.id, stxobj.name, ocloudid, dmsendpoint, description, - supportedLocations, capabilities, capacity) + supportedLocations, capabilities, capacity, profile) localmodel.createtime = stxobj.createtime localmodel.updatetime = stxobj.updatetime localmodel.hash = stxobj.hash @@ -100,8 +104,38 @@ def update_by(target: DeploymentManager, stxobj: StxGenericModel, target.name = stxobj.name target.createtime = stxobj.createtime target.updatetime = stxobj.updatetime - # ocloud.content = stxobj.content target.hash = stxobj.hash target.oCloudId = parentid target.version_number = target.version_number + 1 + target.profile = _convert_content(stxobj.content) target.events = [] + + +def _convert_content(stxobj_content: str): + # Convert api retrun content to dict object + content = json.loads(stxobj_content) + admin_user = content["admin_user"] + cluster_api_endpoint = content["cluster_api_endpoint"] + cluster_ca_cert = _b64_encode_str(content["cluster_ca_cert"]) + admin_client_cert = _b64_encode_str(content["admin_client_cert"]) + admin_client_key = _b64_encode_str(content["admin_client_key"]) + # admin_client_cert = base64.b64encode( + # bytes(content["admin_client_cert"], "utf-8")) + # admin_client_key = base64.b64encode( + # bytes(content["admin_client_key"], "utf-8")) + profile = { + "admin_user": admin_user, + "cluster_api_endpoint": cluster_api_endpoint, + "cluster_ca_cert": cluster_ca_cert, + "admin_client_cert": admin_client_cert, + "admin_client_key": admin_client_key + } + + return json.dumps(profile) + + +def _b64_encode_str(msg: str, encode: str = 'utf-8') -> str: + msg_bytes = msg.encode('utf-8') + base64_bytes = base64.b64encode(msg_bytes) + base64_msg = base64_bytes.decode('utf-8') + return base64_msg diff --git a/o2ims/views/ocloud_dto.py b/o2ims/views/ocloud_dto.py index c6896e0..c7489ae 100644 --- a/o2ims/views/ocloud_dto.py +++ b/o2ims/views/ocloud_dto.py @@ -94,43 +94,68 @@ class ResourceDTO: return api_ims_inventory_v1.model( 'ResourceGetDto' + str(iteration_number), resource_json_mapping) - def _recursive_resource_mapping(self, iteration_number=2): - resource_json_mapping = { - 'resourceId': fields.String(required=True, - description='Resource ID'), - 'resourceTypeId': fields.String, - 'resourcePoolId': fields.String, + # def _recursive_resource_mapping(self, iteration_number=2): + # resource_json_mapping = { + # 'resourceId': fields.String(required=True, + # description='Resource ID'), + # 'resourceTypeId': fields.String, + # 'resourcePoolId': fields.String, + # 'name': fields.String, + # 'parentId': fields.String, + # 'description': fields.String, + # } + # if iteration_number: + # resource_json_mapping['children'] = fields.List( + # fields.Nested(self._recursive_resource_mapping( + # iteration_number-1))) + # # print(type(resource_json_mapping['children'])) + # if resource_json_mapping['children'] is None: + # del resource_json_mapping['children'] + # return resource_json_mapping + + # def get_resource_get(self): + # return api_ims_inventory_v1.model( + # 'ResourceGetDto', + # { + # 'resourceId': fields.String(required=True, + # description='Resource ID'), + # 'resourceTypeId': fields.String, + # 'resourcePoolId': fields.String, + # 'name': fields.String, + # 'parentId': fields.String, + # 'description': fields.String, + # 'children': fields.List(fields.Nested( + # self._recursive_resource_mapping())) + # } + # ) + + +class DeploymentManagerDTO: + + deployment_manager_list = api_ims_inventory_v1.model( + "DeploymentManagerListDto", + { + 'deploymentManagerId': fields.String( + required=True, + description='Deployment manager ID'), 'name': fields.String, - 'parentId': fields.String, 'description': fields.String, + 'deploymentManagementServiceEndpoint': fields.String, + 'supportedLocations': fields.String, + 'capabilities': fields.String, + 'capacity': fields.String, } - if iteration_number: - resource_json_mapping['children'] = fields.List( - fields.Nested(self._recursive_resource_mapping( - iteration_number-1))) - # print(type(resource_json_mapping['children'])) - if resource_json_mapping['children'] is None: - del resource_json_mapping['children'] - return resource_json_mapping - - def get_resource_get(self): - return api_ims_inventory_v1.model( - 'ResourceGetDto', - { - 'resourceId': fields.String(required=True, - description='Resource ID'), - 'resourceTypeId': fields.String, - 'resourcePoolId': fields.String, - 'name': fields.String, - 'parentId': fields.String, - 'description': fields.String, - 'children': fields.List(fields.Nested( - self._recursive_resource_mapping())) - } - ) - + ) -class DeploymentManagerDTO: + profile = api_ims_inventory_v1.model("DeploymentManagerGetDtoProfile", { + 'cluster_api_endpoint': fields.String( + attributes='cluster_api_endpoint'), + 'cluster_ca_cert': fields.String(attributes='cluster_ca_cert'), + 'admin_user': fields.String(attributes='admin_user'), + 'admin_client_cert': fields.String(attributes='admin_client_cert'), + 'admin_client_key': fields.String(attributes='admin_client_key'), + 'kube_config_file': fields.String(attributes='kube_config_file') + }) deployment_manager_get = api_ims_inventory_v1.model( "DeploymentManagerGetDto", @@ -144,6 +169,7 @@ class DeploymentManagerDTO: 'supportedLocations': fields.String, 'capabilities': fields.String, 'capacity': fields.String, + 'profile': fields.Nested(profile, False, True), } ) diff --git a/o2ims/views/ocloud_route.py b/o2ims/views/ocloud_route.py index d6913a9..6aa6b2e 100644 --- a/o2ims/views/ocloud_route.py +++ b/o2ims/views/ocloud_route.py @@ -12,8 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from flask_restx import Resource -from flask_restx import reqparse +from flask_restx import Resource, reqparse from o2common.service.messagebus import MessageBus from o2ims.views import ocloud_view @@ -159,7 +158,7 @@ class ResourceGetRouter(Resource): @api_ims_inventory_v1.route("/deploymentManagers") class DeploymentManagersListRouter(Resource): - model = DeploymentManagerDTO.deployment_manager_get + model = DeploymentManagerDTO.deployment_manager_list @api_ims_inventory_v1.marshal_list_with(model) def get(self): @@ -169,6 +168,8 @@ class DeploymentManagersListRouter(Resource): @api_ims_inventory_v1.route("/deploymentManagers/") @api_ims_inventory_v1.param('deploymentManagerID', 'ID of the deployment manager') +@api_ims_inventory_v1.param('profile', 'DMS profile', + location='args') @api_ims_inventory_v1.response(404, 'Deployment manager not found') class DeploymentManagerGetRouter(Resource): @@ -177,8 +178,11 @@ class DeploymentManagerGetRouter(Resource): @api_ims_inventory_v1.doc('Get deployment manager') @api_ims_inventory_v1.marshal_with(model) def get(self, deploymentManagerID): + parser = reqparse.RequestParser() + parser.add_argument('profile', location='args') + args = parser.parse_args() result = ocloud_view.deployment_manager_one( - deploymentManagerID, bus.uow) + deploymentManagerID, bus.uow, args.profile) if result is not None: return result api_ims_inventory_v1.abort( diff --git a/o2ims/views/ocloud_view.py b/o2ims/views/ocloud_view.py index 7355f71..7213fc4 100644 --- a/o2ims/views/ocloud_view.py +++ b/o2ims/views/ocloud_view.py @@ -13,12 +13,17 @@ # limitations under the License. import uuid +import yaml +import random +import string +from datetime import datetime from o2common.service import unit_of_work from o2ims.views.ocloud_dto import SubscriptionDTO from o2ims.domain.subscription_obj import Subscription from o2common.helper import o2logging +from o2common.config import config logger = o2logging.get_logger(__name__) @@ -99,10 +104,82 @@ def deployment_managers(uow: unit_of_work.AbstractUnitOfWork): def deployment_manager_one(deploymentManagerId: str, - uow: unit_of_work.AbstractUnitOfWork): + uow: unit_of_work.AbstractUnitOfWork, + profile: str = 'params'): with uow: first = uow.deployment_managers.get(deploymentManagerId) - return first.serialize() if first is not None else None + if first is None: + return first + result = first.serialize() + + if "params" == profile: + pass + elif "file" == profile and result.hasattr("profile"): + p = result.pop("profile", None) + result["profile"] = _gen_kube_config(deploymentManagerId, p) + else: + result.pop("profile", None) + + return result + + +def _gen_kube_config(dmId: str, kubeconfig: dict) -> dict: + + # KUBECONFIG environment variable + # reference: + # https://kubernetes.io/docs/tasks/access-application-cluster/configure-access-multiple-clusters/ + data = { + 'apiVersion': 'v1', + 'clusters': [ + { + 'cluster': { + 'server': + kubeconfig.pop('cluster_api_endpoint', None), + 'certificate-authority-data': + kubeconfig.pop('cluster_ca_cert', None), + }, + 'name': 'inf-cluster' + }], + 'contexts': [ + { + 'context': { + 'cluster': 'inf-cluster', + 'user': 'kubernetes-admin' + }, + 'name': 'kubernetes-admin@inf-cluster' + } + ], + 'current-context': 'kubernetes-admin@inf-cluster', + 'kind': 'Config', + 'preferences': {}, + 'users': [ + { + 'name': kubeconfig.pop('admin_user', None), + 'user': { + 'client-certificate-data': + kubeconfig.pop('admin_client_cert', None), + 'client-key-data': + kubeconfig.pop('admin_client_key', None), + } + }] + } + + # Generate a random key for tmp kube config file + letters = string.ascii_uppercase + random_key = ''.join(random.choice(letters) for i in range(10)) + + # Get datetime of now as tag of the tmp file + current_time = datetime.now().strftime("%Y%m%d%H%M%S") + tmp_file_name = random_key + "_" + current_time + + # write down the yaml file of kubectl into tmp folder + with open('/tmp/kubeconfig_' + tmp_file_name, 'w') as file: + yaml.dump(data, file) + + kubeconfig["kube_config_file"] = config.get_api_url() + \ + config.get_o2dms_api_base() + "/" + dmId + "/download/" + tmp_file_name + + return kubeconfig def subscriptions(uow: unit_of_work.AbstractUnitOfWork): -- 2.16.6