class OktawaveApi(object): def __init__(self, username, password, debug=False): """Initialize the API instance Arguments: - username (string) - Oktawave account username - password (string) - Oktawave account password - debug (bool) - enable debug output? """ self.username = username self.password = password self.debug = debug self.client_id = None self.client_object = None self.common = None self.clients = None # HELPER METHODS ### # methods starting with "_" will not be autodispatched to client commands # ### def _d(self, what): if self.debug: print what def _init_common(self): """Convenience method to initialize CommonService client""" if self.common is not None: return self.common = ApiClient(jsonapi_common, self.username, self.password, self.debug) self._d(self.common) def _init_clients(self): """Convenience method to initialize ClientsService client""" if self.clients is not None: return self.clients = ApiClient(jsonapi_clients, self.username, self.password, self.debug) self._d(self.clients) def logon(self, only_common=False): """Initializes CommonService client and calls LogonUser method. Returns the User object, as returned by LogonUser. Also sets self.client_id for convenience. """ self._init_common() if not only_common: self._init_clients() if self.client_object is not None: return self.client_object try: res = self.common.call( "LogonUser", user=self.username, password=self.password, ipAddress=_get_machine_ip(), userAgent="Oktawave CLI", ) except AttributeError: raise OktawaveLoginError() self.client_id = res["User"]["Client"]["ClientId"] self.client_object = res["User"] return res def _simple_vm_method(self, method, vm_id): """Wraps around common simple virtual machine method call pattern""" self.logon() return self.clients.call(method, virtualMachineId=vm_id, clientId=self.client_id) def _find_disk(self, disk_id): """Finds a disk (OVS) by id""" dsp = {"ClientId": self.client_id} disks = [d for d in self.clients.call("GetDisks", searchParams=dsp)["_results"] if d["ClientHddId"] == disk_id] if len(disks) == 0: return None res = disks[0] if res["VirtualMachineHdds"] is None: res["VirtualMachineHdds"] = [] return res def _dict_item(self, dict_id, key): items = self.common.call("GetDictionaryItems", dictionary=dict_id, clientId=self.client_id) for item in items: item = DictionaryItem(item) if item.name == key: return item def _oci_class(self, class_name): """Returns a dictionary item for OCI class with a given name""" return self._dict_item(DICT["OCI_CLASSES_DICT_ID"], class_name) def _ovs_tier(self, tier): """Returns ID of a given disk tier""" tier_name = "Tier " + str(tier) tier_obj = self._dict_item(DICT["OVS_TIERS_DICT_ID"], tier_name) if not tier_obj: raise OktawaveOVSTierNotFound() return tier_obj def _container_simple(self, container_id): """Fetches a container's information using GetContainersSimpleWithVM""" self.logon() cs = self.clients.call("GetContainersSimpleWithVM", clientId=self.client_id) for c in cs: self._d([c, container_id]) if str(c["ContainerId"]) == str(container_id): return c raise OktawaveContainerNotFoundError() # API methods below ### # General / Account ### def Account_Settings(self): """Print basic settings of the client account args is an object containing at least the following fields: - username - Oktawave username - password - Oktawave client password Typically args will be the object returned from argparse. """ self.logon(only_common=True) client = self.client_object # TODO: probably get more settings return { "time_zone": client["TimeZone"]["DisplayName"], "currency": DictionaryItem(client["Currency"]), "date_format": DictionaryItem(client["DateFormat"]), "availability_zone": DictionaryItem( self.common.call("GetDictionaryItemById", dictionaryItemId=client["AvailabilityZone"]) ), "24h_clock": client["Is24HourClock"], } def Account_RunningJobs(self): self.logon() res = self.common.call("GetRunningOperations", clientId=self.client_id) if not res: return for op in res: yield { "id": op["AsynchronousOperationId"], "creation_date": op["CreationDate"], "creation_user_name": op["CreationUserFullName"], "type": RawDictionaryItem(op["OperationTypeId"], op["OperationTypeName"]), "object_type": RawDictionaryItem(op["ObjectTypeId"], op["ObjectTypeName"]), "object_name": op["ObjectName"], "progress_percent": op["Progress"], "status": RawDictionaryItem(op["StatusId"], op["StatusName"]), } def Account_Users(self): """Print users in client account.""" self.logon() users = self.clients.call("GetClientUsers", clientId=self.client_id) self._d(users) for user in users: yield {"email": user["Email"], "name": user["FullName"]} # OCI (VMs) ### def OCI_TemplateCategories(self): """Lists available template categories""" self.logon() data = self.common.call("GetTemplateCategories", clientId=self.client_id) self._d(data) for tc in data: yield TemplateCategory(tc, None) if tc["CategoryChildren"] is not None: for tcc in tc["CategoryChildren"]: yield TemplateCategory(tcc, tc["TemplateCategoryId"]) def OCI_Templates(self, category_id, name_filter=""): """Lists templates in a category""" self.logon() data = self.common.call( "GetTemplatesByCategory", categoryId=category_id, categorySystemId=None, type=None, clientId=self.client_id ) if data: return dict( (template["TemplateId"], template["TemplateName"]) for template in data if name_filter in template["TemplateName"] ) def OCI_TemplateInfo(self, template_id): """Shows more detailed info about a particular template""" self.logon() data = self.clients.call("GetTemplate", templateId=template_id, clientId=self.client_id) template_category = TemplateCategory(data["TemplateCategory"], None) software = [SoftwareItem(item["Software"]) for item in data["SoftwareList"]] return { "template_id": data["TemplateId"], "template_name": data["TemplateName"], "template_category": template_category.tree_path, "vm_class_id": data["VMClass"]["DictionaryItemId"], "vm_class_name": DictionaryItem(data["VMClass"]), "system_category_name": DictionaryItem(data["TemplateSystemCategory"]), "label": data["Name"], "software": software, "eth_count": data["EthernetControllersCount"], "connection_type": DictionaryItem(data["ConnectionType"]), "disks": [ {"name": hdd["HddName"], "capacity_gb": hdd["CapacityGB"], "is_primary": hdd["IsPrimary"]} for hdd in data["DiskDrives"] ], "description": TemplateDescription(data["TemplateDescription"]), } def OCI_List(self): """Lists client's virtual machines' basic info""" self.logon() vms = self.clients.call("GetVirtualMachinesSimple", clientId=self.client_id) self._d(vms) for vm in vms: yield { "id": vm["VirtualMachineId"], "name": vm["VirtualMachineName"], "status": PowerStatus(vm["StatusDictId"]), } def OCI_ListDetails(self): """Lists client's virtual machines""" self.logon() sp = {"ClientId": self.client_id} vms = self.clients.call("GetVirtualMachines", searchParams=sp) self._d(vms) for vm in vms["_results"]: yield { "id": vm["VirtualMachineId"], "name": vm["VirtualMachineName"], "status": PowerStatus(vm["StatusDictId"]), "class_name": DictionaryItem(vm["VMClass"]), "cpu_mhz": vm["CpuMhz"], "cpu_usage_mhz": vm["CpuMhzUsage"], "memory_mb": vm["RamMB"], "memory_usage_mb": vm["RamMBUsage"], } def OCI_Restart(self, oci_id): """Restarts given VM""" self._simple_vm_method("RestartVirtualMachine", oci_id) def OCI_TurnOff(self, oci_id, force=False): """Turns given VM off""" if not force: try: self._simple_vm_method("ShutdownVirtualMachine", oci_id) return except OktawaveAPIError: # XXX: swallow only the "clean shutdown not supported" exception pass self._simple_vm_method("TurnoffVirtualMachine", oci_id) def OCI_TurnOn(self, oci_id): """Turns given virtual machine on""" self._simple_vm_method("TurnOnVirtualMachine", oci_id) def OCI_Delete(self, oci_id): """Deletes given virtual machine""" self._simple_vm_method("DeleteVirtualMachine", oci_id) def OCI_Logs(self, oci_id): """Shows virtual machine logs""" self.logon() sp = {"VirtualMachineId": oci_id, "PageSize": 100, "SortingDirection": 0} # descending data = self.clients.call("GetVirtualMachineHistories", searchParams=sp, clientId=self.client_id) self._d(data) for op in data["_results"]: yield { "time": self.clients.parse_date(op["CreationDate"]), "type": DictionaryItem(op["OperationType"]), "user_name": op["CreationUser"]["FullName"], "status": DictionaryItem(op["Status"]), "parameters": [item["Value"] for item in op["Parameters"]], } def OCI_DefaultPassword(self, oci_id): logs = self.OCI_Logs(oci_id) for entry in logs: if entry["type"] == "Instance access details": try: return entry["parameters"][0] except IndexError: return # no data yet def OCI_Settings(self, oci_id): """Shows basic VM settings (IP addresses, OS, names, autoscaling etc.)""" data = self._simple_vm_method("GetVirtualMachineById", oci_id) res = { "autoscaling": DictionaryItem(data["AutoScalingType"]), "connection_type": DictionaryItem(data["ConnectionType"]), "cpu_mhz": data["CpuMhz"], "cpu_usage_mhz": data["CpuMhzUsage"], "creation_date": self.clients.parse_date(data["CreationDate"]), "creation_user_name": data["CreationUserSimple"]["FullName"], "iops_usage": data["IopsUsage"], "last_change_date": self.clients.parse_date(data["LastChangeDate"]), "payment_type": DictionaryItem(data["PaymentType"]), "memory_mb": data["RamMB"], "memory_usage_mb": data["RamMBUsage"], "status": DictionaryItem(data["Status"]), "name": data["VirtualMachineName"], "vm_class_name": DictionaryItem(data["VMClass"]), "disks": [ { "id": disk["ClientHddId"], "name": disk["ClientHdd"]["HddName"], "capacity_gb": disk["ClientHdd"]["CapacityGB"], "creation_date": self.clients.parse_date(disk["ClientHdd"]["CreationDate"]), "creation_user_name": disk["ClientHdd"]["CreationUser"]["FullName"], "is_primary": disk["IsPrimary"], "is_shared": disk["ClientHdd"]["IsShared"], } for disk in data["DiskDrives"] ], "ips": [ { "ipv4": ip["Address"], "netmask": ip["NetMask"], "ipv6": ip["AddressV6"], "creation_date": self.clients.parse_date(ip["CreationDate"]), "dhcp_branch": ip["DhcpBranch"], "gateway": ip["Gateway"], "status": DictionaryItem(ip["IPStatus"]), "last_change_date": self.clients.parse_date(ip["LastChangeDate"]), "macaddr": ip["MacAddress"], } for ip in data["IPs"] ], "vlans": [], } if data["PrivateIpv4"]: res["vlans"] = [ { "ipv4": vlan["PrivateIpAddress"], "creation_date": self.clients.parse_date(vlan["CreationDate"]), "macaddr": vlan["MacAddress"], } for vlan in data["PrivateIpv4"] ] return res def OCI_Classes(self): self.logon(only_common=True) resp = self.common.call("GetVirtualMachineClassConfigurations") for oci_class in resp: yield { "name": DictionaryItem(oci_class["VirtualMachineClass"]), "category": DictionaryItem(oci_class["Category"]), "cpu_count": oci_class["CpuCount"], "memory_mb": oci_class["RamMB"], } def OCI_ClassChangeNeedsRestart(self, oci_id, oci_class): """Will OCI class change require reboot?""" self.logon() oci_class_obj = self._oci_class(oci_class) if not oci_class_obj: raise OktawaveOCIClassNotFound() return self.clients.call( "IsRestartNeededForClassChange", virtualMachineId=oci_id, clientId=self.client_id, targetClassId=oci_class_obj.id, ) def OCI_ChangeClass(self, oci_id, oci_class, at_midnight=False): """Changes running VM class, potentially rebooting it""" oci = self._simple_vm_method("GetVirtualMachineById", oci_id) oci_class_obj = self._oci_class(oci_class) if not oci_class_obj: raise OktawaveOCIClassNotFound() oci["VMClass"] = oci_class_obj.item self._d(oci) oci.setdefault("PrivateIpv4", "") self.clients.call( "UpdateVirtualMachine", machine=oci, clientId=self.client_id, classChangeInScheduler=at_midnight ) def OCI_Subregions(self): self.logon(only_common=True) resp = self.common.call("GetClusters", onlyClientClusters=False, clientId=self.client_id) for cluster in resp: yield {"id": cluster["ClusterId"], "name": cluster["DisplayName"], "active": cluster["IsActive"]} def OCI_Create( self, name, template, oci_class=None, forced_type=TemplateType.Machine, db_type=None, subregion="Auto", disk_size_gb=None, ip_address_id=None, ): """Creates a new instance from template""" self.logon() oci_class_id = None if oci_class is not None: oci_class_obj = self._oci_class(oci_class) if not oci_class_obj: raise OktawaveOCIClassNotFound() oci_class_id = oci_class_obj.id args = dict( templateId=template, additionalDisks=None, machineName=name, selectedClass=oci_class_id, selectedContainer=None, selectedPaymentMethod=DICT["OCI_PAYMENT_ID"], selectedConnectionType=DICT["OCI_CONNECTION_ID"], clientId=self.client_id, providervAppClientId=None, vAppType=forced_type, databaseTypeId=db_type, clientVmParameter=None, autoScalingTypeId=DICT["OCI_AUTOSCALING_ID"], ) if disk_size_gb is None and ip_address_id is None: self.clients.call("CreateVirtualMachine", disks=None, clusterId=_subregion_id(subregion), **args) else: if disk_size_gb is None: disk_size_gb = 1 # minimum possible size, template will probably override if ip_address_id is None: ip_address_id = 0 self.clients.call( "CreateVirtualMachineWithSpecificDiskSizeAndIp", diskSizeGB=disk_size_gb, ipId=ip_address_id, subRegionId=_subregion_id(subregion), instancesCount=1, **args ) def OCI_Clone(self, oci_id, name, clonetype): """Clones a VM""" self.logon() self.clients.call( "CloneVirtualMachine", virtualMachineId=oci_id, cloneName=name, cloneType=clonetype, clientId=self.client_id ) # OVS (disks) ### def OVS_List(self): """Lists disks""" self.logon() dsp = {"ClientId": self.client_id} data = self.clients.call("GetDisks", searchParams=dsp) for disk in data["_results"]: if disk["VirtualMachineHdds"] is None: vms = [] else: vms = [ { "id": vm["VirtualMachine"]["VirtualMachineId"], "name": vm["VirtualMachine"]["VirtualMachineName"], "primary": vm["IsPrimary"], "vm_status": PowerStatus(vm["VirtualMachine"]["StatusDictId"]), } for vm in disk["VirtualMachineHdds"] ] yield { "id": disk["ClientHddId"], "name": disk["HddName"], "tier": DictionaryItem(disk["HddStandard"]), "capacity_gb": disk["CapacityGB"], "used_gb": disk["UsedCapacityGB"], "is_shared": disk["IsShared"], "vms": vms, } def OVS_Delete(self, ovs_id): """Deletes a disk""" self.logon() res = self.clients.call("DeleteDisk", clientHddId=ovs_id, clientId=self.client_id) if not res: raise OktawaveOVSDeleteError() def OVS_Create(self, name, capacity_gb, tier, shared, subregion="Auto"): """Adds a disk""" self.logon() disk = { "CapacityGB": capacity_gb, "HddName": name, "HddStandardId": self._ovs_tier(tier).id, "IsShared": shared, "PaymentTypeId": DICT["OVS_PAYMENT_ID"], "VirtualMachineIds": [], "ClusterId": _subregion_id(subregion), } self.clients.call("CreateDisk", clientHdd=disk, clientId=self.client_id) def OVS_Map(self, ovs_id, oci_id): """Maps a disk into an instance""" self.logon() disk = self._find_disk(ovs_id) if disk is None: raise OktawaveOVSNotFoundError() disk_mod = _ovs_disk_mod(disk) if oci_id in disk_mod["VirtualMachineIds"]: raise OktawaveOVSMappedError() disk_mod["VirtualMachineIds"].append(oci_id) res = self.clients.call("UpdateDisk", clientHdd=disk_mod, clientId=self.client_id) if not res: raise OktawaveOVSMapError() def OVS_Unmap(self, ovs_id, oci_id): """Unmaps a disk from an instance""" self.logon() disk = self._find_disk(ovs_id) if disk is None: raise OktawaveOVSNotFoundError() disk_mod = _ovs_disk_mod(disk) if oci_id not in disk_mod["VirtualMachineIds"]: raise OktawaveOVSUnmappedError() disk_mod["VirtualMachineIds"].remove(oci_id) res = self.clients.call("UpdateDisk", clientHdd=disk_mod, clientId=self.client_id) if not res: raise OktawaveOVSUnmapError() def OVS_ChangeTier(self, ovs_id, tier): self.logon() disk = self._find_disk(ovs_id) if disk is None: raise OktawaveOVSNotFoundError() disk_mod = _ovs_disk_mod(disk) disk_mod["HddStandardId"] = self._ovs_tier(tier).id self.clients.call("UpdateDisk", clientHdd=disk_mod, clientId=self.client_id) def OVS_Extend(self, ovs_id, capacity_gb): self.logon() disk = self._find_disk(ovs_id) if disk is None: raise OktawaveOVSNotFoundError() disk_mod = _ovs_disk_mod(disk) if disk_mod.pop("LockVirtualMachineIds"): raise OktawaveOVSMappedError() if disk_mod["CapacityGB"] > capacity_gb: raise OktawaveOVSTooSmallError() disk_mod["CapacityGB"] = capacity_gb self.clients.call("UpdateDisk", clientHdd=disk_mod, clientId=self.client_id) # ORDB (databases) ### def ORDB_List(self): """Lists databases""" self.logon() sp = {"ClientId": self.client_id} data = self.clients.call("GetDatabaseInstances", searchParams=sp) for db in data["_results"]: yield { "id": db["VirtualMachineId"], "name": db["VirtualMachineName"], "type": DictionaryItem(db["DatabaseType"]), "size": db["Size"], "available_space": db["AvailableSpace"], } ORDB_TurnOn = OCI_TurnOn ORDB_TurnOff = OCI_TurnOff ORDB_Restart = OCI_Restart ORDB_Clone = OCI_Clone def ORDB_Delete(self, oci_id, db_name=None): """Deletes a database or VM""" self.logon() if db_name is None: self._simple_vm_method("DeleteVirtualMachine", oci_id) else: self.clients.call("DeleteDatabase", virtualMachineId=oci_id, databaseName=db_name, clientId=self.client_id) ORDB_Logs = OCI_Logs def ORDB_LogicalDatabases(self, oci_id): """Shows logical databases""" self.logon() sp = {"ClientId": self.client_id} data = self.clients.call("GetDatabaseInstances", searchParams=sp) for vm in data["_results"]: if oci_id is not None and str(vm["VirtualMachineId"]) != str(oci_id): continue for db in vm["Databases"]: yield { "id": db["VirtualMachineId"], "name": db["DatabaseName"], "type": DictionaryItem(db["DatabaseType"]), "encoding": db["Encoding"], "is_running": db["IsRunning"], "qps": db["QPS"], "size": db["Size"], } ORDB_Settings = OCI_Settings def ORDB_Create(self, name, template, oci_class=None, subregion="Auto"): """Creates a database VM""" self.logon() data = self.clients.call("GetTemplate", templateId=template, clientId=self.client_id) if str(data["TemplateType"]["DictionaryItemId"]) != str(DICT["DB_VM_CATEGORY"]): raise OktawaveORDBInvalidTemplateError() self.OCI_Create( name, template, forced_type=TemplateType.Database, db_type=data["DatabaseType"]["DictionaryItemId"], subregion=subregion, oci_class=oci_class, ) def ORDB_GlobalSettings(self, oci_id): """Shows global database engine settings""" self.logon() data = self.clients.call("GetDatabaseConfig", virtualMachineId=oci_id, clientId=self.client_id) for item in data: yield {"name": item.Name, "value": item.Value} def ORDB_CreateLogicalDatabase(self, oci_id, name, encoding): """Creates a new logical database within an instance""" self.logon() self.clients.call( "CreateDatabase", virtualMachineId=oci_id, databaseName=name, encodingDictId=DICT[encoding.upper() + "_ENCODING"], clientId=self.client_id, ) def ORDB_BackupLogicalDatabase(self, oci_id, name): """Creates a backup of logical database""" self.logon() self.clients.call("BackupDatabase", virtualMachineId=oci_id, databaseName=name, clientId=self.client_id) def ORDB_MoveLogicalDatabase(self, oci_id_from, oci_id_to, name): """Moves a logical database""" self.logon() self.clients.call( "MoveDatabase", virtualMachineIdFrom=oci_id_from, virtualMachineIdTo=oci_id_to, databaseName=name, clientId=self.client_id, ) def ORDB_Backups(self): """Lists logical database backups""" self.logon() mysql_data = self.clients.call("GetBackups", databaseTypeDictId=DICT["MYSQL_DB"], clientId=self.client_id) or [] pgsql_data = ( self.clients.call("GetBackups", databaseTypeDictId=DICT["POSTGRESQL_DB"], clientId=self.client_id) or [] ) for b in mysql_data: yield {"file_name": b["Name"], "type": "MySQL", "path": b["ContainerName"] + "/" + b["FullPath"]} for b in pgsql_data: yield {"file_name": b["Name"], "type": "PostgreSQL", "path": b["ContainerName"] + "/" + b["FullPath"]} def ORDB_RestoreLogicalDatabase(self, oci_id, name, backup_file): """Restores a database from backup""" self.logon() self.clients.call( "RestoreDatabase", virtualMachineId=oci_id, databaseName=name, backupFileName=backup_file, clientId=self.client_id, ) def Container_List(self): """Lists client's containers' basic info""" self.logon() containers = self.clients.call("GetContainers", clientId=self.client_id) for c in containers: yield {"id": c["ContainerId"], "name": c["ContainerName"], "vms": c["VirtualMachineCount"]} def Container_Get(self, container_id): """Displays a container's information""" self.logon() c = self.clients.call("GetContainer", containerId=container_id) res = { "autoscaling": DictionaryItem(c["AutoScalingType"]), "id": c["ContainerId"], "name": c["ContainerName"], "healthcheck": c["IsServiceCheckAvailable"], "load_balancer": c["IsLoadBalancer"], "master_service_id": c["MasterServiceId"], "master_service_name": c["MasterServiceName"], "proxy_cache": c["IsProxyCache"], "ssl": c["IsSSLUsed"], "port": c["PortNumber"], "schedulers": c["SchedulersCount"], "vms": c["VirtualMachineCount"], "db_user": c["DatabaseUserLogin"], "db_password": c["DatabaseUserPassword"], } for label, item in ( ("ip_version", "IPVersion"), ("load_balancer_algorithm", "LoadBalancerAlgorithm"), ("service", "Service"), ("session_type", "SessionType"), ): if c[item]: res[label] = DictionaryItem(c[item]) else: res[label] = None res["ips"] = [{"ipv4": ip["Address"], "ipv6": ip["AddressV6"]} for ip in c["IPs"]] return res def Container_OCIList(self, container_id): c_simple = self._container_simple(container_id) for vm in c_simple["VirtualMachines"]: vms = vm["VirtualMachineSimple"] yield { "oci_id": vms["VirtualMachineId"], "oci_name": vms["VirtualMachineName"], "status": PowerStatus(vms["StatusDictId"]), } def Container_RemoveOCI(self, container_id, oci_id): """Removes an instance from container""" self.logon() c_simple = self._container_simple(container_id) found = False for vm in c_simple["VirtualMachines"]: if vm["VirtualMachineId"] == oci_id: found = True break if not found: raise OktawaveOCINotInContainer() vm_ids = [vm["VirtualMachineId"] for vm in c_simple["VirtualMachines"] if vm["VirtualMachineId"] != oci_id] c = self.clients.call("GetContainer", containerId=container_id) self._d(vm_ids) self.clients.call("UpdateContainer", container=c, virtualMachinesId=vm_ids) def Container_AddOCI(self, container_id, oci_id): """Adds an instance to container""" self.logon() c_simple = self._container_simple(container_id) found = False for vm in c_simple["VirtualMachines"]: if vm["VirtualMachineId"] == oci_id: found = True break if found: raise OktawaveOCIInContainer() vm_ids = [vm["VirtualMachineId"] for vm in c_simple["VirtualMachines"]] + [oci_id] c = self.clients.call("GetContainer", containerId=container_id) self.clients.call("UpdateContainer", container=c, virtualMachinesId=vm_ids) def Container_Delete(self, container_id): self.logon() self.clients.call("DeleteContainers", containerIds=[container_id], clientId=self.client_id) def Container_Create( self, name, load_balancer, service, port, proxy_cache, ssl, healthcheck, master_id, session, lb_algorithm, ip_version, autoscaling, ): if lb_algorithm == "least_response_time" and service != "HTTP" and service != "HTTPS": raise OktawaveLRTNotAllowed() self.logon() vm_ids = [] if master_id is None else [master_id] result = self.clients.call( "CreateContainer", container={ "OwnerClientId": self.client_id, "ContainerName": name, "IsLoadBalancer": load_balancer, "Service": {"DictionaryItemId": _container_service_id(service)}, "LoadBalancerAlgorithm": {"DictionaryItemId": _load_balancer_algorithm_id(lb_algorithm)}, "IsSSLUsed": ssl, "IsProxyCache": proxy_cache, "MasterServiceId": master_id, "PortNumber": port, "SessionType": {"DictionaryItemId": _session_type_id(session)}, "IPVersion": {"DictionaryItemId": _ip_version_id(ip_version)}, "AutoScalingType": {"DictionaryItemId": _autoscaling_id(autoscaling)}, "IsServiceCheckAvailable": healthcheck, }, virtualMachinesId=vm_ids, ) return result # TODO: allow to change only selected parameters, add more validation # without relying on the UpdateContainer API method. def Container_Edit( self, container_id, name, load_balancer, service, port, proxy_cache, ssl, healthcheck, master_id, session, lb_algorithm, ip_version, autoscaling, ): self.logon() if lb_algorithm == "least_response_time" and service != "HTTP" and service != "HTTPS": raise OktawaveLRTNotAllowed() c = self.clients.call("GetContainer", containerId=container_id) c_simple = self._container_simple(container_id) c["ContainerName"] = name c["IsLoadBalancer"] = load_balancer c["Service"] = {"DictionaryItemId": _container_service_id(service)} c["LoadBalancerAlgorithm"] = {"DictionaryItemId": _load_balancer_algorithm_id(lb_algorithm)} c["IsSSLUsed"] = ssl c["IsProxyCache"] = proxy_cache c["MasterServiceId"] = master_id c["PortNumber"] = port c["SessionType"] = {"DictionaryItemId": _session_type_id(session)} c["IPVersion"] = {"DictionaryItemId": _ip_version_id(ip_version)} c["AutoScalingType"] = {"DictionaryItemId": _autoscaling_id(autoscaling)} c["AutoScalingTypeDictId"] = _autoscaling_id(autoscaling) c["IsServiceCheckAvailable"] = healthcheck self._d(c) self.clients.call( "UpdateContainer", container=c, virtualMachinesId=[vm["VirtualMachineId"] for vm in c_simple["VirtualMachines"]], ) def OPN_List(self): """Lists client's OPNs""" self.logon() vlans = self.clients.call("GetVlansByClientId", clientId=self.client_id) for v in vlans: yield { "id": v["VlanId"], "name": v["VlanName"], "address_pool": DictionaryItem(v["AddressPool"]), "payment_type": DictionaryItem(v["PaymentType"]), } def OPN_Get(self, opn_id): self.logon() v = self.clients.call("GetVlanById", vlanId=opn_id, clientId=self.client_id) vms = self.clients.call("GetVirtualMachineVlansByVlanId", vlanId=opn_id, clientId=self.client_id) return { "id": v["VlanId"], "name": v["VlanName"], "address_pool": DictionaryItem(v["AddressPool"]), "payment_type": DictionaryItem(v["PaymentType"]), "vms": vms, } def OPN_Create(self, name, address_pool): self.logon() self._d(self.client_object) return self.clients.call( "CreateVlan", vlan={ "VlanName": name, "AddressPool": {"DictionaryItemId": _address_pool_id(address_pool)}, "OwnerClient": self.client_object["Client"], "PaymentType": {"DictionaryItemId": DICT["OPN_PAYMENT_ID"]}, "CreationUserId": self.client_id, }, ) def OPN_Delete(self, opn_id): self.logon() return self.clients.call("DeleteVlan", vlanId=opn_id, clientId=self.client_id) def OPN_AddOCI(self, opn_id, oci_id, ip_address): self.logon() oci = self.clients.call("GetVirtualMachineById", virtualMachineId=oci_id, clientId=self.client_id) vlan = self.clients.call("GetVlanById", vlanId=opn_id, clientId=self.client_id) for opn in oci["PrivateIpv4"]: if opn["Vlan"]["VlanId"] == opn_id: raise OktawaveOCIInOPN() oci["PrivateIpv4"].append( { "PrivateIpAddress": ip_address, "VirtualMachine": { "VirtualMachineName": oci["VirtualMachineName"], "VirtualMachineId": oci_id, "StatusDictId": oci["Status"]["DictionaryItemId"], }, "Vlan": vlan, "CreationDate": "/Date(" + str(int(time()) * 100) + "+0000)/", # for some reason API server does not fill this field automatically } ) return self.clients.call( "UpdateVirtualMachine", machine=oci, clientId=self.client_id, classChangeInScheduler=False ) def OPN_RemoveOCI(self, opn_id, oci_id): self.logon() oci = self.clients.call("GetVirtualMachineById", virtualMachineId=oci_id, clientId=self.client_id) l1 = len(oci["PrivateIpv4"]) oci["PrivateIpv4"] = filter(lambda x: x["Vlan"]["VlanId"] != opn_id, oci["PrivateIpv4"]) if l1 == len(oci["PrivateIpv4"]): raise OktawaveOCINotInOPN() return self.clients.call( "UpdateVirtualMachine", machine=oci, clientId=self.client_id, classChangeInScheduler=False ) def OPN_Rename(self, opn_id, name): self.logon() vlan = self.clients.call("GetVlanById", vlanId=opn_id, clientId=self.client_id) vlan["VlanName"] = name return self.clients.call("UpdateVlan", vlan=vlan)
class OktawaveApi(object): def __init__(self, username, password, debug=False): """Initialize the API instance Arguments: - username (string) - Oktawave account username - password (string) - Oktawave account password - debug (bool) - enable debug output? """ self.username = username self.password = password self.debug = debug # HELPER METHODS ### # methods starting with "_" will not be autodispatched to client commands # ### def _d(self, what): if self.debug: print what def _init_common(self): """Convenience method to initialize CommonService client""" if hasattr(self, 'common'): return self.common = ApiClient( jsonapi_common, self.username, self.password, self.debug) self._d(self.common) def _init_clients(self): """Convenience method to initialize ClientsService client""" if hasattr(self, 'clients'): return self.clients = ApiClient( jsonapi_clients, self.username, self.password, self.debug) self._d(self.clients) def _logon(self, only_common=False): """Initializes CommonService client and calls LogonUser method. Returns the User object, as returned by LogonUser. Also sets self.client_id for convenience. """ self._init_common() if not only_common: self._init_clients() if hasattr(self, 'client_object'): return self.client_object try: res = self.common.call( 'LogonUser', user=self.username, password=self.password, ipAddress=self._get_machine_ip(), userAgent="Oktawave CLI") except AttributeError: raise raise OktawaveLoginError() self.client_id = res['Client']['ClientId'] self.client_object = res return res def _dict_names(self, data, field='ItemName'): return [item[field] for item in data if item['LanguageDictId'] == 2] def _dict_item_name(self, data): return self._dict_names(data['DictionaryItemNames'], 'ItemName')[0] def _simple_vm_method(self, method, vm_id): """Wraps around common simple virtual machine method call pattern""" self._logon() return self.clients.call(method, virtualMachineId=vm_id, clientId=self.client_id) def _find_disk(self, disk_id): """Finds a disk (OVS) by id""" dsp = { 'ClientId': self.client_id, } disks = [d for d in self.clients.call( 'GetDisks', searchParams=dsp)['_results'] if d['ClientHddId'] == disk_id] if len(disks) == 0: return None res = disks[0] if res['VirtualMachineHdds'] is None: res['VirtualMachineHdds'] = [] return res def _get_machine_ip(self): return '127.0.0.1' def _dict_item(self, dict_id, key, default=0): items = self.common.call( 'GetDictionaryItems', dictionaryId=dict_id, clientId=self.client_id) name2id = dict((self._dict_item_name(item), item['DictionaryItemId']) for item in items) self._d(name2id) return name2id.get(key, default) def _oci_class_id(self, class_name): """Returns ID of an OCI class with a given name""" return self._dict_item(DICT['OCI_CLASSES_DICT_ID'], class_name) def _ovs_tier_id(self, tier): """Returns ID of a given disk tier""" tier_name = 'Tier ' + str(tier) return self._dict_item(DICT['OVS_TIERS_DICT_ID'], tier_name) def _ovs_disk_mod(self, disk): vms = [ vm['VirtualMachine']['VirtualMachineId'] for vm in disk['VirtualMachineHdds']] disk_mod = { 'CapacityGB': disk['CapacityGB'], 'ClientHddId': disk['ClientHddId'], 'HddName': disk['HddName'], 'IsShared': disk['IsShared'], 'HddStandardId': disk['HddStandard']['DictionaryItemId'], 'PaymentTypeId': disk['PaymentType']['DictionaryItemId'], 'VirtualMachineIds': vms, } return disk_mod # API methods below ### # General / Account ### def Account_Settings(self): """Print basic settings of the client account args is an object containing at least the following fields: - username - Oktawave username - password - Oktawave client password Typically args will be the object returned from argparse. """ self._logon(only_common=True) client = self.client_object # TODO: probably get more settings return { 'time_zone': client['TimeZone']['DisplayName'], 'currency': self._dict_item_name(client['Currency']), 'date_format': self._dict_item_name(client['DateFormat']), 'availability_zone': self._dict_item_name( self.common.call('GetDictionaryItemById', dictionaryItemId=client['AvailabilityZone'])), '24h_clock': client['Is24HourClock'], } def Account_RunningJobs(self): self._logon() res = self.common.call('GetRunningOperations', clientId=self.client_id) if not res: return for op in res: yield { 'id': op['AsynchronousOperationId'], 'creation_date': op['CreationDate'], 'creation_user_name': op['CreationUserFullName'], 'type': self._dict_item_name(op['OperationType']), 'object_type': self._dict_item_name(op['ObjectType']), 'object_name': op['ObjectName'], 'progress_percent': op['Progress'], 'status': self._dict_item_name(op['Status']) } def Account_Users(self): """Print users in client account.""" self._logon() users = self.clients.call('GetClientUsers', clientId=self.client_id) self._d(users) for user in users: yield { 'email': user['Email'], 'name': user['FullName'], } # OCI (VMs) ### def OCI_Test(self): self._logon() self._d(self._oci_class_id('Large')) def OCI_TemplateCategories(self): """Lists available template categories""" self._logon() data = self.common.call('GetTemplateCategories', clientId=self.client_id) self._d(data) def _tc_info(tc, parent_id): return { 'id': tc['TemplateCategoryId'], 'name': self._dict_names( tc['TemplateCategoryNames'], 'CategoryName')[0], 'description': self._dict_names( tc['TemplateCategoryNames'], 'CategoryDescription')[0], 'parent_id': parent_id, } for tc in data: yield _tc_info(tc, None) if tc['CategoryChildren'] is not None: for tcc in tc['CategoryChildren']: yield _tc_info(tcc, tc['TemplateCategoryId']) def OCI_Templates(self, category_id, name_filter=''): """Lists templates in a category""" self._logon() data = self.common.call( 'GetTemplatesByCategory', categoryId=category_id, categorySystemId=None, type=None, clientId=self.client_id) if data: return dict((template['TemplateId'], template['TemplateName']) for template in data if name_filter in template['TemplateName']) def OCI_TemplateInfo(self, template_id): """Shows more detailed info about a particular template""" self._logon() data = self.clients.call('GetTemplate', templateId=template_id, clientId=self.client_id) template_category = '/'.join(self._dict_names( data['TemplateCategory']['TemplateCategoryNames'], field='CategoryName')) software = ', '.join( '/'.join(self._dict_names(s['Software']['SoftwareNames'], field="Name")) for s in data['SoftwareList']) return { 'template_id': data['TemplateId'], 'template_name': data['TemplateName'], 'template_category': template_category, 'vm_class_id': data['VMClass']['DictionaryItemId'], 'vm_class_name': self._dict_item_name(data['VMClass']), 'system_category_name': self._dict_item_name(data['TemplateSystemCategory']), 'label': data['Name'], 'software': software, 'eth_count': data['EthernetControllersCount'], 'connection_type': self._dict_item_name(data['ConnectionType']), 'disks': [{ 'name': hdd['HddName'], 'capacity_gb': hdd['CapacityGB'], 'is_primary': hdd['IsPrimary'] } for hdd in data['DiskDrives']], 'description': data['Description'] } def OCI_List(self): """Lists client's virtual machines' basic info""" self._logon() vms = self.clients.call('GetVirtualMachinesSimple', clientId=self.client_id) self._d(vms) for vm in vms: yield { 'id': vm['VirtualMachineId'], 'name': vm['VirtualMachineName'], 'status': PowerStatus(vm['StatusDictId']), } def OCI_ListDetails(self): """Lists client's virtual machines""" self._logon() sp = { 'ClientId': self.client_id } vms = self.clients.call('GetVirtualMachines', searchParams=sp) self._d(vms) for vm in vms['_results']: yield { 'id': vm['VirtualMachineId'], 'name': vm['VirtualMachineName'], 'status': PowerStatus(vm['StatusDictId']), 'class_name': self._dict_item_name(vm['VMClass']), 'cpu_mhz': vm['CpuMhz'], 'cpu_usage_mhz': vm['CpuMhzUsage'], 'memory_mb': vm['RamMB'], 'memory_usage_mb': vm['RamMBUsage'], } def OCI_Restart(self, oci_id): """Restarts given VM""" self._simple_vm_method('RestartVirtualMachine', oci_id) def OCI_TurnOff(self, oci_id): """Turns given VM off""" self._simple_vm_method('TurnoffVirtualMachine', oci_id) def OCI_TurnOn(self, oci_id): """Turns given virtual machine on""" self._simple_vm_method('TurnOnVirtualMachine', oci_id) def OCI_Delete(self, oci_id): """Deletes given virtual machine""" self._simple_vm_method('DeleteVirtualMachine', oci_id) def OCI_Logs(self, oci_id): """Shows virtual machine logs""" self._logon() sp = { 'VirtualMachineId': oci_id, 'PageSize': 100, 'SortingDirection': 0, # descending } data = self.clients.call( 'GetVirtualMachineHistories', searchParams=sp, clientId=self.client_id) self._d(data) for op in data['_results']: yield { 'time': self.clients.parse_date(op['CreationDate']), 'type': self._dict_item_name(op['OperationType']), 'user_name': op['CreationUser']['FullName'], 'status': self._dict_item_name(op['Status']), 'parameters': [item['Value'] for item in op['Parameters']], } def OCI_Settings(self, oci_id): """Shows basic VM settings (IP addresses, OS, names, autoscaling etc.)""" data = self._simple_vm_method('GetVirtualMachineById', oci_id) res = { 'autoscaling': self._dict_item_name(data['AutoScalingType']), 'connection_type': self._dict_item_name(data['ConnectionType']), 'cpu_mhz': data['CpuMhz'], 'cpu_usage_mhz': data['CpuMhzUsage'], 'creation_date': self.clients.parse_date(data['CreationDate']), 'creation_user_name': data['CreationUserSimple']['FullName'], 'iops_usage': data['IopsUsage'], 'last_change_date': self.clients.parse_date(data['LastChangeDate']), 'payment_type': self._dict_item_name(data['PaymentType']), 'memory_mb': data['RamMB'], 'memory_usage_mb': data['RamMBUsage'], 'status': self._dict_item_name(data['Status']), 'name': data['VirtualMachineName'], 'vm_class_name': self._dict_item_name(data['VMClass']), 'disks': [{ 'name': disk['ClientHdd']['HddName'], 'capacity_gb': disk['ClientHdd']['CapacityGB'], 'creation_date': self.clients.parse_date(disk['ClientHdd']['CreationDate']), 'creation_user_name': disk['ClientHdd']['CreationUser']['FullName'], 'is_primary': disk['IsPrimary'] } for disk in data['DiskDrives']], 'ips': [{ 'ipv4': ip['Address'], 'netmask': ip['NetMask'], 'ipv6': ip['AddressV6'], 'creation_date': self.clients.parse_date(ip['CreationDate']), 'dhcp_branch': ip['DhcpBranch'], 'gateway': ip['Gateway'], 'status': self._dict_item_name(ip['IPStatus']), 'last_change_date': self.clients.parse_date(ip['LastChangeDate']), 'macaddr': ip['MacAddress'], } for ip in data['IPs']], 'vlans': [], } if data['PrivateIpv4']: res['vlans'] = [{ 'ipv4': vlan['PrivateIpAddress'], 'creation_date': self.clients.parse_date(vlan['CreationDate']), 'macaddr': vlan['MacAddress'], } for vlan in data['PrivateIpv4']] return res def OCI_ChangeClass(self, oci_id, oci_class, at_midnight=False): """Changes running VM class, potentially rebooting it""" oci = self._simple_vm_method('GetVirtualMachineById', oci_id) oci_class_id = self._oci_class_id(oci_class) if not oci_class_id: raise OktawaveOCIClassNotFound() oci['VMClass'] = self.common.call('GetDictionaryItemById', dictionaryItemId=oci_class_id) self._d(oci) oci.setdefault('PrivateIpv4', '') self.clients.call( 'UpdateVirtualMachine', machine=oci, clientId=self.client_id, classChangeInScheduler=at_midnight) def OCI_Create(self, name, template, oci_class=None, forced_type=TemplateType.Machine, db_type=None): """Creates a new instance from template""" self._logon() oci_class_id = None if oci_class is not None: oci_class_id = self._oci_class_id(oci_class) if not oci_class_id: raise OktawaveOCIClassNotFound() self.clients.call('CreateVirtualMachine', templateId=template, disks=None, additionalDisks=None, machineName=name, selectedClass=oci_class_id, selectedContainer=None, selectedPaymentMethod=DICT['OCI_PAYMENT_ID'], selectedConnectionType=DICT['OCI_CONNECTION_ID'], clientId=self.client_id, providervAppClientId=None, vAppType=forced_type, databaseTypeId=db_type, clientVmParameter=None, autoScalingTypeId=DICT['OCI_AUTOSCALING_ID'] ) def OCI_Clone(self, oci_id, name, clonetype): """Clones a VM""" self._logon() self.clients.call('CloneVirtualMachine', virtualMachineId=oci_id, cloneName=name, cloneType=clonetype, clientId=self.client_id) # OVS (disks) ### def OVS_List(self): """Lists disks""" self._logon() dsp = { 'ClientId': self.client_id, } data = self.clients.call('GetDisks', searchParams=dsp) for disk in data['_results']: if disk['VirtualMachineHdds'] is None: vms = [] else: vms = [{ 'id': vm['VirtualMachine']['VirtualMachineId'], 'name': vm['VirtualMachine']['VirtualMachineName'], } for vm in disk['VirtualMachineHdds']] yield { 'id': disk['ClientHddId'], 'name': disk['HddName'], 'tier': self._dict_item_name(disk['HddStandard']), 'capacity_gb': disk['CapacityGB'], 'used_gb': disk['UsedCapacityGB'], 'is_shared': disk['IsShared'], 'vms': vms, } def OVS_Delete(self, ovs_id): """Deletes a disk""" self._logon() res = self.clients.call('DeleteDisk', clientHddId=ovs_id, clientId=self.client_id) if not res: raise OktawaveOVSDeleteError() def OVS_Create(self, name, capacity_gb, tier, shared): """Adds a disk""" self._logon() disk = { 'CapacityGB': capacity_gb, 'HddName': name, 'HddStandardId': self._ovs_tier_id(tier), 'IsShared': shared, 'PaymentTypeId': DICT['OVS_PAYMENT_ID'], 'VirtualMachineIds': [], } self.clients.call('CreateDisk', clientHdd=disk, clientId=self.client_id) def OVS_Map(self, ovs_id, oci_id): """Maps a disk into an instance""" self._logon() disk = self._find_disk(ovs_id) if disk is None: raise OktawaveOVSNotFoundError() disk_mod = self._ovs_disk_mod(disk) if oci_id in disk_mod['VirtualMachineIds']: raise OktawaveOVSMappedError() disk_mod['VirtualMachineIds'].append('oci_id') res = self.clients.call('UpdateDisk', clientHdd=disk_mod, clientId=self.client_id) if not res: raise OktawaveOVSMapError() def OVS_Unmap(self, ovs_id, oci_id): """Unmaps a disk from an instance""" self._logon() disk = self._find_disk(ovs_id) if disk is None: raise OktawaveOVSNotFoundError() disk_mod = self._ovs_disk_mod(disk) if oci_id not in disk_mod['VirtualMachineIds']: raise OktawaveOVSUnmappedError() disk_mod['VirtualMachineIds'].remove(oci_id) res = self.clients.call('UpdateDisk', clientHdd=disk_mod, clientId=self.client_id) if not res: raise OktawaveOVSUnmapError() def OVS_ChangeTier(self, ovs_id, tier): self._logon() disk = self._find_disk(ovs_id) if disk is None: raise OktawaveOVSNotFoundError() disk_mod = self._ovs_disk_mod(disk) disk_mod['HddStandardId'] = self._ovs_tier_id(tier) self.clients.call('UpdateDisk', clientHdd=disk_mod, clientId=self.client_id) def OVS_Extend(self, ovs_id, capacity_gb): self._logon() disk = self._find_disk(ovs_id) if disk is None: raise OktawaveOVSNotFoundError() disk_mod = self._ovs_disk_mod(disk) if disk_mod['VirtualMachineIds']: raise OktawaveOVSMappedError() if disk_mod['CapacityGB'] > capacity_gb: raise OktawaveOVSTooSmallError() disk_mod['CapacityGB'] = capacity_gb self.clients.call('UpdateDisk', clientHdd=disk_mod, clientId=self.client_id) # ORDB (databases) ### def ORDB_List(self): """Lists databases""" self._logon() sp = { 'ClientId': self.client_id, } data = self.clients.call('GetDatabaseInstances', searchParams=sp) for db in data['_results']: yield { 'id': db['VirtualMachineId'], 'name': db['VirtualMachineName'], 'type': self._dict_item_name(db['DatabaseType']), 'size': db['Size'], 'available_space': db['AvailableSpace'], } def ORDB_TurnOn(self, oci_id): """Turns a database on""" self._simple_vm_method('TurnOnVirtualMachine', oci_id) def ORDB_TurnOff(self, oci_id): """Turns a database off""" self._simple_vm_method('TurnoffVirtualMachine', oci_id) def ORDB_Restart(self, oci_id): """Restarts a database""" self._simple_vm_method('RestartVirtualMachine', oci_id) ORDB_Clone = OCI_Clone def ORDB_Delete(self, oci_id, db_name=None): """Deletes a database or VM""" self._logon() if db_name is None: self._simple_vm_method('DeleteVirtualMachine', oci_id) else: self.clients.call( 'DeleteDatabase', virtualMachineId=oci_id, databaseName=db_name, clientId=self.client_id) ORDB_Logs = OCI_Logs def ORDB_LogicalDatabases(self, oci_id): """Shows logical databases""" self._logon() sp = { 'ClientId': self.client_id, } data = self.clients.call('GetDatabaseInstances', searchParams=sp) for vm in data['_results']: if oci_id is not None and str(vm['VirtualMachineId']) != str(oci_id): continue for db in vm['Databases']: yield { 'id': db['VirtualMachineId'], 'name': db['DatabaseName'], 'type': self._dict_item_name(db['DatabaseType']), 'encoding': db['Encoding'], 'is_running': db['IsRunning'], 'qps': db['QPS'], 'size': db['Size'] } ORDB_Settings = OCI_Settings def ORDB_Create(self, name, template, oci_class=None): """Creates a database VM""" self._logon() data = self.clients.call('GetTemplate', templateId=template, clientId=self.client_id) if str(data['TemplateType']['DictionaryItemId']) != str(DICT['DB_VM_CATEGORY']): raise OktawaveORDBInvalidTemplateError() self.OCI_Create(name, template, forced_type=TemplateType.Database, db_type=data.DatabaseType.DictionaryItemId) def ORDB_GlobalSettings(self, oci_id): """Shows global database engine settings""" self._logon() data = self.clients.call('GetDatabaseConfig', virtualMachineId=oci_id, clientId=self.client_id) for item in data: yield { 'name': item.Name, 'value': item.Value } def ORDB_CreateLogicalDatabase(self, oci_id, name, encoding): """Creates a new logical database within an instance""" self._logon() self.clients.call( 'CreateDatabase', virtualMachineId=oci_id, databaseName=name, encodingDictId=DICT[encoding.upper() + '_ENCODING'], clientId=self.client_id) def ORDB_BackupLogicalDatabase(self, oci_id, name): """Creates a backup of logical database""" self._logon() self.clients.call('BackupDatabase', virtualMachineId=oci_id, databaseName=name, clientId=self.client_id) def ORDB_MoveLogicalDatabase(self, oci_id_from, oci_id_to, name): """Moves a logical database""" self._logon() self.clients.call( 'MoveDatabase', virtualMachineIdFrom=oci_id_from, virtualMachineIdTo=oci_id_to, databaseName=name, clientId=self.client_id) def ORDB_Backups(self): """Lists logical database backups""" self._logon() mysql_data = self.clients.call( 'GetBackups', databaseTypeDictId=DICT['MYSQL_DB'], clientId=self.client_id) or [] pgsql_data = self.clients.call( 'GetBackups', databaseTypeDictId=DICT['POSTGRESQL_DB'], clientId=self.client_id) or [] for b in mysql_data: yield { 'file_name': b['Name'], 'type': 'MySQL', 'path': b['ContainerName'] + "/" + b['FullPath'] } for b in pgsql_data: yield { 'file_name': b['Name'], 'type': 'PostgreSQL', 'path': b['ContainerName'] + "/" + b['FullPath'] } def ORDB_RestoreLogicalDatabase(self, oci_id, name, backup_file): """Restores a database from backup""" self._logon() self.clients.call('RestoreDatabase', virtualMachineId=oci_id, databaseName=name, backupFileName=backup_file, clientId=self.client_id)
class OktawaveApi(object): def __init__(self, username, password, debug=False): """Initialize the API instance Arguments: - username (string) - Oktawave account username - password (string) - Oktawave account password - debug (bool) - enable debug output? """ self.username = username self.password = password self.debug = debug # HELPER METHODS ### # methods starting with "_" will not be autodispatched to client commands # ### def _d(self, what): if self.debug: print what def _init_common(self): """Convenience method to initialize CommonService client""" if hasattr(self, 'common'): return self.common = ApiClient(jsonapi_common, self.username, self.password, self.debug) self._d(self.common) def _init_clients(self): """Convenience method to initialize ClientsService client""" if hasattr(self, 'clients'): return self.clients = ApiClient(jsonapi_clients, self.username, self.password, self.debug) self._d(self.clients) def _logon(self, only_common=False): """Initializes CommonService client and calls LogonUser method. Returns the User object, as returned by LogonUser. Also sets self.client_id for convenience. """ self._init_common() if not only_common: self._init_clients() if hasattr(self, 'client_object'): return self.client_object try: res = self.common.call('LogonUser', user=self.username, password=self.password, ipAddress=self._get_machine_ip(), userAgent="Oktawave CLI") except AttributeError: raise OktawaveLoginError() self.client_id = res['User']['Client']['ClientId'] self.client_object = res return res def _simple_vm_method(self, method, vm_id): """Wraps around common simple virtual machine method call pattern""" self._logon() return self.clients.call(method, virtualMachineId=vm_id, clientId=self.client_id) def _find_disk(self, disk_id): """Finds a disk (OVS) by id""" dsp = { 'ClientId': self.client_id, } disks = [ d for d in self.clients.call('GetDisks', searchParams=dsp) ['_results'] if d['ClientHddId'] == disk_id ] if len(disks) == 0: return None res = disks[0] if res['VirtualMachineHdds'] is None: res['VirtualMachineHdds'] = [] return res def _get_machine_ip(self): return '127.0.0.1' def _dict_item(self, dict_id, key): items = self.common.call('GetDictionaryItems', dictionary=dict_id, clientId=self.client_id) for item in items: item = DictionaryItem(item) if item.name == key: return item def _oci_class(self, class_name): """Returns a dictionary item for OCI class with a given name""" return self._dict_item(DICT['OCI_CLASSES_DICT_ID'], class_name) def _ovs_tier(self, tier): """Returns ID of a given disk tier""" tier_name = 'Tier ' + str(tier) tier_obj = self._dict_item(DICT['OVS_TIERS_DICT_ID'], tier_name) if not tier_obj: raise OktawaveOVSTierNotFound() return tier_obj def _ovs_disk_mod(self, disk): vms = [ vm['VirtualMachine']['VirtualMachineId'] for vm in disk['VirtualMachineHdds'] ] lock_vms = [ vm['VirtualMachine']['VirtualMachineId'] for vm in disk['VirtualMachineHdds'] if vm['VirtualMachine']['StatusDictId'] == PowerStatus.PowerOn or not vm['IsPrimary'] ] disk_mod = { 'CapacityGB': disk['CapacityGB'], 'ClientHddId': disk['ClientHddId'], 'ClusterId': disk['Cluster']['ClusterId'], 'HddName': disk['HddName'], 'IsShared': disk['IsShared'], 'HddStandardId': disk['HddStandard']['DictionaryItemId'], 'PaymentTypeId': disk['PaymentType']['DictionaryItemId'], 'VirtualMachineIds': vms, 'LockVirtualMachineIds': lock_vms, } return disk_mod def _container_simple(self, container_id): """Fetches a container's information using GetContainersSimpleWithVM""" self._logon() cs = self.clients.call('GetContainersSimpleWithVM', clientId=self.client_id) for c in cs: self._d([c, container_id]) if str(c['ContainerId']) == str(container_id): return c raise OktawaveContainerNotFoundError() # API methods below ### # General / Account ### def Account_Settings(self): """Print basic settings of the client account args is an object containing at least the following fields: - username - Oktawave username - password - Oktawave client password Typically args will be the object returned from argparse. """ self._logon(only_common=True) client = self.client_object # TODO: probably get more settings return { 'time_zone': client['TimeZone']['DisplayName'], 'currency': DictionaryItem(client['Currency']), 'date_format': DictionaryItem(client['DateFormat']), 'availability_zone': DictionaryItem( self.common.call('GetDictionaryItemById', dictionaryItemId=client['AvailabilityZone'])), '24h_clock': client['Is24HourClock'], } def Account_RunningJobs(self): self._logon() res = self.common.call('GetRunningOperations', clientId=self.client_id) if not res: return for op in res: yield { 'id': op['AsynchronousOperationId'], 'creation_date': op['CreationDate'], 'creation_user_name': op['CreationUserFullName'], 'type': RawDictionaryItem(op['OperationTypeId'], op['OperationTypeName']), 'object_type': RawDictionaryItem(op['ObjectTypeId'], op['ObjectTypeName']), 'object_name': op['ObjectName'], 'progress_percent': op['Progress'], 'status': RawDictionaryItem(op['StatusId'], op['StatusName']) } def Account_Users(self): """Print users in client account.""" self._logon() users = self.clients.call('GetClientUsers', clientId=self.client_id) self._d(users) for user in users: yield { 'email': user['Email'], 'name': user['FullName'], } ### OCI Templates ### def templates_in_category(self, category_id, name_filter=''): """Lists templates in a category""" self._logon() data = self.common.call('GetTemplatesByCategory', categoryId=category_id, categorySystemId=None, type=None, clientId=self.client_id) if data: return dict((template['TemplateId'], template['TemplateName']) for template in data if name_filter in template['TemplateName']) def Template_Show(self, template_id): """Shows more detailed info about a particular template""" self._logon() data = self.clients.call('GetTemplate', templateId=template_id, clientId=self.client_id) template_category = TemplateCategory(data['TemplateCategory'], None) software = [ SoftwareItem(item['Software']) for item in data['SoftwareList'] ] return { 'template_id': data['TemplateId'], 'template_name': data['TemplateName'], 'template_category': template_category.tree_path, 'vm_class_id': data['VMClass']['DictionaryItemId'], 'vm_class_name': DictionaryItem(data['VMClass']), 'system_category_name': DictionaryItem(data['TemplateSystemCategory']), 'label': data['Name'], 'software': software, 'eth_count': data['EthernetControllersCount'], 'connection_type': DictionaryItem(data['ConnectionType']), 'disks': [{ 'name': hdd['HddName'], 'capacity_gb': hdd['CapacityGB'], 'is_primary': hdd['IsPrimary'] } for hdd in data['DiskDrives']], 'description': data['TemplateDescription']['TemplateDescriptionsNames'][0] ['Description'], } def Template_List(self, origin, name_filter=''): """Lists templates of a chosen origin""" self._logon() data = self.common.call('GetTemplatesByOrigin', clientId=self.client_id, origin=TemplateOrigin(origin).id) if data: return [{ 'id': template['TemplateId'], 'name': template['TemplateName'], 'category': str(TemplateOrigin(origin)), 'system_category': DictionaryItem(template['TemplateSystemCategory']) } for template in data if name_filter in template['TemplateName']] ### OCI (VMs) ### def OCI_List(self): """Lists client's virtual machines' basic info""" self._logon() vms = self.clients.call('GetVirtualMachinesSimple', clientId=self.client_id) self._d(vms) for vm in vms: yield { 'id': vm['VirtualMachineId'], 'name': vm['VirtualMachineName'], 'status': PowerStatus(vm['StatusDictId']), } def OCI_ListDetails(self): """Lists client's virtual machines""" self._logon() sp = {'ClientId': self.client_id} vms = self.clients.call('GetVirtualMachines', searchParams=sp) self._d(vms) for vm in vms['_results']: yield { 'id': vm['VirtualMachineId'], 'name': vm['VirtualMachineName'], 'status': PowerStatus(vm['StatusDictId']), 'class_name': DictionaryItem(vm['VMClass']), 'cpu_mhz': vm['CpuMhz'], 'cpu_usage_mhz': vm['CpuMhzUsage'], 'memory_mb': vm['RamMB'], 'memory_usage_mb': vm['RamMBUsage'], } def OCI_Restart(self, oci_id): """Restarts given VM""" self._simple_vm_method('RestartVirtualMachine', oci_id) def OCI_TurnOff(self, oci_id, force=False): """Turns given VM off""" if not force: try: self._simple_vm_method('ShutdownVirtualMachine', oci_id) return except OktawaveAPIError: # XXX: swallow only the "clean shutdown not supported" exception pass self._simple_vm_method('TurnoffVirtualMachine', oci_id) def OCI_TurnOn(self, oci_id): """Turns given virtual machine on""" self._simple_vm_method('TurnOnVirtualMachine', oci_id) def OCI_Delete(self, oci_id): """Deletes given virtual machine""" self._simple_vm_method('DeleteVirtualMachine', oci_id) def OCI_Logs(self, oci_id): """Shows virtual machine logs""" self._logon() sp = { 'VirtualMachineId': oci_id, 'PageSize': 100, 'SortingDirection': 0, # descending } data = self.clients.call('GetVirtualMachineHistories', searchParams=sp, clientId=self.client_id) self._d(data) for op in data['_results']: yield { 'time': self.clients.parse_date(op['CreationDate']), 'type': DictionaryItem(op['OperationType']), 'user_name': op['CreationUser']['FullName'], 'status': DictionaryItem(op['Status']), 'parameters': [item['Value'] for item in op['Parameters']], } def OCI_DefaultPassword(self, oci_id): logs = self.OCI_Logs(oci_id) for entry in logs: if entry['type'] == 'Instance access details': try: return entry['parameters'][0] except IndexError: return # no data yet def OCI_Settings(self, oci_id): """Shows basic VM settings (IP addresses, OS, names, autoscaling etc.)""" data = self._simple_vm_method('GetVirtualMachineById', oci_id) res = { 'autoscaling': DictionaryItem(data['AutoScalingType']), 'connection_type': DictionaryItem(data['ConnectionType']), 'cpu_mhz': data['CpuMhz'], 'cpu_usage_mhz': data['CpuMhzUsage'], 'creation_date': self.clients.parse_date(data['CreationDate']), 'creation_user_name': data['CreationUserSimple']['FullName'], 'iops_usage': data['IopsUsage'], 'last_change_date': self.clients.parse_date(data['LastChangeDate']), 'payment_type': DictionaryItem(data['PaymentType']), 'memory_mb': data['RamMB'], 'memory_usage_mb': data['RamMBUsage'], 'status': DictionaryItem(data['Status']), 'name': data['VirtualMachineName'], 'vm_class_name': DictionaryItem(data['VMClass']), 'disks': [{ 'id': disk['ClientHddId'], 'name': disk['ClientHdd']['HddName'], 'capacity_gb': disk['ClientHdd']['CapacityGB'], 'creation_date': self.clients.parse_date(disk['ClientHdd']['CreationDate']), 'creation_user_name': disk['ClientHdd']['CreationUser']['FullName'], 'is_primary': disk['IsPrimary'], 'is_shared': disk['ClientHdd']['IsShared'] } for disk in data['DiskDrives']], 'ips': [{ 'ipv4': ip['Address'], 'netmask': ip['NetMask'], 'ipv6': ip['AddressV6'], 'creation_date': self.clients.parse_date(ip['CreationDate']), 'dhcp_branch': ip['DhcpBranch'], 'gateway': ip['Gateway'], 'status': DictionaryItem(ip['IPStatus']), 'last_change_date': self.clients.parse_date(ip['LastChangeDate']), 'macaddr': ip['MacAddress'], } for ip in data['IPs']], 'vlans': [], } if data['PrivateIpv4']: res['vlans'] = [{ 'ipv4': vlan['PrivateIpAddress'], 'creation_date': self.clients.parse_date(vlan['CreationDate']), 'macaddr': vlan['MacAddress'], } for vlan in data['PrivateIpv4']] return res def OCI_ChangeClass(self, oci_id, oci_class, at_midnight=False): """Changes running VM class, potentially rebooting it""" oci = self._simple_vm_method('GetVirtualMachineById', oci_id) oci_class_obj = self._oci_class(oci_class) if not oci_class_obj: raise OktawaveOCIClassNotFound() oci['VMClass'] = oci_class_obj.item self._d(oci) oci.setdefault('PrivateIpv4', '') self.clients.call('UpdateVirtualMachine', machine=oci, clientId=self.client_id, classChangeInScheduler=at_midnight) def _subregion_id(self, subregion): if str(subregion) == 'Auto': return None if str(subregion) == '1': return 1 return 4 def OCI_Create(self, name, template, oci_class=None, forced_type=TemplateType.Machine, db_type=None, subregion='Auto'): """Creates a new instance from template""" self._logon() oci_class_id = None if oci_class is not None: oci_class_obj = self._oci_class(oci_class) if not oci_class_obj: raise OktawaveOCIClassNotFound() oci_class_id = oci_class_obj.id self.clients.call('CreateVirtualMachine', templateId=template, disks=None, additionalDisks=None, machineName=name, selectedClass=oci_class_id, selectedContainer=None, selectedPaymentMethod=DICT['OCI_PAYMENT_ID'], selectedConnectionType=DICT['OCI_CONNECTION_ID'], clientId=self.client_id, providervAppClientId=None, vAppType=forced_type, databaseTypeId=db_type, clientVmParameter=None, autoScalingTypeId=DICT['OCI_AUTOSCALING_ID'], clusterId=self._subregion_id(subregion)) def OCI_Clone(self, oci_id, name, clonetype): """Clones a VM""" self._logon() self.clients.call('CloneVirtualMachine', virtualMachineId=oci_id, cloneName=name, cloneType=clonetype, clientId=self.client_id) # OVS (disks) ### def OVS_List(self): """Lists disks""" self._logon() dsp = { 'ClientId': self.client_id, } data = self.clients.call('GetDisks', searchParams=dsp) for disk in data['_results']: if disk['VirtualMachineHdds'] is None: vms = [] else: vms = [{ 'id': vm['VirtualMachine']['VirtualMachineId'], 'name': vm['VirtualMachine']['VirtualMachineName'], 'primary': vm['IsPrimary'], 'vm_status': PowerStatus(vm['VirtualMachine']['StatusDictId']), } for vm in disk['VirtualMachineHdds']] yield { 'id': disk['ClientHddId'], 'name': disk['HddName'], 'tier': DictionaryItem(disk['HddStandard']), 'capacity_gb': disk['CapacityGB'], 'used_gb': disk['UsedCapacityGB'], 'is_shared': disk['IsShared'], 'vms': vms, } def OVS_Delete(self, ovs_id): """Deletes a disk""" self._logon() res = self.clients.call('DeleteDisk', clientHddId=ovs_id, clientId=self.client_id) if not res: raise OktawaveOVSDeleteError() def OVS_Create(self, name, capacity_gb, tier, shared, subregion='Auto'): """Adds a disk""" self._logon() disk = { 'CapacityGB': capacity_gb, 'HddName': name, 'HddStandardId': self._ovs_tier(tier).id, 'IsShared': shared, 'PaymentTypeId': DICT['OVS_PAYMENT_ID'], 'VirtualMachineIds': [], 'ClusterId': self._subregion_id(subregion) } self.clients.call('CreateDisk', clientHdd=disk, clientId=self.client_id) def OVS_Map(self, ovs_id, oci_id): """Maps a disk into an instance""" self._logon() disk = self._find_disk(ovs_id) if disk is None: raise OktawaveOVSNotFoundError() disk_mod = self._ovs_disk_mod(disk) if oci_id in disk_mod['VirtualMachineIds']: raise OktawaveOVSMappedError() disk_mod['VirtualMachineIds'].append(oci_id) res = self.clients.call('UpdateDisk', clientHdd=disk_mod, clientId=self.client_id) if not res: raise OktawaveOVSMapError() def OVS_Unmap(self, ovs_id, oci_id): """Unmaps a disk from an instance""" self._logon() disk = self._find_disk(ovs_id) if disk is None: raise OktawaveOVSNotFoundError() disk_mod = self._ovs_disk_mod(disk) if oci_id not in disk_mod['VirtualMachineIds']: raise OktawaveOVSUnmappedError() disk_mod['VirtualMachineIds'].remove(oci_id) res = self.clients.call('UpdateDisk', clientHdd=disk_mod, clientId=self.client_id) if not res: raise OktawaveOVSUnmapError() def OVS_ChangeTier(self, ovs_id, tier): self._logon() disk = self._find_disk(ovs_id) if disk is None: raise OktawaveOVSNotFoundError() disk_mod = self._ovs_disk_mod(disk) disk_mod['HddStandardId'] = self._ovs_tier(tier).id self.clients.call('UpdateDisk', clientHdd=disk_mod, clientId=self.client_id) def OVS_Extend(self, ovs_id, capacity_gb): self._logon() disk = self._find_disk(ovs_id) if disk is None: raise OktawaveOVSNotFoundError() disk_mod = self._ovs_disk_mod(disk) if disk_mod.pop('LockVirtualMachineIds'): raise OktawaveOVSMappedError() if disk_mod['CapacityGB'] > capacity_gb: raise OktawaveOVSTooSmallError() disk_mod['CapacityGB'] = capacity_gb self.clients.call('UpdateDisk', clientHdd=disk_mod, clientId=self.client_id) # ORDB (databases) ### def ORDB_List(self): """Lists databases""" self._logon() sp = { 'ClientId': self.client_id, } data = self.clients.call('GetDatabaseInstances', searchParams=sp) for db in data['_results']: yield { 'id': db['VirtualMachineId'], 'name': db['VirtualMachineName'], 'type': DictionaryItem(db['DatabaseType']), 'size': db['Size'], 'available_space': db['AvailableSpace'], } ORDB_TurnOn = OCI_TurnOn ORDB_TurnOff = OCI_TurnOff ORDB_Restart = OCI_Restart ORDB_Clone = OCI_Clone def ORDB_Delete(self, oci_id, db_name=None): """Deletes a database or VM""" self._logon() if db_name is None: self._simple_vm_method('DeleteVirtualMachine', oci_id) else: self.clients.call('DeleteDatabase', virtualMachineId=oci_id, databaseName=db_name, clientId=self.client_id) ORDB_Logs = OCI_Logs def ORDB_LogicalDatabases(self, oci_id): """Shows logical databases""" self._logon() sp = { 'ClientId': self.client_id, } data = self.clients.call('GetDatabaseInstances', searchParams=sp) for vm in data['_results']: if oci_id is not None and str( vm['VirtualMachineId']) != str(oci_id): continue for db in vm['Databases']: yield { 'id': db['VirtualMachineId'], 'name': db['DatabaseName'], 'type': DictionaryItem(db['DatabaseType']), 'encoding': db['Encoding'], 'is_running': db['IsRunning'], 'qps': db['QPS'], 'size': db['Size'] } ORDB_Settings = OCI_Settings def ORDB_Create(self, name, template, oci_class=None, subregion='Auto'): """Creates a database VM""" self._logon() data = self.clients.call('GetTemplate', templateId=template, clientId=self.client_id) if str(data['TemplateType']['DictionaryItemId']) != str( DICT['DB_VM_CATEGORY']): raise OktawaveORDBInvalidTemplateError() self.OCI_Create(name, template, forced_type=TemplateType.Database, db_type=data['DatabaseType']['DictionaryItemId'], subregion=subregion, oci_class=oci_class) def ORDB_GlobalSettings(self, oci_id): """Shows global database engine settings""" self._logon() data = self.clients.call('GetDatabaseConfig', virtualMachineId=oci_id, clientId=self.client_id) for item in data: yield {'name': item.Name, 'value': item.Value} def ORDB_CreateLogicalDatabase(self, oci_id, name, encoding): """Creates a new logical database within an instance""" self._logon() self.clients.call('CreateDatabase', virtualMachineId=oci_id, databaseName=name, encodingDictId=DICT[encoding.upper() + '_ENCODING'], clientId=self.client_id) def ORDB_BackupLogicalDatabase(self, oci_id, name): """Creates a backup of logical database""" self._logon() self.clients.call('BackupDatabase', virtualMachineId=oci_id, databaseName=name, clientId=self.client_id) def ORDB_MoveLogicalDatabase(self, oci_id_from, oci_id_to, name): """Moves a logical database""" self._logon() self.clients.call('MoveDatabase', virtualMachineIdFrom=oci_id_from, virtualMachineIdTo=oci_id_to, databaseName=name, clientId=self.client_id) def ORDB_Backups(self): """Lists logical database backups""" self._logon() mysql_data = self.clients.call('GetBackups', databaseTypeDictId=DICT['MYSQL_DB'], clientId=self.client_id) or [] pgsql_data = self.clients.call( 'GetBackups', databaseTypeDictId=DICT['POSTGRESQL_DB'], clientId=self.client_id) or [] for b in mysql_data: yield { 'file_name': b['Name'], 'type': 'MySQL', 'path': b['ContainerName'] + "/" + b['FullPath'] } for b in pgsql_data: yield { 'file_name': b['Name'], 'type': 'PostgreSQL', 'path': b['ContainerName'] + "/" + b['FullPath'] } def ORDB_RestoreLogicalDatabase(self, oci_id, name, backup_file): """Restores a database from backup""" self._logon() self.clients.call('RestoreDatabase', virtualMachineId=oci_id, databaseName=name, backupFileName=backup_file, clientId=self.client_id) def Container_List(self): """Lists client's containers' basic info""" self._logon() containers = self.clients.call('GetContainers', clientId=self.client_id) for c in containers: yield { 'id': c['ContainerId'], 'name': c['ContainerName'], 'vms': c['VirtualMachineCount'] } def Container_Get(self, container_id): """Displays a container's information""" self._logon() c = self.clients.call('GetContainer', containerId=container_id) res = { 'autoscaling': DictionaryItem(c['AutoScalingType']), 'id': c['ContainerId'], 'name': c['ContainerName'], 'healthcheck': c['IsServiceCheckAvailable'], 'load_balancer': c['IsLoadBalancer'], 'master_service_id': c['MasterServiceId'], 'master_service_name': c['MasterServiceName'], 'proxy_cache': c['IsProxyCache'], 'ssl': c['IsSSLUsed'], 'port': c['PortNumber'], 'schedulers': c['SchedulersCount'], 'vms': c['VirtualMachineCount'], 'db_user': c['DatabaseUserLogin'], 'db_password': c['DatabaseUserPassword'] } for label, item in (('ip_version', 'IPVersion'), ('load_balancer_algorithm', 'LoadBalancerAlgorithm'), ('service', 'Service'), ('session_type', 'SessionType')): if c[item]: res[label] = DictionaryItem(c[item]) else: res[label] = None res['ips'] = [{ 'ipv4': ip['Address'], 'ipv6': ip['AddressV6'] } for ip in c['IPs']] return res def Container_OCIList(self, container_id): c_simple = self._container_simple(container_id) for vm in c_simple['VirtualMachines']: vms = vm['VirtualMachineSimple'] yield { 'oci_id': vms['VirtualMachineId'], 'oci_name': vms['VirtualMachineName'], 'status': PowerStatus(vms['StatusDictId']) } def Container_RemoveOCI(self, container_id, oci_id): """Removes an instance from container""" self._logon() c_simple = self._container_simple(container_id) found = False for vm in c_simple['VirtualMachines']: if vm['VirtualMachineId'] == oci_id: found = True break if not found: raise OktawaveOCINotInContainer() vm_ids = [ vm['VirtualMachineId'] for vm in c_simple['VirtualMachines'] if vm['VirtualMachineId'] != oci_id ] c = self.clients.call('GetContainer', containerId=container_id) self._d(vm_ids) self.clients.call('UpdateContainer', container=c, virtualMachinesId=vm_ids) def Container_AddOCI(self, container_id, oci_id): """Adds an instance to container""" self._logon() c_simple = self._container_simple(container_id) found = False for vm in c_simple['VirtualMachines']: if vm['VirtualMachineId'] == oci_id: found = True break if found: raise OktawaveOCIInContainer() vm_ids = [ vm['VirtualMachineId'] for vm in c_simple['VirtualMachines'] ] + [oci_id] c = self.clients.call('GetContainer', containerId=container_id) self.clients.call('UpdateContainer', container=c, virtualMachinesId=vm_ids) def Container_Delete(self, container_id): self._logon() self.clients.call('DeleteContainers', containerIds=[container_id], clientId=self.client_id) def _container_service_id(self, service): services = { 'HTTP': 43, 'HTTPS': 44, 'SMTP': 45, 'MySQL': 287, 'Port': 155 } return services[service] def _load_balancer_algorithm_id(self, algorithm): algorithms = { 'least_response_time': 282, 'least_connections': 281, 'source_ip_hash': 288, 'round_robin': 612 } return algorithms[algorithm] def _session_type_id(self, s_type): s_types = {'none': 47, 'by_source_ip': 46, 'by_cookie': 280} return s_types[s_type] def _ip_version_id(self, version): versions = {'4': 115, '6': 116, 'both': 565} return versions[version] def _autoscaling_id(self, autoscaling): types = {'on': 185, 'off': 184} return types[autoscaling] def Container_Create(self, name, load_balancer, service, port, proxy_cache, ssl, healthcheck, master_id, session, lb_algorithm, ip_version, autoscaling): if lb_algorithm == 'least_response_time' and service != 'HTTP' and service != 'HTTPS': raise OktawaveLRTNotAllowed() self._logon() vm_ids = [] if master_id is None else [master_id] result = self.clients.call( 'CreateContainer', container={ 'OwnerClientId': self.client_id, 'ContainerName': name, 'IsLoadBalancer': load_balancer, 'Service': { 'DictionaryItemId': self._container_service_id(service) }, 'LoadBalancerAlgorithm': { 'DictionaryItemId': self._load_balancer_algorithm_id(lb_algorithm) }, 'IsSSLUsed': ssl, 'IsProxyCache': proxy_cache, 'MasterServiceId': master_id, 'PortNumber': port, 'SessionType': { 'DictionaryItemId': self._session_type_id(session) }, 'IPVersion': { 'DictionaryItemId': self._ip_version_id(ip_version) }, 'AutoScalingType': { 'DictionaryItemId': self._autoscaling_id(autoscaling) }, 'IsServiceCheckAvailable': healthcheck }, virtualMachinesId=vm_ids) return result # TODO: allow to change only selected parameters, add more validation # without relying on the UpdateContainer API method. def Container_Edit(self, container_id, name, load_balancer, service, port, proxy_cache, ssl, healthcheck, master_id, session, lb_algorithm, ip_version, autoscaling): self._logon() if lb_algorithm == 'least_response_time' and service != 'HTTP' and service != 'HTTPS': raise OktawaveLRTNotAllowed() c = self.clients.call('GetContainer', containerId=container_id) c_simple = self._container_simple(container_id) c['ContainerName'] = name c['IsLoadBalancer'] = load_balancer c['Service'] = { 'DictionaryItemId': self._container_service_id(service) } c['LoadBalancerAlgorithm'] = { 'DictionaryItemId': self._load_balancer_algorithm_id(lb_algorithm) } c['IsSSLUsed'] = ssl c['IsProxyCache'] = proxy_cache c['MasterServiceId'] = master_id c['PortNumber'] = port c['SessionType'] = {'DictionaryItemId': self._session_type_id(session)} c['IPVersion'] = {'DictionaryItemId': self._ip_version_id(ip_version)} c['AutoScalingType'] = { 'DictionaryItemId': self._autoscaling_id(autoscaling) } c['AutoScalingTypeDictId'] = self._autoscaling_id(autoscaling) c['IsServiceCheckAvailable'] = healthcheck self._d(c) self.clients.call('UpdateContainer', container=c, virtualMachinesId=[ vm['VirtualMachineId'] for vm in c_simple['VirtualMachines'] ]) def _address_pool_id(self, name): pools = {'10.0.0.0/24': 278, '192.168.0.0/24': 279} return pools[name] def OPN_List(self): """Lists client's OPNs""" self._logon() vlans = self.clients.call('GetVlansByClientId', clientId=self.client_id) for v in vlans: yield { 'id': v['VlanId'], 'name': v['VlanName'], 'address_pool': DictionaryItem(v['AddressPool']), 'payment_type': DictionaryItem(v['PaymentType']) } def OPN_Get(self, opn_id): self._logon() v = self.clients.call('GetVlanById', vlanId=opn_id, clientId=self.client_id) vms = self.clients.call('GetVirtualMachineVlansByVlanId', vlanId=opn_id, clientId=self.client_id) return { 'id': v['VlanId'], 'name': v['VlanName'], 'address_pool': DictionaryItem(v['AddressPool']), 'payment_type': DictionaryItem(v['PaymentType']), 'vms': vms } def OPN_Create(self, name, address_pool): self._logon() self._d(self.client_object) return self.clients.call('CreateVlan', vlan={ 'VlanName': name, 'AddressPool': { 'DictionaryItemId': self._address_pool_id(address_pool) }, 'OwnerClient': self.client_object['Client'], 'PaymentType': { 'DictionaryItemId': DICT['OPN_PAYMENT_ID'] }, 'CreationUserId': self.client_id }) def OPN_Delete(self, opn_id): self._logon() return self.clients.call('DeleteVlan', vlanId=opn_id, clientId=self.client_id) def OPN_AddOCI(self, opn_id, oci_id, ip_address): self._logon() oci = self.clients.call('GetVirtualMachineById', virtualMachineId=oci_id, clientId=self.client_id) vlan = self.clients.call('GetVlanById', vlanId=opn_id, clientId=self.client_id) for opn in oci['PrivateIpv4']: if opn['Vlan']['VlanId'] == opn_id: raise OktawaveOCIInOPN() oci['PrivateIpv4'].append({ 'PrivateIpAddress': ip_address, 'VirtualMachine': { 'VirtualMachineName': oci['VirtualMachineName'], 'VirtualMachineId': oci_id, 'StatusDictId': oci['Status']['DictionaryItemId'] }, 'Vlan': vlan, 'CreationDate': '/Date(' + str(int(time()) * 100) + '+0000)/', # for some reason API server does not fill this field automatically }) return self.clients.call('UpdateVirtualMachine', machine=oci, clientId=self.client_id, classChangeInScheduler=False) def OPN_RemoveOCI(self, opn_id, oci_id): self._logon() oci = self.clients.call('GetVirtualMachineById', virtualMachineId=oci_id, clientId=self.client_id) vlan = self.clients.call('GetVlanById', vlanId=opn_id, clientId=self.client_id) l1 = len(oci['PrivateIpv4']) oci['PrivateIpv4'] = filter(lambda x: x['Vlan']['VlanId'] != opn_id, oci['PrivateIpv4']) if l1 == len(oci['PrivateIpv4']): raise OktawaveOCINotInOPN() return self.clients.call('UpdateVirtualMachine', machine=oci, clientId=self.client_id, classChangeInScheduler=False) def OPN_Rename(self, opn_id, name): self._logon() vlan = self.clients.call('GetVlanById', vlanId=opn_id, clientId=self.client_id) vlan['VlanName'] = name return self.clients.call('UpdateVlan', vlan=vlan)
class OktawaveApi(object): def __init__(self, username, password, debug=False): """Initialize the API instance Arguments: - username (string) - Oktawave account username - password (string) - Oktawave account password - debug (bool) - enable debug output? """ self.username = username self.password = password self.debug = debug # HELPER METHODS ### # methods starting with "_" will not be autodispatched to client commands # ### def _d(self, what): if self.debug: print what def _init_common(self): """Convenience method to initialize CommonService client""" if hasattr(self, 'common'): return self.common = ApiClient( jsonapi_common, self.username, self.password, self.debug) self._d(self.common) def _init_clients(self): """Convenience method to initialize ClientsService client""" if hasattr(self, 'clients'): return self.clients = ApiClient( jsonapi_clients, self.username, self.password, self.debug) self._d(self.clients) def _logon(self, only_common=False): """Initializes CommonService client and calls LogonUser method. Returns the User object, as returned by LogonUser. Also sets self.client_id for convenience. """ self._init_common() if not only_common: self._init_clients() if hasattr(self, 'client_object'): return self.client_object try: res = self.common.call( 'LogonUser', user=self.username, password=self.password, ipAddress=self._get_machine_ip(), userAgent="Oktawave CLI") except AttributeError: raise OktawaveLoginError() self.client_id = res['User']['Client']['ClientId'] self.client_object = res return res def _simple_vm_method(self, method, vm_id): """Wraps around common simple virtual machine method call pattern""" self._logon() return self.clients.call(method, virtualMachineId=vm_id, clientId=self.client_id) def _find_disk(self, disk_id): """Finds a disk (OVS) by id""" dsp = { 'ClientId': self.client_id, } disks = [d for d in self.clients.call( 'GetDisks', searchParams=dsp)['_results'] if d['ClientHddId'] == disk_id] if len(disks) == 0: return None res = disks[0] if res['VirtualMachineHdds'] is None: res['VirtualMachineHdds'] = [] return res def _get_machine_ip(self): return '127.0.0.1' def _dict_item(self, dict_id, key): items = self.common.call( 'GetDictionaryItems', dictionary=dict_id, clientId=self.client_id) for item in items: item = DictionaryItem(item) if item.name == key: return item def _oci_class(self, class_name): """Returns a dictionary item for OCI class with a given name""" return self._dict_item(DICT['OCI_CLASSES_DICT_ID'], class_name) def _ovs_tier(self, tier): """Returns ID of a given disk tier""" tier_name = 'Tier ' + str(tier) tier_obj = self._dict_item(DICT['OVS_TIERS_DICT_ID'], tier_name) if not tier_obj: raise OktawaveOVSTierNotFound() return tier_obj def _ovs_disk_mod(self, disk): vms = [ vm['VirtualMachine']['VirtualMachineId'] for vm in disk['VirtualMachineHdds']] lock_vms = [ vm['VirtualMachine']['VirtualMachineId'] for vm in disk['VirtualMachineHdds'] if vm['VirtualMachine']['StatusDictId'] == PowerStatus.PowerOn or not vm['IsPrimary'] ] disk_mod = { 'CapacityGB': disk['CapacityGB'], 'ClientHddId': disk['ClientHddId'], 'ClusterId': disk['Cluster']['ClusterId'], 'HddName': disk['HddName'], 'IsShared': disk['IsShared'], 'HddStandardId': disk['HddStandard']['DictionaryItemId'], 'PaymentTypeId': disk['PaymentType']['DictionaryItemId'], 'VirtualMachineIds': vms, 'LockVirtualMachineIds': lock_vms, } return disk_mod def _container_simple(self, container_id): """Fetches a container's information using GetContainersSimpleWithVM""" self._logon() cs = self.clients.call('GetContainersSimpleWithVM', clientId=self.client_id) for c in cs: self._d([c, container_id]) if str(c['ContainerId']) == str(container_id): return c raise OktawaveContainerNotFoundError() # API methods below ### # General / Account ### def Account_Settings(self): """Print basic settings of the client account args is an object containing at least the following fields: - username - Oktawave username - password - Oktawave client password Typically args will be the object returned from argparse. """ self._logon(only_common=True) client = self.client_object # TODO: probably get more settings return { 'time_zone': client['TimeZone']['DisplayName'], 'currency': DictionaryItem(client['Currency']), 'date_format': DictionaryItem(client['DateFormat']), 'availability_zone': DictionaryItem( self.common.call('GetDictionaryItemById', dictionaryItemId=client['AvailabilityZone'])), '24h_clock': client['Is24HourClock'], } def Account_RunningJobs(self): self._logon() res = self.common.call('GetRunningOperations', clientId=self.client_id) if not res: return for op in res: yield { 'id': op['AsynchronousOperationId'], 'creation_date': op['CreationDate'], 'creation_user_name': op['CreationUserFullName'], 'type': RawDictionaryItem(op['OperationTypeId'], op['OperationTypeName']), 'object_type': RawDictionaryItem(op['ObjectTypeId'], op['ObjectTypeName']), 'object_name': op['ObjectName'], 'progress_percent': op['Progress'], 'status': RawDictionaryItem(op['StatusId'], op['StatusName']) } def Account_Users(self): """Print users in client account.""" self._logon() users = self.clients.call('GetClientUsers', clientId=self.client_id) self._d(users) for user in users: yield { 'email': user['Email'], 'name': user['FullName'], } ### OCI Templates ### def templates_in_category(self, category_id, name_filter=''): """Lists templates in a category""" self._logon() data = self.common.call( 'GetTemplatesByCategory', categoryId=category_id, categorySystemId=None, type=None, clientId=self.client_id) if data: return dict((template['TemplateId'], template['TemplateName']) for template in data if name_filter in template['TemplateName']) def Template_Show(self, template_id): """Shows more detailed info about a particular template""" self._logon() data = self.clients.call('GetTemplate', templateId=template_id, clientId=self.client_id) template_category = TemplateCategory(data['TemplateCategory'], None) software = [SoftwareItem(item['Software']) for item in data['SoftwareList']] return { 'template_id': data['TemplateId'], 'template_name': data['TemplateName'], 'template_category': template_category.tree_path, 'vm_class_id': data['VMClass']['DictionaryItemId'], 'vm_class_name': DictionaryItem(data['VMClass']), 'system_category_name': DictionaryItem(data['TemplateSystemCategory']), 'label': data['Name'], 'software': software, 'eth_count': data['EthernetControllersCount'], 'connection_type': DictionaryItem(data['ConnectionType']), 'disks': [{ 'name': hdd['HddName'], 'capacity_gb': hdd['CapacityGB'], 'is_primary': hdd['IsPrimary'] } for hdd in data['DiskDrives']], 'description': data['TemplateDescription']['TemplateDescriptionsNames'][0]['Description'], } def Template_List(self, origin, name_filter=''): """Lists templates of a chosen origin""" self._logon() data = self.common.call('GetTemplatesByOrigin', clientId=self.client_id, origin=TemplateOrigin(origin).id) if data: return [{ 'id': template['TemplateId'], 'name': template['TemplateName'], 'category': str(TemplateOrigin(origin)), 'system_category': DictionaryItem(template['TemplateSystemCategory']) } for template in data if name_filter in template['TemplateName']] ### OCI (VMs) ### def OCI_List(self): """Lists client's virtual machines' basic info""" self._logon() vms = self.clients.call('GetVirtualMachinesSimple', clientId=self.client_id) self._d(vms) for vm in vms: yield { 'id': vm['VirtualMachineId'], 'name': vm['VirtualMachineName'], 'status': PowerStatus(vm['StatusDictId']), } def OCI_ListDetails(self): """Lists client's virtual machines""" self._logon() sp = {'ClientId': self.client_id} vms = self.clients.call('GetVirtualMachines', searchParams=sp) self._d(vms) for vm in vms['_results']: yield { 'id': vm['VirtualMachineId'], 'name': vm['VirtualMachineName'], 'status': PowerStatus(vm['StatusDictId']), 'class_name': DictionaryItem(vm['VMClass']), 'cpu_mhz': vm['CpuMhz'], 'cpu_usage_mhz': vm['CpuMhzUsage'], 'memory_mb': vm['RamMB'], 'memory_usage_mb': vm['RamMBUsage'], } def OCI_Restart(self, oci_id): """Restarts given VM""" self._simple_vm_method('RestartVirtualMachine', oci_id) def OCI_TurnOff(self, oci_id, force=False): """Turns given VM off""" if not force: try: self._simple_vm_method('ShutdownVirtualMachine', oci_id) return except OktawaveAPIError: # XXX: swallow only the "clean shutdown not supported" exception pass self._simple_vm_method('TurnoffVirtualMachine', oci_id) def OCI_TurnOn(self, oci_id): """Turns given virtual machine on""" self._simple_vm_method('TurnOnVirtualMachine', oci_id) def OCI_Delete(self, oci_id): """Deletes given virtual machine""" self._simple_vm_method('DeleteVirtualMachine', oci_id) def OCI_Logs(self, oci_id): """Shows virtual machine logs""" self._logon() sp = { 'VirtualMachineId': oci_id, 'PageSize': 100, 'SortingDirection': 0, # descending } data = self.clients.call( 'GetVirtualMachineHistories', searchParams=sp, clientId=self.client_id) self._d(data) for op in data['_results']: yield { 'time': self.clients.parse_date(op['CreationDate']), 'type': DictionaryItem(op['OperationType']), 'user_name': op['CreationUser']['FullName'], 'status': DictionaryItem(op['Status']), 'parameters': [item['Value'] for item in op['Parameters']], } def OCI_DefaultPassword(self, oci_id): logs = self.OCI_Logs(oci_id) for entry in logs: if entry['type'] == 'Instance access details': try: return entry['parameters'][0] except IndexError: return # no data yet def OCI_Settings(self, oci_id): """Shows basic VM settings (IP addresses, OS, names, autoscaling etc.)""" data = self._simple_vm_method('GetVirtualMachineById', oci_id) res = { 'autoscaling': DictionaryItem(data['AutoScalingType']), 'connection_type': DictionaryItem(data['ConnectionType']), 'cpu_mhz': data['CpuMhz'], 'cpu_usage_mhz': data['CpuMhzUsage'], 'creation_date': self.clients.parse_date(data['CreationDate']), 'creation_user_name': data['CreationUserSimple']['FullName'], 'iops_usage': data['IopsUsage'], 'last_change_date': self.clients.parse_date(data['LastChangeDate']), 'payment_type': DictionaryItem(data['PaymentType']), 'memory_mb': data['RamMB'], 'memory_usage_mb': data['RamMBUsage'], 'status': DictionaryItem(data['Status']), 'name': data['VirtualMachineName'], 'vm_class_name': DictionaryItem(data['VMClass']), 'disks': [{ 'id': disk['ClientHddId'], 'name': disk['ClientHdd']['HddName'], 'capacity_gb': disk['ClientHdd']['CapacityGB'], 'creation_date': self.clients.parse_date(disk['ClientHdd']['CreationDate']), 'creation_user_name': disk['ClientHdd']['CreationUser']['FullName'], 'is_primary': disk['IsPrimary'], 'is_shared': disk['ClientHdd']['IsShared'] } for disk in data['DiskDrives']], 'ips': [{ 'ipv4': ip['Address'], 'netmask': ip['NetMask'], 'ipv6': ip['AddressV6'], 'creation_date': self.clients.parse_date(ip['CreationDate']), 'dhcp_branch': ip['DhcpBranch'], 'gateway': ip['Gateway'], 'status': DictionaryItem(ip['IPStatus']), 'last_change_date': self.clients.parse_date(ip['LastChangeDate']), 'macaddr': ip['MacAddress'], } for ip in data['IPs']], 'vlans': [], } if data['PrivateIpv4']: res['vlans'] = [{ 'ipv4': vlan['PrivateIpAddress'], 'creation_date': self.clients.parse_date(vlan['CreationDate']), 'macaddr': vlan['MacAddress'], } for vlan in data['PrivateIpv4']] return res def OCI_ChangeClass(self, oci_id, oci_class, at_midnight=False): """Changes running VM class, potentially rebooting it""" oci = self._simple_vm_method('GetVirtualMachineById', oci_id) oci_class_obj = self._oci_class(oci_class) if not oci_class_obj: raise OktawaveOCIClassNotFound() oci['VMClass'] = oci_class_obj.item self._d(oci) oci.setdefault('PrivateIpv4', '') self.clients.call( 'UpdateVirtualMachine', machine=oci, clientId=self.client_id, classChangeInScheduler=at_midnight) def _subregion_id(self, subregion): if str(subregion) == 'Auto': return None if str(subregion) == '1': return 1 return 4 def OCI_Create(self, name, template, oci_class=None, forced_type=TemplateType.Machine, db_type=None, subregion='Auto'): """Creates a new instance from template""" self._logon() oci_class_id = None if oci_class is not None: oci_class_obj = self._oci_class(oci_class) if not oci_class_obj: raise OktawaveOCIClassNotFound() oci_class_id = oci_class_obj.id self.clients.call('CreateVirtualMachine', templateId=template, disks=None, additionalDisks=None, machineName=name, selectedClass=oci_class_id, selectedContainer=None, selectedPaymentMethod=DICT['OCI_PAYMENT_ID'], selectedConnectionType=DICT['OCI_CONNECTION_ID'], clientId=self.client_id, providervAppClientId=None, vAppType=forced_type, databaseTypeId=db_type, clientVmParameter=None, autoScalingTypeId=DICT['OCI_AUTOSCALING_ID'], clusterId=self._subregion_id(subregion)) def OCI_Clone(self, oci_id, name, clonetype): """Clones a VM""" self._logon() self.clients.call('CloneVirtualMachine', virtualMachineId=oci_id, cloneName=name, cloneType=clonetype, clientId=self.client_id) # OVS (disks) ### def OVS_List(self): """Lists disks""" self._logon() dsp = { 'ClientId': self.client_id, } data = self.clients.call('GetDisks', searchParams=dsp) for disk in data['_results']: if disk['VirtualMachineHdds'] is None: vms = [] else: vms = [{ 'id': vm['VirtualMachine']['VirtualMachineId'], 'name': vm['VirtualMachine']['VirtualMachineName'], 'primary': vm['IsPrimary'], 'vm_status': PowerStatus(vm['VirtualMachine']['StatusDictId']), } for vm in disk['VirtualMachineHdds']] yield { 'id': disk['ClientHddId'], 'name': disk['HddName'], 'tier': DictionaryItem(disk['HddStandard']), 'capacity_gb': disk['CapacityGB'], 'used_gb': disk['UsedCapacityGB'], 'is_shared': disk['IsShared'], 'vms': vms, } def OVS_Delete(self, ovs_id): """Deletes a disk""" self._logon() res = self.clients.call('DeleteDisk', clientHddId=ovs_id, clientId=self.client_id) if not res: raise OktawaveOVSDeleteError() def OVS_Create(self, name, capacity_gb, tier, shared, subregion='Auto'): """Adds a disk""" self._logon() disk = { 'CapacityGB': capacity_gb, 'HddName': name, 'HddStandardId': self._ovs_tier(tier).id, 'IsShared': shared, 'PaymentTypeId': DICT['OVS_PAYMENT_ID'], 'VirtualMachineIds': [], 'ClusterId': self._subregion_id(subregion) } self.clients.call('CreateDisk', clientHdd=disk, clientId=self.client_id) def OVS_Map(self, ovs_id, oci_id): """Maps a disk into an instance""" self._logon() disk = self._find_disk(ovs_id) if disk is None: raise OktawaveOVSNotFoundError() disk_mod = self._ovs_disk_mod(disk) if oci_id in disk_mod['VirtualMachineIds']: raise OktawaveOVSMappedError() disk_mod['VirtualMachineIds'].append(oci_id) res = self.clients.call('UpdateDisk', clientHdd=disk_mod, clientId=self.client_id) if not res: raise OktawaveOVSMapError() def OVS_Unmap(self, ovs_id, oci_id): """Unmaps a disk from an instance""" self._logon() disk = self._find_disk(ovs_id) if disk is None: raise OktawaveOVSNotFoundError() disk_mod = self._ovs_disk_mod(disk) if oci_id not in disk_mod['VirtualMachineIds']: raise OktawaveOVSUnmappedError() disk_mod['VirtualMachineIds'].remove(oci_id) res = self.clients.call('UpdateDisk', clientHdd=disk_mod, clientId=self.client_id) if not res: raise OktawaveOVSUnmapError() def OVS_ChangeTier(self, ovs_id, tier): self._logon() disk = self._find_disk(ovs_id) if disk is None: raise OktawaveOVSNotFoundError() disk_mod = self._ovs_disk_mod(disk) disk_mod['HddStandardId'] = self._ovs_tier(tier).id self.clients.call('UpdateDisk', clientHdd=disk_mod, clientId=self.client_id) def OVS_Extend(self, ovs_id, capacity_gb): self._logon() disk = self._find_disk(ovs_id) if disk is None: raise OktawaveOVSNotFoundError() disk_mod = self._ovs_disk_mod(disk) if disk_mod.pop('LockVirtualMachineIds'): raise OktawaveOVSMappedError() if disk_mod['CapacityGB'] > capacity_gb: raise OktawaveOVSTooSmallError() disk_mod['CapacityGB'] = capacity_gb self.clients.call('UpdateDisk', clientHdd=disk_mod, clientId=self.client_id) # ORDB (databases) ### def ORDB_List(self): """Lists databases""" self._logon() sp = { 'ClientId': self.client_id, } data = self.clients.call('GetDatabaseInstances', searchParams=sp) for db in data['_results']: yield { 'id': db['VirtualMachineId'], 'name': db['VirtualMachineName'], 'type': DictionaryItem(db['DatabaseType']), 'size': db['Size'], 'available_space': db['AvailableSpace'], } ORDB_TurnOn = OCI_TurnOn ORDB_TurnOff = OCI_TurnOff ORDB_Restart = OCI_Restart ORDB_Clone = OCI_Clone def ORDB_Delete(self, oci_id, db_name=None): """Deletes a database or VM""" self._logon() if db_name is None: self._simple_vm_method('DeleteVirtualMachine', oci_id) else: self.clients.call( 'DeleteDatabase', virtualMachineId=oci_id, databaseName=db_name, clientId=self.client_id) ORDB_Logs = OCI_Logs def ORDB_LogicalDatabases(self, oci_id): """Shows logical databases""" self._logon() sp = { 'ClientId': self.client_id, } data = self.clients.call('GetDatabaseInstances', searchParams=sp) for vm in data['_results']: if oci_id is not None and str(vm['VirtualMachineId']) != str(oci_id): continue for db in vm['Databases']: yield { 'id': db['VirtualMachineId'], 'name': db['DatabaseName'], 'type': DictionaryItem(db['DatabaseType']), 'encoding': db['Encoding'], 'is_running': db['IsRunning'], 'qps': db['QPS'], 'size': db['Size'] } ORDB_Settings = OCI_Settings def ORDB_Create(self, name, template, oci_class=None, subregion='Auto'): """Creates a database VM""" self._logon() data = self.clients.call('GetTemplate', templateId=template, clientId=self.client_id) if str(data['TemplateType']['DictionaryItemId']) != str(DICT['DB_VM_CATEGORY']): raise OktawaveORDBInvalidTemplateError() self.OCI_Create(name, template, forced_type=TemplateType.Database, db_type=data['DatabaseType']['DictionaryItemId'], subregion=subregion, oci_class=oci_class) def ORDB_GlobalSettings(self, oci_id): """Shows global database engine settings""" self._logon() data = self.clients.call('GetDatabaseConfig', virtualMachineId=oci_id, clientId=self.client_id) for item in data: yield { 'name': item.Name, 'value': item.Value } def ORDB_CreateLogicalDatabase(self, oci_id, name, encoding): """Creates a new logical database within an instance""" self._logon() self.clients.call( 'CreateDatabase', virtualMachineId=oci_id, databaseName=name, encodingDictId=DICT[encoding.upper() + '_ENCODING'], clientId=self.client_id) def ORDB_BackupLogicalDatabase(self, oci_id, name): """Creates a backup of logical database""" self._logon() self.clients.call('BackupDatabase', virtualMachineId=oci_id, databaseName=name, clientId=self.client_id) def ORDB_MoveLogicalDatabase(self, oci_id_from, oci_id_to, name): """Moves a logical database""" self._logon() self.clients.call( 'MoveDatabase', virtualMachineIdFrom=oci_id_from, virtualMachineIdTo=oci_id_to, databaseName=name, clientId=self.client_id) def ORDB_Backups(self): """Lists logical database backups""" self._logon() mysql_data = self.clients.call( 'GetBackups', databaseTypeDictId=DICT['MYSQL_DB'], clientId=self.client_id) or [] pgsql_data = self.clients.call( 'GetBackups', databaseTypeDictId=DICT['POSTGRESQL_DB'], clientId=self.client_id) or [] for b in mysql_data: yield { 'file_name': b['Name'], 'type': 'MySQL', 'path': b['ContainerName'] + "/" + b['FullPath'] } for b in pgsql_data: yield { 'file_name': b['Name'], 'type': 'PostgreSQL', 'path': b['ContainerName'] + "/" + b['FullPath'] } def ORDB_RestoreLogicalDatabase(self, oci_id, name, backup_file): """Restores a database from backup""" self._logon() self.clients.call('RestoreDatabase', virtualMachineId=oci_id, databaseName=name, backupFileName=backup_file, clientId=self.client_id) def Container_List(self): """Lists client's containers' basic info""" self._logon() containers = self.clients.call('GetContainers', clientId=self.client_id) for c in containers: yield { 'id': c['ContainerId'], 'name': c['ContainerName'], 'vms': c['VirtualMachineCount'] } def Container_Get(self, container_id): """Displays a container's information""" self._logon() c = self.clients.call('GetContainer', containerId=container_id) res = { 'autoscaling': DictionaryItem(c['AutoScalingType']), 'id': c['ContainerId'], 'name': c['ContainerName'], 'healthcheck': c['IsServiceCheckAvailable'], 'load_balancer': c['IsLoadBalancer'], 'master_service_id': c['MasterServiceId'], 'master_service_name': c['MasterServiceName'], 'proxy_cache': c['IsProxyCache'], 'ssl': c['IsSSLUsed'], 'port': c['PortNumber'], 'schedulers': c['SchedulersCount'], 'vms': c['VirtualMachineCount'], 'db_user': c['DatabaseUserLogin'], 'db_password': c['DatabaseUserPassword'] } for label, item in ( ('ip_version', 'IPVersion'), ('load_balancer_algorithm', 'LoadBalancerAlgorithm'), ('service', 'Service'), ('session_type', 'SessionType')): if c[item]: res[label] = DictionaryItem(c[item]) else: res[label] = None res['ips'] = [ {'ipv4': ip['Address'], 'ipv6': ip['AddressV6']} for ip in c['IPs'] ] return res def Container_OCIList(self, container_id): c_simple = self._container_simple(container_id) for vm in c_simple['VirtualMachines']: vms = vm['VirtualMachineSimple'] yield { 'oci_id': vms['VirtualMachineId'], 'oci_name': vms['VirtualMachineName'], 'status': PowerStatus(vms['StatusDictId']) } def Container_RemoveOCI(self, container_id, oci_id): """Removes an instance from container""" self._logon() c_simple = self._container_simple(container_id) found = False for vm in c_simple['VirtualMachines']: if vm['VirtualMachineId'] == oci_id: found = True break if not found: raise OktawaveOCINotInContainer() vm_ids = [vm['VirtualMachineId'] for vm in c_simple['VirtualMachines'] if vm['VirtualMachineId'] != oci_id] c = self.clients.call('GetContainer', containerId=container_id) self._d(vm_ids) self.clients.call('UpdateContainer', container=c, virtualMachinesId=vm_ids) def Container_AddOCI(self, container_id, oci_id): """Adds an instance to container""" self._logon() c_simple = self._container_simple(container_id) found = False for vm in c_simple['VirtualMachines']: if vm['VirtualMachineId'] == oci_id: found = True break if found: raise OktawaveOCIInContainer() vm_ids = [vm['VirtualMachineId'] for vm in c_simple['VirtualMachines']] + [oci_id] c = self.clients.call('GetContainer', containerId=container_id) self.clients.call('UpdateContainer', container=c, virtualMachinesId=vm_ids) def Container_Delete(self, container_id): self._logon() self.clients.call('DeleteContainers', containerIds=[container_id], clientId=self.client_id) def _container_service_id(self, service): services = {'HTTP': 43, 'HTTPS': 44, 'SMTP': 45, 'MySQL': 287, 'Port': 155} return services[service] def _load_balancer_algorithm_id(self, algorithm): algorithms = {'least_response_time': 282, 'least_connections': 281, 'source_ip_hash': 288, 'round_robin': 612} return algorithms[algorithm] def _session_type_id(self, s_type): s_types = {'none': 47, 'by_source_ip': 46, 'by_cookie': 280} return s_types[s_type] def _ip_version_id(self, version): versions = {'4': 115, '6': 116, 'both': 565} return versions[version] def _autoscaling_id(self, autoscaling): types = {'on': 185, 'off': 184} return types[autoscaling] def Container_Create( self, name, load_balancer, service, port, proxy_cache, ssl, healthcheck, master_id, session, lb_algorithm, ip_version, autoscaling): if lb_algorithm == 'least_response_time' and service != 'HTTP' and service != 'HTTPS': raise OktawaveLRTNotAllowed() self._logon() vm_ids = [] if master_id is None else [master_id] result = self.clients.call('CreateContainer', container={ 'OwnerClientId': self.client_id, 'ContainerName': name, 'IsLoadBalancer': load_balancer, 'Service': {'DictionaryItemId': self._container_service_id(service)}, 'LoadBalancerAlgorithm': {'DictionaryItemId': self._load_balancer_algorithm_id(lb_algorithm)}, 'IsSSLUsed': ssl, 'IsProxyCache': proxy_cache, 'MasterServiceId': master_id, 'PortNumber': port, 'SessionType': {'DictionaryItemId': self._session_type_id(session)}, 'IPVersion': {'DictionaryItemId': self._ip_version_id(ip_version)}, 'AutoScalingType': {'DictionaryItemId': self._autoscaling_id(autoscaling)}, 'IsServiceCheckAvailable': healthcheck }, virtualMachinesId=vm_ids) return result # TODO: allow to change only selected parameters, add more validation # without relying on the UpdateContainer API method. def Container_Edit( self, container_id, name, load_balancer, service, port, proxy_cache, ssl, healthcheck, master_id, session, lb_algorithm, ip_version, autoscaling): self._logon() if lb_algorithm == 'least_response_time' and service != 'HTTP' and service != 'HTTPS': raise OktawaveLRTNotAllowed() c = self.clients.call('GetContainer', containerId=container_id) c_simple = self._container_simple(container_id) c['ContainerName'] = name c['IsLoadBalancer'] = load_balancer c['Service'] = {'DictionaryItemId': self._container_service_id(service)} c['LoadBalancerAlgorithm'] = {'DictionaryItemId': self._load_balancer_algorithm_id(lb_algorithm)} c['IsSSLUsed'] = ssl c['IsProxyCache'] = proxy_cache c['MasterServiceId'] = master_id c['PortNumber'] = port c['SessionType'] = {'DictionaryItemId': self._session_type_id(session)} c['IPVersion'] = {'DictionaryItemId': self._ip_version_id(ip_version)} c['AutoScalingType'] = {'DictionaryItemId': self._autoscaling_id(autoscaling)} c['AutoScalingTypeDictId'] = self._autoscaling_id(autoscaling) c['IsServiceCheckAvailable'] = healthcheck self._d(c) self.clients.call('UpdateContainer', container=c, virtualMachinesId=[vm['VirtualMachineId'] for vm in c_simple['VirtualMachines']]) def _address_pool_id(self, name): pools = {'10.0.0.0/24': 278, '192.168.0.0/24': 279} return pools[name] def OPN_List(self): """Lists client's OPNs""" self._logon() vlans = self.clients.call('GetVlansByClientId', clientId=self.client_id) for v in vlans: yield { 'id': v['VlanId'], 'name': v['VlanName'], 'address_pool': DictionaryItem(v['AddressPool']), 'payment_type': DictionaryItem(v['PaymentType']) } def OPN_Get(self, opn_id): self._logon() v = self.clients.call('GetVlanById', vlanId=opn_id, clientId=self.client_id) vms = self.clients.call('GetVirtualMachineVlansByVlanId', vlanId=opn_id, clientId=self.client_id) return { 'id': v['VlanId'], 'name': v['VlanName'], 'address_pool': DictionaryItem(v['AddressPool']), 'payment_type': DictionaryItem(v['PaymentType']), 'vms': vms } def OPN_Create(self, name, address_pool): self._logon() self._d(self.client_object) return self.clients.call('CreateVlan', vlan={ 'VlanName': name, 'AddressPool': {'DictionaryItemId': self._address_pool_id(address_pool)}, 'OwnerClient': self.client_object['Client'], 'PaymentType': {'DictionaryItemId': DICT['OPN_PAYMENT_ID']}, 'CreationUserId': self.client_id }) def OPN_Delete(self, opn_id): self._logon() return self.clients.call('DeleteVlan', vlanId=opn_id, clientId=self.client_id) def OPN_AddOCI(self, opn_id, oci_id, ip_address): self._logon() oci = self.clients.call('GetVirtualMachineById', virtualMachineId=oci_id, clientId=self.client_id) vlan = self.clients.call('GetVlanById', vlanId=opn_id, clientId=self.client_id) for opn in oci['PrivateIpv4']: if opn['Vlan']['VlanId'] == opn_id: raise OktawaveOCIInOPN() oci['PrivateIpv4'].append({ 'PrivateIpAddress': ip_address, 'VirtualMachine': {'VirtualMachineName': oci['VirtualMachineName'], 'VirtualMachineId': oci_id, 'StatusDictId': oci['Status']['DictionaryItemId']}, 'Vlan': vlan, 'CreationDate': '/Date(' + str(int(time()) * 100) + '+0000)/', # for some reason API server does not fill this field automatically }) return self.clients.call('UpdateVirtualMachine', machine=oci, clientId=self.client_id, classChangeInScheduler=False) def OPN_RemoveOCI(self, opn_id, oci_id): self._logon() oci = self.clients.call('GetVirtualMachineById', virtualMachineId=oci_id, clientId=self.client_id) vlan = self.clients.call('GetVlanById', vlanId=opn_id, clientId=self.client_id) l1 = len(oci['PrivateIpv4']) oci['PrivateIpv4'] = filter(lambda x: x['Vlan']['VlanId'] != opn_id, oci['PrivateIpv4']) if l1 == len(oci['PrivateIpv4']): raise OktawaveOCINotInOPN() return self.clients.call('UpdateVirtualMachine', machine=oci, clientId=self.client_id, classChangeInScheduler=False) def OPN_Rename(self, opn_id, name): self._logon() vlan = self.clients.call('GetVlanById', vlanId=opn_id, clientId=self.client_id) vlan['VlanName'] = name return self.clients.call('UpdateVlan', vlan=vlan)