class Searcher(dict): """ Class responsible for search methods on strings """ def __init__(self): self.log = Logger().get_logger() def wrap(self, regular_expression): """ Turns windows/search type of regexp into python re regexp. It adds explicit begining '^' and end '$' to matcher and converts all wildcards to re wildcard '(.*)' :rtype: str :param string: regular_expression """ return '^' + regular_expression.replace('*', '(.*)') + '$' def match(self, string, regular_expression): """ Match string with a regular expression :rtype: bool :param string: given string for matching :param regular_expression: expression to be run against the given string """ regular_expression = self.wrap(regular_expression) self.log.info("Trying to match regexp: %s against string: %s" % (regular_expression, string)) regex = re.compile(regular_expression) if re.match(regex, string): return True else: return False
class Configurable(Communicable): """ Should be responsible for sending the "configure" action """ def __init__(self): self.log = Logger().get_logger() super(Configurable, self).__init__() def send_configure(self): configure_route = Path().get_route_for( self.class_name, 'configure') % self.data_hash['id'] if self._send_configure_request(configure_route): self.log.info("Successfully sent 'configure' for url=%s" % self.absolute_url) return True else: return False def _send_configure_request(self, configure_route): """ Makes a 'configure' request """ url = "%s%s" % (self.uri, configure_route) self.log.info("Sending configure GET request to url=%s" % url) try: response = self._api_get_json(url) except urllib2.HTTPError, e: response = None self.log.error("Could not send configure GET request because %s" % e) if response: return True else: return False
class Shutdownable(Communicable): """ Should be responsible for sending "shutdown" command to live activities, controllers, spaces and live groups. """ def __init__(self): self.log = Logger().get_logger() super(Shutdownable, self).__init__() def send_shutdown(self): shutdown_route = Path().get_route_for(self.class_name, 'shutdown') % self.data_hash['id'] if self._send_shutdown_request(shutdown_route): self.log.info("Successfully sent shutdown for url=%s" % self.absolute_url) return True else: return False def _send_shutdown_request(self, shutdown_route): """ Makes a shutdown request """ url = "%s%s" % (self.uri, shutdown_route) self.log.info("Sending 'shutdown' GET request to url=%s" %url) try: response = self._api_get_json(url) except urllib2.HTTPError, e: response = None self.log.error("Could not send 'shutdown' GET request because %s" % e) if e=='HTTP Error 500: No connection to controller in 5000 milliseconds': raise CommunicableException('HTTP Error 500: No connection to controller in 5000 milliseconds') if response: return True else: return False
class Deletable(Communicable): """ @summary: Should be responsible for the deletion of an object """ def __init__(self): self.log = Logger().get_logger() super(Deletable, self).__init__() def send_delete(self): """ @summary: sends the "delete" GET request to a route """ delete_route = Path().get_route_for(self.class_name, 'delete') % self.data_hash['id'] if self._send_delete_request(delete_route): self.log.info("Successfully sent 'delete' to url=%s" % self.absolute_url) return True else: return False def _send_delete_request(self, delete_route): """ @rtype: bool """ url = "%s%s" % (self.uri, delete_route) self.log.info("Sending 'delete' to url=%s" %url) try: response = self._api_get_html(url) except urllib2.HTTPError, e: response = None self.log.error("Could not send 'delete' because %s" % e) if response: return True else: return False
class Deployable(Communicable): """ Should be responsible for sending the "deploy" action """ def __init__(self): self.log = Logger().get_logger() super(Deployable, self).__init__() def send_deploy(self): deploy_route = Path().get_route_for(self.class_name, 'deploy') % self.data_hash['id'] if self._send_deploy_request(deploy_route): self.log.info("Successfully sent 'deploy' for url=%s" % self.absolute_url) return True else: return False def _send_deploy_request(self, deploy_route): """ Makes a 'deploy' request """ url = "%s%s" % (self.uri, deploy_route) self.log.info("Sending deploy GET request to url=%s" %url) try: response = self._api_get_json(url) except urllib2.HTTPError, e: response = None self.log.error("Could not send deploy GET request because %s" % e) if response: return True else: return False
class Searcher(dict): """ Class responsible for search methods on strings """ def __init__(self): self.log = Logger().get_logger() def wrap(self, regular_expression): """ Turns windows/search type of regexp into python re regexp. It adds explicit begining '^' and end '$' to matcher and converts all wildcards to re wildcard '(.*)' :rtype: str :param string: regular_expression """ return '^' + regular_expression.replace('*', '(.*)') + '$' def match(self, string, regular_expression): """ Match string with a regular expression :rtype: bool :param string: given string for matching :param regular_expression: expression to be run against the given string """ regular_expression = self.wrap(regular_expression) self.log.info("Trying to match regexp: %s against string: %s" % (regular_expression, string)) regex = re.compile(regular_expression) if re.match(regex, string): return True else: return False
class Startupable(Communicable): """ @summary: Should be responsible for sending "startup" command to live activities, controllers, spaces and live groups. """ def __init__(self): self.log = Logger().get_logger() super(Startupable, self).__init__() def send_startup(self): startup_route = Path().get_route_for(self.class_name, 'startup') % self.data_hash['id'] if self._send_startup_request(startup_route): self.log.info("Successfully sent 'startup' for url=%s" % self.absolute_url) return True else: return False def _send_startup_request(self, startup_route): """ @summary: makes a startup request """ url = "%s%s" % (self.uri, startup_route) self.log.info("Sending 'startup' GET request to url=%s" %url) try: response = self._api_get_json(url) except urllib2.HTTPError, e: response = None self.log.error("Could not send 'startup' GET request because %s" % e) if response: return True else: return False
class Configurable(Communicable): """ @summary: Should be responsible for sending the "configure" action """ def __init__(self): self.log = Logger().get_logger() super(Configurable, self).__init__() def send_configure(self): configure_route = Path().get_route_for(self.class_name, 'configure') % self.data_hash['id'] if self._send_configure_request(configure_route): self.log.info("Successfully sent 'configure' for url=%s" % self.absolute_url) return True else: return False def _send_configure_request(self, configure_route): """ @summary: makes a 'configure' request """ url = "%s%s" % (self.uri, configure_route) self.log.info("Sending configure GET request to url=%s" %url) try: response = self._api_get_json(url) except urllib2.HTTPError, e: response = None self.log.error("Could not send configure GET request because %s" % e) if response: return True else: return False
class Serializer(object): """ Should be responsible for representing instances of classes in desired form """ def __init__(self): self.log = Logger().get_logger() self.log.debug("Instantiated Serializer with data %s" % self.data_hash) pass def __repr__(self): return str(self.data_hash) def __str__(self): return self.data_hash def _assign_attributes(self): """ Should change the key names from original API keys to the ones we want :return: None """ for new_key, old_key in self.attributes.iteritems(): try: self.data[new_key] = self.data_hash[old_key] except SerializerException, e: """ possible do something fancy here """ except Exception, e: self.log.info("Could not assign attribute %s while operating on Object: %s because %s" % (old_key, self.data_hash, e))
class Space(Fetchable, Statusable, Deletable, Shutdownable, Startupable, Activatable, Configurable, Metadatable, Deployable, Cleanable): """ @summary: Space is a LiveActivityGroup aggregator """ def __init__(self, data_hash, uri, name=None, ): self.log = Logger().get_logger() self.data_hash = data_hash self.uri = uri self.absolute_url = self._get_absolute_url() self.class_name = self.__class__.__name__ super(Space, self).__init__() self.log.info("Instantiated Activity object with url=%s" % self.absolute_url) def __repr__(self): return str(self.data_hash) def __str__(self): return self.data_hash def create(self, live_activity_group_name, live_activity_names): """ @summary: Should be responsible for creating space @param live_activity_group_name: string @param live_activity_names: list of existing names """ raise NotImplementedError def to_json(self): """ @summary: Should selected attributes in json form defined by the template """ self.serializer = SpaceSerializer(self.data_hash) return self.serializer.to_json() def id(self): return self.data_hash['id'] def name(self): """ @param: Should return live activity name """ return self.data_hash['name'] def description(self): """ @param: Should return Space description """ return self.data_hash['description'] """ Private methods below """ def _get_absolute_url(self): live_activity_group_id = self.data_hash['id'] url = "%s/space/%s/view.json" % (self.uri, live_activity_group_id) return url
class Statusable(Communicable): """ Should be responsible for _refreshing_ status of the object, which means that it will send "status" command to IS Controllers. In order to fetch the fresh and most up-to-date status you should use .fetch() method on the object. """ def __init__(self): self.log = Logger().get_logger() super(Statusable, self).__init__() def send_status_refresh(self): """ Extracts self.data_hash and self.class_name from children class and finds out to which route send GET request to ands sends it """ refresh_route = Path().get_route_for(self.class_name, 'status') % self.data_hash['id'] if self._send_status_refresh(refresh_route): self.log.info("Successfully refreshed status for url=%s" % self.absolute_url) return True else: return False def _send_status_refresh(self, refresh_route): """ Should tell master to retrieve status info from controller so master has the most up to date info from the controller :param refresh_route: status.json route for specific class :rtype: bool """ url = "%s%s" % (self.uri, refresh_route) self.log.info("Sending status refresh to url=%s" % url) try: response = self._api_get_json(url) except urllib2.HTTPError, e: response = None self.log.error("Could not send status refresh because %s" % e) if response: return True else: return False
class Statusable(Communicable): """ Should be responsible for _refreshing_ status of the object, which means that it will send "status" command to IS Controllers. In order to fetch the fresh and most up-to-date status you should use .fetch() method on the object. """ def __init__(self): self.log = Logger().get_logger() super(Statusable, self).__init__() def send_status_refresh(self): """ Extracts self.data_hash and self.class_name from children class and finds out to which route send GET request to ands sends it """ refresh_route = Path().get_route_for(self.class_name, 'status') % self.data_hash['id'] if self._send_status_refresh(refresh_route): self.log.info("Successfully refreshed status for url=%s" % self.absolute_url) return True else: return False def _send_status_refresh(self, refresh_route): """ Should tell master to retrieve status info from controller so master has the most up to date info from the controller :param refresh_route: status.json route for specific class :rtype: bool """ url = "%s%s" % (self.uri, refresh_route) self.log.info("Sending status refresh to url=%s" %url) try: response = self._api_get_json(url) except urllib2.HTTPError, e: response = None self.log.error("Could not send status refresh because %s" % e) if response: return True else: return False
class Metadatable(Communicable): """ Should be responsible for setting metadata """ def __init__(self): self.log = Logger().get_logger() super(Metadatable, self).__init__() def set_metadata(self, metadata_dictionary): """ Accepts dictionary of keys that will be unpacked to "key=value" strings and makes a request overwriting any previous metadata :rtype: bool :param metadata_args: Dictionary with keys and values """ metadata = {"values": self._unpack_metadata(metadata_dictionary)} self.log.info("Updating metadata of %s with %s" % (self.class_name, metadata)) metadata_route = Path().get_route_for( self.class_name, 'metadata') % self.data_hash['id'] if self._send_metadatable_request(metadata_route, metadata): self.log.info("Successfully sent metadata for url=%s" % self.absolute_url) return True else: return False def _unpack_metadata(self, metadata_dictionary): """ Accepts dictionary and converts it to string :rtype: string :param metadata_dictionary: dict containing metadata """ metadata_text = "" try: for key, value in metadata_dictionary.iteritems(): metadata_text = metadata_text + ("\r\n") + key + "=" + value return metadata_text except Exception, e: self.log.error( "Could not unpack supplied metadata dictionary because %s" % e) raise
class Startupable(Communicable): """ Should be responsible for sending "startup" command to live activities, controllers, spaces and live groups. """ def __init__(self): self.log = Logger().get_logger() super(Startupable, self).__init__() def send_startup(self): startup_route = Path().get_route_for(self.class_name, 'startup') % self.data_hash['id'] if self._send_startup_request(startup_route): self.log.info("Successfully sent 'startup' for url=%s" % self.absolute_url) return True else: return False def _send_startup_request(self, startup_route): """ Makes a startup request """ url = "%s%s" % (self.uri, startup_route) self.log.info("Sending 'startup' GET request to url=%s" % url) try: response = self._api_get_json(url) except urllib2.HTTPError, e: response = None self.log.error("Could not send 'startup' GET request because %s" % e) if e == 'HTTP Error 500: No connection to controller in 5000 milliseconds': raise CommunicableException( 'HTTP Error 500: No connection to controller in 5000 milliseconds' ) if response: return True else: return False
class Metadatable(Communicable): """ @summary: Should be responsible for setting metadata """ def __init__(self): self.log = Logger().get_logger() super(Metadatable, self).__init__() def set_metadata(self, metadata_dictionary): """ @summary: Accepts dictionary of keys that will be unpacked to "key=value" strings and makes a request overwriting any previous metadata @rtype: bool @param metadata_args: Dictionary with keys and values """ metadata = {"values" : self._unpack_metadata(metadata_dictionary)} self.log.info("Updating metadata of %s with %s" % (self.class_name, metadata)) metadata_route = Path().get_route_for(self.class_name, 'metadata') % self.data_hash['id'] if self._send_metadatable_request(metadata_route, metadata): self.log.info("Successfully sent metadata for url=%s" % self.absolute_url) return True else: return False def _unpack_metadata(self, metadata_dictionary): """ @summary: accepts dictionary and converts it to string @rtype: string @param metadata_dictionary: dict containing metadata """ metadata_text = "" try: for key, value in metadata_dictionary.iteritems(): metadata_text = metadata_text + ("\r\n") + key + "=" + value return metadata_text except Exception, e: self.log.error("Could not unpack supplied metadata dictionary because %s" % e) raise
class Deletable(Communicable): """ Should be responsible for the deletion of an object """ def __init__(self): self.log = Logger().get_logger() super(Deletable, self).__init__() def send_delete(self): """ Sends the "delete" GET request to a route """ delete_route = Path().get_route_for(self.class_name, 'delete') % self.data_hash['id'] if self._send_delete_request(delete_route): self.log.info("Successfully sent 'delete' to url=%s" % self.absolute_url) return True else: return False def _send_delete_request(self, delete_route): """ :rtype: bool """ url = "%s%s" % (self.uri, delete_route) self.log.info("Sending 'delete' to url=%s" % url) try: response = self._api_get_html(url) except urllib2.HTTPError, e: response = None self.log.error("Could not send 'delete' because %s" % e) if response: return True else: return False
class Cleanable(Communicable): """ Should be responsible for permanent clean and cleaning the tmp """ def __init__(self): self.log = Logger().get_logger() super(Cleanable, self).__init__() def send_clean_permanent(self): configure_route = Path().get_route_for( self.class_name, 'clean_permanent') % self.data_hash['id'] if self._send_cleanable_request(configure_route): self.log.info("Successfully sent 'clean_permanent' for url=%s" % self.absolute_url) return True else: return False def send_clean_tmp(self): configure_route = Path().get_route_for( self.class_name, 'clean_tmp') % self.data_hash['id'] if self._send_cleanable_request(configure_route): self.log.info("Successfully sent 'clean_tmp' for url=%s" % self.absolute_url) return True else: return False def _send_cleanable_request(self, cleanable_route): """ Makes a cleanable request """ url = "%s%s" % (self.uri, cleanable_route) self.log.info("Sending cleanable GET request to url=%s" % url) try: response = self._api_get_json(url) except urllib2.HTTPError, e: response = None self.log.error("Could not send cleanable GET request because %s" % e) if e == 'HTTP Error 500: No connection to controller in 5000 milliseconds': raise CommunicableException( 'HTTP Error 500: No connection to controller in 5000 milliseconds' ) if response: return True else: return False
class Cleanable(Communicable): """ Should be responsible for permanent clean and cleaning the tmp """ def __init__(self): self.log = Logger().get_logger() super(Cleanable, self).__init__() def send_clean_permanent(self): configure_route = Path().get_route_for(self.class_name, 'clean_permanent') % self.data_hash['id'] if self._send_cleanable_request(configure_route): self.log.info("Successfully sent 'clean_permanent' for url=%s" % self.absolute_url) return True else: return False def send_clean_tmp(self): configure_route = Path().get_route_for(self.class_name, 'clean_tmp') % self.data_hash['id'] if self._send_cleanable_request(configure_route): self.log.info("Successfully sent 'clean_tmp' for url=%s" % self.absolute_url) return True else: return False def _send_cleanable_request(self, cleanable_route): """ Makes a cleanable request """ url = "%s%s" % (self.uri, cleanable_route) self.log.info("Sending cleanable GET request to url=%s" %url) try: response = self._api_get_json(url) except urllib2.HTTPError, e: response = None self.log.error("Could not send cleanable GET request because %s" % e) if e=='HTTP Error 500: No connection to controller in 5000 milliseconds': raise CommunicableException('HTTP Error 500: No connection to controller in 5000 milliseconds') if response: return True else: return False
class Connectable(Communicable): """ Should be responsible for connecting/disconnecting space controllers """ def __init__(self): self.log = Logger().get_logger() super(Connectable, self).__init__() def send_connect(self): connect_route = Path().get_route_for(self.class_name, 'connect') % self.data_hash['id'] if self._send_connectable_request(connect_route): self.log.info("Successfully sent 'connect' for url=%s" % self.absolute_url) return True else: return False def send_disconnect(self): disconnect_route = Path().get_route_for(self.class_name, 'disconnect') % self.data_hash['id'] if self._send_connectable_request(disconnect_route): self.log.info("Successfully sent 'disconnect' for url=%s" % self.absolute_url) return True else: return False def _send_connectable_request(self, connectable_route): """ Makes a connectable request """ url = "%s%s" % (self.uri, connectable_route) self.log.info("Sending connectable GET request to url=%s" %url) try: response = self._api_get_json(url) except urllib2.HTTPError, e: response = None self.log.error("Could not send connectable GET request because %s" % e) if response: return True else: return False
class Communicable(object): def __init__(self): """ @summary: Should be responsible for communication with the API """ self.log = Logger().get_logger() def _compose_url(self, uri, class_name=None, method_name=None, context=None, action=None): """ @summary: Should compose URL trying to do that in two steps: 1. return if object that tries to retrieve the url has route that is already staticaly defined 2. try to compose the custom route on the basis of URL data @rtype: string """ if class_name and method_name: self.log.info("Composing url for class_name '%s' and method name '%s'" % (class_name, method_name)) static_route = Path().get_route_for(class_name, method_name) if static_route : self.log.info("Returned auto url %s" % (static_route)) url = "%s%s" % (uri, static_route) return url elif context and action: url = "%s%s%s" % (uri, context, action) self.log.info("Composed url %s" % (url)) return url else: self.log.info("Could not compose an url.") raise CommunicableException def _urlopen(self, url, data=None): """Helper for opening urls.""" return urllib2.urlopen(url, data) def _api_get_json(self, url): """ @summary: Sends a json request to the master. Returns only ['data'] part of the json response @rtype: dict or bool """ response = urllib2.urlopen(url) data = json.loads(response.read()) try: out_data = data['data'] except Exception: out_data = None if data['result'] != 'success': self.log.info("Could not retrieve data for URL=%s" % url) return False if out_data: return out_data else: return True def _api_get_html(self, url): """ @summary: Sends a request to the master, returns True if 200, False if anything else. @rtype: bool """ response = urllib2.urlopen(url) data = response.read() if response.getcode() == 200: return True else: return False def _api_post_json(self, url, payload, file_handler=None): """ @summary: Sends data to the master. @rtype: string or False @param payload: dictionary containing data to send @param url: string containing url that we talk to @param file_handler: path to local zipfile - if provided, a multi-part post will be sent to the URL """ if file_handler: head, tail = os.path.split(file_handler.name) file_name = tail file_content_type = "application/zip" files = {"activityFile" : (file_name , file_handler, file_content_type)} else: files = None self.log.info("Doing a POST request to %s with payload %s" %(url, payload)) session = requests.session() get_response = session.get(url) query = urlparse.urlparse(get_response.url).query cookies = {"JSESSIONID" : session.cookies['JSESSIONID']} url = url + "?" + query post_response = session.post(url=url, cookies=cookies, data=payload, files=files) if post_response.status_code == 200: self.log.info("_api_post_json returned 200 with post_response.url=%s" % post_response.url) return post_response else: self.log.info("_api_post_json returned post_response.status_code %s" % post_response.status_code) return False def _api_post_json_no_cookies(self, url, payload, file_handler=None): """ @summary: Sends data to the master without looking for cookies @rtype: string or False @param payload: dictionary containing data to send @param url: string containing url that we talk to @param file_handler: path to local zipfile - if provided, a multi-part post will be sent to the URL """ if file_handler: head, tail = os.path.split(file_handler.name) file_name = tail file_content_type = "application/zip" files = {"activityFile" : (file_name , file_handler, file_content_type)} else: files = None self.log.info("Doing a POST request to %s with payload %s" %(url, payload)) session = requests.session() get_response = session.get(url) query = urlparse.urlparse(get_response.url).query url = url + "?" + query post_response = session.post(url=url, data=payload, files=files) if post_response.status_code == 200: self.log.info("_api_post_json returned 200 with post_response.url=%s" % post_response.url) return post_response else: self.log.info("_api_post_json returned post_response.status_code %s" % post_response.status_code) return False def _api_post_html(self, command, query=None, data=None): """Sends data to the master.""" raise NotImplementedError def json_raw(self): """ @summary: returns raw unformatted/unmapped json from Master API @rtype: dict """ return self.data
class LiveActivityGroup(Fetchable, Statusable, Deletable, Shutdownable, Startupable, Activatable, Configurable, Metadatable, Deployable): """ @summary: Should be responsible for managing single live activity group """ def __init__(self, data_hash=None, uri=None): self.log = Logger().get_logger() self.class_name = self.__class__.__name__ super(LiveActivityGroup, self).__init__() if data_hash==None and uri==None: self.log.info("No data_hash and uri provided for LiveActivityGroup constructor, assuming creation") else: self.data_hash = data_hash self.uri = uri self.absolute_url = self._get_absolute_url() self.log.info("Instantiated Activity object with url=%s" % self.absolute_url) def __repr__(self): return str(self.data_hash) def new(self, uri, constructor_args): """ @summary: used to create new live activity group through API and set the "uri" so that we can operate on this instance of LiveActivityGroup right away after .new() returns True @param constructor_args: dictionary with following structure: { 'liveActivityGroup.name' : 'live_activity_group_name', 'liveActivityGroup.description' : 'live_activity_group_description', '_eventId_save' : 'Save', 'liveActivityIds' : [1,2,666] } @param uri: "http://some_server/prefix (passed by master)" @rtype: new LiveActivityGroup object or False """ self.log.info("Creating new LiveActivityGroup with arguments: %s" % constructor_args) route = Path().get_route_for('LiveActivityGroup', 'new') url = "%s%s" % (uri, route) request_response = self._api_post_json(url, constructor_args) if request_response.url: self.absolute_url = request_response.url.replace("view.html", "view.json") self.fetch() self.log.info("Created new LiveActivityGroup with url=%s, data_hash is now %s" % (self.absolute_url, self.data_hash)) return self else: self.log.info("Created new LiveActivityGroup %s but returned False" % self) return False def to_json(self): """ Should selected attributes in json form defined by the template """ self.serializer = LiveActivityGroupSerializer(self.data_hash) return self.serializer.to_json() def id(self): return self.data_hash['id'] def name(self): """ Should return live activity name""" return self.data_hash['name'] def live_activities(self): """ Should return live activity name""" live_activities = [] data = self.data_hash['liveActivities'] for live_activity in data: live_activity_group_live_activity = {"id" : live_activity['id'], "description" : live_activity['description'], "name" : live_activity['name'] } live_activities.append(live_activity_group_live_activity) return live_activities def description(self): """ Should return Activity description """ return self.data_hash['description'] """ Private methods below """ def _get_absolute_url(self): live_activity_group_id = self.data_hash['id'] url = "%s/liveactivitygroup/%s/view.json" % (self.uri, live_activity_group_id) return url
class Configable(Communicable): """ Should be responsible for setting the 'Edit config' section. Name of thix mixin is such because there's already "configure" action for live activities """ def __init__(self): self.log = Logger().get_logger() super(Configable, self).__init__() def get_config(self): config_route = Path().get_route_for(self.class_name, 'config') % self.data_hash['id'] self.log.info("Getting config of %s" % (self.class_name)) response = self._send_configable_get_request(config_route) if response: self.log.info("Successfully got config from url=%s" % self.absolute_url) return self._scrap_config(response) else: return False def set_config(self, config_dictionary): """ Accepts dictionary of keys that will be unpacked to "key=value" strings and makes a request overwriting any previous config :rtype: bool :param config_dictionary: Dictionary with keys and values """ config = {"values" : self._unpack_config(config_dictionary)} self.log.info("Updating config of %s with %s" % (self.class_name, config)) config_route = Path().get_route_for(self.class_name, 'config') % self.data_hash['id'] if self._send_configable_set_request(config_route, config): self.log.info("Successfully sent config for url=%s" % self.absolute_url) return True else: return False def _scrap_config(self, html): import BeautifulSoup soup = BeautifulSoup.BeautifulSoup(html) self.log.info("Received config response: %s" % soup) textarea = soup.findAll('textarea')[0].text.split('\n') config_dict = self._pack_config(textarea) self.log.info("Scrapped config: %s" % config_dict) return config_dict def _pack_config(self, config_list): """ Accepts list of strings and converts it into dictionary :rtype: dict :param config_string: string containing scraped config """ config_dict = {} self.log.info("Textarea: %s" % config_list) try: for config_item in config_list: key, value = config_item.split('=') self.log.info("Assigning %s to %s" % (key,value)) config_dict[key] = value except: self.log.info("Could not do a _pack_config for scraped config on item: %s for config_list %s" % (config_item, config_list)) config_dict = {} return config_dict def _unpack_config(self, config_dictionary): """ Accepts dictionary and converts it to string :rtype: string :param config_dictionary: dict containing config """ config_text = "" try: for key, value in config_dictionary.iteritems(): config_text = config_text + ("\r\n") + key + "=" + value return config_text except Exception, e: self.log.error("Could not unpack supplied config dictionary because %s" % e) raise
class Configable(Communicable): """ Should be responsible for setting the 'Edit config' section. Name of thix mixin is such because there's already "configure" action for live activities """ def __init__(self): self.log = Logger().get_logger() super(Configable, self).__init__() def get_config(self): config_route = Path().get_route_for(self.class_name, 'config') % self.data_hash['id'] self.log.info("Getting config of %s" % (self.class_name)) response = self._send_configable_get_request(config_route) if response: self.log.info("Successfully got config from url=%s" % self.absolute_url) return self._scrap_config(response) else: return False def set_config(self, config_dictionary): """ Accepts dictionary of keys that will be unpacked to "key=value" strings and makes a request overwriting any previous config :rtype: bool :param config_dictionary: Dictionary with keys and values """ config = {"values": self._unpack_config(config_dictionary)} self.log.info("Updating config of %s with %s" % (self.class_name, config)) config_route = Path().get_route_for(self.class_name, 'config') % self.data_hash['id'] if self._send_configable_set_request(config_route, config): self.log.info("Successfully sent config for url=%s" % self.absolute_url) return True else: return False def _scrap_config(self, html): import BeautifulSoup soup = BeautifulSoup.BeautifulSoup(html) self.log.info("Received config response: %s" % soup) textarea = soup.findAll('textarea')[0].text.split('\n') config_dict = self._pack_config(textarea) self.log.info("Scrapped config: %s" % config_dict) return config_dict def _pack_config(self, config_list): """ Accepts list of strings and converts it into dictionary :rtype: dict :param config_string: string containing scraped config """ config_dict = {} self.log.info("Textarea: %s" % config_list) try: for config_item in config_list: key, value = config_item.split('=') self.log.info("Assigning %s to %s" % (key, value)) config_dict[key] = value except: self.log.info( "Could not do a _pack_config for scraped config on item: %s for config_list %s" % (config_item, config_list)) config_dict = {} return config_dict def _unpack_config(self, config_dictionary): """ Accepts dictionary and converts it to string :rtype: string :param config_dictionary: dict containing config """ config_text = "" try: for key, value in config_dictionary.iteritems(): config_text = config_text + ("\r\n") + key + "=" + value return config_text except Exception, e: self.log.error( "Could not unpack supplied config dictionary because %s" % e) raise
class Space(Fetchable, Statusable, Deletable, Shutdownable, Startupable, Activatable, Configurable, Metadatable, Deployable, Cleanable): """ Space is a LiveActivityGroup container """ def __init__(self, data_hash=None, uri=None): self.log = Logger().get_logger() self.class_name = self.__class__.__name__ super(Space, self).__init__() if (data_hash==None and uri==None): self.log.info("No data provided - assuming creation of new Space") elif (data_hash!=None and uri!=None): self.data_hash = data_hash self.uri = uri self.absolute_url = self._get_absolute_url() self.log.info("Instantiated Space object with url=%s" % self.absolute_url) def __repr__(self): return str(self.data_hash) def __str__(self): return str(self.data_hash) def new(self, uri, constructor_args): """ Used to create new space through API and set the "uri" so that we can operate on this instance of Space right away after .new() returns True :param constructor_args: dictionary with following structure:: {\ 'space.name' : 'space_name',\ 'space.description' : 'space_description',\ '_eventId_save' : 'Save',\ 'liveActivityGroupIds' : [1,2,666]\ } :param uri: "http://some_server/prefix" (passed by master) :rtype: new LiveActivityGroup object or False """ self.log.info("Creating new Space with arguments: %s" % constructor_args) route = Path().get_route_for('Space', 'new') url = "%s%s" % (uri, route) request_response = self._api_post_json(url, constructor_args) if request_response.url: self.absolute_url = request_response.url.replace("view.html", "view.json") self.fetch() self.log.info("Created new Space with url=%s, data_hash is now %s" % (self.absolute_url, self.data_hash)) return self else: self.log.info("Created new Space %s but returned False" % self) return False def to_json(self): """ Should selected attributes in json form defined by the template """ self.serializer = SpaceSerializer(self.data_hash) return self.serializer.to_json() def id(self): return self.data_hash['id'] def name(self): """ :param: Should return Space name """ return self.data_hash['name'] def description(self): """ :param: Should return Space description """ return self.data_hash['description'] def metadata(self): """ :param: Should return Space metadata """ return self.data_hash['metadata'] def live_activity_groups(self): """ :param: Should return Space metadata """ return self.data_hash['liveActivityGroups'] def set_live_activity_groups(self, live_activity_groups_list): """ Used to set new live acitivity groups list for space :param: dictionary with following structure:: {\ 'space.name' : 'name of the space',\ 'liveActivityGroupsIds' : [1,2,666]\ } :rtype: new Space object """ params = { 'space.name' : self.name(), 'liveActivityGroupIds' : live_activity_groups_list, 'space.description' : self.description() } self.log.info("Updating Space with arguments: %s" % params) route = Path().get_route_for('Space', 'edit') % self.id() url = "%s%s" % (self.uri, route) request_response = self._api_post_json_no_cookies(url, params) if request_response.url: self.absolute_url = request_response.url.replace("view.html", "view.json") self.fetch() self.log.info("Updated Space with url=%s, data_hash is now %s" % (self.absolute_url, self.data_hash)) return self else: self.log.info("Updated Space %s but returned False" % self) return False """ Private methods below """ def _get_absolute_url(self): live_activity_group_id = self.data_hash['id'] url = "%s/space/%s/view.json" % (self.uri, live_activity_group_id) return url
class Communicable(object): def __init__(self): """ Should be responsible for communication with the API """ self.log = Logger().get_logger() def _compose_url(self, uri, class_name=None, method_name=None, context=None, action=None): """ Should compose URL trying to do that in two steps: 1. return if object that tries to retrieve the url has route that is already staticaly defined 2. try to compose the custom route on the basis of URL data :rtype: string """ if class_name and method_name: self.log.info("Composing url for class_name '%s' and method name '%s'" % (class_name, method_name)) static_route = Path().get_route_for(class_name, method_name) if static_route : self.log.info("Returned auto url %s" % (static_route)) url = "%s%s" % (uri, static_route) return url elif context and action: url = "%s%s%s" % (uri, context, action) self.log.info("Composed url %s" % (url)) return url else: self.log.info("Could not compose an url.") raise CommunicableException def _urlopen(self, url, data=None): """Helper for opening urls.""" return urllib2.urlopen(url, data) def _api_get_json(self, url): """ Sends a json request to the master. Returns only ['data'] part of the json response :rtype: dict or bool """ try: response = urllib2.urlopen(url) data = json.loads(response.read()) except urllib2.URLError, e: self.log.error("Could not communicate with Master API becasue: %s" % e) print "Could not communicate with Master API because %s" % e sys.exit(1) try: out_data = data['data'] except Exception: out_data = None if data['result'] != 'success': self.log.info("Could not retrieve data for URL=%s" % url) return {} if out_data: return out_data else: return {}
class LiveActivity(Fetchable, Statusable, Deletable, Shutdownable, Startupable, Activatable, Configurable, Cleanable, Metadatable, Deployable, Configable): """ Should be responsible for managing single LiveActivity :todo: .new() should return instance of fetched live activity """ def __init__(self, data_hash=None, uri=None): """ When called with constructor_args and other vars set to None, new LiveActivity will be created. :param data_hash: should be master API liveActivity json, may be blank :param uri: should be a link to "view.json" of the given live activity """ self.log = Logger().get_logger() self.class_name = self.__class__.__name__ super(LiveActivity, self).__init__() if (data_hash == None and uri == None): self.log.info( "No data provided - assuming creation of new LiveActivity") elif (data_hash != None and uri != None): self.data_hash = data_hash self.uri = uri self.absolute_url = self._get_absolute_url() self.log.info("Instantiated LiveActivity object with url=%s" % self.absolute_url) def __repr__(self): return str(self.data_hash) def new(self, uri, new_data_hash): """ Used to create new live activity through API and set the "uri" so that we can operate on this instance of LiveActivity right away after .new() returns True :param new_data_hash: dictionary of a following structure:: {"live_activity_name" : "",\ "live_activity_description" : "",\ "activity_id" : "",\ "controller_id" : ""} :param uri: "http://some_server/prefix" (passed by master) :rtype: new LiveActivity object or False """ self.log.info("Creating new Live Activity with arguments: %s" % new_data_hash) route = Path().get_route_for('LiveActivity', 'new') url = "%s%s" % (uri, route) request_response = self._api_post_json(url, new_data_hash) if request_response.url: self.absolute_url = request_response.url.replace( "view.html", "view.json") self.fetch() self.log.info( "Created new LiveActivity with url=%s, data_hash is now %s" % (self.absolute_url, self.data_hash)) return self else: self.log.info("Created new LiveActivity %s but returned False" % self) return False def to_json(self): """ Should selected attributes in json form defined by the template """ self.serializer = LiveActivitySerializer(self.data_hash) return self.serializer.to_json() def name(self): """ Should return live activity name """ return self.data_hash['name'] def status(self): """ Should return status that is currently held in the object instance """ try: status_data = self.data_hash['active']['runtimeState'] return status_data except LiveActivityException("Activity not running or non existent"): return "UNKNOWN" def identifying_name(self): """ Should return LiveActivity identifying name """ return self.data_hash['activity']['identifyingName'] def version(self): """ Should return LiveActivity version """ return self.data_hash['activity']['version'] def metadata(self): """ Should return LiveActivity metadata """ return self.data_hash['metadata'] def config(self): """ Should return LiveActivity config """ return self.get_config() def id(self): """ Should return LiveActivity id :rtype: string """ return self.data_hash['id'] def controller(self): """ Should return LiveActivity controller data :rtype: string """ return self.data_hash['controller']['name'] """ Private methods below this text """ def _get_absolute_url(self): """ :rtype: string """ route = Path().get_route_for(self.class_name, 'view') % self.data_hash['id'] url = "%s%s" % (self.uri, route) return url
class SpaceController(Fetchable, Statusable, Deletable, Shutdownable, Connectable): """ Should be responsible for managing live activity groups """ def __init__(self, data_hash=None, uri=None): self.log = Logger().get_logger() self.class_name = self.__class__.__name__ super(SpaceController, self).__init__() if data_hash == None and uri == None: self.log.info( "No data provided - assuming creation of new LiveActivity") else: self.data_hash = data_hash self.uri = uri self.absolute_url = self._get_absolute_url() self.log.info("Instantiated Activity object with url=%s" % self.absolute_url) def __repr__(self): return str(self.data_hash) def new(self, uri, constructor_args): """ Used to create new space controller through API and set the "uri" so that we can operate on this instance of SpaceController right away after .new() returns True :param constructor_args: dictionary with following structure:: {"space_controller_name" : "mandatory string",\ "space_controller_description" : "non mandatory string",\ "space_controller_host_id" : "mandatory string"} :param uri: "http://some_server/prefix" (passed by master) :rtype: new SpaceController object or False """ unpacked_arguments = {} unpacked_arguments['name'] = constructor_args['space_controller_name'] unpacked_arguments['description'] = constructor_args[ 'space_controller_description'] unpacked_arguments['hostId'] = constructor_args[ 'space_controller_host_id'] unpacked_arguments['_eventId_save'] = 'Save' self.log.info("Creating new SpaceController with arguments: %s" % unpacked_arguments) route = Path().get_route_for('SpaceController', 'new') url = "%s%s" % (uri, route) request_response = self._api_post_json(url, unpacked_arguments) if request_response.url: self.absolute_url = request_response.url.replace( "view.html", "view.json") self.fetch() self.log.info( "Created new SpaceController with url=%s, data_hash is now %s" % (self.absolute_url, self.data_hash)) return self else: self.log.info("Created new SpaceController %s but returned False" % self) return False def to_json(self): """ Should selected attributes in json form defined by the template """ self.serializer = SpaceControllerSerializer(self.data_hash) return self.serializer.to_json() def id(self): return self.data_hash['id'] def uuid(self): return self.data_hash['uuid'] def name(self): """ Should return space controller name""" return self.data_hash['name'] def description(self): """ Should return space controller description """ return self.data_hash['description'] def mode(self): """ Should return status of the controller """ return self.data_hash['mode'] def state(self): """ Should return state of the controller """ return self.data_hash['state'] """ Private methods below """ def _get_absolute_url(self): live_activity_group_id = self.data_hash['id'] url = "%s/spacecontroller/%s/view.json" % (self.uri, live_activity_group_id) return url
class Master(Communicable): """ @summary: This is the main class with all the logic needed for high level stuff. You will typically use instance of Master for all your scripts. """ def __init__(self, host='lg-head', port='8080', prefix='/interactivespaces'): """ @param host: default value is lg-head @param port: default value is 8080 @param prefix: default value is /interactivespaces @todo: refactor filter_* methods because they're not DRY """ self.host, self.port, self.prefix = host, port, prefix self.log = Logger().get_logger() self.uri = "http://%s:%s%s" % (self.host, self.port, prefix) super(Master, self).__init__() def get_activities(self, search_pattern=None): """ Retrieves a list of Activity objects @rtype: list @param search_pattern: dictionary of regexps used for searching through Activities - example regexp dict: { "activity_name" : "regexp", "activity_version" : "regexp" } - every search_pattern dictionary key may be blank/null """ url = self._compose_url(class_name='Master', method_name='get_activities', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.info('Got response for "get_activities" %s ' % str(response)) self.log.info('get_activities returned %s objects' % str(len(response))) activities = self._filter_activities(response, search_pattern) return activities def get_activity(self, search_pattern=None): """ Retrieves a list of Activity objects @rtype: list @param search_pattern: dictionary of regexps used for searching through Activities - example regexp dict: { "activity_name" : "regexp", "activity_version" : "regexp" } - every search_pattern dictionary key may be blank/null """ url = self._compose_url(class_name='Master', method_name='get_activities', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.info('Got response for "get_activities" %s ' % str(response)) self.log.info('get_activities returned %s objects' % str(len(response))) activity = self._filter_activities(response, search_pattern) if len(activity) > 1: raise MasterException("get_activity returned more than one row (%s)" % len(activity)) elif isinstance(activity[0], Activity): activity[0].fetch() self.log.info("get_activity returned Activity:%s" % str(activity)) return activity else: raise MasterException("Could not get specific activity for given search pattern") def get_live_activities(self, search_pattern=None): """ Retrieves a list of LiveActivity objects @rtype: list @param search_pattern: dictionary of regexps used for searching through LiveActivity names - example regexp dict: { "live_activity_name" : "regexp", "space_controller_name" : "regexp" } - each search_pattern dictionary key may be blank/null """ url = self._compose_url(class_name='Master', method_name='get_live_activities', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.debug('Got response for "get_live_activities" %s ' % str(response)) self.log.info('get_live_activities returned %s objects' % str(len(response))) live_activities = self._filter_live_activities(response, search_pattern) return live_activities def get_live_activity(self, search_pattern=None): """ Retrieves a list of LiveActivity objects @rtype: LiveActivity or False @param search_pattern: dictionary of regexps used for searching through LiveActivity names - example regexp dict: { "live_activity_name" : "GE ViewSync Master on Node A", "space_controller_name" : "ISCtlDispAScreen00" } - each search_pattern dictionary key may be blank/null """ url = self._compose_url(class_name='Master', method_name='get_live_activities', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.debug('Got response for "get_live_activities" %s ' % str(response)) self.log.info('get_live_activities returned %s objects' % str(len(response))) live_activity = self._filter_live_activities(response, search_pattern) if len(live_activity) > 1: raise MasterException("get_live_activity returned more than one row (%s)" % len(live_activity)) elif isinstance(live_activity[0], LiveActivity): live_activity[0].fetch() self.log.info("get_live_activity returned LiveActivity:%s" % live_activity) return live_activity[0] else: raise MasterException("Could not get specific live activity for given search pattern") def get_live_activity_groups(self, search_pattern=None): """ Retrieves a list of live activity groups. @rtype: list @param search_pattern: dictionary of regexps used for searching through LiveActivity names - example regexp dict: { "live_activity_group_name" : "regexp" } """ url = self._compose_url(class_name='Master', method_name='get_live_activity_groups', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.debug('Got response for "get_live_activity_groups" %s ' % str(response)) self.log.info('get_live_activity_groups returned %s objects' % str(len(response))) live_activity_groups = self._filter_live_activity_groups(response, search_pattern) return live_activity_groups def get_live_activity_group(self, search_pattern=None): """ Retrieves a list of live activity groups. @rtype: list @param search_pattern: dictionary of regexps used for searching through LiveActivity names - example regexp dict: { "live_activity_group_name" : "regexp" } """ url = self._compose_url(class_name='Master', method_name='get_live_activity_groups', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.debug('Got response for "get_live_activity_groups" %s ' % str(response)) self.log.info('get_live_activity_groups returned %s objects' % str(len(response))) live_activity_group = self._filter_live_activity_groups(response, search_pattern) if len(live_activity_group) > 1: raise MasterException("get_live_activity_group returned more than one row (%s)" % len(live_activity_group)) elif isinstance(live_activity_group[0], LiveActivityGroup): live_activity_group[0].fetch() self.log.info("get_live_activity_group returned LiveActivityGroup:%s" % str(live_activity_group)) return live_activity_group[0] else: raise MasterException("Could not get specific live activity group for given search pattern") def get_spaces(self, search_pattern=None): """ @summary: Retrieves a list of live activity groups. @rtype: list @param search_pattern: dictionary containing space name regexp - example regexp dict: {"space_name" : "regexp"} """ url = self._compose_url(class_name='Master', method_name='get_spaces', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.debug('Got response for "get_spaces" %s ' % str(response)) spaces = self._filter_spaces(response, search_pattern) return spaces def get_space(self, search_pattern=None): """ @summary: Retrieves a Space @rtype: Space @param search_pattern: dictionary containing space name regexp - example regexp dict: {"space_name" : "regexp"} """ url = self._compose_url(class_name='Master', method_name='get_spaces', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.debug('Got response for "get_spaces" %s ' % str(response)) space = self._filter_spaces(response, search_pattern) if len(space) > 1: raise MasterException("get_space returned more than one row (%s)" % len(space)) elif isinstance(space[0], Space): space[0].fetch() self.log.info("get_space returned Space:%s" % str(space)) return space[0] else: raise MasterException("Could not get specific space for given search pattern") def get_space_controllers(self, search_pattern=None): """ Retrieves a list of live space controllers. @rtype: list @param search_pattern: dictionary containing regexps and strings - example regexp dict: { "state" : "STRING", "mode" : "STRING", "name" : "regexp", "uuid" : "STRING" } """ url = self._compose_url(class_name='Master', method_name='get_space_controllers', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.debug('Got response for "get_controllers" %s ' % str(response)) space_controllers = self._filter_space_controllers(response, search_pattern) return space_controllers def get_space_controller(self, search_pattern=None): """ Retrieves a list of live space controllers. @rtype: SpaceController @param search_pattern: dictionary containing regexps and strings - example regexp dict: { "space_controller_state" : "STRING", "space_controller_mode" : "STRING", "space_controller_name" : "regexp", "space_controller_uuid" : "STRING" } """ url = self._compose_url(class_name='Master', method_name='get_space_controllers', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.debug('Got response for "get_controllers" %s ' % str(response)) space_controller = self._filter_space_controllers(response, search_pattern) if len(space_controller) > 1: raise MasterException("get_space_controller returned more than one row") elif isinstance(space_controller[0], SpaceController): space_controller[0].fetch() self.log.info("get_space_controller returned SpaceController:%s" % str(space_controller)) return space_controller[0] else: raise MasterException("Could not get specific space controller for given search pattern") def get_named_scripts(self, pattern=None): """Retrieves a list of named scripts.""" raise NotImplementedError def new_live_activity(self, constructor_args): """ @summary: creates a new live activity and returns it @param constructor_args: - dictionary containing all of below keys: { "live_activity_name": "string containing name of a new live activity (mandatory)" "live_activity_description" : "string containing description" "activity_name" : "string containing activity name" "space_controller_name" : "string containing controller name" } @rtype: LiveActivity """ unpacked_arguments={} unpacked_arguments['activityId'] = self.get_activity({"activity_name" : constructor_args['activity_name']}).id() unpacked_arguments['controllerId'] = self.get_space_controller({"space_controller_name" : constructor_args['space_controller_name']}).id() unpacked_arguments['live_activity_description'] = constructor_args['live_activity_description'] unpacked_arguments['liveActivity.name'] = constructor_args['live_activity_name'] unpacked_arguments['_eventId_save'] = 'Save' activity = LiveActivity().new(self.uri, unpacked_arguments) self.log.info("Master:new_live_activity returned activity:%s" % activity) return activity def new_activity(self, constructor_args): """ @summary: creates a new activity and returns it @param constructor_args: - dictionary containing all of below keys: { "zip_file_handler": "zipfile object (mandatory)" } @rtype: Activity or False """ activity = Activity().new(self.uri, constructor_args) self.log.info("Master:new_activity returned activity:%s" % activity) return activity def new_space_controller(self, constructor_args): """ @summary: creates new controller @param constructor_args: dictionary containing all of below keys: { "space_controller_name" : "mandatory string", "space_controller_description" : "non mandatory string", "space_controller_host_id" : "mandatory string" } """ space_controller = SpaceController().new(self.uri, constructor_args) return space_controller def new_live_activity_group(self, constructor_args): """ @summary: Creates a new live activity group. @param constructor_args: dictionary with following structure: { "live_activity_group_name" : "example.py live_activity_group_name", "live_activity_group_description" : "created by example.py", "live_activities" : [{"live_activity_name" : "SV Master on Node A", "space_controller_name" : "ISCtlDispAScreen00"}, {"live_activity_name" : "SV Slave 01 on Node A", "space_controller_name" : "ISCtlDispAScreen00"}] } """ live_activity_ids = self._translate_live_activities_names_to_ids(constructor_args['live_activities']) unpacked_arguments = {} unpacked_arguments['liveActivityGroup.name'] = constructor_args['live_activity_group_name'] unpacked_arguments['liveActivityGroup.description'] = constructor_args['live_activity_group_description'] unpacked_arguments['_eventId_save'] = 'Save' unpacked_arguments['liveActivityIds'] = live_activity_ids live_activity_group = LiveActivityGroup().new(self.uri, unpacked_arguments) return live_activity_group def new_space(self, name, description, live_activity_groups, spaces): """Creates a new space.""" raise NotImplementedError def new_controller(self, name, description, host_id): """Creates a new controller.""" raise NotImplementedError def new_named_script(self, name, description, language, content, scheduled=None): """Creates a new named script.""" raise NotImplementedError """ Private methods below """ def _filter_live_activities(self, response, search_pattern): """ @summary: Should iterate over response from Master API and filter live activites with regards to their name" @param response: response['data'] from master API @param search_pattern: dictionary where values may be regexps @todo: refactor filtering because it looks ugly and make it global for all classes """ live_activities = [] """ Make a search pattern with default values set to None""" if isinstance(search_pattern, dict): search_pattern = SearchPattern(search_pattern) else: search_pattern = SearchPattern() live_activity_name = search_pattern['live_activity_name'] """ Nested values are returning exception so do it manually here """ try: space_controller_name = search_pattern['controller']['name'] except Exception: space_controller_name = None self.log.debug("Filtering activities with pattern=%s" % search_pattern) for live_activity_data in response: do_filter = True """ Var for holding the state of filtering """ current_live_activity_name = live_activity_data['name'] current_space_controller_name = live_activity_data['controller']['name'] if space_controller_name and do_filter: if Searcher().match(current_space_controller_name, space_controller_name): pass else: do_filter = False if live_activity_name and do_filter: if Searcher().match(current_live_activity_name, live_activity_name): pass else: do_filter = False if do_filter==True: live_activities.append(LiveActivity(live_activity_data, self.uri)) self.log.info("Filtered live_activities and returned %s object(s)" % str(len(live_activities))) return live_activities def _filter_activities(self, response, search_pattern): """ @summary: Should iterate over response from Master API and filter live activites with regards to their name" @param response: response['data'] from master API @param search_pattern: dictionary where values may be regexps @rtype: list of Activity objects """ activities = [] """ Make a search pattern with default values set to None""" if isinstance(search_pattern, dict): search_pattern = SearchPattern(search_pattern) else: search_pattern = SearchPattern() activity_name = search_pattern['activity_name'] activity_version = search_pattern['activity_version'] self.log.debug("Filtering activities with pattern=%s" % search_pattern) for activity_data in response: do_filter = True """ Var for holding the state of filtering """ current_activity_name = activity_data['name'] current_activity_version = activity_data['version'] if activity_version and do_filter: if Searcher().match(current_activity_version, activity_version): pass else: do_filter = False if activity_name and do_filter: if Searcher().match(current_activity_name, activity_name): pass else: do_filter = False if do_filter==True: activities.append(Activity(activity_data, self.uri)) self.log.info("Filtered activities and returned %s object(s) : %s" % (str(len(activities)), activities)) return activities def _filter_live_activity_groups(self, response, search_pattern): """ @summary: Should iterate over response from Master API and filter live activity groups with regards to their name" @param response: response['data'] from master API @param search_pattern: dictionary where values may be regexps @rtype: list of LiveActivityGroup objects """ live_activity_groups = [] """ Make a search pattern with default values set to None""" if isinstance(search_pattern, dict): search_pattern = SearchPattern(search_pattern) else: search_pattern = SearchPattern() live_activity_group_name = search_pattern['live_activity_group_name'] self.log.debug("Filtering activities with pattern=%s" % search_pattern) for live_activity_group_data in response: do_filter = True """ Var for holding the state of filtering """ current_live_activity_group_name = live_activity_group_data['name'] if live_activity_group_name and do_filter: if Searcher().match(current_live_activity_group_name, live_activity_group_name): pass else: do_filter = False if do_filter==True: live_activity_groups.append(LiveActivityGroup(live_activity_group_data, self.uri)) self.log.info("Filtered live_activity_groups and returned %s object(s)" % str(len(live_activity_groups))) return live_activity_groups def _filter_spaces(self, response, search_pattern): """ @summary: Should iterate over response from Master API and filter live activity groups with regards to their name" @param response: response['data'] from master API @param search_pattern: dictionary where values may be regexps @rtype: list of Space objects """ spaces = [] """ Make a search pattern with default values set to None""" if isinstance(search_pattern, dict): search_pattern = SearchPattern(search_pattern) else: search_pattern = SearchPattern() space_name = search_pattern['space_name'] self.log.debug("Filtering spaces with pattern=%s" % search_pattern) for space_data in response: do_filter = True """ Var for holding the state of filtering """ current_space_name = space_data['name'] if space_name and do_filter: if Searcher().match(current_space_name, space_name): pass else: do_filter = False if do_filter==True: spaces.append(Space(space_data, self.uri)) self.log.info("Filtered spaces and returned %s object(s)" % str(len(spaces))) return spaces def _filter_space_controllers(self, response, search_pattern): """ @summary: Should iterate over response from Master API and filter space controllers with regards to the given search dictionary consisting of name, uuid, mode and state (none of them are mandatory)" @param response: response['data'] from master API @param search_pattern: dictionary where values may be regexps @rtype: list of Space objects """ space_controllers = [] """ Make a search pattern with default values set to None""" if isinstance(search_pattern, dict): search_pattern = SearchPattern(search_pattern) else: search_pattern = SearchPattern() space_controller_name = search_pattern['space_controller_name'] space_controller_uuid = search_pattern['space_controller_uuid'] space_controller_state = search_pattern['space_controller_state'] space_controller_mode = search_pattern['space_controller_mode'] self.log.debug("Filtering space controllers with pattern=%s" % search_pattern) for space_controller_data in response: do_filter = True current_space_controller_name = space_controller_data['name'] current_space_controller_uuid = space_controller_data['uuid'] current_space_controller_mode = space_controller_data['mode'] current_space_controller_state = space_controller_data['state'] if space_controller_name and do_filter: if Searcher().match(current_space_controller_name, space_controller_name): pass else: do_filter = False if space_controller_uuid and do_filter: if current_space_controller_uuid == space_controller_uuid: pass else: do_filter = False if space_controller_mode and do_filter: if current_space_controller_mode == space_controller_mode: pass else: do_filter = False if space_controller_state and do_filter: if current_space_controller_state == space_controller_state: pass else: do_filter = False if do_filter==True: space_controllers.append(SpaceController(space_controller_data, self.uri)) self.log.info("Filtered space_controllers and returned %s object(s)" % str(len(space_controllers))) return space_controllers def _translate_live_activities_names_to_ids(self, live_activities): """ @param live_activities: list of dictionaries containing following keys: { "live_activity_name" : "some_name", "space_controller_name" : "some controller name" } @rtype: list """ live_activity_ids = [] for la_data in live_activities: live_activity = self.get_live_activity(la_data) live_activity_ids.append(live_activity.id()) self.log.info("Translated %s live_activity_names to ids with a result of %s" % (len(live_activity_ids), live_activity_ids) ) return live_activity_ids
class LiveActivityGroup(Fetchable, Statusable, Deletable, Shutdownable, Startupable, Activatable, Configurable, Metadatable, Deployable): """ Should be responsible for managing single live activity group """ def __init__(self, data_hash=None, uri=None): self.log = Logger().get_logger() self.class_name = self.__class__.__name__ super(LiveActivityGroup, self).__init__() if data_hash == None and uri == None: self.log.info( "No data_hash and uri provided for LiveActivityGroup constructor, assuming creation" ) else: self.data_hash = data_hash self.uri = uri self.absolute_url = self._get_absolute_url() self.log.info("Instantiated Activity object with url=%s" % self.absolute_url) def __repr__(self): return str(self.data_hash) def new(self, uri, constructor_args): """ Used to create new live activity group through API and set the "uri" so that we can operate on this instance of LiveActivityGroup right away after .new() returns True :param constructor_args: dictionary with following structure:: {\ 'liveActivityGroup.name' : 'live_activity_group_name',\ 'liveActivityGroup.description' : 'live_activity_group_description',\ '_eventId_save' : 'Save',\ 'liveActivityIds' : [1,2,666]\ } :param uri: "http://some_server/prefix" (passed by master) :rtype: new LiveActivityGroup object or False """ self.log.info("Creating new LiveActivityGroup with arguments: %s" % constructor_args) route = Path().get_route_for('LiveActivityGroup', 'new') url = "%s%s" % (uri, route) request_response = self._api_post_json(url, constructor_args) if request_response.url: self.absolute_url = request_response.url.replace( "view.html", "view.json") self.fetch() self.log.info( "Created new LiveActivityGroup with url=%s, data_hash is now %s" % (self.absolute_url, self.data_hash)) return self else: self.log.info( "Created new LiveActivityGroup %s but returned False" % self) return False def set_live_activities(self, live_activities_list): """ Used to set new live activities list :param: dictionary with following structure:: {\ 'liveActivityGroup.name' : 'live_activity_group_name',\ 'liveActivityIds' : [1,2,666]\ } :param uri: "http://some_server/prefix" (passed by master) :rtype: new LiveActivityGroup object or False """ params = { 'liveActivityGroup.name': self.name(), 'liveActivityIds': live_activities_list, 'liveActivityGroup.description': self.description() } self.log.info("Updating LiveActivityGroup with arguments: %s" % params) route = Path().get_route_for('LiveActivityGroup', 'edit') % self.id() url = "%s%s" % (self.uri, route) request_response = self._api_post_json_no_cookies(url, params) if request_response.url: self.absolute_url = request_response.url.replace( "view.html", "view.json") self.fetch() self.log.info( "Updated LiveActivityGroup with url=%s, data_hash is now %s" % (self.absolute_url, self.data_hash)) return self else: self.log.info("Updated LiveActivityGroup %s but returned False" % self) return False def to_json(self): """ Should selected attributes in json form defined by the template """ self.serializer = LiveActivityGroupSerializer(self.data_hash) return self.serializer.to_json() def id(self): return self.data_hash['id'] def name(self): """ Should return live activity group name""" return self.data_hash['name'] def live_activities(self): """ Should return list of live LiveActivity instances""" live_activities = [] data = self.data_hash['liveActivities'] for live_activity_data in data: try: status = live_activity_data['active']['runtimeState'] except Exception: status = 'UNKNOWN' live_activity_group_live_activity = LiveActivity( data_hash=live_activity_data, uri=self.uri) live_activities.append(live_activity_group_live_activity) return live_activities def description(self): """ Should return Live Activity Group description """ return self.data_hash['description'] """ Private methods below """ def metadata(self): """ Should return Live Activity Group metadata """ return self.data_hash['metadata'] """ Private methods below """ def _get_absolute_url(self): live_activity_group_id = self.data_hash['id'] url = "%s/liveactivitygroup/%s/view.json" % (self.uri, live_activity_group_id) return url
class SpaceController(Fetchable, Statusable, Deletable, Shutdownable, Connectable): """ @summary: Should be responsible for managing live activity groups """ def __init__(self, data_hash=None, uri=None): self.log = Logger().get_logger() self.class_name = self.__class__.__name__ super(SpaceController, self).__init__() if data_hash==None and uri==None: self.log.info("No data provided - assuming creation of new LiveActivity") else: self.data_hash = data_hash self.uri = uri self.absolute_url = self._get_absolute_url() self.log.info("Instantiated Activity object with url=%s" % self.absolute_url) def __repr__(self): return str(self.data_hash) def new(self, uri, constructor_args): """ @summary: used to create new space controller through API and set the "uri" so that we can operate on this instance of SpaceController right away after .new() returns True @param constructor_args: dictionary with following structure: { "space_controller_name" : "mandatory string", "space_controller_description" : "non mandatory string", "space_controller_host_id" : "mandatory string" } @param uri: "http://some_server/prefix (passed by master)" @rtype: new SpaceController object or False """ unpacked_arguments = {} unpacked_arguments['name'] = constructor_args['space_controller_name'] unpacked_arguments['description'] = constructor_args['space_controller_description'] unpacked_arguments['hostId'] = constructor_args['space_controller_host_id'] unpacked_arguments['_eventId_save'] = 'Save' self.log.info("Creating new SpaceController with arguments: %s" % unpacked_arguments) route = Path().get_route_for('SpaceController', 'new') url = "%s%s" % (uri, route) request_response = self._api_post_json(url, unpacked_arguments) if request_response.url: self.absolute_url = request_response.url.replace("view.html", "view.json") self.fetch() self.log.info("Created new SpaceController with url=%s, data_hash is now %s" % (self.absolute_url, self.data_hash)) return self else: self.log.info("Created new SpaceController %s but returned False" % self) return False def to_json(self): """ Should selected attributes in json form defined by the template """ self.serializer = SpaceControllerSerializer(self.data_hash) return self.serializer.to_json() def id(self): return self.data_hash['id'] def name(self): """ Should return live activity name""" return self.data_hash['name'] def description(self): """ Should return Activity description """ return self.data_hash['description'] def mode(self): """ Should return Activity description """ return self.data_hash['mode'] """ Private methods below """ def _get_absolute_url(self): live_activity_group_id = self.data_hash['id'] url = "%s/spacecontroller/%s/view.json" % (self.uri, live_activity_group_id) return url
class Communicable(object): def __init__(self): """ Should be responsible for communication with the API """ self.log = Logger().get_logger() def _compose_url(self, uri, class_name=None, method_name=None, context=None, action=None): """ Should compose URL trying to do that in two steps: 1. return if object that tries to retrieve the url has route that is already staticaly defined 2. try to compose the custom route on the basis of URL data :rtype: string """ if class_name and method_name: self.log.info( "Composing url for class_name '%s' and method name '%s'" % (class_name, method_name)) static_route = Path().get_route_for(class_name, method_name) if static_route: self.log.info("Returned auto url %s" % (static_route)) url = "%s%s" % (uri, static_route) return url elif context and action: url = "%s%s%s" % (uri, context, action) self.log.info("Composed url %s" % (url)) return url else: self.log.info("Could not compose an url.") raise CommunicableException def _urlopen(self, url, data=None): """Helper for opening urls.""" return urllib2.urlopen(url, data) def _api_get_json(self, url): """ Sends a json request to the master. Returns only ['data'] part of the json response :rtype: dict or bool """ try: response = urllib2.urlopen(url) data = json.loads(response.read()) except urllib2.URLError, e: self.log.error( "Could not communicate with Master API becasue: %s" % e) print "Could not communicate with Master API because %s" % e sys.exit(1) try: out_data = data['data'] except Exception: out_data = None if data['result'] != 'success': self.log.info("Could not retrieve data for URL=%s" % url) return {} if out_data: return out_data else: return {}
class LiveActivityGroup( Fetchable, Statusable, Deletable, Shutdownable, Startupable, Activatable, Configurable, Metadatable, Deployable ): """ Should be responsible for managing single live activity group """ def __init__(self, data_hash=None, uri=None): self.log = Logger().get_logger() self.class_name = self.__class__.__name__ super(LiveActivityGroup, self).__init__() if data_hash == None and uri == None: self.log.info("No data_hash and uri provided for LiveActivityGroup constructor, assuming creation") else: self.data_hash = data_hash self.uri = uri self.absolute_url = self._get_absolute_url() self.log.info("Instantiated Activity object with url=%s" % self.absolute_url) def __repr__(self): return str(self.data_hash) def new(self, uri, constructor_args): """ Used to create new live activity group through API and set the "uri" so that we can operate on this instance of LiveActivityGroup right away after .new() returns True :param constructor_args: dictionary with following structure:: {\ 'liveActivityGroup.name' : 'live_activity_group_name',\ 'liveActivityGroup.description' : 'live_activity_group_description',\ '_eventId_save' : 'Save',\ 'liveActivityIds' : [1,2,666]\ } :param uri: "http://some_server/prefix" (passed by master) :rtype: new LiveActivityGroup object or False """ self.log.info("Creating new LiveActivityGroup with arguments: %s" % constructor_args) route = Path().get_route_for("LiveActivityGroup", "new") url = "%s%s" % (uri, route) request_response = self._api_post_json(url, constructor_args) if request_response.url: self.absolute_url = request_response.url.replace("view.html", "view.json") self.fetch() self.log.info( "Created new LiveActivityGroup with url=%s, data_hash is now %s" % (self.absolute_url, self.data_hash) ) return self else: self.log.info("Created new LiveActivityGroup %s but returned False" % self) return False def set_live_activities(self, live_activities_list): """ Used to set new live activities list :param: dictionary with following structure:: {\ 'liveActivityGroup.name' : 'live_activity_group_name',\ 'liveActivityIds' : [1,2,666]\ } :param uri: "http://some_server/prefix" (passed by master) :rtype: new LiveActivityGroup object or False """ params = { "liveActivityGroup.name": self.name(), "liveActivityIds": live_activities_list, "liveActivityGroup.description": self.description(), } self.log.info("Updating LiveActivityGroup with arguments: %s" % params) route = Path().get_route_for("LiveActivityGroup", "edit") % self.id() url = "%s%s" % (self.uri, route) request_response = self._api_post_json_no_cookies(url, params) if request_response.url: self.absolute_url = request_response.url.replace("view.html", "view.json") self.fetch() self.log.info( "Updated LiveActivityGroup with url=%s, data_hash is now %s" % (self.absolute_url, self.data_hash) ) return self else: self.log.info("Updated LiveActivityGroup %s but returned False" % self) return False def to_json(self): """ Should selected attributes in json form defined by the template """ self.serializer = LiveActivityGroupSerializer(self.data_hash) return self.serializer.to_json() def id(self): return self.data_hash["id"] def name(self): """ Should return live activity group name""" return self.data_hash["name"] def live_activities(self): """ Should return list of live LiveActivity instances""" live_activities = [] data = self.data_hash["liveActivities"] for live_activity_data in data: try: status = live_activity_data["active"]["runtimeState"] except Exception: status = "UNKNOWN" live_activity_group_live_activity = LiveActivity(data_hash=live_activity_data, uri=self.uri) live_activities.append(live_activity_group_live_activity) return live_activities def description(self): """ Should return Live Activity Group description """ return self.data_hash["description"] """ Private methods below """ def metadata(self): """ Should return Live Activity Group metadata """ return self.data_hash["metadata"] """ Private methods below """ def _get_absolute_url(self): live_activity_group_id = self.data_hash["id"] url = "%s/liveactivitygroup/%s/view.json" % (self.uri, live_activity_group_id) return url
class Master(Communicable): """ This is the main class with all the logic needed for high level stuff. You will typically use instance of Master for all your scripts. """ def __init__(self, host='lg-head', port='8080', prefix='/interactivespaces', logfile_path='ispaces-client.log'): """ :param host: default value is lg-head :param port: default value is 8080 :param prefix: default value is /interactivespaces :todo: refactor filter_* methods because they're not DRY """ self.host, self.port, self.prefix = host, port, prefix self.log = Logger(logfile_path=logfile_path).get_logger() self.uri = "http://%s:%s%s" % (self.host, self.port, prefix) super(Master, self).__init__() def get_activities(self, search_pattern=None): """ Retrieves a list of Activity objects :rtype: list :param search_pattern: dictionary of regexps used for searching through Activities example regexp dict:: {\ "activity_name" : "regexp"\ "activity_version" : "regexp"\ } every search_pattern dictionary key may be blank/null """ url = self._compose_url(class_name='Master', method_name='get_activities', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.info('Got response for "get_activities" %s ' % str(response)) self.log.info('get_activities returned %s objects' % str(len(response))) activities = self._filter_activities(response, search_pattern) return activities def get_activity(self, search_pattern=None): """ Retrieves a list of Activity objects :rtype: list :param search_pattern: dictionary of regexps used for searching through Activities example regexp dict:: {\ "activity_name" : "regexp",\ "activity_version" : "regexp"\ } every search_pattern dictionary key may be blank/null """ url = self._compose_url(class_name='Master', method_name='get_activities', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.info('Got response for "get_activities" %s ' % str(response)) self.log.info('get_activities returned %s objects' % str(len(response))) activity = self._filter_activities(response, search_pattern) return self._validate_single_getter_results(activity, Activity, ActivityNotFoundException) def get_live_activities(self, search_pattern=None): """ Retrieves a list of LiveActivity objects :rtype: list :param search_pattern: dictionary of regexps used for searching through LiveActivity names example regexp dict:: {\ "live_activity_name" : "regexp",\ "space_controller_name" : "regexp"\ } - each search_pattern dictionary key may be blank/null """ url = self._compose_url(class_name='Master', method_name='get_live_activities', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.debug('Got response for "get_live_activities" %s ' % str(response)) self.log.info('get_live_activities returned %s objects' % str(len(response))) live_activities = self._filter_live_activities(response, search_pattern) return live_activities def get_live_activity(self, search_pattern=None): """ Retrieves a list of LiveActivity objects :rtype: LiveActivity or False :param search_pattern: dictionary of regexps used for searching through LiveActivity names example regexp dict:: {\ "live_activity_name" : "GE ViewSync Master on Node A",\ "space_controller_name" : "ISCtlDispAScreen00"\ } each search_pattern dictionary key may be blank/null """ url = self._compose_url(class_name='Master', method_name='get_live_activities', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.debug('Got response for "get_live_activities" %s ' % str(response)) self.log.info('get_live_activities returned %s objects' % str(len(response))) live_activity = self._filter_live_activities(response, search_pattern) return self._validate_single_getter_results( live_activity, LiveActivity, LiveActivityNotFoundException) def get_live_activity_groups(self, search_pattern=None): """ Retrieves a list of live activity groups. :rtype: list :param search_pattern: dictionary of regexps used for searching through LiveActivity names example regexp dict:: {\ "live_activity_group_name" : "regexp"\ } """ url = self._compose_url(class_name='Master', method_name='get_live_activity_groups', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.debug('Got response for "get_live_activity_groups" %s ' % str(response)) self.log.info( 'get_live_activity_groups returned %s objects - filtering with %s' % (str(len(response)), search_pattern)) live_activity_groups = self._filter_live_activity_groups( response, search_pattern) return live_activity_groups def get_live_activity_group(self, search_pattern=None): """ Retrieves a list of live activity groups. :rtype: list :param search_pattern: dictionary of regexps used for searching through LiveActivity names example regexp dict:: {\ "live_activity_group_name" : "regexp"\ } """ url = self._compose_url(class_name='Master', method_name='get_live_activity_groups', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.debug('Got response for "get_live_activity_groups" %s ' % str(response)) self.log.info('get_live_activity_groups returned %s objects' % str(len(response))) live_activity_group = self._filter_live_activity_groups( response, search_pattern) return self._validate_single_getter_results( live_activity_group, LiveActivityGroup, LiveActivityGroupNotFoundException) def get_spaces(self, search_pattern=None): """ Retrieves a list of live activity groups. :rtype: list :param search_pattern: dictionary containing space name regexp example regexp dict:: {"space_name" : "regexp"} """ url = self._compose_url(class_name='Master', method_name='get_spaces', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.debug('Got response for "get_spaces" %s ' % str(response)) spaces = self._filter_spaces(response, search_pattern) return spaces def get_space(self, search_pattern=None): """ Retrieves a Space :rtype: Space :param search_pattern: dictionary containing space name regexp example regexp dict:: {"space_name" : "regexp"} """ url = self._compose_url(class_name='Master', method_name='get_spaces', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.debug('Got response for "get_spaces" %s ' % str(response)) space = self._filter_spaces(response, search_pattern) return self._validate_single_getter_results(space, Space, SpaceNotFoundException) def get_space_controllers(self, search_pattern=None): """ Retrieves a list of live space controllers. :rtype: list :param search_pattern: dictionary containing regexps and strings example regexp dict:: {\ "state" : "STRING",\ "mode" : "STRING",\ "name" : "regexp",\ "uuid" : "STRING"\ } """ url = self._compose_url(class_name='Master', method_name='get_space_controllers', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.debug('Got response for "get_controllers" %s ' % str(response)) space_controllers = self._filter_space_controllers( response, search_pattern) return space_controllers def get_space_controller(self, search_pattern=None): """ Retrieves a list of live space controllers. :rtype: SpaceController :param search_pattern: dictionary containing regexps and strings example regexp dict:: {\ "space_controller_state" : "STRING",\ "space_controller_mode" : "STRING",\ "space_controller_name" : "regexp",\ "space_controller_uuid" : "STRING"\ } """ url = self._compose_url(class_name='Master', method_name='get_space_controllers', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.debug('Got response for "get_controllers" %s ' % str(response)) space_controller = self._filter_space_controllers( response, search_pattern) return self._validate_single_getter_results( space_controller, SpaceController, ControllerNotFoundException) def get_named_scripts(self, pattern=None): """Retrieves a list of named scripts.""" raise NotImplementedError def new_live_activity(self, constructor_args): """ Creates a new live activity and returns it :param constructor_args: dictionary containing all of below keys:: {"live_activity_name": "string containing name of a new live activity (mandatory)",\ "live_activity_description" : "string containing description",\ "activity_name" : "string containing activity name",\ "space_controller_name" : "string containing controller name"} :rtype: LiveActivity """ unpacked_arguments = {} unpacked_arguments['activityId'] = self.get_activity({ "activity_name": constructor_args['activity_name'] }).id() unpacked_arguments['controllerId'] = self.get_space_controller({ "space_controller_name": constructor_args['space_controller_name'] }).id() unpacked_arguments['liveActivity.description'] = constructor_args[ 'live_activity_description'] unpacked_arguments['liveActivity.name'] = constructor_args[ 'live_activity_name'] unpacked_arguments['_eventId_save'] = 'Save' if not self._api_object_exists(LiveActivity, constructor_args, self.get_live_activity): activity = LiveActivity().new(self.uri, unpacked_arguments) self.log.info("Master:new_live_activity returned activity:%s" % activity) return activity else: return [] def new_activity(self, constructor_args): """ Creates a new activity and returns it :param constructor_args: dictionary containing all of below keys:: {\ "zip_file_handler": "zipfile object (mandatory)",\ "activity_name" : "some name",\ "activity_version": "some version"\ } :rtype: Activity or False """ self.log.info("Going to create new activity with arguments: %s" % constructor_args) if not self._api_object_exists(Activity, constructor_args, self.get_activity): activity = Activity().new(self.uri, constructor_args) self.log.info("Master:new_activity returned activity:%s" % activity) return activity else: return [] def new_space_controller(self, constructor_args): """ Creates new controller :param constructor_args: dictionary containing all of below keys:: {\ "space_controller_name" : "mandatory string",\ "space_controller_description" : "non mandatory string",\ "space_controller_host_id" : "mandatory string"\ } """ if not self._api_object_exists(SpaceController, constructor_args, self.get_space_controller): space_controller = SpaceController().new(self.uri, constructor_args) self.log.info("Master:new_space_controller:%s" % space_controller) return space_controller else: return [] def new_live_activity_group(self, constructor_args): """ Creates a new live activity group. :param constructor_args: dictionary with following structure:: {\ "live_activity_group_name" : "example.py live_activity_group_name",\ "live_activity_group_description" : "created by example.py",\ "live_activities" : [{"live_activity_name" : "SV Master on Node A",\ "space_controller_name" : "ISCtlDispAScreen00"},\ {"live_activity_name" : "SV Slave 01 on Node A",\ "space_controller_name" : "ISCtlDispAScreen00"}]\ } """ self.log.info( "Live activities that will comprise new live activity group: %s" % constructor_args['live_activities']) live_activity_ids = self._translate_live_activities_names_to_ids( constructor_args['live_activities']) unpacked_arguments = {} unpacked_arguments['liveActivityGroup.name'] = constructor_args[ 'live_activity_group_name'] unpacked_arguments['liveActivityGroup.description'] = constructor_args[ 'live_activity_group_description'] unpacked_arguments['_eventId_save'] = 'Save' unpacked_arguments['liveActivityIds'] = live_activity_ids if not self._api_object_exists(LiveActivityGroup, constructor_args, self.get_live_activity_group): live_activity_group = LiveActivityGroup().new( self.uri, unpacked_arguments) self.log.info("Master:new_live_activity_group:%s" % live_activity_group) return live_activity_group else: return [] def new_space(self, constructor_args): """ Creates a new Space. :param constructor_args: dictionary with following structure:: {\ "space_name" : "example.py live_activity_group_name",\ "space_description" : "created by example.py",\ "live_activity_groups" : [{"live_activity_group_name" : "Media Services"}]\ } """ live_activity_group_ids = self._translate_live_activity_groups_names_to_ids( constructor_args['live_activity_groups']) unpacked_arguments = {} unpacked_arguments['space.name'] = constructor_args['space_name'] unpacked_arguments['space.description'] = constructor_args[ 'space_description'] unpacked_arguments['_eventId_save'] = 'Save' unpacked_arguments['liveActivityGroupIds'] = live_activity_group_ids if not self._api_object_exists(Space, constructor_args, self.get_space): space = Space().new(self.uri, unpacked_arguments) self.log.info("Master:new_space:%s" % space) return space else: return [] def new_named_script(self, name, description, language, content, scheduled=None): """Creates a new named script.""" raise NotImplementedError """ Private methods below """ def _translate_live_activity_groups_names_to_ids(self, live_activity_groups): """ Converts live activity groups dicts to list of ids :param live_activities: list of dictionaries containing following keys:: {\ "live_activity_group_name" : "some_name",\ } :rtype: list """ live_activity_groups_ids = [] for lag_data in live_activity_groups: live_activity_group = self.get_live_activity_group(lag_data) live_activity_groups_ids.append(live_activity_group.id()) self.log.info( "Translated %s live_activity_groups_names to ids with a result of %s" % (len(live_activity_groups_ids), live_activity_groups_ids)) return live_activity_groups_ids def _translate_live_activities_names_to_ids(self, live_activities): """ Converts live activities dicts to list of ids :param live_activities: list of dictionaries containing following keys:: {\ "live_activity_name" : "some_name",\ "space_controller_name" : "some controller name"\ } :rtype: list """ live_activity_ids = [] for la_data in live_activities: self.log.info("Getting Live Activity for data: %s" % la_data) live_activity = self.get_live_activity(la_data) live_activity_ids.append(live_activity.id()) self.log.info( "Translated %s live_activity_names to ids with a result of %s" % (len(live_activity_ids), live_activity_ids)) return live_activity_ids """ Private methods below """ def _api_object_exists(self, object_type, constructor_args, getter_method): self.log.info( "Checking whether object %s with following attributes %s exists in the API" % (object_type, constructor_args)) api_object = getter_method(constructor_args) if api_object: self.log.warn("Object already exists: %s" % api_object) return True else: self.log.info("Object does not exist yet") return False def _validate_single_getter_results(self, response, expected_type, exception): """ Validates response from the API. Runs type and other simple checks. :param response: list of objects returned from api :param expected_type: expected type of the object :param exception: exception to throw if response is invalid :rtype: interactivespaces object """ if len(response) > 1: raise exception("API query returned more than one row") elif len(response) == 0: return None elif isinstance(response[0], expected_type): try: api_object = response[0].fetch() self.log.info("Getter method returned Object:%s" % str(api_object)) return api_object except Exception, e: raise else:
class Activity(Fetchable, Deletable): """ Should be responsible for managing a single activity """ def __init__(self, data_hash=None, uri=None, activity_archive_uri=None, name=None): self.class_name = self.__class__.__name__ self.log = Logger().get_logger() super(Activity, self).__init__() if (data_hash == None and uri == None): self.log.info("No data provided - assuming creation of new Activity") elif (data_hash != None and uri != None): self.data_hash = data_hash self.uri = uri self.absolute_url = self._get_absolute_url() self.log.info("Instantiated Activity object with url=%s" % self.absolute_url) def __repr__(self): return str(self.data_hash) def new(self, uri, constructor_args): """ Method to keep naming convention of .new() methods """ new_activity = self.upload(uri, constructor_args['zip_file_handler']) return new_activity def upload(self, uri, zip_file_handler): """ Should make a deployment of the activity with following steps: - receive handler to a local zipfile - upload it to the API - save - set instance variables for the object :return: False or URL to a new Activity :param uri: stirng :param zip_file_handler: 'file' class instance :rtype: new Activity object or False """ self.log.info("Uploading new Activity from file %s" % zip_file_handler) route = Path().get_route_for('Activity', 'upload') url = "%s%s" % (uri, route) payload = {"_eventId_save" : "Save"} request_response = self._api_post_json(url, payload, zip_file_handler) return self.check_upload_response(request_response) def check_upload_response(self, request_response): """ Dirty workaround for nasty html redirect """ if request_response.url: self.absolute_url = request_response.url.replace("view.html", "view.json") self.fetch() self.log.info("Created new Activity with url=%s, data_hash is now %s" % (self.absolute_url, self.data_hash)) return self else: self.log.info("Created new Activity %s but returned False" % self) return False def to_json(self): """ Should selected attributes in json form defined by the template """ self.serializer = ActivitySerializer(self.data_hash) return self.serializer.to_json() def fetch(self): """ Should retrieve data from Master API""" self.data_hash = self._refresh_object(self.absolute_url) return self def name(self): """ Should return live activity name""" return self.data_hash['activity']['name'] def identifying_name(self): """ Should return identifying name """ return self.data_hash['activity']['identifyingName'] def version(self): """ Should return Activity version """ return self.data_hash['activity']['version'] def id(self): """ Should return Activity id """ return self.data_hash['activity']['id'] def description(self): """ Should return Activity description """ return self.data_hash['activity']['description'] """ Private methods below""" def _get_absolute_url(self): """ Initial data hash without subattributes that comes from the all.json method """ activity_id = self.data_hash['id'] url = "%s/activity/%s/view.json" % (self.uri, activity_id) return url
class LiveActivity(Fetchable, Statusable, Deletable, Shutdownable, Startupable, Activatable, Configurable, Cleanable, Metadatable, Deployable): """ @summary: Should be responsible for managing single LiveActivity @todo: .new() should return instance of fetched live activity """ def __init__(self, data_hash=None, uri=None): """ @summary: when called with constructor_args and other vars set to None, new LiveActivity will be created @param data_hash: should be master API liveActivity json, may be blank @param uri: should be a link to "view.json" of the given live activity """ self.log = Logger().get_logger() self.class_name = self.__class__.__name__ super(LiveActivity, self).__init__() if (data_hash==None and uri==None): self.log.info("No data provided - assuming creation of new LiveActivity") elif (data_hash!=None and uri!=None): self.data_hash = data_hash self.uri = uri self.absolute_url = self._get_absolute_url() self.log.info("Instantiated LiveActivity object with url=%s" % self.absolute_url) def __repr__(self): return str(self.data_hash) def new(self, uri, new_data_hash): """ @summary: used to create new live activity through API and set the "uri" so that we can operate on this instance of LiveActivity right away after .new() returns True @param new_data_hash: dict {"live_activity_name" : "", "live_activity_description" : "", "activity_id" : "", "controller_id" : "" } @param uri: "http://some_server/prefix (passed by master)" @rtype: new LiveActivity object or False """ self.log.info("Creating new Live Activity with arguments: %s" % new_data_hash) route = Path().get_route_for('LiveActivity', 'new') url = "%s%s" % (uri, route) request_response = self._api_post_json(url, new_data_hash) if request_response.url: self.absolute_url = request_response.url.replace("view.html", "view.json") self.fetch() self.log.info("Created new LiveActivity with url=%s, data_hash is now %s" % (self.absolute_url, self.data_hash)) return self else: self.log.info("Created new LiveActivity %s but returned False" % self) return False def to_json(self): """ @summary: Should selected attributes in json form defined by the template """ self.serializer = LiveActivitySerializer(self.data_hash) return self.serializer.to_json() def name(self): """ @summary: Should return live activity name """ return self.data_hash['name'] def status(self): """ @summary: Should return status that is currently held in the object instance """ try: status_data = self.data_hash['active']['runtimeState'] return status_data except LiveActivityException("Activity not running or non existent"): return "UNKNOWN" def identifying_name(self): """ @summary: Should return LiveActivity identifying name """ return self.data_hash['activity']['identifyingName'] def version(self): """ @summary: Should return LiveActivity version """ return self.data_hash['activity']['version'] def id(self): """ @summary: Should return LiveActivity id @rtype: string """ return self.data_hash['id'] """ Private methods below this text """ def _get_absolute_url(self): """ @rtype: string """ route = Path().get_route_for(self.class_name, 'view') % self.data_hash['id'] url = "%s%s" % (self.uri, route) return url
class Master(Communicable): """ This is the main class with all the logic needed for high level stuff. You will typically use instance of Master for all your scripts. """ def __init__(self, host='lg-head', port='8080', prefix='/interactivespaces', logfile_path='ispaces-client.log'): """ :param host: default value is lg-head :param port: default value is 8080 :param prefix: default value is /interactivespaces :todo: refactor filter_* methods because they're not DRY """ self.host, self.port, self.prefix = host, port, prefix self.log = Logger(logfile_path=logfile_path).get_logger() self.uri = "http://%s:%s%s" % (self.host, self.port, prefix) super(Master, self).__init__() def get_activities(self, search_pattern=None): """ Retrieves a list of Activity objects :rtype: list :param search_pattern: dictionary of regexps used for searching through Activities example regexp dict:: {\ "activity_name" : "regexp"\ "activity_version" : "regexp"\ } every search_pattern dictionary key may be blank/null """ url = self._compose_url(class_name='Master', method_name='get_activities', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.info('Got response for "get_activities" %s ' % str(response)) self.log.info('get_activities returned %s objects' % str(len(response))) activities = self._filter_activities(response, search_pattern) return activities def get_activity(self, search_pattern=None): """ Retrieves a list of Activity objects :rtype: list :param search_pattern: dictionary of regexps used for searching through Activities example regexp dict:: {\ "activity_name" : "regexp",\ "activity_version" : "regexp"\ } every search_pattern dictionary key may be blank/null """ url = self._compose_url(class_name='Master', method_name='get_activities', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.info('Got response for "get_activities" %s ' % str(response)) self.log.info('get_activities returned %s objects' % str(len(response))) activity = self._filter_activities(response, search_pattern) return self._validate_single_getter_results(activity, Activity, ActivityNotFoundException) def get_live_activities(self, search_pattern=None): """ Retrieves a list of LiveActivity objects :rtype: list :param search_pattern: dictionary of regexps used for searching through LiveActivity names example regexp dict:: {\ "live_activity_name" : "regexp",\ "space_controller_name" : "regexp"\ } - each search_pattern dictionary key may be blank/null """ url = self._compose_url(class_name='Master', method_name='get_live_activities', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.debug('Got response for "get_live_activities" %s ' % str(response)) self.log.info('get_live_activities returned %s objects' % str(len(response))) live_activities = self._filter_live_activities(response, search_pattern) return live_activities def get_live_activity(self, search_pattern=None): """ Retrieves a list of LiveActivity objects :rtype: LiveActivity or False :param search_pattern: dictionary of regexps used for searching through LiveActivity names example regexp dict:: {\ "live_activity_name" : "GE ViewSync Master on Node A",\ "space_controller_name" : "ISCtlDispAScreen00"\ } each search_pattern dictionary key may be blank/null """ url = self._compose_url(class_name='Master', method_name='get_live_activities', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.debug('Got response for "get_live_activities" %s ' % str(response)) self.log.info('get_live_activities returned %s objects' % str(len(response))) live_activity = self._filter_live_activities(response, search_pattern) return self._validate_single_getter_results(live_activity, LiveActivity, LiveActivityNotFoundException) def get_live_activity_groups(self, search_pattern=None): """ Retrieves a list of live activity groups. :rtype: list :param search_pattern: dictionary of regexps used for searching through LiveActivity names example regexp dict:: {\ "live_activity_group_name" : "regexp"\ } """ url = self._compose_url(class_name='Master', method_name='get_live_activity_groups', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.debug('Got response for "get_live_activity_groups" %s ' % str(response)) self.log.info('get_live_activity_groups returned %s objects - filtering with %s' % (str(len(response)), search_pattern)) live_activity_groups = self._filter_live_activity_groups(response, search_pattern) return live_activity_groups def get_live_activity_group(self, search_pattern=None): """ Retrieves a list of live activity groups. :rtype: list :param search_pattern: dictionary of regexps used for searching through LiveActivity names example regexp dict:: {\ "live_activity_group_name" : "regexp"\ } """ url = self._compose_url(class_name='Master', method_name='get_live_activity_groups', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.debug('Got response for "get_live_activity_groups" %s ' % str(response)) self.log.info('get_live_activity_groups returned %s objects' % str(len(response))) live_activity_group = self._filter_live_activity_groups(response, search_pattern) return self._validate_single_getter_results(live_activity_group, LiveActivityGroup, LiveActivityGroupNotFoundException) def get_spaces(self, search_pattern=None): """ Retrieves a list of live activity groups. :rtype: list :param search_pattern: dictionary containing space name regexp example regexp dict:: {"space_name" : "regexp"} """ url = self._compose_url(class_name='Master', method_name='get_spaces', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.debug('Got response for "get_spaces" %s ' % str(response)) spaces = self._filter_spaces(response, search_pattern) return spaces def get_space(self, search_pattern=None): """ Retrieves a Space :rtype: Space :param search_pattern: dictionary containing space name regexp example regexp dict:: {"space_name" : "regexp"} """ url = self._compose_url(class_name='Master', method_name='get_spaces', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.debug('Got response for "get_spaces" %s ' % str(response)) space = self._filter_spaces(response, search_pattern) return self._validate_single_getter_results(space, Space, SpaceNotFoundException) def get_space_controllers(self, search_pattern=None): """ Retrieves a list of live space controllers. :rtype: list :param search_pattern: dictionary containing regexps and strings example regexp dict:: {\ "state" : "STRING",\ "mode" : "STRING",\ "name" : "regexp",\ "uuid" : "STRING"\ } """ url = self._compose_url(class_name='Master', method_name='get_space_controllers', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.debug('Got response for "get_controllers" %s ' % str(response)) space_controllers = self._filter_space_controllers(response, search_pattern) return space_controllers def get_space_controller(self, search_pattern=None): """ Retrieves a list of live space controllers. :rtype: SpaceController :param search_pattern: dictionary containing regexps and strings example regexp dict:: {\ "space_controller_state" : "STRING",\ "space_controller_mode" : "STRING",\ "space_controller_name" : "regexp",\ "space_controller_uuid" : "STRING"\ } """ url = self._compose_url(class_name='Master', method_name='get_space_controllers', uri=self.uri) self.log.info("Trying to retrieve url=%s" % url) response = self._api_get_json(url) self.log.debug('Got response for "get_controllers" %s ' % str(response)) space_controller = self._filter_space_controllers(response, search_pattern) return self._validate_single_getter_results(space_controller, SpaceController, ControllerNotFoundException) def get_named_scripts(self, pattern=None): """Retrieves a list of named scripts.""" raise NotImplementedError def new_live_activity(self, constructor_args): """ Creates a new live activity and returns it :param constructor_args: dictionary containing all of below keys:: {"live_activity_name": "string containing name of a new live activity (mandatory)",\ "live_activity_description" : "string containing description",\ "activity_name" : "string containing activity name",\ "space_controller_name" : "string containing controller name"} :rtype: LiveActivity """ unpacked_arguments={} unpacked_arguments['activityId'] = self.get_activity({"activity_name" : constructor_args['activity_name']}).id() unpacked_arguments['controllerId'] = self.get_space_controller({"space_controller_name" : constructor_args['space_controller_name']}).id() unpacked_arguments['liveActivity.description'] = constructor_args['live_activity_description'] unpacked_arguments['liveActivity.name'] = constructor_args['live_activity_name'] unpacked_arguments['_eventId_save'] = 'Save' if not self._api_object_exists(LiveActivity, constructor_args, self.get_live_activity): activity = LiveActivity().new(self.uri, unpacked_arguments) self.log.info("Master:new_live_activity returned activity:%s" % activity) return activity else: return [] def new_activity(self, constructor_args): """ Creates a new activity and returns it :param constructor_args: dictionary containing all of below keys:: {\ "zip_file_handler": "zipfile object (mandatory)",\ "activity_name" : "some name",\ "activity_version": "some version"\ } :rtype: Activity or False """ self.log.info("Going to create new activity with arguments: %s" % constructor_args) if not self._api_object_exists(Activity, constructor_args, self.get_activity): activity = Activity().new(self.uri, constructor_args) self.log.info("Master:new_activity returned activity:%s" % activity) return activity else: return [] def new_space_controller(self, constructor_args): """ Creates new controller :param constructor_args: dictionary containing all of below keys:: {\ "space_controller_name" : "mandatory string",\ "space_controller_description" : "non mandatory string",\ "space_controller_host_id" : "mandatory string"\ } """ if not self._api_object_exists(SpaceController, constructor_args, self.get_space_controller): space_controller = SpaceController().new(self.uri, constructor_args) self.log.info("Master:new_space_controller:%s" % space_controller) return space_controller else: return [] def new_live_activity_group(self, constructor_args): """ Creates a new live activity group. :param constructor_args: dictionary with following structure:: {\ "live_activity_group_name" : "example.py live_activity_group_name",\ "live_activity_group_description" : "created by example.py",\ "live_activities" : [{"live_activity_name" : "SV Master on Node A",\ "space_controller_name" : "ISCtlDispAScreen00"},\ {"live_activity_name" : "SV Slave 01 on Node A",\ "space_controller_name" : "ISCtlDispAScreen00"}]\ } """ self.log.info("Live activities that will comprise new live activity group: %s" % constructor_args['live_activities']) live_activity_ids = self._translate_live_activities_names_to_ids(constructor_args['live_activities']) unpacked_arguments = {} unpacked_arguments['liveActivityGroup.name'] = constructor_args['live_activity_group_name'] unpacked_arguments['liveActivityGroup.description'] = constructor_args['live_activity_group_description'] unpacked_arguments['_eventId_save'] = 'Save' unpacked_arguments['liveActivityIds'] = live_activity_ids if not self._api_object_exists(LiveActivityGroup, constructor_args, self.get_live_activity_group): live_activity_group = LiveActivityGroup().new(self.uri, unpacked_arguments) self.log.info("Master:new_live_activity_group:%s" % live_activity_group) return live_activity_group else: return [] def new_space(self, constructor_args): """ Creates a new Space. :param constructor_args: dictionary with following structure:: {\ "space_name" : "example.py live_activity_group_name",\ "space_description" : "created by example.py",\ "live_activity_groups" : [{"live_activity_group_name" : "Media Services"}]\ } """ live_activity_group_ids = self._translate_live_activity_groups_names_to_ids(constructor_args['live_activity_groups']) unpacked_arguments = {} unpacked_arguments['space.name'] = constructor_args['space_name'] unpacked_arguments['space.description'] = constructor_args['space_description'] unpacked_arguments['_eventId_save'] = 'Save' unpacked_arguments['liveActivityGroupIds'] = live_activity_group_ids if not self._api_object_exists(Space, constructor_args, self.get_space): space = Space().new(self.uri, unpacked_arguments) self.log.info("Master:new_space:%s" % space) return space else: return [] def new_named_script(self, name, description, language, content, scheduled=None): """Creates a new named script.""" raise NotImplementedError """ Private methods below """ def _translate_live_activity_groups_names_to_ids(self, live_activity_groups): """ Converts live activity groups dicts to list of ids :param live_activities: list of dictionaries containing following keys:: {\ "live_activity_group_name" : "some_name",\ } :rtype: list """ live_activity_groups_ids = [] for lag_data in live_activity_groups: live_activity_group = self.get_live_activity_group(lag_data) live_activity_groups_ids.append(live_activity_group.id()) self.log.info("Translated %s live_activity_groups_names to ids with a result of %s" % (len(live_activity_groups_ids), live_activity_groups_ids) ) return live_activity_groups_ids def _translate_live_activities_names_to_ids(self, live_activities): """ Converts live activities dicts to list of ids :param live_activities: list of dictionaries containing following keys:: {\ "live_activity_name" : "some_name",\ "space_controller_name" : "some controller name"\ } :rtype: list """ live_activity_ids = [] for la_data in live_activities: self.log.info("Getting Live Activity for data: %s" % la_data) live_activity = self.get_live_activity(la_data) live_activity_ids.append(live_activity.id()) self.log.info("Translated %s live_activity_names to ids with a result of %s" % (len(live_activity_ids), live_activity_ids) ) return live_activity_ids """ Private methods below """ def _api_object_exists(self, object_type, constructor_args, getter_method): self.log.info("Checking whether object %s with following attributes %s exists in the API" % (object_type, constructor_args)) api_object = getter_method(constructor_args) if api_object: self.log.warn("Object already exists: %s" % api_object) return True else: self.log.info("Object does not exist yet") return False def _validate_single_getter_results(self, response, expected_type, exception): """ Validates response from the API. Runs type and other simple checks. :param response: list of objects returned from api :param expected_type: expected type of the object :param exception: exception to throw if response is invalid :rtype: interactivespaces object """ if len(response) > 1: raise exception("API query returned more than one row") elif len(response) == 0: return None elif isinstance(response[0], expected_type): try: api_object = response[0].fetch() self.log.info("Getter method returned Object:%s" % str(api_object)) return api_object except Exception, e: raise else:
class Path(object): ''' Should be responsible for static translation of routes ''' def __init__(self): self.routes = { 'Master': { 'get_activities': '/activity/all.json', 'get_live_activities': '/liveactivity/all.json', 'get_live_activity_groups': '/liveactivitygroup/all.json', 'get_spaces': '/space/all.json', 'get_space_controllers': '/spacecontroller/all.json', 'get_named_scripts': '/admin/namedscript/all.json', 'new_live_activity_group': '/liveactivitygroup/new', 'new_space': '/space/new.json', 'new_controller': '/spacecontroller/new.json', 'new_named_script': '/admin/namedscript/new.json' }, 'Activity': { 'view': '/activity/%s/view.json', 'upload': '/activity/upload', 'delete': '/activity/%s/delete.html' }, 'LiveActivity': { 'status': '/liveactivity/%s/status.json', 'view': '/liveactivity/%s/view.json', 'new': '/liveactivity/new', 'delete': '/liveactivity/%s/delete.html', 'shutdown': '/liveactivity/%s/shutdown.json', 'startup': '/liveactivity/%s/startup.json', 'activate': '/liveactivity/%s/activate.json', 'deactivate': '/liveactivity/%s/deactivate.json', 'deploy': '/liveactivity/%s/deploy.json', 'configure': '/liveactivity/%s/configure.json', 'clean_tmp': '/liveactivity/%s/cleantmpdata.json', 'clean_permanent': '/liveactivity/%s/cleanpermanentdata.json', 'metadata': '/liveactivity/%s/metadata/edit', 'config': '/liveactivity/%s/config/edit' }, 'LiveActivityGroup': { 'view': '/liveactivitygroup/%s/view.json', 'new': '/liveactivitygroup/new', 'status': '/liveactivitygroup/%s/liveactivitystatus.json', 'delete': '/liveactivitygroup/%s/delete.html', 'shutdown': '/liveactivitygroup/%s/shutdown.json', 'startup': '/liveactivitygroup/%s/startup.json', 'activate': '/liveactivitygroup/%s/activate.json', 'deactivate': '/liveactivitygroup/%s/deactivate.json', 'deploy': '/liveactivitygroup/%s/deploy.json', 'configure': '/liveactivitygroup/%s/configure.json', 'metadata': '/liveactivitygroup/%s/metadata/edit', 'edit': '/liveactivitygroup/%s/edit.json' }, 'Space': { 'view': '/space/%s/view.json', 'status': '/space/%s/status.json', 'delete': '/space/%s/delete.html', 'new': '/space/new', 'shutdown': '/space/%s/shutdown.json', 'startup': '/space/%s/startup.json', 'activate': '/space/%s/activate.json', 'deactivate': '/space/%s/deactivate.json', 'deploy': '/space/%s/deploy.json', 'configure': '/space/%s/configure.json', 'metadata': '/space/%s/metadata/edit', 'edit': '/space/%s/edit.json' }, 'SpaceController': { 'new': '/spacecontroller/new', 'status': '/spacecontroller/%s/status.json', 'delete': '/spacecontroller/%s/delete.html', 'shutdown': '/spacecontroller/%s/shutdown.json', 'deploy': '/spacecontroller/%s/deploy.json', 'connect': '/spacecontroller/%s/connect.json', 'disconnect': '/spacecontroller/%s/disconnect.json' } } self.log = Logger().get_logger() def get_route_for(self, class_name, method_name): """ Should receive caller class name and caller method in order to return a proper route in the master API :rtype: string """ try: return self.routes[class_name][method_name] except PathException, e: self.log.info( "Could not return route for class_name %s and method %s because %s" % (class_name, method_name, e))
class Activity(Fetchable, Deletable): """ @summary: Should be responsible for managing a single activity """ def __init__(self, data_hash=None, uri=None, activity_archive_uri=None, name=None): self.log = Logger().get_logger() super(Activity, self).__init__() if (data_hash==None and uri==None): self.log.info("No data provided - assuming creation of new Activity") elif (data_hash!=None and uri!=None): self.data_hash = data_hash self.uri = uri self.absolute_url = self._get_absolute_url() self.log.info("Instantiated Activity object with url=%s" % self.absolute_url) def __repr__(self): return str(self.data_hash) def new(self, uri, constructor_args): """ @summary: method to keep naming convention of .new() methods """ new_activity = self.upload(uri, constructor_args['zip_file_handler']) return new_activity def upload(self, uri, zip_file_handler): """ @summary: Should make a deployment of the activity with followin steps: - receive handler to a local zipfile - upload it to the API - save - set instance variables for the object @return: False or URL to a new Activity @param uri: stirng @param zip_file_handler: 'file' class instance @rtype: new Activity object or False """ self.log.info("Uploading new Activity from file %s" % zip_file_handler) route = Path().get_route_for('Activity', 'upload') url = "%s%s" % (uri, route) payload = {"_eventId_save" : "Save"} request_response = self._api_post_json(url, payload, zip_file_handler) if request_response.url: self.absolute_url = request_response.url.replace("view.html", "view.json") self.fetch() self.log.info("Created new Activity with url=%s, data_hash is now %s" % (self.absolute_url, self.data_hash)) return self else: self.log.info("Created new Activity %s but returned False" % self) return False def to_json(self): """ Should selected attributes in json form defined by the template """ self.serializer = ActivitySerializer(self.data_hash) return self.serializer.to_json() def fetch(self): """ Should retrieve data from Master API""" self.data_hash = self._refresh_object(self.absolute_url) def name(self): """ Should return live activity name""" return self.data_hash['activity']['name'] def identifying_name(self): """ Should return identifying name """ return self.data_hash['activity']['identifyingName'] def version(self): """ Should return Activity version """ return self.data_hash['activity']['version'] def id(self): """ Should return Activity id """ return self.data_hash['activity']['id'] def description(self): """ Should return Activity description """ return self.data_hash['activity']['description'] """ Private methods below""" def _get_absolute_url(self): activity_id = self.data_hash['id'] url = "%s/activity/%s/view.json" % (self.uri, activity_id) return url
class Path(object): ''' Should be responsible for static translation of routes ''' def __init__(self): self.routes = { 'Master': { 'get_activities' : '/activity/all.json', 'get_live_activities' : '/liveactivity/all.json', 'get_live_activity_groups' : '/liveactivitygroup/all.json', 'get_spaces' : '/space/all.json', 'get_space_controllers' : '/spacecontroller/all.json', 'get_named_scripts' : '/admin/namedscript/all.json', 'new_live_activity_group' : '/liveactivitygroup/new', 'new_space' : '/space/new.json', 'new_controller' : '/spacecontroller/new.json', 'new_named_script' : '/admin/namedscript/new.json' }, 'Activity' : { 'view' : '/activity/%s/view.json', 'upload' : '/activity/upload', 'delete' : '/activity/%s/delete.html' }, 'LiveActivity' : { 'status' : '/liveactivity/%s/status.json', 'view' : '/liveactivity/%s/view.json', 'new' : '/liveactivity/new', 'delete' : '/liveactivity/%s/delete.html', 'shutdown' : '/liveactivity/%s/shutdown.json', 'startup' : '/liveactivity/%s/startup.json', 'activate' : '/liveactivity/%s/activate.json', 'deactivate' : '/liveactivity/%s/deactivate.json', 'deploy' : '/liveactivity/%s/deploy.json', 'configure' : '/liveactivity/%s/configure.json', 'clean_tmp' : '/liveactivity/%s/cleantmpdata.json', 'clean_permanent' : '/liveactivity/%s/cleanpermanentdata.json', 'metadata' : '/liveactivity/%s/metadata/edit', 'config' : '/liveactivity/%s/config/edit' }, 'LiveActivityGroup' : { 'view' : '/liveactivitygroup/%s/view.json', 'new' : '/liveactivitygroup/new', 'status' : '/liveactivitygroup/%s/liveactivitystatus.json', 'delete' : '/liveactivitygroup/%s/delete.html', 'shutdown' : '/liveactivitygroup/%s/shutdown.json', 'startup' : '/liveactivitygroup/%s/startup.json', 'activate' : '/liveactivitygroup/%s/activate.json', 'deactivate' : '/liveactivitygroup/%s/deactivate.json', 'deploy' : '/liveactivitygroup/%s/deploy.json', 'configure' : '/liveactivitygroup/%s/configure.json', 'metadata' : '/liveactivitygroup/%s/metadata/edit', 'edit' : '/liveactivitygroup/%s/edit.json' }, 'Space' : { 'view' : '/space/%s/view.json', 'status' : '/space/%s/status.json', 'delete' : '/space/%s/delete.html', 'new' : '/space/new', 'shutdown' : '/space/%s/shutdown.json', 'startup' : '/space/%s/startup.json', 'activate' : '/space/%s/activate.json', 'deactivate' : '/space/%s/deactivate.json', 'deploy' : '/space/%s/deploy.json', 'configure' : '/space/%s/configure.json', 'metadata' : '/space/%s/metadata/edit', 'edit' : '/space/%s/edit.json' }, 'SpaceController' :{ 'new' : '/spacecontroller/new', 'status': '/spacecontroller/%s/status.json', 'delete': '/spacecontroller/%s/delete.html', 'shutdown': '/spacecontroller/%s/shutdown.json', 'deploy': '/spacecontroller/%s/deploy.json', 'connect' : '/spacecontroller/%s/connect.json', 'disconnect' : '/spacecontroller/%s/disconnect.json' } } self.log = Logger().get_logger() def get_route_for(self, class_name, method_name): """ Should receive caller class name and caller method in order to return a proper route in the master API :rtype: string """ try: return self.routes[class_name][method_name] except PathException, e: self.log.info("Could not return route for class_name %s and method %s because %s" % (class_name, method_name, e))