Beispiel #1
0
    def add_master(
        self,
        node_id: str,
        network_name: str,
        cluster_secret: str,
        ip_address: str,
        size: int,
        ssh_keys: List[str],
        pool_id: int,
        public_ip_wid: int = 0,
        disable_default_ingress=True,
        datastore_endpoint="",
    ) -> K8s:
        """create a kubernetes marster workload object

        Args:
          node_id(str): node ID on which to deploy the k8s master
          network_name(str): name of the network to use
          cluster_secret(str): secret of the cluster. all the member of a same cluster must share the same secret
          ip_address(str): ip address of the k8s master
          size(int): size of the VM.
          ssh_keys(List[str]): list of public SSH key to authorize in the VM
          pool_id(int): capacity pool ID

        Returns:
          K8s: K8s

        Raises:
          Input: if size is not supported

        """
        if size not in range(1, 19):
            raise Input(f"VM size {size} is not supported")

        master = K8s()
        master.info.node_id = node_id
        master.info.workload_type = WorkloadType.Kubernetes
        master.info.pool_id = pool_id

        node = self._nodes.get(node_id)
        master.cluster_secret = encrypt_for_node(self._identity,
                                                 node.public_key_hex,
                                                 cluster_secret).decode()
        master.network_id = network_name
        master.ipaddress = ip_address
        master.size = size
        if not isinstance(ssh_keys, list):
            ssh_keys = [ssh_keys]
        master.ssh_keys = ssh_keys
        master.public_ip = public_ip_wid
        master.disable_default_ingress = disable_default_ingress
        master.datastore_endpoint = datastore_endpoint

        return master
Beispiel #2
0
    def _preprare_extension_pool(self, farm_name, k8s_flavor, no_nodes, duration, public_ip=False, no_extend=False):
        """
        returns pool id after extension with enough cloud units
        duration in seconds
        """
        self.vdc_deployer.info(
            f"Preperaring pool for kubernetes cluster extension on farm {farm_name}, flavor: {k8s_flavor}, no_nodes: {no_nodes}, duration: {duration}, public_ip: {public_ip},vdc uuid: {self.vdc_uuid}"
        )
        k8s = K8s()
        k8s.size = k8s_flavor.value
        cloud_units = k8s.resource_units().cloud_units()
        cus = int(cloud_units.cu * duration * no_nodes)
        sus = int(cloud_units.su * duration * no_nodes)
        ipv4us = 0
        if public_ip:
            ipv4us = int(duration)

        farm = self.explorer.farms.get(farm_name=farm_name)
        pool_id = None
        self.vdc_deployer.info(f"kubernetes cluster extension: searching for existing pool on farm {farm_name}")
        for pool in self.zos.pools.list():
            farm_id = deployer.get_pool_farm_id(pool.pool_id, pool, self.identity.instance_name)
            if farm_id == farm.id:
                pool_id = pool.pool_id
                break

        if not pool_id:
            pool_info = self.vdc_deployer._retry_call(
                self.zos.pools.create,
                args=[math.ceil(cus), math.ceil(sus), ipv4us, farm_name, ["TFT"], j.core.identity.me],
            )
            pool_id = pool_info.reservation_id
            self.vdc_deployer.info(f"Kubernetes cluster extension: creating a new pool {pool_id}")
            self.vdc_deployer.info(f"New pool {pool_info.reservation_id} for kubernetes cluster extension.")
        elif no_extend:
            return pool_id
        else:
            self.vdc_deployer.info(f"Kubernetes cluster extension: found pool {pool_id}")
            node_ids = [node.node_id for node in self.zos.nodes_finder.nodes_search(farm_name=farm_name)]
            pool_info = self.vdc_deployer._retry_call(
                self.zos.pools.extend, args=[pool_id, cus, sus, ipv4us], kwargs={"node_ids": node_ids}
            )
            self.vdc_deployer.info(
                f"Using pool {pool_id} extension reservation: {pool_info.reservation_id} for kubernetes cluster extension."
            )

        self.vdc_deployer.pay(pool_info)

        success = self.vdc_deployer.wait_pool_payment(pool_info.reservation_id)
        if not success:
            raise j.exceptions.Runtime(f"Pool {pool_info.reservation_id} resource reservation timedout")

        return pool_id
Beispiel #3
0
    def show_external_node_payment(self, bot, farm_name, size, no_nodes=1, expiry=5, wallet_name=None, public_ip=False):
        discount = FARM_DISCOUNT.get()
        duration = self.calculate_expiration_value() - j.data.time.utcnow().timestamp
        month = 60 * 60 * 24 * 30
        if duration > month:
            duration = month

        zos = j.sals.zos.get()
        farm_id = zos._explorer.farms.get(farm_name=farm_name).id
        k8s = K8s()
        if isinstance(size, str):
            size = VDC_SIZE.K8SNodeFlavor[size.upper()].value
        k8s.size = size
        amount = j.tools.zos.consumption.cost(k8s, duration, farm_id) + TRANSACTION_FEES

        if public_ip:
            pub_ip = PublicIP()
            amount += j.tools.zos.consumption.cost(pub_ip, duration, farm_id)
        amount *= no_nodes

        prepaid_balance = self._get_wallet_balance(self.prepaid_wallet)
        if prepaid_balance >= amount:
            if bot:
                result = bot.single_choice(
                    f"Do you want to use your existing balance to pay {round(amount,4)} TFT? (This will impact the overall expiration of your plan)",
                    ["Yes", "No"],
                    required=True,
                )
                if result == "Yes":
                    amount = 0
            else:
                amount = 0
        elif not bot:
            # Not enough funds in prepaid wallet and no bot passed to use to view QRcode
            return False, amount, None

        payment_id, _ = j.sals.billing.submit_payment(
            amount=amount,
            wallet_name=wallet_name or self.prepaid_wallet.instance_name,
            refund_extra=False,
            expiry=expiry,
            description=j.data.serializers.json.dumps(
                {"type": "VDC_K8S_EXTEND", "owner": self.owner_tname, "solution_uuid": self.solution_uuid}
            ),
        )
        if amount > 0:
            notes = []
            if discount:
                notes = ["For testing purposes, we applied a discount of {:.0f}%".format(discount * 100)]
            return j.sals.billing.wait_payment(payment_id, bot=bot, notes=notes), amount, payment_id
        else:
            return True, amount, payment_id
Beispiel #4
0
    def add_master(
        self,
        node_id: str,
        network_name: str,
        cluster_secret: str,
        ip_address: str,
        size: int,
        ssh_keys: List[str],
        pool_id: int,
    ) -> K8s:
        """create a kubernetes marster workload object

        Args:
          node_id(str): node ID on which to deploy the k8s master
          network_name(str): name of the network to use
          cluster_secret(str): secret of the cluster. all the member of a same cluster must share the same secret
          ip_address(str): ip address of the k8s master
          size(int): size of the VM.
          ssh_keys(List[str]): list of public SSH key to authorize in the VM
          pool_id(int): capacity pool ID

        Returns:
          K8s: K8s

        Raises:
          Input: if size is not supported

        """
        if size not in [1, 2]:
            raise Input("size can only be 1 or 2")

        master = K8s()
        master.info.node_id = node_id
        master.info.workload_type = WorkloadType.Kubernetes
        master.info.pool_id = pool_id

        node = self._nodes.get(node_id)
        master.cluster_secret = encrypt_for_node(node.public_key_hex,
                                                 cluster_secret).decode()
        master.network_id = network_name
        master.ipaddress = ip_address
        master.size = size
        if not isinstance(ssh_keys, list):
            ssh_keys = [ssh_keys]
        master.ssh_keys = ssh_keys

        return master
Beispiel #5
0
    def test06_add_delete_k8s_node(self):
        """Test case for adding and deleting node.

        **Test Scenario**

        - Deploy VDC.
        - Calculate the price of added zdb and fund the provisioning wallet.
        - Add kubernetes node.
        - Check that the node has been added.
        - Delete this node.
        - Check that this node has been deleted.
        """
        self.vdc.load_info()
        k8s_before_add = len(self.vdc.kubernetes)

        self.info(
            "Calculate the price of added zdb and fund the provisioning wallet."
        )
        kubernetes = K8s()
        kubernetes.size = VDC_SIZE.K8SNodeFlavor.MEDIUM.value
        # It will be deployed for an hour.
        zos = j.sals.zos.get()
        farm_name = self.get_farm_name()
        farm_id = zos._explorer.farms.get(farm_name=farm_name).id
        price = j.tools.zos.consumption.cost(
            kubernetes, 60 * 60,
            farm_id) + TRANSACTION_FEES  # transactions fees.
        self.vdc.transfer_to_provisioning_wallet(round(price, 6),
                                                 "test_wallet")

        self.info("Add kubernetes node")
        wid = self.deployer.add_k8s_nodes("medium")

        self.info("Check that node has been added")
        self.vdc.load_info()
        self.assertEqual(len(self.vdc.kubernetes), k8s_before_add + 1)

        self.info("Delete this node")
        self.deployer.delete_k8s_node(wid[0])

        self.info("Check that this node has been deleted")
        self.vdc.load_info()
        self.assertEqual(len(self.vdc.kubernetes), k8s_before_add)
Beispiel #6
0
def test_k8s_ru():
    z = K8s()
    tt = [{
        "size": 1,
        "cru": 1,
        "mru": 2,
        "sru": 50,
        "hru": 0
    }, {
        "size": 2,
        "cru": 2,
        "mru": 4,
        "sru": 100,
        "hru": 0
    }]
    for tc in tt:
        z.size = tc["size"]

        ru = z.resource_units()
        assert ru.cru == tc["cru"]
        assert ru.mru == tc["mru"]
        assert ru.sru == tc["sru"]
        assert ru.hru == tc["hru"]
Beispiel #7
0
def calculate_vdc_price(flavor, farm_name=None):
    """calculate the workloads price for vdcs in TFT

    Args:
        flavor (str): vdc flavor in [silver, gold, platinum, diamond]

    Returns:
        str: vdc price
    """
    all_cus = 0
    all_sus = 0
    all_ipv4us = 0

    # get the flavor enum
    for item in VDC_SIZE.VDC_FLAVORS.keys():
        if flavor == item.value:
            flavor = item

    # calculate cloud units enough for a month
    def get_cloud_units(workload):
        ru = workload.resource_units()
        cloud_units = ru.cloud_units()
        return (
            cloud_units.cu * 60 * 60 * 24 * 30,
            cloud_units.su * 60 * 60 * 24 * 30,
            cloud_units.ipv4u * 60 * 60 * 24 * 30,
        )

    # get zdbs usage
    zdb = ZdbNamespace()
    zdb.size = VDC_SIZE.S3_ZDB_SIZES[VDC_SIZE.VDC_FLAVORS[flavor]["s3"]
                                     ["size"]]["sru"]
    zdb.disk_type = DiskType.HDD
    _, sus, _ = get_cloud_units(zdb)
    all_cus += sus

    # get kubernetes controller usage
    master_size = VDC_SIZE.VDC_FLAVORS[flavor]["k8s"]["controller_size"]
    k8s = K8s()
    k8s.size = master_size.value
    cus, sus, ipv4us = get_cloud_units(k8s)

    all_cus += cus
    all_sus += sus
    all_ipv4us += ipv4us

    # get kubernetes workers usage
    no_nodes = VDC_SIZE.VDC_FLAVORS[flavor]["k8s"]["no_nodes"]
    worker_size = VDC_SIZE.VDC_FLAVORS[flavor]["k8s"]["size"]
    k8s = K8s()
    k8s.size = worker_size.value
    cus, sus, ipv4us = get_cloud_units(k8s)

    all_cus += cus * (no_nodes + 1)
    all_sus += sus * (no_nodes + 1)

    # get 3bot container usage
    cont2 = Container()
    cont2.capacity.cpu = THREEBOT_CPU
    cont2.capacity.memory = THREEBOT_MEMORY
    cont2.capacity.disk_size = THREEBOT_DISK
    cont2.capacity.disk_type = DiskType.SSD
    n_cus, n_sus, _ = get_cloud_units(cont2)

    all_cus += n_cus
    all_sus += n_sus

    # create empty pool and get the payment amount
    zos = j.sals.zos.get()
    farm_name = farm_name or zos._explorer.farms.list()[0].name
    pool = zos.pools.create(round(all_cus), round(all_sus), round(all_ipv4us),
                            farm_name)
    amount = pool.escrow_information.amount
    total_amount_dec = Decimal(amount) / Decimal(1e7)
    total_amount = "{0:f}".format(total_amount_dec)

    return total_amount
Beispiel #8
0
def calculate_vdc_price(flavor, farm_name=None):
    """calculate the workloads price for vdcs in TFT

    Args:
        flavor (str): vdc flavor in [silver, gold, platinum, diamond]

    Returns:
        str: vdc price
    """
    all_cus = 0
    all_sus = 0
    all_ipv4us = 0

    # get the flavor enum
    for item in VDC_SIZE.VDC_FLAVORS.keys():
        if flavor == item.value:
            flavor = item

    # calculate cloud units enough for a month
    def get_cloud_units(workload):
        ru = workload.resource_units()
        cloud_units = ru.cloud_units()
        return (
            cloud_units.cu * 60 * 60 * 24 * 30,
            cloud_units.su * 60 * 60 * 24 * 30,
            cloud_units.ipv4u * 60 * 60 * 24 * 30,
        )

    # get zdbs usage
    zdb = ZdbNamespace()
    zdb.size = VDC_SIZE.S3_ZDB_SIZES[VDC_SIZE.VDC_FLAVORS[flavor]["s3"]
                                     ["size"]]["sru"]
    zdb.disk_type = DiskType.HDD
    _, sus, _ = get_cloud_units(zdb)
    all_sus += sus

    # get kubernetes controller usage
    master_size = VDC_SIZE.VDC_FLAVORS[flavor]["k8s"]["controller_size"]
    k8s = K8s()
    k8s.size = master_size.value
    cus, sus, ipv4us = get_cloud_units(k8s)

    all_cus += cus
    all_sus += sus
    all_ipv4us += 60 * 60 * 24 * 30  # hardcoded since not calculated in the explorer client

    # get kubernetes workers usage
    no_nodes = VDC_SIZE.VDC_FLAVORS[flavor]["k8s"]["no_nodes"]
    worker_size = VDC_SIZE.VDC_FLAVORS[flavor]["k8s"]["size"]
    k8s = K8s()
    k8s.size = worker_size.value
    cus, sus, ipv4us = get_cloud_units(k8s)

    all_cus += cus * (no_nodes)
    all_sus += sus * (no_nodes)

    # get 3bot container usage
    cont2 = Container()
    cont2.capacity.cpu = THREEBOT_CPU
    cont2.capacity.memory = THREEBOT_MEMORY
    cont2.capacity.disk_size = THREEBOT_DISK
    cont2.capacity.disk_type = DiskType.SSD
    n_cus, n_sus, _ = get_cloud_units(cont2)

    all_cus += n_cus
    all_sus += n_sus

    # etcd containers usage
    etcd_cont = Container()
    etcd_cont.capacity.cpu = ETCD_CPU
    etcd_cont.capacity.memory = ETCD_MEMORY
    etcd_cont.capacity.disk_size = ETCD_DISK
    etcd_cont.capacity.disk_type = DiskType.SSD
    n_cus, n_sus, _ = get_cloud_units(etcd_cont)

    all_cus += n_cus * ETCD_CLUSTER_SIZE
    all_sus += n_sus * ETCD_CLUSTER_SIZE

    zos = j.sals.zos.get()
    farm_prices = zos._explorer.farms.get_deal_for_threebot(
        1, j.core.identity.me.tid)["custom_cloudunits_price"]
    total_amount = zos._explorer.prices.calculate(cus=all_cus,
                                                  sus=all_sus,
                                                  ipv4us=all_ipv4us,
                                                  farm_prices=farm_prices)
    total_amount = round(total_amount, 6)
    return total_amount
Beispiel #9
0
    def setUpClass(cls):
        super().setUpClass()
        cls._import_wallet("demos_wallet")
        no_deployment = "single"
        cls.flavor = "platinum"

        if cls.no_deployment == "single":
            cls.kube_config = cls.deploy_vdc()
        else:
            cls.kube_config = cls.deploy_vdc(hours=2)

        if not cls.kube_config:
            raise RuntimeError("VDC is not deployed")

        j.sals.fs.copy_file(
            f"{j.sals.fs.home()}/sandbox/cfg/vdc/kube/{cls.vdc.owner_tname}/{cls.vdc.vdc_name}.yaml",
            j.sals.fs.expanduser("~/.kube/config"),
        )
        cls.kube_manager = j.sals.kubernetes.Manager()

        cls.info(
            "Check that resources are available and ready in 5 min maximum")
        cls.kube_monitor = cls.vdc.get_kubernetes_monitor()
        has_resource = False
        resources_expiry = j.data.time.now().timestamp + 300
        while j.data.time.now(
        ).timestamp < resources_expiry and not has_resource:
            res = cls.kube_monitor.fetch_resource_reservations()
            for node in res:
                if node.has_enough_resources(1000, 1024):
                    cls.info("Cluster resources are ready")
                    has_resource = True
                    break

        cls.info("Check that traefik is ready in 5 min maximum")
        is_traefik_ready = False
        traefik_expiry = j.data.time.now().timestamp + 300
        while j.data.time.now(
        ).timestamp < traefik_expiry and not is_traefik_ready:
            rc, system_pods, err = cls.kube_manager._execute(
                f"kubectl get pods --kubeconfig {cls.kube_manager.config_path} --namespace kube-system | grep traefik"
            )
            running = system_pods.count("Running") + system_pods.count(
                "Completed")
            all = len(system_pods.splitlines())
            if running == all:
                cls.info("Traefik is ready")
                is_traefik_ready = True
                break

        assert is_traefik_ready == True, "Traefik not ready within 5 minutes"

        # Add tokens needed in case of extending the cluster automatically.
        kubernetes = K8s()
        kubernetes.size = VDC_SIZE.K8SNodeFlavor.MEDIUM.value
        # It will be deployed for an hour.
        zos = j.sals.zos.get()
        farm_name = cls.get_farm_name()
        farm_id = zos._explorer.farms.get(farm_name=farm_name).id
        price = j.tools.zos.consumption.cost(
            kubernetes, 60 * 60,
            farm_id) + TRANSACTION_FEES  # transactions fees.
        cls.vdc.transfer_to_provisioning_wallet(round(price, 6), "test_wallet")

        # Timeout for any exposed solution to be reachable and certified.
        cls.timeout = 60
        # Accept Marketplace T&C for testing identity.
        ChatflowsBase.tname = cls.tname
        ChatflowsBase.accept_terms_conditions(type_="marketplace")