def _platform_detach_volume(self, volume): """ Detach a volume using the Google Cloud Platform API Some specific steps are performed here: - Check if the selected volume is a boot disk Args: volume (<volume>): The volume to detach Returns: bool: True if the volume is successfully detached, False otherwise """ # Search for the instance the volume is attached to instance = self._get_instance_from_volume(volume) if instance is None: return False if not self._is_volume_removable(volume): SimpleTUI.msg_dialog( "Detaching status", "This volume is mounted as boot disk and cannot be detached until the VM is stopped!", SimpleTUI.DIALOG_ERROR) return result = self.gcp_client.detach_volume(volume, instance) if result: while True: # No direct way to refresh a Volume status, so we look if # it is still attached to its previous instance updated_volume = self.gcp_client.ex_get_volume(volume.name) if not self._platform_is_volume_attached(updated_volume): break time.sleep(3) return result
def _platform_override_menu(self, menu, choice): """ Override a menu voice Args: menu (str): a menu identifier choice (int): a menu entry index Returns: int: 0 for normal, 1 for going back to the main menu of EasyCloud, 2 for closing the whole application """ if menu == "floating_ips": if choice == 5: # Detach floating ip SimpleTUI.msg_dialog( "Detach Floating IP", "Detaching an IP address from an instance is not supported on GCP: \n" + "an instance must always have an IP associated while running.\n" + "However, you can assign another address to the desired instance to\n" + "replace the current one.", SimpleTUI.DIALOG_INFO) return 0 SimpleTUI.error("Unavailable choice!") SimpleTUI.pause() SimpleTUI.clear_console() pass
def load_all_modules(self, no_libs_check): """ Loads all the Modules from the modules folder """ _all_modules = list(iter_modules(modules.__path__)) for _module in _all_modules: module_object = Module(_module[1]) try: if no_libs_check or self.check_dependencies(module_object): module_object.load_manager_class() self.loaded_modules.append(module_object) except ImportError as e: SimpleTUI.msg_dialog("Missing packages", "There was an error while importing a package for the\n" + module_object.platform_name + " module: \n\n" + str(e) + "\n\n" + "This module won't be loaded.", SimpleTUI.DIALOG_ERROR)
def _platform_extra_menu(self): """ Print the extra Functions Menu (specific for each platform) """ while (True): menu_header = self.platform_name + " Extra Commands" menu_subheader = [ "Region: \033[1;94m" + self._platform_get_region() + "\033[0m" ] menu_items = [ "Promote ephimeral IP to static", "Demote a static IP", "List instances for all the regions", "List volumes for all the regions", "List floating ips for all the regions", "Back to the Main Menu" ] choice = SimpleTUI.print_menu(menu_header, menu_items, menu_subheader) if choice == 1: # Promote IP self.promote_ephimeral_ip() elif choice == 2: # Demote IP answer = SimpleTUI.yn_dialog( "Demotion status", "You can demote a static IP easily deleting it through \"Manage floating IPs\" > \"Release a reserved Floating IP\".\n" + "NOTE: the static IP won't be removed from the associated instance until the latter is stopped/rebooted/deleted.\n" + "For more infos about Ephimeral/Static IPs on GCP, please visit https://cloud.google.com/compute/docs/ip-addresses/.\n" + "Would you like to start the \"Release a reserved Floating IP\" wizard now?", SimpleTUI.DIALOG_INFO) if answer: self.release_floating_ip() elif choice == 3: # List all the instances (Global) SimpleTUI.list_dialog("Instances available (Global view)", list_printer=self.print_global_instances) elif choice == 4: # List all the volumes (Global) SimpleTUI.list_dialog("Volumes available (Global view)", list_printer=self.print_global_volumes) elif choice == 5: # List all the floating ips (Global) SimpleTUI.list_dialog( "Floating IPs available (Global view)", list_printer=self.print_global_floating_ips) elif choice == 6: break else: SimpleTUI.msg_dialog("Error", "Unimplemented functionality", SimpleTUI.DIALOG_ERROR)
def _platform_extra_menu(self): """ Print the extra Functions Menu (specific for each platform) """ while (True): menu_header = self.platform_name + " Extra Commands" menu_subheader = [ "Region: \033[1;94m" + self._platform_get_region() + "\033[0m" ] menu_items = ["Back to the Main Menu"] choice = SimpleTUI.print_menu(menu_header, menu_items, menu_subheader) # if choice == 1 and self._is_barebone(): # Blazar menu if choice == 1: break else: SimpleTUI.msg_dialog("Error", "Unimplemented functionality", SimpleTUI.DIALOG_ERROR)
def _platform_delete_volume(self, volume): """ Delete a volume using the Google Cloud Platform API Some specific steps are performed here: - Check if the selected volume is a boot disk Args: volume (<volume>): The volume to delete Returns: bool: True if the volume is successfully deleted, False otherwise """ if not self._is_volume_removable(volume): SimpleTUI.msg_dialog( "Volume deletion", "This volume is mounted as boot disk and cannot be detached until the VM is stopped!", SimpleTUI.DIALOG_ERROR) return return self.gcp_client.destroy_volume(volume)
def promote_ephimeral_ip(self): """ Promote an Ephimeral IP to a Static one For more infos about Ephimeral/Static IPs on GCP, please visit https://cloud.google.com/compute/docs/ip-addresses/ """ # Select an instance floating_ip = None while (True): instance_index = SimpleTUI.list_dialog( "Instances available", self.print_all_instances, question= "Select the instance which floating IP has to be promoted to \"static\"" ) if instance_index is None: return instance = self.instances[instance_index - 1] # Check if the instance has an IP assigned (e.g. no IP is assigned while stopped) if len(instance.public_ips) == 0 or None in instance.public_ips: SimpleTUI.msg_dialog( "Promotion status", "This instance has no available floating IPs to promote!", SimpleTUI.DIALOG_ERROR) # Check if the instance has already a static IP assigned elif self._is_instance_floating_ip_static(instance): SimpleTUI.msg_dialog( "Promotion status", "This instance floating IP is already promoted to \"static\"!", SimpleTUI.DIALOG_ERROR) # Continue the ephimeral to static conversion else: floating_ip = instance.public_ips[0] break # Specify address name address_name = SimpleTUI.input_dialog( "Static Floating IP Name", question="Specify a name for the new Static Floating IP", return_type=str, regex="^[a-zA-Z0-9-]+$") if address_name is None: return if self._promote_ephimeral_ip_to_static(floating_ip, address_name): SimpleTUI.msg_dialog("Static Floating IP Promotion", "Floating IP promoted!", SimpleTUI.DIALOG_SUCCESS) else: SimpleTUI.msg_dialog( "Static Floating IP Promotion", "There was an error while promoting this Floating IP!", SimpleTUI.DIALOG_ERROR)
def menu(self): """ Prints the modules menu """ global kill while True: # Header creation menu_header = "******************** EasyCloud ********************" # Subheader creation disclaimer = "\033[34;1mThis is a proof-of-concept build, not suitable\nfor production use.\033[0m\n" debug_status = None hffr_status = None if "LIBCLOUD_DEBUG" in environ: debug_status = "\033[93mLibcloud Debug Mode ON\033[0m" else: debug_status = "\033[90mLibcloud Debug Mode OFF\033[0m" if "LIBCLOUD_DEBUG_PRETTY_PRINT_RESPONSE" in environ and environ["LIBCLOUD_DEBUG_PRETTY_PRINT_RESPONSE"] == "1": hffr_status = "\033[93mLibcloud Human friendly formatted response ON\033[0m" else: hffr_status = "\033[90mLibcloud Human friendly formatted response OFF\033[0m" menu_subheader = [disclaimer, debug_status, hffr_status] # Menu items creation menu_items = [] for module in self.loaded_modules: menu_items.append(module.platform_name) menu_items.append("Close application") # Menu print choice = SimpleTUI.print_menu(menu_header, menu_items, subheader_items=menu_subheader, custom_question="Select a platform") try: if(choice >= 1 and choice <= len(self.loaded_modules)): return self.loaded_modules[choice - 1] # Load a module elif(choice == len(self.loaded_modules) + 1): # Close application self.close(0) else: SimpleTUI.msg_dialog("Error", "Unimplemented functionality", SimpleTUI.DIALOG_ERROR) except Exception as e: SimpleTUI.exception_dialog(e)
def check_dependencies(self, module): """ Check if all the dependencies (pip packages) are satisfied for a certain module, and can install them if the user approves Args: module (Module): a module object (no manager class must be loaded through load_manager_class method of this object) Returns: bool: the result of the check """ required_packages = getattr(module, "dependencies") installed_packages = subprocess.check_output(['pip3', 'list', '--format=json'], stderr=subprocess.STDOUT).decode() missing_packages_names = [] # package names displayed to the user missing_packages_commands = [] # packages names/urls passed to pip3 for required_package in required_packages: # Format: pip-package-name:package-url|package-git (the latter is optional) # # Symbolic: pip package name (the one you see with "pip3 list") # Package URL (optional): package url from where pip3 will download the library # Package Git (optional): package git url in the form git+git://github.com/my_user/my_project.git(@branch) # # e.g. libcloud:apache-libcloud required_package_data = required_package.split(":", 1) if required_package_data[0] not in installed_packages: missing_packages_names.append(required_package_data[0]) if len(required_package_data) == 2: missing_packages_commands.append(required_package_data[1]) else: missing_packages_commands.append(required_package_data[0]) if len(missing_packages_names) > 0: packages_list = "" for missing_package_name in missing_packages_names: packages_list += "- " + missing_package_name + "\n" choice = SimpleTUI.yn_dialog("Missing packages", "The following packages are required by the " + module.platform_name + " module:\n" + "\n" + packages_list + "\nIf these packages are not installed, this module won't be loaded.\n" + "Do you want to install them through pip?", warning=True) if choice: SimpleTUI.msg_dialog("Library installer", "Installing the required libraries, this can take a bit...", SimpleTUI.DIALOG_INFO, pause_on_exit=False, clear_on_exit=False) if self.install_libraries(missing_packages_commands): SimpleTUI.msg_dialog("Library installer", "All the packages for " + module.platform_name + "\n" + "were successfully installed!\n\n" + packages_list, SimpleTUI.DIALOG_SUCCESS) return True else: SimpleTUI.msg_dialog("Library installer", "There was an error while installing the missing packages\n" + "for " + module.platform_name + ".\n" "Please check logs" + sep + "installer.log in the main directory for details.", SimpleTUI.DIALOG_ERROR) return False return False return True
def _platform_associate_floating_ip(self, floating_ip, instance): """ Associate a floating IP to an instance using the Google Cloud Platform API Some specific steps are performed here: - NIC (Network interface controller) selection - Access config name Args: floating_ip (GCEAddress): The floating IP to attach instance (Node): The instance where the floating IP is to be assigned Returns: bool: True if the floating IP is successfully associated, False otherwise """ # Set an instance, as required by print_all_nics() self.current_instance = instance nic_index = SimpleTUI.list_dialog( "NICs available", self.print_all_nics, question="Select the VM NIC to assign this IP") if nic_index is None: return nic = instance.extra["networkInterfaces"][ nic_index - 1] # serve nome per rimuovere # Check if there's already an active Access Configuration and ask the user for confirm remove_old_access_config = False if self._nic_has_access_config(nic): choice = SimpleTUI.yn_dialog( "Access Configuration Overwrite", "Warning: there's already an access configuration associated to this NIC.\n" + "Do you really want to continue (the current access configuration will be overwritten)?", warning=True) if not choice: return remove_old_access_config = True # Access Configuration name access_config_name = SimpleTUI.input_dialog( "Access configuration", question="Specify an access configuration name", return_type=str, regex="^[a-zA-Z0-9-]+$") if access_config_name is None: return # Remove the old access configuration if it's already existing if remove_old_access_config: SimpleTUI.msg_dialog("Access Configuration Overwrite", "Removing old access configuration...", SimpleTUI.DIALOG_INFO, pause_on_exit=False, clear_on_exit=False) if not self._delete_access_config(instance, nic): SimpleTUI.msg_dialog( "Access Configuration Overwrite", "There was an error while removing the current access configuration!", SimpleTUI.DIALOG_ERROR) return # Associate the Access Configuration to the NIC if self.gcp_client.ex_add_access_config(node=instance, name=access_config_name, nic=nic, nat_ip=floating_ip.address, config_type="ONE_TO_ONE_NAT"): return True return False