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
Esempio n. 4
0
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
Esempio n. 7
0
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
Esempio n. 8
0
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))
Esempio n. 10
0
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
Esempio n. 15
0
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
Esempio n. 20
0
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
Esempio n. 24
0
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 {}
Esempio n. 26
0
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
Esempio n. 27
0
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
Esempio n. 28
0
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
Esempio n. 29
0
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
Esempio n. 33
0
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))