def create_disks_and_attach(self, node): # Create a disk list disk_list = "" for i in range(0, self.disks): disk_list += "{0}-disk-{1} ".format(node, i) Log.info( "Creating and attaching disk(s) for node {0}. One moment...". format(node), True) result, status = self.invoke_gcloud( "compute disks create --size {0}GB --type pd-ssd --project {1} --zone {2} {3}" .format(self.block_disk_size, self.project, self.zone, disk_list)) Log.info("Created {0} disk(s) for node {1}".format(self.disks, node)) Log.debug(result) for i in range(0, self.disks): disk_name = "{0}-disk-{1} ".format(node, i) result, status = self.invoke_gcloud( "compute instances attach-disk {0} --disk {1} --project {2} --zone {3}" .format(node, disk_name, self.project, self.zone)) Log.info("Added disk {0} to node {1}".format(disk_name, node)) Log.debug(result) result, status = self.invoke_gcloud( "compute instances set-disk-auto-delete {0} --disk {1} --project {2} --zone {3}" .format(node, disk_name, self.project, self.zone)) Log.info("Set set-disk-auto-delete on disk {0}".format(disk_name)) Log.debug(result) Log.info("Created and attached disk(s) for node {0}".format(node), True)
def _create_update_resource_group(self): Log.info( "Creating or updating resource group: {0}...".format( self.context.resource_group), True) resource = ResourceGroup(location=self.context.location) self.resource_client.resource_groups.create_or_update( self.context.resource_group, resource)
def __init__(self, mode, response_file): if mode != Prompts.PROMPT_MODE and mode != Prompts.RECORD_MODE and mode != Prompts.HEADLESS_MODE: raise InstallException('Invalid prompt mode %s' % str(mode)) self.response_file = None self.mode = mode self.recorded_options = dict() self.headless_parser = None if mode == Prompts.HEADLESS_MODE: if response_file is None: raise InstallException( 'In order to run in headless mode, you must supply a response file.' ) if not os.path.exists(response_file): raise InstallException( 'In order to run in headless mode, you must supply an existing response file. %s does not exist.' % response_file) self.headless_parser = Parser.get_properties_parser(response_file) Log.info( 'Installer prompts have been initialized in headless mode.') elif mode == Prompts.RECORD_MODE: if response_file is None: raise InstallException( 'In order to run in record mode, you must supply a response file destination.' ) dirname = os.path.dirname(response_file) if not os.path.exists(dirname): raise InstallException( 'In order to run in record mode, the directory for the response file must exist.' ) self.response_file = open(response_file, 'w') Log.info( 'Installer prompts have been initialized in record mode. response file: {0}' .format(response_file))
def complete_uninstallation(): print(os.linesep) msg = "This Kubernetes environment" warnings = Log.get_warning_count() errors = Log.get_error_count() if errors > 0 and warnings > 0: msg = "{0} had {1} error(s) and {2} warning(s) during the uninstall process for MapR".format( msg, errors, warnings) Log.error(msg) elif errors > 0 and warnings == 0: msg = "{0} had {1} error(s) during the uninstall process for MapR".format( msg, errors) Log.error(msg) elif errors == 0 and warnings > 0: msg = "{0} had {1} warnings(s) during the uninstall process for MapR".format( msg, warnings) Log.warning(msg) else: msg = "{0} has had MapR successfully uninstalled".format(msg) Log.info(msg, True) if errors > 0 or warnings > 0: msg = "Please check the bootstrap log file for this session here: {0}".format( Log.get_log_filename()) Log.warning(msg) Log.info("")
def confirm_delete_installation(self): print(os.linesep) Log.info( "This will uninstall ALL MapR operators from your Kubernetes environment. This will cause all Compute Spaces to be destroyed. They cannot be recovered!", True) agree = self._prompts.prompt_boolean("Do you agree?", False, key_name="AGREEMENT") if not agree: Log.info("Very wise decision. Exiting uninstall...", True) BootstrapBase.exit_application(2)
def exit_application(signum, _=None): if signum == 0: Log.info("Bootstrap terminated {0}".format(signum)) else: print(os.linesep) Log.warning("Bootstrap terminated {0}".format(signum)) if BootstrapBase._prompts is not None: BootstrapBase._prompts.write_response_file() BootstrapBase._prompts = None Log.close() exit(signum)
def initialize(prompts): Cloud.prompts = prompts Cloud._clouds = list() Cloud._cloud_instances = dict() files = os.listdir(Cloud.DIR) Log.info("Initializing cloud support. One moment please...") for afile in files: full_file = os.path.join(Cloud.DIR, afile) if not full_file.endswith(".py"): Log.debug( "Not inspecting: {0} because it is not a py file".format( full_file)) continue if not os.path.isfile(full_file): Log.debug( "Not inspecting: {0} because it is not a file".format( full_file)) continue if afile == "cloud.py" or afile == "__init__.py": Log.debug( "Not inspecting: {0} because it is not a file".format( full_file)) continue module_name = full_file[:full_file.index(".")] file_module = imp.load_source(module_name, os.path.join(Cloud.DIR, full_file)) class_members = inspect.getmembers(file_module, inspect.isclass) Log.debug("class_members for file_module {0} is: {1}".format( str(file_module), str(class_members))) for clazz in class_members: # if the class is of the correct subclass add it to the list of tests if not issubclass(clazz[1], Cloud) or clazz[ 1] == Cloud or clazz[1] in Cloud._clouds: continue Cloud._clouds.append(clazz[1]) instance = clazz[1]() name = instance.get_name() if instance.is_enabled(): Cloud._cloud_instances[name] = instance Log.debug( "Cloud {0} was added to list because it is enabled". format(name)) else: Log.debug( "Cloud {0} was not added to list because it is not enabled" .format(name)) Log.debug("There were {0} cloud providers found".format( len(Cloud._clouds)))
def check_if_storage(self): print(os.linesep) agree = self._prompts.prompt_boolean("Install MapR Data Platform?", True, key_name="CREATE_STORAGE") Log.info( "Attention: MapR Data Platform on Kubernetes is PRE-ALPHA Software. Please DO NOT store critical data in clusters you create with this " "operator. You WILL lose data and these clusters WILL NOT be upgradable.", stdout=True) print("") return agree
def collect(self): Log.debug('Checking kubectl is installed correctly...') response, status = OSCommand.run2("command -v kubectl") if status == 0: Log.info("Looking good... Found kubectl") self.operation = Validator.OPERATION_OK else: self.operation = Validator.OPERATION_INSTALL Log.error( "You will need to have kubectl installed on this machine.") Log.error( "To install kubectl please see: https://kubernetes.io/docs/tasks/tools/install-kubectl/" )
def collect(self): Log.debug('Checking for oc (OpenShift CLI) installation...') response, status = OSCommand.run2("command -v oc") if status == 0: Log.info("Looking good... Found oc (OpenShift CLI) installed", True) self.operation = Validator.OPERATION_OK else: self.operation = Validator.OPERATION_NONE Log.error( "You will need to have oc (OpenShift CLI) installed on this machine." ) Log.error( "To install oc please see: https://docs.openshift.com/container-platform/3.11/cli_reference/get_started_cli.html" )
def process_labels(self): self._get_json() nodes_not_set = self.get_mapr_use_node_labels(NodeLabels.MAPR_LABEL) if nodes_not_set is not None and len(nodes_not_set) > 0: Log.info("Setting MapR usage tag {0} for {1} nodes...".format(NodeLabels.MAPR_LABEL, len(nodes_not_set)), stdout=True) for node_not_set in nodes_not_set: self.k8s.run_label_mapr_node(node_not_set, NodeLabels.MAPR_LABEL, True) nodes_not_set = self.get_mapr_use_node_labels(NodeLabels.EXCLUSIVE_LABEL) if nodes_not_set is not None and len(nodes_not_set) > 0: Log.info("Setting MapR usage tag {0} for {1} nodes...".format(NodeLabels.EXCLUSIVE_LABEL, len(nodes_not_set)), stdout=True) for node_not_set in nodes_not_set: self.k8s.run_label_mapr_node(node_not_set, NodeLabels.EXCLUSIVE_LABEL, "None")
def _get_json(self): Log.info("Retrieving node information...", stdout=True) result, status = self.k8s.run_get("nodes -o=json") if status != 0: return None self._json = json.loads(result) if self._json is None: Log.error("No JSON was returned from get nodes command") return self._items = self._json.get("items") if self._items is None: Log.error("No items dictonary in get nodes JSON") return self._node_count = len(self._items)
def build_cloud(self): self.cluster_name = self.prompts.prompt("Enter cluster name", self.cluster_name, key_name="GKE_CLUSTER_NAME") self.nodes = self.prompts.prompt_integer("Enter number of nodes", self.nodes, 1, key_name="GKE_NODE_COUNT") self.disks = self.prompts.prompt_integer( "Enter number of local SSD disks for MapR FS. Each disk will be a fixed 375GB", self.disks, 1, key_name="GKE_NUM_DISKS") self.instance_type = self.prompts.prompt("GKE compute instance type?", self.instance_type, key_name="GKE_INSTANCE_TYPE") if self.alpha: self.k8s_version = self.prompts.prompt("Kubernetes version?", self.k8s_alpha_version, key_name="GKE_K8S_VERSION") else: self.k8s_version = self.prompts.prompt("Kubernetes version?", self.k8s_version, key_name="GKE_K8S_VERSION") self.zone = self.prompts.prompt("GCE Zone to deploy into?", self.zone, key_name="GKE_ZONE") self.project = self.prompts.prompt("GCE project id?", self.project, key_name="GKE_PROJECT_ID") self.user = self.prompts.prompt("GCE user id?", self.user, key_name="GKE_USER") # self.image_type = self.prompts.prompt("GKE image type?", self.image_type) Log.info("Using GKE compute image type: {0}".format(self.image_type), True) if self.alpha: Log.info("Using alpha Kubernetes version", True) else: Log.info("Using non-alpha Kubernetes version", True) if not self.prompts.prompt_boolean( "Ready to create Google GKS cluster. Do you want to continue?", True, key_name="GKE_CREATE"): Log.error("Exiiting since user is not ready to continue") BootstrapBase.exit_application(100) before = time.time() self.create_k8s_cluster() after = time.time() diff = int(after - before) Log.info( "Cluster creation took {0}m {1}s".format(diff / 60, diff % 60), True)
def validate_nodes(self): print(os.linesep) Log.info( "We must validate and annotate your Kubernetes nodes. " "MapR node validation pods will be installed.", stdout=True) agree = self._prompts.prompt_boolean("Do you agree?", True, key_name="AGREEMENT_VALIDATE") if not agree: Log.error("Exiting due to non-agreement...") BootstrapBase.exit_application(2) # TODO: Add node exclusion code here # exclude = self._prompts.prompt_boolean("Do you want to exclude any nodes?", False, key_name="EXCLUDE_NODES") # if exclude: # Log.error("Operation not currently supported...") # BootstrapBase.exit_application(6) print("")
def invoke_stable_cluster(self, args): cmd = "container --project {0} clusters create {1} {2}".format( self.project, self.cluster_name, args) Log.info("kubernetes version = {0}".format(self.k8s_version), True) Log.info( "Creating GKE environment via the following command: gcloud {0}". format(cmd), True) Log.info("Create log follows...") result, status = self.invoke_gcloud(cmd) Log.info(result, True)
def run(self): logdir = os.path.join(self.script_dir, "logs") if os.path.exists(logdir): if not os.path.isdir(logdir): print( "ERROR: {0} is not a directory and cannot be used as alog directory" .format(logdir)) BootstrapBase.exit_application(1) else: os.mkdir(logdir) logname = os.path.join( logdir, BootstrapBase.NOW.strftime("bootstrap-%m-%d_%H:%M:%S.log")) Log.initialize(self.log_config_file, logname) BootstrapBase._prompts = Prompts.initialize(self.prompt_mode, self.prompt_response_file) Log.info("Prompt mode: {0}, response file: {1}".format( self.prompt_mode, self.prompt_response_file))
def is_available(self): if not self.enabled: return False if self.available is None: Log.info("Checking Google cloud availability. One moment...", True) results, status = OSCommand.run2( ["command -v gcloud", "gcloud compute instances list"]) self.available = True if status == 0 else False if not self.available: Log.warning( "Google Cloud SDK not found or not configured correctly. Quit bootstrapper, install and " "confgure Google Cloud SDK and restart bootstrapper. See: https://cloud.google.com/sdk/. " "More information on the error in the bootstrapper log here: " + Log.get_log_filename()) Log.warning(results) return self.available
def complete_installation(): print(os.linesep) msg = "This Kubernetes environment" warnings = Log.get_warning_count() errors = Log.get_error_count() if errors > 0 and warnings > 0: msg = "{0} had {1} error(s) and {2} warning(s) during the bootstraping process for MapR".format( msg, errors, warnings) Log.error(msg) elif errors > 0 and warnings == 0: msg = "{0} had {1} error(s) during the bootstraping process for MapR".format( msg, errors) Log.error(msg) elif errors == 0 and warnings > 0: msg = "{0} had {1} warnings(s) during the bootstraping process for MapR".format( msg, warnings) Log.warning(msg) else: msg = "{0} has been successfully bootstrapped for MapR".format(msg) Log.info(msg, True) Log.info( "MapR components can now be created via the newly installed operators", True) if errors > 0 or warnings > 0: msg = "Please check the bootstrap log file for this session here: {0}".format( Log.get_log_filename()) Log.warning(msg) Log.info("")
def prologue(self): title = os.linesep + "MapR for Kubernetes Bootstrap " title += "Installer" if self.is_install is True else "Uninstaller" title += " (Version {0})".format(BOOTSTRAP_BUILD_VERSION_NO) Log.info(title, True) Log.info("Copyright 2019 MapR Technologies, Inc., All Rights Reserved", True) Log.info("https://mapr.com/legal/eula/", True)
def collect(self): Log.debug("Getting Python information...") python_major, python_version = self._get_python_version() Log.info("Python version: {0}".format(python_version)) self.results[Validator.FOUND] = True self.results[Validator.VERSION] = python_version if python_major == 2: pmin = PythonValidator.PYTHON2_MIN pmax = PythonValidator.PYTHON2_MAX elif python_major == 3: pmin = PythonValidator.PYTHON3_MIN pmax = PythonValidator.PYTHON3_MAX if python_version <= PythonValidator.PYTHON3_ERROR_MAX: Log.error("The virtual environments created with your python version {0} are incompatible. " "Please use Python 3.3 or greater".format(python_version)) self.operation = Validator.OPERATION_INSTALL return else: Log.error("The major Python version '{0}' is not supported; Only version 2 and 3 supported".format(python_major)) self.operation = Validator.OPERATION_TOO_NEW return expected = "Expected versions between {0} and {1} or between {2} and {3}"\ .format(PythonValidator.PYTHON2_MIN, PythonValidator.PYTHON2_MAX, PythonValidator.PYTHON3_MIN, PythonValidator.PYTHON3_MAX) if python_version > pmax or python_version < pmin: Log.warning("The Python version on this system is {0}. {1}" .format(python_version, expected)) self.operation = Validator.OPERATION_WARNING else: Log.debug("The Python version on this system is compatible") self.operation = Validator.OPERATION_OK
def _create_aks(self): with open(os.path.join(self.base_path, CreateAKS.PARAMETERS_JSON)) as in_file: parameters = json.load(in_file) parameters[u"resourceName"][u"value"] = self.context.aks_name parameters[u"agentCount"][u"value"] = self.context.node_count parameters[u"agentVMSize"][u'Value'] = self.context.vm_size parameters[u"osDiskSizeGB"][u'Value'] = int(self.context.os_disk_size) parameters[u"sshRSAPublicKey"][u'Value'] = self.context.public_key parameters[u"servicePrincipalClientId"][ u'Value'] = self.context.client_id parameters[u"servicePrincipalClientSecret"][ u'Value'] = self.context.secret parameters[u"kubernetesVersion"][u'Value'] = self.context.k8s_version parameters[u"dnsPrefix"][u'Value'] = "{0}-maprtech".format( self.context.aks_name) with open(os.path.join(self.base_path, CreateAKS.ARM_TEMPLATE_JSON)) as in_file: template = json.load(in_file) deployment_properties = DeploymentProperties() deployment_properties.template = template deployment_properties.parameters = parameters deployment_properties.mode = DeploymentMode.incremental Log.info( "{0}: Run K8S deployment test with {1} Nodes OS Disk Size {2}...". format(self.context.resource_group, self.context.node_count, self.context.os_disk_size), True) deployment_async_operation = self.resource_client.deployments.create_or_update( self.context.resource_group, "maprk8s.deployment", deployment_properties) deployment_async_operation.wait() Log.info("K8S cluster deployment complete", True)
def configure_cloud(self): cmd = GoogleCloud.CMD_ROLE_BINDING.format(self.user) Log.info("Now we will configure RBAC for your kubernetes env...", True) Log.info( "Binding cluster-admin role to GCE user: {0}...".format(self.user), True) response, status = OSCommand.run2(cmd) if status != 0: Log.error("Could not bind cluster-admin role: {0}:{1}", status, response) Log.info("Configured GKE permissions", True)
def configure_kubernetes(self): print(os.linesep) Log.info("Ensuring proper kubernetes configuration...", True) Log.info("Checking kubectl can connect to your kubernetes cluster...", True) response, status = OSCommand.run2("kubectl get nodes") if status != 0: Log.error( "Cannot connect to Kubernetes. Make sure kubectl is pre-configured to communicate with a Kubernetes cluster." ) BootstrapBase.exit_application(4) Log.info("Looking good... Connected to Kubernetes", True) if self.cloud_instance is not None: self.cloud_instance.configure_cloud()
def get_mapr_use_node_labels(self, label): nodes_set = 0 nodes_not_set = set() for node in self._items: node_name = node["metadata"]["name"] mapr_usenode = node["metadata"]["labels"].get(label) if mapr_usenode is not None: nodes_set += 1 Log.info("Node: {0} has {1} label set to: {2}".format(node_name, label, mapr_usenode)) else: nodes_not_set.add(node_name) Log.info("Node: {0} does not have {1} label set".format(node_name, label)) Log.info("{0} node(s) found, {1} node(s) tagged with the MapR usage tag {2} while {3} node(s) not" .format(self._node_count, nodes_set, label, len(nodes_not_set)), stdout=True) return nodes_not_set
def run(self): # vms = self.compute_client.virtual_machines.list(self.resource_group) # for vm in vms: # nic = vm.network_profile.network_interfaces # pass nics = self.network_client.network_interfaces.list(self.resource_group) for nic in nics: ipconfig = nic.ip_configurations[0] public_ip_name = "{0}-publicip".format(nic.name) public_ip = { 'location': self.location, 'public_ip_allocation_method': 'Dynamic' } if ipconfig.public_ip_address is not None: Log.info( "{0} NIC already has a public ip address".format(nic.name), True) continue Log.info( "Creating or updating public ip address {0}...".format( public_ip_name), True) ip_rslt = self.network_client.public_ip_addresses.create_or_update( self.resource_group, public_ip_name, public_ip) ip_rslt.wait() public_ip = self.network_client.public_ip_addresses.get( self.resource_group, public_ip_name) ipconfig.public_ip_address = public_ip params = { 'location': self.location, 'ip_configurations': [ipconfig] } Log.info( "Associate public ip address {0} with NIC {1}".format( public_ip_name, nic.name), True) ip_rslt = self.network_client.network_interfaces.create_or_update( self.resource_group, nic.name, params) ip_rslt.wait()
def __init__(self, env, compute_client): self.env = env self.compute_client = compute_client self.aks_rg = self.env.get("RESOURCE_GROUP") self.aks_name = self.env.get("AKS_NAME") self.location = self.env.get("LOCATION") self.data_disk_count = self.env.get_int("DATA_DISK_COUNT") self.data_disk_size = self.env.get_int("DATA_DISK_SIZE") self.data_disk_type = self.env.get("DATA_DISK_TYPE") self.resource_group = "MC_{0}_{1}_{2}".format(self.aks_rg, self.aks_name, self.location) if self.data_disk_type is None: Log.info("DATA_DISK_TYPE not supplied, using Standard_LRS", True) self.data_disk_type = StorageAccountTypes.standard_lrs elif (self.data_disk_type != "Standard_LRS") and \ (self.data_disk_type != "StandardSSD_LRS") and \ (self.data_disk_type != "Premium_LRS"): Log.info("Invalid disk type {0}, using Standard_LRS".format(self.data_disk_type), True) self.data_disk_type = StorageAccountTypes.standard_lrs else: Log.info("Creating data disks of type; {0}".format(self.data_disk_type), True)
def get_clients(self): Log.info("Getting Azure clients...", True) self.resource_client = ResourceManagementClient(self.credentials, self.context.subscription_id) self.compute_client = ComputeManagementClient(self.credentials, self.context.subscription_id) self.network_client = NetworkManagementClient(self.credentials, self.context.subscription_id) self.monitor_client = MonitorClient(self.credentials, self.context.subscription_id)
def run(self): Log.info("Checking disks in resource group: {0}...".format(self.resource_group), True) existing_disks = self.compute_client.disks.list_by_resource_group(self.resource_group) for disk in existing_disks: Log.info("Found managed disk: {0}".format(disk.name), True) vms = self.compute_client.virtual_machines.list(self.resource_group) for vm in vms: Log.info("Checking VM: {0} for data disks".format(vm.name), True) attached_disks = vm.storage_profile.data_disks Log.info("Found {0} data disk(s) attached to the VM".format(len(attached_disks)), True) if len(attached_disks) >= self.data_disk_count: Log.info("There are already {0} data disk(s) attached when only {1} were required".format(len(attached_disks), self.data_disk_count), True) continue disks = list() for i in range(len(attached_disks), self.data_disk_count): data_disk_name = "{0}_DataDisk_{1}".format(vm.name, i) Log.info("Creating or updating data disk: {0}...".format(data_disk_name), True) disk = self.create_disk(data_disk_name) disks.append(disk) Log.info("The data disk is: {0}".format(disk.name), True) Log.info("Attaching the data disks to the vm...", True) self.attach_disks(vm, disks, len(attached_disks))
def pas_complete_installation(): Log.info("PAS Installation complete")
def install_cloud(self): print("") Cloud.initialize(self._prompts) cloud_names = Cloud.get_cloud_names() if len(cloud_names) == 0: Log.warning( "There are no supported cloud providers found in this bootstrapper application" ) return False Log.info( "If you are installing in a cloud provider, we can help you create your kubernetes environment.", True) Log.info( "ATTENTION: Cloud Environment installation is provided AS IS with no support.", True) Log.info( "Work with your IT Team to help create kubernetes environments with the security and reliability features that suit your enterprise needs.", True) create = self._prompts.prompt_boolean( "Do you want to create a kubernetes environment in the Cloud?", False, key_name="CLOUD_ENV") if not create: Log.info("Not building cloud environment") return False # Check the availability of each enabled cloud provider Cloud.check_available() cloud_names = Cloud.get_cloud_names() if len(cloud_names) == 0: Log.warning( "Some clouds were enabled but necessary modules that support these clouds are not available" ) BootstrapBase.exit_application(7) choice = self._prompts.prompt_choices("Choose a cloud provider", Cloud.get_cloud_names(), key_name="CLOUD_PROVIDER") Log.info("Using cloud provider {0}".format(choice)) self.cloud_instance = Cloud.get_instance(choice) Log.debug("Using cloud instance {0}".format(str(self.cloud_instance))) Log.info("Building {0} cloud k8s...".format(choice)) self.cloud_instance.build_cloud() Log.info("Created {0} cloud k8s".format(choice)) self.cloud_created = True return True