Пример #1
0
def check_pod_status(pod_dict):
    v1 = kube_client.CoreV1Api()
    w = watch.Watch()
    for event in w.stream(v1.list_namespaced_pod,
                          namespace='azure-arc',
                          timeout_seconds=360):
        pod_status = event['raw_object'].get('status')
        pod_name = event['object'].metadata.name
        if pod_status.get('containerStatuses'):
            for container in pod_status.get('containerStatuses'):
                if container.get('state').get('running') is None:
                    pod_dict[pod_name] = 0
                    break
                else:
                    pod_dict[pod_name] = 1
                if container.get('state').get('terminated') is not None:
                    logger.warning(
                        "%s%s%s", "The pod {} was terminated. ".format(
                            container.get('name')),
                        "Please ensure it is in running state once the operation completes. ",
                        "Run 'kubectl get pods -n azure-arc' to check the pod status."
                    )
        if all(ele == 1 for ele in list(pod_dict.values())):
            return
    telemetry.add_extension_event(
        'connectedk8s', {'Context.Default.AzureCLI.ExitStatus': 'Timedout'})
    logger.warning(
        "%s%s", 'The pods were unable to start before timeout. ',
        'Please run "kubectl get pods -n azure-arc" to ensure if the pods are in running state.'
    )
Пример #2
0
def conclude():
    if not _session.aliases_hit:
        return

    _session.end_time = datetime.datetime.now()
    for properties in _session.generate_payload():
        telemetry_core.add_extension_event(EXTENSION_NAME, properties)
Пример #3
0
def conclude():
    if not _session.aliases_hit and not _session.exceptions:
        return

    _session.end_time = datetime.datetime.now()
    for properties in _session.generate_payload():
        telemetry_core.add_extension_event(EXTENSION_NAME, properties)
Пример #4
0
def process_query(cli_term):
    print(random.choice(WAIT_MESSAGE))
    response = call_aladdin_service(cli_term)

    if response.status_code != 200:
        err_msg = '[?] Unexpected Error: [HTTP {0}]: Content: {1}'.format(
            response.status_code, response.content)
        logger.error(err_msg)
    else:
        if (platform.system() == 'Windows' and should_enable_styling()):
            colorama.init(convert=True)

        answer_list = json.loads(response.content)
        if (not answer_list or answer_list[0]['source'] == 'bing'):
            print("\nSorry I am not able to help with [" + cli_term + "]."
                  "\nTry typing the beginning of a command e.g. " +
                  style_message('az vm') + ".")
        else:
            print("\nHere are the most common ways to use [" + cli_term +
                  "]: \n")
            num_results_to_show = min(3, len(answer_list))
            for i in range(num_results_to_show):
                current_title = answer_list[i]['title'].strip()
                current_snippet = answer_list[i]['snippet'].strip()
                if current_title.startswith("az "):
                    current_title, current_snippet = current_snippet, current_title
                    current_title = current_title.split('\r\n')[0]
                elif '```azurecli\r\n' in current_snippet:
                    start_index = current_snippet.index(
                        '```azurecli\r\n') + len('```azurecli\r\n')
                    current_snippet = current_snippet[start_index:]
                current_snippet = current_snippet.replace('```', '').replace(
                    current_title, '').strip()
                current_snippet = re.sub(r'\[.*\]', '',
                                         current_snippet).strip()
                print(style_message(current_title))
                print(current_snippet)

                print("")
            feedback = prompt(
                "[Enter to close. Press + or - to give feedback]:")
            if feedback in ['+', '-']:
                print('Wow, you are a true hero!')
                print("""\
        O_
       """ + style_message("""<T>""") + """`-.
        """ + style_message("""|""") + """`-‘
        """ + style_message("""I""") + """
                        """)
                print(
                    'My human overlords review each of these reports; I\'m told these reports makes me smarter.'
                )
                print(
                    'Send us more feedback by email: [email protected]')
            properties = {}
            set_custom_properties(properties, 'Feedback', feedback)
            telemetry_core.add_extension_event(EXTENSION_NAME, properties)
Пример #5
0
def send_cloud_telemetry(cmd):
    telemetry.add_extension_event(
        'connectedk8s',
        {'Context.Default.AzureCLI.AzureCloud': cmd.cli_ctx.cloud.name})
    cloud_name = cmd.cli_ctx.cloud.name.upper()
    # Setting cloud name to format that is understood by golang SDK.
    if cloud_name == consts.PublicCloud_OriginalName:
        cloud_name = consts.Azure_PublicCloudName
    elif cloud_name == consts.USGovCloud_OriginalName:
        cloud_name = consts.Azure_USGovCloudName
    return cloud_name
Пример #6
0
def start_rdp_connection(ssh_info, delete_keys, delete_cert):
    try:
        ssh_process = None
        log_list = []
        print_ssh_logs = False
        ssh_success = False

        resource_port = 3389
        local_port = _get_open_port()

        while not is_local_port_open(local_port):
            local_port = _get_open_port()

        if ssh_info.ssh_args is None:
            ssh_info.ssh_args = [
                '-L', f"{local_port}:localhost:{resource_port}", "-N"
            ]
        else:
            ssh_info.ssh_args = [
                '-L', f"{local_port}:localhost:{resource_port}", "-N"
            ] + ssh_info.ssh_args

        ssh_process, print_ssh_logs = start_ssh_tunnel(ssh_info)
        ssh_connection_t0 = time.time()
        ssh_success, log_list = wait_for_ssh_connection(
            ssh_process, print_ssh_logs)
        ssh_utils.do_cleanup(delete_keys, delete_cert, ssh_info.cert_file,
                             ssh_info.private_key_file,
                             ssh_info.public_key_file)
        if ssh_success and ssh_process.poll() is None:
            call_rdp(local_port)

    finally:
        if ssh_success:
            ssh_connection_data = {
                'Context.Default.AzureCLI.SSHConnectionDurationInMinutes':
                (time.time() - ssh_connection_t0) / 60
            }
            ssh_connection_data[
                'Context.Default.AzureCLI.SSHConnectionStatus'] = "Success"
            telemetry.add_extension_event('ssh', ssh_connection_data)

        terminate_ssh(ssh_process, log_list, print_ssh_logs)
        ssh_utils.do_cleanup(delete_keys, delete_cert, ssh_info.cert_file,
                             ssh_info.private_key_file,
                             ssh_info.public_key_file)
        if delete_keys:
            # This is only true if keys were generated, so they must be in a temp folder.
            temp_dir = os.path.dirname(ssh_info.cert_file)
            file_utils.delete_folder(
                temp_dir, f"Couldn't delete temporary folder {temp_dir}", True)
Пример #7
0
def get_client_side_proxy(arc_proxy_folder):

    request_uri, install_location, older_version_location = _get_proxy_filename_and_url(
        arc_proxy_folder)
    install_dir = os.path.dirname(install_location)

    # Only download new proxy if it doesn't exist already
    if not os.path.isfile(install_location):
        t0 = time.time()
        # download the executable
        try:
            with urllib.request.urlopen(request_uri) as response:
                response_content = response.read()
                response.close()
        except Exception as e:
            raise azclierror.ClientRequestError(
                f"Failed to download client proxy executable from {request_uri}. "
                "Error: " + str(e)) from e
        time_elapsed = time.time() - t0

        proxy_data = {
            'Context.Default.AzureCLI.SSHProxyDownloadTime': time_elapsed,
            'Context.Default.AzureCLI.SSHProxyVersion':
            consts.CLIENT_PROXY_VERSION
        }
        telemetry.add_extension_event('ssh', proxy_data)

        # if directory doesn't exist, create it
        if not os.path.isdir(install_dir):
            file_utils.create_directory(
                install_dir,
                f"Failed to create client proxy directory '{install_dir}'. ")
        # if directory exists, delete any older versions of the proxy
        else:
            older_version_files = glob(older_version_location)
            for f in older_version_files:
                file_utils.delete_file(
                    f,
                    f"failed to delete older version file {f}",
                    warning=True)

        # write executable in the install location
        file_utils.write_to_file(install_location, 'wb', response_content,
                                 "Failed to create client proxy file. ")
        os.chmod(install_location,
                 os.stat(install_location).st_mode | stat.S_IXUSR)
        colorama.init()
        print(Fore.GREEN + f"SSH Client Proxy saved to {install_location}" +
              Style.RESET_ALL)

    return install_location
Пример #8
0
def process_query(cli_term):
    print(random.choice(WAIT_MESSAGE))
    response = call_aladdin_service(cli_term)

    if response.status_code != 200:
        logger.error('[?] Unexpected Error: [HTTP {0}]: Content: {1}'.format(response.status_code, response.content))
    else:
        if (platform.system() == 'Windows' and should_enable_styling()):
            colorama.init(convert=True)

        answer_list = json.loads(response.content)
        if (not answer_list or answer_list[0]['source'] == 'bing'):
            print("\nSorry I am not able to help with [" + cli_term + "]."
                  "\nTry typing the beginning of a command e.g. " + style_message('az vm') + ".")
        else:
            print("\nHere are the most common ways to use [" + cli_term + "]: \n")
            num_results_to_show = min(3, len(answer_list))
            for i in range(num_results_to_show):
                current_title = answer_list[i]['title'].strip()
                current_snippet = answer_list[i]['snippet'].strip()
                if current_title.startswith("az "):
                    current_title, current_snippet = current_snippet, current_title
                    current_title = current_title.split('\r\n')[0]
                elif '```azurecli\r\n' in current_snippet:
                    start_index = current_snippet.index('```azurecli\r\n') + len('```azurecli\r\n')
                    current_snippet = current_snippet[start_index:]
                current_snippet = current_snippet.replace('```', '').replace(current_title, '').strip()
                current_snippet = re.sub(r'\[.*\]', '', current_snippet).strip()
                print(style_message(current_title))
                print(current_snippet)

                print("")
            feedback = prompt("[Enter to close. Press + or - to give feedback]:")
            if feedback in ['+', '-']:
                print('Wow, you are a true hero!')
                print("""\
        O_
       """ + style_message("""<T>""") + """`-.
        """ + style_message("""|""") + """`-‘
        """ + style_message("""I""") + """
                        """)
                print('My human overlords review each of these reports; I\'m told these reports makes me smarter.')
                print('Send us more feedback by email: [email protected]')
            properties = {}
            set_custom_properties(properties, 'Feedback', feedback)
            telemetry_core.add_extension_event(EXTENSION_NAME, properties)
def get_relay_information(cmd, resource_group, vm_name,
                          certificate_validity_in_seconds):
    from azext_ssh._client_factory import cf_endpoint
    client = cf_endpoint(cmd.cli_ctx)

    if not certificate_validity_in_seconds or \
       certificate_validity_in_seconds > consts.RELAY_INFO_MAXIMUM_DURATION_IN_SECONDS:
        certificate_validity_in_seconds = consts.RELAY_INFO_MAXIMUM_DURATION_IN_SECONDS

    try:
        t0 = time.time()
        result = client.list_credentials(
            resource_group_name=resource_group,
            machine_name=vm_name,
            endpoint_name="default",
            expiresin=certificate_validity_in_seconds)
        time_elapsed = time.time() - t0
        telemetry.add_extension_event(
            'ssh',
            {'Context.Default.AzureCLI.SSHListCredentialsTime': time_elapsed})
    except ResourceNotFoundError:
        logger.debug(
            "Default Endpoint couldn't be found. Trying to create Default Endpoint."
        )
        _create_default_endpoint(cmd, resource_group, vm_name, client)
        try:
            t0 = time.time()
            result = client.list_credentials(
                resource_group_name=resource_group,
                machine_name=vm_name,
                endpoint_name="default",
                expiresin=certificate_validity_in_seconds)
            time_elapsed = time.time() - t0
            telemetry.add_extension_event('ssh', {
                'Context.Default.AzureCLI.SSHListCredentialsTime':
                time_elapsed
            })
        except Exception as e:
            raise azclierror.ClientRequestError(
                f"Request for Azure Relay Information Failed:\n{str(e)}")
    except Exception as e:
        raise azclierror.ClientRequestError(
            f"Request for Azure Relay Information Failed:\n{str(e)}")
    return result
Пример #10
0
def _get_and_write_certificate(cmd, public_key_file, cert_file, ssh_client_folder):
    cloudtoscope = {
        "azurecloud": "https://pas.windows.net/CheckMyAccess/Linux/.default",
        "azurechinacloud": "https://pas.chinacloudapi.cn/CheckMyAccess/Linux/.default",
        "azureusgovernment": "https://pasff.usgovcloudapi.net/CheckMyAccess/Linux/.default"
    }
    scope = cloudtoscope.get(cmd.cli_ctx.cloud.name.lower(), None)
    if not scope:
        raise azclierror.InvalidArgumentValueError(
            f"Unsupported cloud {cmd.cli_ctx.cloud.name.lower()}",
            "Supported clouds include azurecloud,azurechinacloud,azureusgovernment")

    scopes = [scope]
    data = _prepare_jwk_data(public_key_file)
    from azure.cli.core._profile import Profile
    profile = Profile(cli_ctx=cmd.cli_ctx)

    t0 = time.time()
    # We currently are using the presence of get_msal_token to detect if we are running on an older azure cli client
    # TODO: Remove when adal has been deprecated for a while
    if hasattr(profile, "get_msal_token"):
        # we used to use the username from the token but now we throw it away
        _, certificate = profile.get_msal_token(scopes, data)
    else:
        credential, _, _ = profile.get_login_credentials(subscription_id=profile.get_subscription()["id"])
        certificatedata = credential.get_token(*scopes, data=data)
        certificate = certificatedata.token

    time_elapsed = time.time() - t0
    telemetry.add_extension_event('ssh', {'Context.Default.AzureCLI.SSHGetCertificateTime': time_elapsed})

    if not cert_file:
        cert_file = public_key_file + "-aadcert.pub"

    logger.debug("Generating certificate %s", cert_file)
    _write_cert_file(certificate, cert_file)
    # instead we use the validprincipals from the cert due to mismatched upn and email in guest scenarios
    username = ssh_utils.get_ssh_cert_principals(cert_file, ssh_client_folder)[0]
    return cert_file, username.lower()
Пример #11
0
def update_agents(cmd,
                  client,
                  resource_group_name,
                  cluster_name,
                  https_proxy="",
                  http_proxy="",
                  no_proxy="",
                  kube_config=None,
                  kube_context=None,
                  no_wait=False):
    logger.warning(
        "Ensure that you have the latest helm version installed before proceeding."
    )
    logger.warning("This operation might take a while...\n")

    # Send cloud information to telemetry
    send_cloud_telemetry(cmd)

    # Setting kubeconfig
    kube_config = set_kube_config(kube_config)

    # Escaping comma, forward slash present in https proxy urls, needed for helm params.
    https_proxy = escape_proxy_settings(https_proxy)

    # Escaping comma, forward slash present in http proxy urls, needed for helm params.
    http_proxy = escape_proxy_settings(http_proxy)

    # Escaping comma, forward slash present in no proxy urls, needed for helm params.
    no_proxy = escape_proxy_settings(no_proxy)

    # Checking whether optional extra values file has been provided.
    values_file_provided = False
    values_file = os.getenv('HELMVALUESPATH')
    if (values_file is not None) and (os.path.isfile(values_file)):
        values_file_provided = True
        logger.warning(
            "Values files detected. Reading additional helm parameters from same."
        )
        # trimming required for windows os
        if (values_file.startswith("'") or values_file.startswith('"')):
            values_file = values_file[1:]
        if (values_file.endswith("'") or values_file.endswith('"')):
            values_file = values_file[:-1]

    # Validate the helm environment file for Dogfood.
    dp_endpoint_dogfood = None
    release_train_dogfood = None
    if cmd.cli_ctx.cloud.endpoints.resource_manager == consts.Dogfood_RMEndpoint:
        dp_endpoint_dogfood, release_train_dogfood = validate_env_file_dogfood(
            values_file, values_file_provided)

    # Loading the kubeconfig file in kubernetes client configuration
    try:
        config.load_kube_config(config_file=kube_config, context=kube_context)
    except Exception as e:
        telemetry.set_user_fault()
        telemetry.set_exception(exception=e,
                                fault_type=consts.Load_Kubeconfig_Fault_Type,
                                summary='Problem loading the kubeconfig file')
        raise CLIError("Problem loading the kubeconfig file." + str(e))
    configuration = kube_client.Configuration()

    # Checking the connection to kubernetes cluster.
    # This check was added to avoid large timeouts when connecting to AAD Enabled AKS clusters
    # if the user had not logged in.
    check_kube_connection(configuration)

    # Get kubernetes cluster info for telemetry
    kubernetes_version = get_server_version(configuration)
    kubernetes_distro = get_kubernetes_distro(configuration)

    kubernetes_properties = {
        'Context.Default.AzureCLI.KubernetesVersion': kubernetes_version,
        'Context.Default.AzureCLI.KubernetesDistro': kubernetes_distro
    }
    telemetry.add_extension_event('connectedk8s', kubernetes_properties)

    # Checking helm installation
    check_helm_install(kube_config, kube_context)

    # Check helm version
    helm_version = check_helm_version(kube_config, kube_context)
    telemetry.add_extension_event(
        'connectedk8s', {'Context.Default.AzureCLI.HelmVersion': helm_version})

    # Check whether Connected Cluster is present
    if not connected_cluster_exists(client, resource_group_name, cluster_name):
        telemetry.set_user_fault()
        telemetry.set_exception(
            exception='The connected cluster resource does not exist',
            fault_type=consts.Resource_Does_Not_Exist_Fault_Type,
            summary='Connected cluster resource does not exist')
        raise CLIError(
            "The connected cluster resource {} does not exist ".format(
                cluster_name) +
            "in the resource group {} ".format(resource_group_name) +
            "Please onboard the connected cluster using: az connectedk8s connect -n <connected-cluster-name> -g <resource-group-name>"
        )

    # Fetch Connected Cluster for agent version
    connected_cluster = get_connectedk8s(cmd, client, resource_group_name,
                                         cluster_name)

    # Adding helm repo
    if os.getenv('HELMREPONAME') and os.getenv('HELMREPOURL'):
        utils.add_helm_repo(kube_config, kube_context)

    # Retrieving Helm chart OCI Artifact location
    registry_path = os.getenv('HELMREGISTRY') if os.getenv(
        'HELMREGISTRY') else utils.get_helm_registry(
            cmd, connected_cluster.location, dp_endpoint_dogfood,
            release_train_dogfood)

    reg_path_array = registry_path.split(':')
    agent_version = reg_path_array[1]

    # Set agent version in registry path
    if connected_cluster.agent_version is not None:
        agent_version = connected_cluster.agent_version
        registry_path = reg_path_array[
            0] + ":" + connected_cluster.agent_version

    telemetry.add_extension_event(
        'connectedk8s',
        {'Context.Default.AzureCLI.AgentVersion': agent_version})

    # Get Helm chart path
    chart_path = utils.get_chart_path(registry_path, kube_config, kube_context)

    cmd_helm_upgrade = [
        "helm", "upgrade", "azure-arc", chart_path, "--reuse-values", "--set",
        "global.httpsProxy={}".format(https_proxy), "--set",
        "global.httpProxy={}".format(http_proxy), "--set",
        "global.noProxy={}".format(no_proxy), "--wait", "--output", "json"
    ]
    if values_file_provided:
        cmd_helm_upgrade.extend(["-f", values_file])
    if kube_config:
        cmd_helm_upgrade.extend(["--kubeconfig", kube_config])
    if kube_context:
        cmd_helm_upgrade.extend(["--kube-context", kube_context])
    response_helm_upgrade = Popen(cmd_helm_upgrade, stdout=PIPE, stderr=PIPE)
    _, error_helm_upgrade = response_helm_upgrade.communicate()
    if response_helm_upgrade.returncode != 0:
        if ('forbidden' in error_helm_upgrade.decode("ascii")
                or 'timed out waiting for the condition'
                in error_helm_upgrade.decode("ascii")):
            telemetry.set_user_fault()
        telemetry.set_exception(
            exception=error_helm_upgrade.decode("ascii"),
            fault_type=consts.Install_HelmRelease_Fault_Type,
            summary='Unable to install helm release')
        raise CLIError(
            str.format(consts.Update_Agent_Failure,
                       error_helm_upgrade.decode("ascii")))

    return str.format(consts.Update_Agent_Success, connected_cluster.name)
Пример #12
0
def create_connectedk8s(cmd,
                        client,
                        resource_group_name,
                        cluster_name,
                        https_proxy="",
                        http_proxy="",
                        no_proxy="",
                        location=None,
                        kube_config=None,
                        kube_context=None,
                        no_wait=False,
                        tags=None):
    logger.warning(
        "Ensure that you have the latest helm version installed before proceeding."
    )
    logger.warning("This operation might take a while...\n")

    # Setting subscription id
    subscription_id = get_subscription_id(cmd.cli_ctx)

    # Send cloud information to telemetry
    send_cloud_telemetry(cmd)

    # Fetching Tenant Id
    graph_client = _graph_client_factory(cmd.cli_ctx)
    onboarding_tenant_id = graph_client.config.tenant_id

    # Setting kubeconfig
    kube_config = set_kube_config(kube_config)

    # Escaping comma, forward slash present in https proxy urls, needed for helm params.
    https_proxy = escape_proxy_settings(https_proxy)

    # Escaping comma, forward slash present in http proxy urls, needed for helm params.
    http_proxy = escape_proxy_settings(http_proxy)

    # Escaping comma, forward slash present in no proxy urls, needed for helm params.
    no_proxy = escape_proxy_settings(no_proxy)

    # Checking whether optional extra values file has been provided.
    values_file_provided = False
    values_file = os.getenv('HELMVALUESPATH')
    if (values_file is not None) and (os.path.isfile(values_file)):
        values_file_provided = True
        logger.warning(
            "Values files detected. Reading additional helm parameters from same."
        )
        # trimming required for windows os
        if (values_file.startswith("'") or values_file.startswith('"')):
            values_file = values_file[1:]
        if (values_file.endswith("'") or values_file.endswith('"')):
            values_file = values_file[:-1]

    # Validate the helm environment file for Dogfood.
    dp_endpoint_dogfood = None
    release_train_dogfood = None
    if cmd.cli_ctx.cloud.endpoints.resource_manager == consts.Dogfood_RMEndpoint:
        dp_endpoint_dogfood, release_train_dogfood = validate_env_file_dogfood(
            values_file, values_file_provided)

    # Loading the kubeconfig file in kubernetes client configuration
    try:
        config.load_kube_config(config_file=kube_config, context=kube_context)
    except Exception as e:
        telemetry.set_user_fault()
        telemetry.set_exception(exception=e,
                                fault_type=consts.Load_Kubeconfig_Fault_Type,
                                summary='Problem loading the kubeconfig file')
        raise CLIError("Problem loading the kubeconfig file." + str(e))
    configuration = kube_client.Configuration()

    # Checking the connection to kubernetes cluster.
    # This check was added to avoid large timeouts when connecting to AAD Enabled AKS clusters
    # if the user had not logged in.
    check_kube_connection(configuration)

    # Get kubernetes cluster info for telemetry
    kubernetes_version = get_server_version(configuration)
    kubernetes_distro = get_kubernetes_distro(configuration)

    kubernetes_properties = {
        'Context.Default.AzureCLI.KubernetesVersion': kubernetes_version,
        'Context.Default.AzureCLI.KubernetesDistro': kubernetes_distro
    }
    telemetry.add_extension_event('connectedk8s', kubernetes_properties)

    # Checking helm installation
    check_helm_install(kube_config, kube_context)

    # Check helm version
    helm_version = check_helm_version(kube_config, kube_context)
    telemetry.add_extension_event(
        'connectedk8s', {'Context.Default.AzureCLI.HelmVersion': helm_version})

    # Validate location
    utils.validate_location(cmd, location)
    resourceClient = _resource_client_factory(cmd.cli_ctx,
                                              subscription_id=subscription_id)

    # Check Release Existance
    release_namespace = get_release_namespace(kube_config, kube_context)
    if release_namespace:
        # Loading config map
        api_instance = kube_client.CoreV1Api(
            kube_client.ApiClient(configuration))
        try:
            configmap = api_instance.read_namespaced_config_map(
                'azure-clusterconfig', 'azure-arc')
        except Exception as e:  # pylint: disable=broad-except
            utils.kubernetes_exception_handler(
                e,
                consts.Read_ConfigMap_Fault_Type,
                'Unable to read ConfigMap',
                error_message=
                "Unable to read ConfigMap 'azure-clusterconfig' in 'azure-arc' namespace: ",
                message_for_not_found=
                "The helm release 'azure-arc' is present but the azure-arc namespace/configmap is missing. Please run 'helm delete azure-arc --no-hooks' to cleanup the release before onboarding the cluster again."
            )
        configmap_rg_name = configmap.data["AZURE_RESOURCE_GROUP"]
        configmap_cluster_name = configmap.data["AZURE_RESOURCE_NAME"]
        if connected_cluster_exists(client, configmap_rg_name,
                                    configmap_cluster_name):
            if (configmap_rg_name.lower() == resource_group_name.lower() and
                    configmap_cluster_name.lower() == cluster_name.lower()):
                # Re-put connected cluster
                try:
                    public_key = client.get(
                        configmap_rg_name,
                        configmap_cluster_name).agent_public_key_certificate
                except Exception as e:  # pylint: disable=broad-except
                    utils.arm_exception_handler(
                        e, consts.Get_ConnectedCluster_Fault_Type,
                        'Failed to check if connected cluster resource already exists.'
                    )
                cc = generate_request_payload(configuration, location,
                                              public_key, tags)
                create_cc_resource(client, resource_group_name, cluster_name,
                                   cc, no_wait)
            else:
                telemetry.set_user_fault()
                telemetry.set_exception(
                    exception='The kubernetes cluster is already onboarded',
                    fault_type=consts.Cluster_Already_Onboarded_Fault_Type,
                    summary='Kubernetes cluster already onboarded')
                raise CLIError(
                    "The kubernetes cluster you are trying to onboard " +
                    "is already onboarded to the resource group" +
                    " '{}' with resource name '{}'.".format(
                        configmap_rg_name, configmap_cluster_name))
        else:
            # Cleanup agents and continue with put
            delete_arc_agents(release_namespace, kube_config, kube_context,
                              configuration)
    else:
        if connected_cluster_exists(client, resource_group_name, cluster_name):
            telemetry.set_user_fault()
            telemetry.set_exception(
                exception='The connected cluster resource already exists',
                fault_type=consts.Resource_Already_Exists_Fault_Type,
                summary='Connected cluster resource already exists')
            raise CLIError(
                "The connected cluster resource {} already exists ".format(
                    cluster_name) +
                "in the resource group {} ".format(resource_group_name) +
                "and corresponds to a different Kubernetes cluster. To onboard this Kubernetes cluster"
                +
                "to Azure, specify different resource name or resource group name."
            )

    # Resource group Creation
    if resource_group_exists(cmd.cli_ctx, resource_group_name,
                             subscription_id) is False:
        resource_group_params = {'location': location}
        try:
            resourceClient.resource_groups.create_or_update(
                resource_group_name, resource_group_params)
        except Exception as e:  # pylint: disable=broad-except
            utils.arm_exception_handler(e,
                                        consts.Create_ResourceGroup_Fault_Type,
                                        'Failed to create the resource group')

    # Adding helm repo
    if os.getenv('HELMREPONAME') and os.getenv('HELMREPOURL'):
        utils.add_helm_repo(kube_config, kube_context)

    # Retrieving Helm chart OCI Artifact location
    registry_path = os.getenv('HELMREGISTRY') if os.getenv(
        'HELMREGISTRY') else utils.get_helm_registry(
            cmd, location, dp_endpoint_dogfood, release_train_dogfood)

    # Get azure-arc agent version for telemetry
    azure_arc_agent_version = registry_path.split(':')[1]
    telemetry.add_extension_event(
        'connectedk8s',
        {'Context.Default.AzureCLI.AgentVersion': azure_arc_agent_version})

    # Get helm chart path
    chart_path = utils.get_chart_path(registry_path, kube_config, kube_context)

    # Generate public-private key pair
    try:
        key_pair = RSA.generate(4096)
    except Exception as e:
        telemetry.set_exception(
            exception=e,
            fault_type=consts.KeyPair_Generate_Fault_Type,
            summary='Failed to generate public-private key pair')
        raise CLIError("Failed to generate public-private key pair. " + str(e))
    try:
        public_key = get_public_key(key_pair)
    except Exception as e:
        telemetry.set_exception(exception=e,
                                fault_type=consts.PublicKey_Export_Fault_Type,
                                summary='Failed to export public key')
        raise CLIError("Failed to export public key." + str(e))
    try:
        private_key_pem = get_private_key(key_pair)
    except Exception as e:
        telemetry.set_exception(exception=e,
                                fault_type=consts.PrivateKey_Export_Fault_Type,
                                summary='Failed to export private key')
        raise CLIError("Failed to export private key." + str(e))

    # Generate request payload
    cc = generate_request_payload(configuration, location, public_key, tags)

    # Create connected cluster resource
    put_cc_response = create_cc_resource(client, resource_group_name,
                                         cluster_name, cc, no_wait)

    # Install azure-arc agents
    helm_install_release(chart_path, subscription_id, kubernetes_distro,
                         resource_group_name, cluster_name, location,
                         onboarding_tenant_id, http_proxy, https_proxy,
                         no_proxy, private_key_pem, kube_config, kube_context,
                         no_wait, values_file_provided, values_file)

    return put_cc_response
Пример #13
0
def send_cloud_telemetry(cmd):
    cloud_name = cmd.cli_ctx.cloud.name
    telemetry.add_extension_event(
        'connectedk8s', {'Context.Default.AzureCLI.AzureCloud': cloud_name})
Пример #14
0
def create_connectedk8s(cmd, client, resource_group_name, cluster_name, location=None,
                        kube_config=None, kube_context=None, no_wait=False, tags=None):
    logger.warning("Ensure that you have the latest helm version installed before proceeding.")
    logger.warning("This operation might take a while...\n")

    # Setting subscription id
    subscription_id = get_subscription_id(cmd.cli_ctx)

    # Setting user profile
    profile = Profile(cli_ctx=cmd.cli_ctx)

    # Fetching Tenant Id
    graph_client = _graph_client_factory(cmd.cli_ctx)
    onboarding_tenant_id = graph_client.config.tenant_id

    # Setting kubeconfig
    kube_config = set_kube_config(kube_config)

    # Removing quotes from kubeconfig path. This is necessary for windows OS.
    trim_kube_config(kube_config)

    # Loading the kubeconfig file in kubernetes client configuration
    try:
        config.load_kube_config(config_file=kube_config, context=kube_context)
    except Exception as e:
        telemetry.set_user_fault()
        telemetry.set_exception(exception=e, fault_type=Load_Kubeconfig_Fault_Type,
                                summary='Problem loading the kubeconfig file')
        raise CLIError("Problem loading the kubeconfig file." + str(e))
    configuration = kube_client.Configuration()

    # Checking the connection to kubernetes cluster.
    # This check was added to avoid large timeouts when connecting to AAD Enabled AKS clusters
    # if the user had not logged in.
    check_kube_connection(configuration)

    # Get kubernetes cluster info for telemetry
    kubernetes_version = get_server_version(configuration)
    kubernetes_distro = get_kubernetes_distro(configuration)

    kubernetes_properties = {
        'Context.Default.AzureCLI.KubernetesVersion': kubernetes_version,
        'Context.Default.AzureCLI.KubernetesDistro': kubernetes_distro
    }
    telemetry.add_extension_event('connectedk8s', kubernetes_properties)

    # Checking helm installation
    check_helm_install(kube_config, kube_context)

    # Check helm version
    helm_version = check_helm_version(kube_config, kube_context)
    telemetry.add_extension_event('connectedk8s', {'Context.Default.AzureCLI.HelmVersion': helm_version})

    # Validate location
    rp_locations = []
    resourceClient = _resource_client_factory(cmd.cli_ctx, subscription_id=subscription_id)
    providerDetails = resourceClient.providers.get('Microsoft.Kubernetes')
    for resourceTypes in providerDetails.resource_types:
        if resourceTypes.resource_type == 'connectedClusters':
            rp_locations = [location.replace(" ", "").lower() for location in resourceTypes.locations]
            if location.lower() not in rp_locations:
                telemetry.set_user_fault()
                telemetry.set_exception(exception='Location not supported', fault_type=Invalid_Location_Fault_Type,
                                        summary='Provided location is not supported for creating connected clusters')
                raise CLIError("Connected cluster resource creation is supported only in the following locations: " +
                               ', '.join(map(str, rp_locations)) +
                               ". Use the --location flag to specify one of these locations.")
            break

    # Check Release Existance
    release_namespace = get_release_namespace(kube_config, kube_context)
    if release_namespace is not None:
        # Loading config map
        api_instance = kube_client.CoreV1Api(kube_client.ApiClient(configuration))
        try:
            configmap = api_instance.read_namespaced_config_map('azure-clusterconfig', 'azure-arc')
        except Exception as e:  # pylint: disable=broad-except
            telemetry.set_exception(exception=e, fault_type=Read_ConfigMap_Fault_Type,
                                    summary='Unable to read ConfigMap')
            raise CLIError("Unable to read ConfigMap 'azure-clusterconfig' in 'azure-arc' namespace: %s\n" % e)
        configmap_rg_name = configmap.data["AZURE_RESOURCE_GROUP"]
        configmap_cluster_name = configmap.data["AZURE_RESOURCE_NAME"]
        if connected_cluster_exists(client, configmap_rg_name, configmap_cluster_name):
            if (configmap_rg_name.lower() == resource_group_name.lower() and
                    configmap_cluster_name.lower() == cluster_name.lower()):
                # Re-put connected cluster
                public_key = client.get(configmap_rg_name,
                                        configmap_cluster_name).agent_public_key_certificate
                cc = generate_request_payload(configuration, location, public_key, tags)
                try:
                    return sdk_no_wait(no_wait, client.create, resource_group_name=resource_group_name,
                                       cluster_name=cluster_name, connected_cluster=cc)
                except CloudError as ex:
                    telemetry.set_exception(exception=ex, fault_type=Create_ConnectedCluster_Fault_Type,
                                            summary='Unable to create connected cluster resource')
                    raise CLIError(ex)
            else:
                telemetry.set_user_fault()
                telemetry.set_exception(exception='The kubernetes cluster is already onboarded', fault_type=Cluster_Already_Onboarded_Fault_Type,
                                        summary='Kubernetes cluster already onboarded')
                raise CLIError("The kubernetes cluster you are trying to onboard " +
                               "is already onboarded to the resource group" +
                               " '{}' with resource name '{}'.".format(configmap_rg_name, configmap_cluster_name))
        else:
            # Cleanup agents and continue with put
            delete_arc_agents(release_namespace, kube_config, kube_context, configuration)
    else:
        if connected_cluster_exists(client, resource_group_name, cluster_name):
            telemetry.set_user_fault()
            telemetry.set_exception(exception='The connected cluster resource already exists', fault_type=Resource_Already_Exists_Fault_Type,
                                    summary='Connected cluster resource already exists')
            raise CLIError("The connected cluster resource {} already exists ".format(cluster_name) +
                           "in the resource group {} ".format(resource_group_name) +
                           "and corresponds to a different Kubernetes cluster. To onboard this Kubernetes cluster" +
                           "to Azure, specify different resource name or resource group name.")

    # Resource group Creation
    if resource_group_exists(cmd.cli_ctx, resource_group_name, subscription_id) is False:
        resource_group_params = {'location': location}
        try:
            resourceClient.resource_groups.create_or_update(resource_group_name, resource_group_params)
        except Exception as e:
            telemetry.set_exception(exception=e, fault_type=Create_ResourceGroup_Fault_Type,
                                    summary='Failed to create the resource group')
            raise CLIError("Failed to create the resource group {} :".format(resource_group_name) + str(e))

    # Adding helm repo
    if os.getenv('HELMREPONAME') and os.getenv('HELMREPOURL'):
        repo_name = os.getenv('HELMREPONAME')
        repo_url = os.getenv('HELMREPOURL')
        cmd_helm_repo = ["helm", "repo", "add", repo_name, repo_url, "--kubeconfig", kube_config]
        if kube_context:
            cmd_helm_repo.extend(["--kube-context", kube_context])
        response_helm_repo = Popen(cmd_helm_repo, stdout=PIPE, stderr=PIPE)
        _, error_helm_repo = response_helm_repo.communicate()
        if response_helm_repo.returncode != 0:
            telemetry.set_exception(exception=error_helm_repo.decode("ascii"), fault_type=Add_HelmRepo_Fault_Type,
                                    summary='Failed to add helm repository')
            raise CLIError("Unable to add repository {} to helm: ".format(repo_url) + error_helm_repo.decode("ascii"))

    # Retrieving Helm chart OCI Artifact location
    registry_path = os.getenv('HELMREGISTRY') if os.getenv('HELMREGISTRY') else get_helm_registry(profile, location)

    # Get azure-arc agent version for telemetry
    azure_arc_agent_version = registry_path.split(':')[1]
    telemetry.add_extension_event('connectedk8s', {'Context.Default.AzureCLI.AgentVersion': azure_arc_agent_version})

    # Pulling helm chart from registry
    os.environ['HELM_EXPERIMENTAL_OCI'] = '1'
    pull_helm_chart(registry_path, kube_config, kube_context)

    # Exporting helm chart
    chart_export_path = os.path.join(os.path.expanduser('~'), '.azure', 'AzureArcCharts')
    export_helm_chart(registry_path, chart_export_path, kube_config, kube_context)

    # Generate public-private key pair
    try:
        key_pair = RSA.generate(4096)
    except Exception as e:
        telemetry.set_exception(exception=e, fault_type=KeyPair_Generate_Fault_Type,
                                summary='Failed to generate public-private key pair')
        raise CLIError("Failed to generate public-private key pair. " + str(e))
    try:
        public_key = get_public_key(key_pair)
    except Exception as e:
        telemetry.set_exception(exception=e, fault_type=PublicKey_Export_Fault_Type,
                                summary='Failed to export public key')
        raise CLIError("Failed to export public key." + str(e))
    try:
        private_key_pem = get_private_key(key_pair)
    except Exception as e:
        telemetry.set_exception(exception=e, fault_type=PrivateKey_Export_Fault_Type,
                                summary='Failed to export private key')
        raise CLIError("Failed to export private key." + str(e))

    # Helm Install
    helm_chart_path = os.path.join(chart_export_path, 'azure-arc-k8sagents')
    chart_path = os.getenv('HELMCHART') if os.getenv('HELMCHART') else helm_chart_path
    cmd_helm_install = ["helm", "upgrade", "--install", "azure-arc", chart_path,
                        "--set", "global.subscriptionId={}".format(subscription_id),
                        "--set", "global.kubernetesDistro={}".format(kubernetes_distro),
                        "--set", "global.resourceGroupName={}".format(resource_group_name),
                        "--set", "global.resourceName={}".format(cluster_name),
                        "--set", "global.location={}".format(location),
                        "--set", "global.tenantId={}".format(onboarding_tenant_id),
                        "--set", "global.onboardingPrivateKey={}".format(private_key_pem),
                        "--set", "systemDefaultValues.spnOnboarding=false",
                        "--kubeconfig", kube_config, "--output", "json"]
    if kube_context:
        cmd_helm_install.extend(["--kube-context", kube_context])
    response_helm_install = Popen(cmd_helm_install, stdout=PIPE, stderr=PIPE)
    _, error_helm_install = response_helm_install.communicate()
    if response_helm_install.returncode != 0:
        telemetry.set_exception(exception=error_helm_install.decode("ascii"), fault_type=Install_HelmRelease_Fault_Type,
                                summary='Unable to install helm release')
        raise CLIError("Unable to install helm release: " + error_helm_install.decode("ascii"))

    # Create connected cluster resource
    cc = generate_request_payload(configuration, location, public_key, tags)
    try:
        put_cc_response = sdk_no_wait(no_wait, client.create,
                                      resource_group_name=resource_group_name,
                                      cluster_name=cluster_name, connected_cluster=cc)
        if no_wait:
            return put_cc_response
    except CloudError as ex:
        telemetry.set_exception(exception=ex, fault_type=Create_ConnectedCluster_Fault_Type,
                                summary='Unable to create connected cluster resource')
        raise CLIError(ex)

    # Getting total number of pods scheduled to run in azure-arc namespace
    api_instance = kube_client.CoreV1Api(kube_client.ApiClient(configuration))
    pod_dict = get_pod_dict(api_instance)

    # Checking azure-arc pod statuses
    try:
        check_pod_status(pod_dict)
    except Exception as e:  # pylint: disable=broad-except
        telemetry.set_exception(exception=e, fault_type=Check_PodStatus_Fault_Type,
                                summary='Failed to check arc agent pods statuses')
        logger.warning("Failed to check arc agent pods statuses: %s", e)

    return put_cc_response
Пример #15
0
def start_ssh_connection(op_info, delete_keys, delete_cert):
    try:
        # Initialize these so that if something fails in the try block before these
        # are initialized, then the finally block won't fail.
        cleanup_process = None
        log_file = None
        connection_status = None

        ssh_arg_list = []
        if op_info.ssh_args:
            ssh_arg_list = op_info.ssh_args

        env = os.environ.copy()
        if op_info.is_arc():
            env['SSHPROXY_RELAY_INFO'] = connectivity_utils.format_relay_info_string(
                op_info.relay_info)

        # Get ssh client before starting the clean up process in case there is an error in getting client.
        command = [
            get_ssh_client_path('ssh', op_info.ssh_client_folder),
            op_info.get_host()
        ]

        if not op_info.cert_file and not op_info.private_key_file:
            # In this case, even if delete_credentials is true, there is nothing to clean-up.
            op_info.delete_credentials = False

        log_file, ssh_arg_list, cleanup_process = _start_cleanup(
            op_info.cert_file, op_info.private_key_file,
            op_info.public_key_file, op_info.delete_credentials, delete_keys,
            delete_cert, ssh_arg_list)
        command = command + op_info.build_args() + ssh_arg_list

        connection_duration = time.time()
        logger.debug("Running ssh command %s", ' '.join(command))

        # pylint: disable=subprocess-run-check
        try:
            if set(['-v', '-vv', '-vvv']).isdisjoint(ssh_arg_list) or log_file:
                connection_status = subprocess.run(
                    command,
                    shell=platform.system() == 'Windows',
                    env=env,
                    stderr=subprocess.PIPE,
                    encoding='utf-8')
            else:
                # Logs are sent to stderr. In that case, we shouldn't capture stderr.
                connection_status = subprocess.run(
                    command, shell=platform.system() == 'Windows', env=env)
        except OSError as e:
            colorama.init()
            raise azclierror.BadRequestError(
                f"Failed to run ssh command with error: {str(e)}.",
                const.RECOMMENDATION_SSH_CLIENT_NOT_FOUND)

        connection_duration = (time.time() - connection_duration) / 60
        ssh_connection_data = {
            'Context.Default.AzureCLI.SSHConnectionDurationInMinutes':
            connection_duration
        }
        if connection_status and connection_status.returncode == 0:
            ssh_connection_data[
                'Context.Default.AzureCLI.SSHConnectionStatus'] = "Success"
        telemetry.add_extension_event('ssh', ssh_connection_data)

    finally:
        # Even if something fails between the creation of the credentials and the end of the ssh connection, we
        # want to make sure that all credentials are cleaned up, and that the clean up process is terminated.
        _terminate_cleanup(delete_keys, delete_cert,
                           op_info.delete_credentials, cleanup_process,
                           op_info.cert_file, op_info.private_key_file,
                           op_info.public_key_file, log_file,
                           connection_status)
Пример #16
0
def _decide_resource_type(cmd, op_info):
    # If the user provides an IP address the target will be treated as an Azure VM even if it is an
    # Arc Server. Which just means that the Connectivity Proxy won't be used to establish connection.
    is_arc_server = False
    is_azure_vm = False

    if op_info.ip:
        is_azure_vm = True
        vm = None

    elif op_info.resource_type:
        if op_info.resource_type.lower() == "microsoft.hybridcompute":
            arc, arc_error, is_arc_server = _check_if_arc_server(cmd, op_info.resource_group_name, op_info.vm_name)
            if not is_arc_server:
                colorama.init()
                if isinstance(arc_error, ResourceNotFoundError):
                    raise azclierror.ResourceNotFoundError(f"The resource {op_info.vm_name} in the resource group "
                                                           f"{op_info.resource_group_name} was not found.",
                                                           const.RECOMMENDATION_RESOURCE_NOT_FOUND)
                raise azclierror.BadRequestError("Unable to determine that the target machine is an Arc Server. "
                                                 f"Error:\n{str(arc_error)}", const.RECOMMENDATION_RESOURCE_NOT_FOUND)

        elif op_info.resource_type.lower() == "microsoft.compute":
            vm, vm_error, is_azure_vm = _check_if_azure_vm(cmd, op_info.resource_group_name, op_info.vm_name)
            if not is_azure_vm:
                colorama.init()
                if isinstance(vm_error, ResourceNotFoundError):
                    raise azclierror.ResourceNotFoundError(f"The resource {op_info.vm_name} in the resource group "
                                                           f"{op_info.resource_group_name} was not found.",
                                                           const.RECOMMENDATION_RESOURCE_NOT_FOUND)
                raise azclierror.BadRequestError("Unable to determine that the target machine is an Azure VM. "
                                                 f"Error:\n{str(vm_error)}", const.RECOMMENDATION_RESOURCE_NOT_FOUND)

    else:
        vm, vm_error, is_azure_vm = _check_if_azure_vm(cmd, op_info.resource_group_name, op_info.vm_name)
        arc, arc_error, is_arc_server = _check_if_arc_server(cmd, op_info.resource_group_name, op_info.vm_name)

        if is_azure_vm and is_arc_server:
            colorama.init()
            raise azclierror.BadRequestError(f"{op_info.resource_group_name} has Azure VM and Arc Server with the "
                                             f"same name: {op_info.vm_name}.",
                                             colorama.Fore.YELLOW + "Please provide a --resource-type." +
                                             colorama.Style.RESET_ALL)
        if not is_azure_vm and not is_arc_server:
            colorama.init()
            if isinstance(arc_error, ResourceNotFoundError) and isinstance(vm_error, ResourceNotFoundError):
                raise azclierror.ResourceNotFoundError(f"The resource {op_info.vm_name} in the resource group "
                                                       f"{op_info.resource_group_name} was not found. ",
                                                       const.RECOMMENDATION_RESOURCE_NOT_FOUND)
            raise azclierror.BadRequestError("Unable to determine the target machine type as Azure VM or "
                                             f"Arc Server. Errors:\n{str(arc_error)}\n{str(vm_error)}",
                                             const.RECOMMENDATION_RESOURCE_NOT_FOUND)

    # Note: We are not able to determine the os of the target if the user only provides an IP address.
    os_type = None
    if is_azure_vm and vm and vm.storage_profile and vm.storage_profile.os_disk and vm.storage_profile.os_disk.os_type:
        os_type = vm.storage_profile.os_disk.os_type

    if is_arc_server and arc and arc.properties and arc.properties and arc.properties.os_name:
        os_type = arc.properties.os_name

    if os_type:
        telemetry.add_extension_event('ssh', {'Context.Default.AzureCLI.TargetOSType': os_type})

    # Note 2: This is a temporary check while AAD login is not enabled for Windows.
    if os_type and os_type.lower() == 'windows' and not op_info.local_user:
        colorama.init()
        raise azclierror.RequiredArgumentMissingError("SSH Login using AAD credentials is not currently supported "
                                                      "for Windows.",
                                                      colorama.Fore.YELLOW + "Please provide --local-user." +
                                                      colorama.Style.RESET_ALL)

    target_resource_type = "Microsoft.Compute"
    if is_arc_server:
        target_resource_type = "Microsoft.HybridCompute"
    telemetry.add_extension_event('ssh', {'Context.Default.AzureCLI.TargetResourceType': target_resource_type})

    return target_resource_type