def _convert_data_elements(self): """Convert some data elements into variable types that make sense.""" try: self.data['created_at'] = Utils.convert_date(self.data['created_at']) # noqa except (ValueError, AttributeError, KeyError): pass try: self.data['last_login'] = Utils.convert_date(self.data['last_login']) # noqa except (ValueError, AttributeError, KeyError): pass try: self.data['last_run'] = Utils.convert_date(self.data['last_run']) except (ValueError, AttributeError, KeyError): pass try: self.data['updated_at'] = Utils.convert_date(self.data['updated_at']) # noqa except (ValueError, AttributeError, KeyError): pass try: self.data['last_installed'] = Utils.convert_date(self.data['last_installed']) # noqa except (ValueError, AttributeError, KeyError): pass try: self.data['id'] = int(self.data['id']) except (ValueError, AttributeError, KeyError): pass
def add(self, login_name, email=None): """Add one user. Args: login_name (str): The login id of the account, usually an email. email (str): The email of the account. If blank, will use login_name. Returns: int: The new user id from Skytap. Example: .. code-block:: python users = skytap.Users() new_user = users.add('*****@*****.**') print(users[new_user].login_name) """ Utils.info('Adding user: '******'.json' response = api.rest(url, data, 'POST') new_user = json.loads(response) self.refresh() if 'id' in new_user: return int(new_user['id']) Utils.warning('Trying to create user (' + login_name + '), but ' + 'got an unexpected return from Skytap. Response:\n' + response) return 0
def add(self, group, description=''): """Add one group. Args: group (str): The group name to add. description (str): The group description to add. Returns: int: The new group id from Skytap. Example: .. code-block:: python groups = skytap.Groups() new_group = groups.add('muppets', 'felt covered friends') print(groups[new_group].name) """ Utils.info('Adding group: ' + group) api = ApiClient() data = {"name": group, "description": description} url = self.url + '.json' response = api.rest(url, data, 'POST') new_group = json.loads(response) self.refresh() if 'id' in new_group: return int(new_group['id']) Utils.warning('Trying to create group (' + group + '), but ' + 'got an unexpected return from Skytap. Response:\n' + response) return 0
def add_user(self, user): """Add a :class:`User` to the group. Args: user (int): id of the user to add. Raises: TypeError: If user is not an :class:`int`. KeyError: If user is not in :class:`Users` list. Returns: bool: True if the user was added. Example: >>> groups = skytap.Groups() >>> users = skytap.Users() >>> for u in users: ... groups[12345].add(u.id) """ if type(user) is not int: raise TypeError('User must be an int.') Utils.info('Adding user ' + str(user) + ' to group: ' + self.name) api = ApiClient() api.rest(self.url + '/users/' + str(user) + '.json', {}, 'PUT') self.refresh() return user in self.users
def add_user(self, user): """Add a :class:`User` to the group. Args: user (int): id of the user to add. Raises: TypeError: If user is not an :class:`int`. KeyError: If user is not in :class:`Users` list. Returns: bool: True if the user was added. Example: >>> groups = skytap.Groups() >>> users = skytap.Users() >>> for u in users: ... groups[12345].add(u.id) """ if (type(user) is not int): raise TypeError('User must be an int.') Utils.info('Adding user ' + str(user) + ' to group: ' + self.name) api = ApiClient() user_json = api.rest(self.url + '/users/' + str(user) + '.json', {}, 'PUT') self.refresh() return user in self.users
def delete(self, key): """Delete key/value from environment's userdata. Args: key (str): The name of key to delete, along with value Returns: str: The response from Skytap, or "{}". """ new_content = "" del_key = False lines = self.contents.split("\n") for i in lines: if i != "": j = i.split() if len(j) > 0 and j[0] == (key + ":"): del_key = True else: new_content += (i.strip() + "\n") if del_key: Utils.info('Deleting key \"' + key + '\".') api = ApiClient() data = {"contents": "" + new_content} response = api.rest(self.url, data, 'POST') self.refresh() return response else: Utils.info('Key \"' + key + '\" already exists.') return "{}"
def delete_line(self, line): """Delete line from environment's userdata. Args: line (int): line number to delete. Returns: str: The response from Skytap. """ line = str(line) lines = self.contents.split("\n") new_content = "" for i in lines: if i != "": if i.strip() != line.strip(): new_content += (i.strip() + "\n") Utils.info('Removing line: \"' + str(line) + '\"') api = ApiClient() data = {"contents": new_content.lstrip()} response = api.rest(self.url, data, 'POST') self.refresh() return response
def add(self, key, value): """Add value to environment's userdata. Args: key (str): The name of the value's key. value (str): The value to add. Returns: str: The response from Skytap, or "{}". """ add_key = True lines = self.contents.split("\n") for i in lines: if i != "": j = i.split() if len(j) > 0 and j[0] == (key + ":"): add_key = False if add_key: Utils.info('Adding key \"' + key + '\" with value \"' '' + value + '\"') api = ApiClient() new_content = "" + key + ": " + value + "\n" + self.contents data = {"contents": new_content} response = api.rest(self.url, data, 'POST') self.data[key] = value self.refresh() return response else: Utils.info('Key \"' + key + '\" with value \"' + value + '\"' 'already exists.') return "{}"
def remove_user(self, user): """Remove a :class:`user` from the group. Args: user (int): id of the user to remove. Raises: TypeError: If user is not an :class:`int`. KeyError: If user is not in :class:`Users` list. Returns: bool: True if the user was removed. Example: >>> groups = skytap.Groups() >>> groups[1234].remove_user(12345) """ if (type(user) is not int): raise TypeError('User must be an int.') Utils.info('Removing user ' + str(user) + ' from group: ' + self.name) api = ApiClient() user_json = api.rest(self.url + '/users/' + str(user), {}, 'DELETE') self.refresh() return user not in self.users
def remove_user(self, user): """Remove a :class:`user` from the group. Args: user (int): id of the user to remove. Raises: TypeError: If user is not an :class:`int`. KeyError: If user is not in :class:`Users` list. Returns: bool: True if the user was removed. Example: >>> groups = skytap.Groups() >>> groups[1234].remove_user(12345) """ if type(user) is not int: raise TypeError('User must be an int.') Utils.info('Removing user ' + str(user) + ' from group: ' + self.name) api = ApiClient() api.rest(self.url + '/users/' + str(user), {}, 'DELETE') self.refresh() return user not in self.users
def delete(self): """Delete this export job.""" Utils.info('Deleting job ' + str(self.id) + ' from queue.') api = ApiClient() response = api.rest(self.url, {}, 'DELETE') return response
def delete(self): """Delete a VM. In general, it'd seem wise not to do this very often. """ Utils.info('Deleting VM: ' + str(self.id) + '(' + self.name + ')') api = ApiClient() response = api.rest(self.url, {}, 'DELETE') return response
def delete(self): """Delete the group.""" Utils.info('Deleting group: ' + str(self.id) + ' (' + self.name + ')') api = ApiClient() response = api.rest(self.url_v1, {}, 'DELETE') return response
def disable(self, label_id): """Disable a label category (the closest thing to deleting).""" if not self.url.endswith("label_categories"): return "Can only disable label categories." Utils.info("Disabling label category of id " + str(label_id) + ".") api = ApiClient() data = {"enabled": "False"} response = api.rest(self.url + "/" + str(label_id), data, "PUT") self.refresh() return response
def enable(self, label_id): """Enable a label category.""" if not self.url.endswith("label_categories"): return "Can only enable label categories." Utils.info("Enabling label category of id " + str(label_id) + ".") api = ApiClient() data = {"enabled": "True"} response = api.rest(self.url + "/" + str(label_id), data, "PUT") self.refresh() return response
def add(self, value, category): """Add label to environment or VM.""" if self.url.endswith("label_categories"): return "Cannot add label. Did you mean to use \"create()\"?" Utils.info("Adding Label to " + category + " with value " + value + ".") api = ApiClient() data = [{"label_category": category, "value": value}] response = api.rest(self.url, data, "PUT") self.refresh() return response
def create(self, name, single_value): """Create label that is single or multi-valued.""" if not self.url.endswith("label_categories"): return "Cannot create label. Did you mean to use \"add()\"?" Utils.info("Creating Label: " + name + "" ". Single-value: " + str(single_value)) api = ApiClient() data = [{"name": name, "single-value": single_value}] response = api.rest(self.url, data, "POST") self.refresh() return response
def __getattr__(cls, key): """Make the config values accessible. This allows all config values to be available via calls like: Config.user """ if key not in cls.config_data: # These are called during nose setup before logging is turned off # during testing. Not the best, but tests look better with these # supressed. if key not in ['__test__', 'address', 'im_class', '__self__']: Utils.error("Tried to access config value '" + str(key) + "', which doesn't exist.") raise AttributeError return cls.config_data[key]
def delete_all(self): """Delete all notes. Returns: int: count of deleted notes. Use with care! """ Utils.debug('Deleting all notes.') keys = self.data.keys() count = len(keys) for key in keys: self.delete(self.data[key]) self.refresh() return count
def add(self, note): """Add one note. Args: note (str): The note text to add. Returns: str: The response from Skytap, typically the new note. """ Utils.info('Adding note: ' + note) api = ApiClient() data = {"text": note} response = api.rest(self.url, data, 'POST') self.refresh() return response
def delete(self, transfer_user): """Delete the user.""" if isinstance(transfer_user, User): transfer_user = transfer_user.id if not isinstance(transfer_user, int): raise TypeError('transfer_user must be a User or int.') Utils.info('Deleting user: '******' (' + self.name + ') and transferring ' + 'resources to user id: ' + str(transfer_user)) api = ApiClient() transfer = {"transfer_user_id": str(transfer_user)} response = api.rest(self.url, transfer, 'DELETE') return response
def create(self, vmid): """Create an export job from a vm in a template.""" if type(vmid) is not int: raise TypeError('vmid must be an int') Utils.info('creating export job for VM ' + str(vmid)) api = ApiClient() data = {"vm_id": vmid} url = self.url + '.json' response = api.rest(url, data, "POST") exports = json.loads(response) self.refresh() if 'id' in exports: return int(exports['id']) Utils.warning('Trying to create new export job from VM ' + vmid + ', but got an unexpected response from Skytap. ' + 'Response:\n' + response) return 0
def add_line(self, text, line=-1): """Add line to environment's userdata. Args: text (str): line of text to be added. (Required) line (int): line number to add to. If too large, default to last. Returns: str: The response from Skytap. """ try: line = int(line) except ValueError: return "{}" # Not an integer lines = self.contents.split("\n") new_content = "" line_found = False count = 0 for i in lines: if i != "": if line == count: new_content += (text.strip() + "\n") new_content += (i.strip() + "\n") line_found = True else: new_content += (i.strip() + "\n") count += 1 if not line_found: new_content += (text.strip() + "\n") Utils.info('Adding line: \"' + text + '\"') api = ApiClient() data = {"contents": new_content} response = api.rest(self.url, data, 'POST') self.refresh() return response
def _check_response(self, resp, attempts=1): """Return true if the rseponse a good/reasonable one. If the HTTP status code is in the 200s, return True, otherwise try to determine what happened. If we're asked to retry, politely wait the appropraite amount of time and retry, otherwise, wait the retry_wait amount of time. Fail (return False) if we've exceeded our retry amount. """ if resp is None: raise ValueError('A response wasn\'t received') if 200 <= resp.status_code < 300: return True # If we made it this far, we need to handle an exception if attempts >= Config.max_http_attempts or (resp.status_code != 429 and resp.status_code != 423): raise Exception(json.loads(resp.text)) if resp.status_code == 423: # "Busy" if 'Retry-After' in resp.headers: Utils.info('Received HTTP 423. Retry-After set to ' + resp.headers['Retry-After'] + ' sec. Waiting to retry.') # noqa time.sleep(int(resp.headers['Retry-After']) + 1) else: Utils.info('Received HTTP 429. Too many requests. Waiting to retry.') # noqa time.sleep(Config.retry_wait) return False # Assume we're going to retry with exponential backoff # Should only get here on a 429 "too many requests" but it's # not clear from Skytap what their limits are on when we should retry. time.sleep(2 ** (attempts - 1)) return False
def delete(self, note): """Delete one note. Args: note: The :class:`~skytap.models.Note` to delete. Returns: str: The response from Skytap. Raises: TypeError: If note is not a Note object. """ if note is None: return False if not isinstance(note, Note): raise TypeError Utils.info('Deleting note ID: ' + str(note.id)) api = ApiClient() url = self.url.replace('.json', '/' + str(note.id)) response = api.rest(url, {}, 'DELETE') self.refresh() return response
def _check_response(self, resp, attempts=1): """Return true if the rseponse a good/reasonable one. If the HTTP status code is in the 200s, return True, otherwise try to determine what happened. If we're asked to retry, politely wait the appropraite amount of time and retry, otherwise, wait the retry_wait amount of time. Fail (return False) if we've exceeded our retry amount. """ if resp is None: raise ValueError('A response wasn\'t received') if 200 <= resp.status_code < 300: return True # If we made it this far, we need to handle an exception if attempts >= Config.max_http_attempts or (resp.status_code != 429 and resp.status_code != 423): Utils.error('Error recieved in API return. Response code: ' + str(resp.status_code) + '. Reponse text: ' + resp.text) # print(resp.headers) error_response = json.loads(resp.text) error_response['status_code'] = resp.status_code raise Exception(error_response) if resp.status_code == 423: # "Busy" if 'Retry-After' in resp.headers: Utils.info('Received HTTP 423. Retry-After set to ' + resp.headers['Retry-After'] + ' sec. Waiting to retry.') # noqa time.sleep(int(resp.headers['Retry-After']) + 1) else: Utils.info( 'Received HTTP 429. Too many requests. Waiting to retry.' ) # noqa time.sleep(Config.retry_wait) return False # Assume we're going to retry with exponential backoff # Should only get here on a 429 "too many requests" but it's # not clear from Skytap what their limits are on when we should retry. time.sleep(2**(attempts - 1)) return False
# for most things you'd want to do. config_data = INITIAL_CONFIG # Load config values and set up the class. for key in Config: env_val = "SKYTAP_" + key.upper() if env_val in os.environ: Config.config_data[key] = os.environ[env_val] try: Config.config_data[key] = int(Config.config_data[key]) except ValueError: pass if os.environ.get('READTHEDOCS', None) != 'True': if Config.base_url != 'https://cloud.skytap.com': Utils.warning('Base URL is not Skytap\'s recommended value. ' + 'This very likely will break things.') if len(Config.token) == 0: Utils.error('No environment variable SKYTAP_TOKEN found. ' + 'Set this variable and try again.') raise ValueError if len(Config.user) == 0: Utils.error('No environment variable SKYTAP_USER found. ' + 'Set this variable and try again.') raise ValueError Utils.log_level(Config.log_level)
def delete(self): """Delete a service. Cannot be undone!""" Utils.info('Deleting published service: ' + str(self.id)) api = ApiClient() response = api.rest(self.url, {}, 'DELETE') return response
def _convert_date(self, element): """Try to convert element to a date.""" try: self.data[element] = Utils.convert_date(self.data[element]) except (ValueError, AttributeError, KeyError): pass
def test_config_value_doesnt_exist(): """Test that an invalid config request raises an AttributeError.""" log_lvl = Utils.log_level() Utils.log_level(50) place_holder = Config.this_value_should_not_exist Utils.log_level(log_lvl)
def delete(self): """Delete a service. Warning: Cannot be undone.""" Utils.info('Deleting published service: ' + str(self.id)) api = ApiClient() response = api.rest(self.url, {}, 'DELETE') return response