Example #1
0
 def setUp(self):
     '''
     If the cloud info file is not present assume running in a
     UNITTEST environment. This will allow for exercising some
     of the code without having to be running in a cloud VM.
     '''
     # Create the client Object
     self.cs_client = CSClient(**DUMMY_CS_CONFIG)
     self.cs_client.http = HttpUnitTest()
Example #2
0
    def __init__(self, conf):
        '''
        conf: argparse dict
        '''
        tool_dir = {}
        if 'pwd' in conf and conf['pwd']:
            tool_dir = {'tool_dir': PWD_TOOLING}

        # Create the Client Object
        self.client = CSClient(**conf)
        self.client.test_connection()

        # Get any optional tooling from the Config Server
        tooling_status, tarball = self.client.get_tooling()
        if tooling_status != 200:
            LOGGER.error('Get Tooling returned: %s' % tooling_status)
            raise AAErrorGetTooling('Get Tooling returned: %s' %
                                    tooling_status)

        self.tooling = Tooling(tarball, **tool_dir)
Example #3
0
def main():
    '''
    Description:
        This script will be used on EC2 for configuring the running
        instance based on Cloud Engine configuration supplied at
        launch time in the user data.

        Config Server Status:
        200 HTTP OK - Success and no more data of this type
        202 HTTP Accepted - Success and more data of this type
        404 HTTP Not Found - This may be temporary so try again
    '''
    # parse the args and setup logging
    conf = parse_args()
    log_file = {}
    if 'pwd' in conf and conf.pwd:
        log_file = {'logfile_name': 'audrey.log'}

    logger = setup_logging(level=conf.log_level, **log_file)

    if not conf.endpoint:
        # discover the cloud I'm on
        # update the conf with the user data
        #conf = dict(vars(conf).items() + user_data.discover().read().items())
        vars(conf).update(user_data.discover().read().items())

    # ensure the conf is a dictionary, not a namespace
    if hasattr(conf, '__dict__'):
        conf = vars(conf)

    logger.info('Invoked audrey main')

    # Create the Client Object and test connectivity
    # to CS by negotiating the api version
    client = CSClient(**conf)
    client.test_connection()

    # Get the agent object
    agent = AudreyFactory(client.api_version).agent
    # run the agent
    agent(conf).run()
Example #4
0
    def parse_cs_str(self, src, tooling):
        """
        Description:
            Parse the provides parameters text message sent from the
            Config Server.

        Input:
            The provides parameters string obtained from the Config Server.

            The delimiters will be an | and an &

            To ensure all the data was received the entire string will be
            terminated with an "|".

            This will be a continuous text string (no CR or New Line).

            Format:
            |name1&name2...&nameN|

            e.g.:
            |ipaddress&virtual|

        Returns:
            - Provides populated with param names
                  as the keys and None as the value
        """

        CSClient.validate_message(src)

        # split and prune the payload
        src = src[1:-1].split("|")
        if len(src) >= 1:
            for provides in src[0].split("&"):
                if provides:
                    self[provides] = None

        return self
Example #5
0
    def parse_cs_str(self, src, tooling):
        '''
        Description:
            Parse the provides parameters text message sent from the
            Config Server.

        Input:
            The provides parameters string obtained from the Config Server.

            The delimiters will be an | and an &

            To ensure all the data was received the entire string will be
            terminated with an "|".

            This will be a continuous text string (no CR or New Line).

            Format:
            |name1&name2...&nameN|

            e.g.:
            |ipaddress&virtual|

        Returns:
            - Provides populated with param names
                  as the keys and None as the value
        '''

        CSClient.validate_message(src)

        # split and prune the payload
        src = src[1:-1].split('|')
        if len(src) >= 1:
            for provides in src[0].split('&'):
                if provides:
                    self[provides] = None

        return self
Example #6
0
    def __init__(self, conf):
        '''
        conf: argparse dict
        '''
        tool_dir = {}
        if 'pwd' in conf and conf['pwd']:
            tool_dir = {'tool_dir': PWD_TOOLING}

        # Create the Client Object
        self.client = CSClient(**conf)
        self.client.test_connection()

        # Get any optional tooling from the Config Server
        tooling_status, tarball = self.client.get_tooling()
        if tooling_status != 200:
            LOGGER.error('Get Tooling returned: %s' % tooling_status)
            raise AAErrorGetTooling('Get Tooling returned: %s' % tooling_status)
        
        self.tooling = Tooling(tarball, **tool_dir)
Example #7
0
class TestAudreyCSClient(unittest.TestCase):
    '''
    Class for exercising the gets and put to and from the CS
    '''

    def setUp(self):
        '''
        If the cloud info file is not present assume running in a
        UNITTEST environment. This will allow for exercising some
        of the code without having to be running in a cloud VM.
        '''
        # Create the client Object
        self.cs_client = CSClient(**DUMMY_CS_CONFIG)
        self.cs_client.http = HttpUnitTest()

    def test_success_get_configs(self):
        '''
        Success case:
        - Exercise get_configs()
        '''
        self.cs_client.get_configs()

    def test_success_get_tooling(self):
        '''
        Success case:
        - Exercise get_tooling()
        '''
        self.cs_client.get_tooling()

    def test_success_get_provides(self):
        '''
        Success case:
        - Exercise get_provides()
        '''
        self.cs_client.get_provides()

    def test_success_get_confs_n_provides(self):
        '''
        Success case:
        - Exercise get_configs() and get_provides()
        '''
        self.cs_client.get_configs()
        self.cs_client.get_provides()

    def test_success_put_provides(self):
        '''
        Success case:
        - Exercise put_provides_values()
        '''
        self.cs_client.put_provides('')

    def test_error_http_status(self):
        '''
        Success case:
        - Get a 401
        '''
        self.assertRaises(AAError, self.cs_client._validate_http_status,
                          HttpUnitTest.HttpUnitTestResponse(401))

    def test_catch_get_exception(self):
        '''
        Success case:
        - get fails but audrey recovers
        '''
        self.cs_client._get('http://hostname/raiseException')

    def test_catch_put_exception(self):
        '''
        Success case:
        - put fails but audrey recovers
        '''
        self.cs_client._put('http://hostname/raiseException')

    def test_failed_version(self):
        audrey.csclient.VERSION_URL = '/badversion'
        self.assertRaises(AAErrorApiNegotiation,
                          self.cs_client.test_connection)
Example #8
0
    def parse_require_config(src, tooling):
        '''
        Description:
          Parse the required config text message sent from the Config Server.

        Input:
          The required config string obtained from the Config Server,
          delimited by an | and an &

          Two tags will mark the sections of the data,
          '|service|' and  '|parameters|'

          To ensure all the data was received the entire string will be
          terminated with an "|".

          The string "|service|" will precede a service names.

          The string "|parameters|" will precede the parameters for
          the preceeding service, in the form: names&<b64 encoded values>.

        This will be a continuous text string (no CR or New Line).

          Format (repeating for each service):

          |service|<s1>|parameters|name1&<b64val>|name2&<b64val>|nameN&<b64v>|

          e.g.:
          |service|ssh::server|parameters|ssh_port&<b64('22')>
          |service|apache2::common|apache_port&<b64('8081')>|

        Returns:
            - A list of ServiceParams objects.
        '''

        services = []
        new = None

        CSClient.validate_message(src)

        # Message specific validation
        if src == '||':
            # special case indicating no required config needed.
            return []

        # split on pipe and chop of first and last, they will always be empty
        src = src.split('|')[1:-1]
        # get the indexes of the service identifiers
        srvs = deque([i for i, x in enumerate(src) if x == 'service'])
        srvs.append(len(src))

        if srvs[0] != 0:
            raise AAError(('|service| is not the first tag found. %s') % (src))

        while len(srvs) > 1:
            # rebuild a single service's cs string
            svc_str = "|%s|" % "|".join(src[srvs[0]:srvs[1]])
            name = src[srvs[0] + 1]
            if name in ['service', 'parameters'] or '&' in name:
                raise AAError('invalid service name: %s' % name)
            # instanciate the service with it's name
            svc = Service(name, tooling)
            svc.parse_configs(svc_str)
            services.append(svc)
            srvs.popleft()

        return services
Example #9
0
class AgentV1(object):
    '''
    contains the main logic for processing
    This object is compatible with API Version 1
    '''
    def __init__(self, conf):
        '''
        conf: argparse dict
        '''
        tool_dir = {}
        if 'pwd' in conf and conf['pwd']:
            tool_dir = {'tool_dir': PWD_TOOLING}

        # Create the Client Object
        self.client = CSClient(**conf)
        self.client.test_connection()

        # Get any optional tooling from the Config Server
        tooling_status, tarball = self.client.get_tooling()
        if tooling_status != 200:
            LOGGER.error('Get Tooling returned: %s' % tooling_status)
            raise AAErrorGetTooling('Get Tooling returned: %s' % tooling_status)
        
        self.tooling = Tooling(tarball, **tool_dir)

    def run(self):
        '''
        Main agent loop, called by main() in /usr/bin/audrey
        '''
        # 0 means don't run again
        # -1 is non zero so initial runs will happen
        config_status = -1
        provides_status = -1

        max_retry = MAX_RETRY
        loop_count = 60
        services = []

        # Process the Requires and Provides parameters until the HTTP status
        # from the get_configs and the get_params both return 200
        while config_status or provides_status:

            LOGGER.debug('Config Parameter status: ' + str(config_status))
            LOGGER.debug('Return Parameter status: ' + str(provides_status))

            # Get the Required Configs from the Config Server
            if config_status:
                config_status, configs = self.client.get_configs()

                # Configure the system with the provided Required Configs
                if config_status == 200:
                    services = Service.parse_require_config(configs, self.tooling)
                    self.tooling.invoke_tooling(services)
                    # don't do any more config status work
                    # now that the tooling has run
                    config_status = 0
                else:
                    LOGGER.info(
                        'No configuration parameters provided. status: ' + \
                        str(config_status))

            # Get the requested provides from the Config Server
            if provides_status:
                get_status = self.client.get_provides()[0]

                # Gather the values from the system for the requested provides
                if get_status == 200:
                    params_values = Provides().generate_cs_str()
                else:
                    params_values = '|&|'

                # Put the requested provides with values to the Config Server
                provides_status = self.client.put_provides(params_values)[0]
                if provides_status == 200:
                    # don't operate on params anymore, all have been provided.
                    provides_status = 0

            # Retry a number of times if 404 HTTP Not Found is returned.
            if config_status == 404 or provides_status == 404:
                LOGGER.error('404 from Config Server.')
                LOGGER.error('Required Config status: %s' % config_status)
                LOGGER.info('Return Parameter status: %s' % provides_status)

                max_retry -= 1
                if max_retry < 0:
                    raise AAError('Too many 404 Config Server responses.')

            if loop_count:
                loop_count-=1
                sleep(SLEEP_SECS)
            else:
                break
Example #10
0
    def parse_require_config(src, tooling):
        '''
        Description:
          Parse the required config text message sent from the Config Server.

        Input:
          The required config string obtained from the Config Server,
          delimited by an | and an &

          Two tags will mark the sections of the data,
          '|service|' and  '|parameters|'

          To ensure all the data was received the entire string will be
          terminated with an "|".

          The string "|service|" will precede a service names.

          The string "|parameters|" will precede the parameters for
          the preceeding service, in the form: names&<b64 encoded values>.

        This will be a continuous text string (no CR or New Line).

          Format (repeating for each service):

          |service|<s1>|parameters|name1&<b64val>|name2&<b64val>|nameN&<b64v>|

          e.g.:
          |service|ssh::server|parameters|ssh_port&<b64('22')>
          |service|apache2::common|apache_port&<b64('8081')>|

        Returns:
            - A list of ServiceParams objects.
        '''

        services = []
        new = None

        CSClient.validate_message(src)

        # Message specific validation
        if src == '||':
            # special case indicating no required config needed.
            return []

        # split on pipe and chop of first and last, they will always be empty
        src = src.split('|')[1:-1]
        # get the indexes of the service identifiers
        srvs = deque([i for i,x in enumerate(src) if x == 'service'])
        srvs.append(len(src))

        if srvs[0] != 0:
            raise AAError(('|service| is not the first tag found. %s') % (src))

        while len(srvs) > 1:
            # rebuild a single service's cs string
            svc_str = "|%s|" % "|".join(src[srvs[0]:srvs[1]])
            name = src[srvs[0]+1]
            if name in ['service', 'parameters'] or '&' in name:
                raise AAError('invalid service name: %s' % name)
            # instanciate the service with it's name
            svc = Service(name, tooling)
            svc.parse_configs(svc_str)
            services.append(svc)
            srvs.popleft()

        return services
Example #11
0
class AgentV1(object):
    '''
    contains the main logic for processing
    This object is compatible with API Version 1
    '''
    def __init__(self, conf):
        '''
        conf: argparse dict
        '''
        tool_dir = {}
        if 'pwd' in conf and conf['pwd']:
            tool_dir = {'tool_dir': PWD_TOOLING}

        # Create the Client Object
        self.client = CSClient(**conf)
        self.client.test_connection()

        # Get any optional tooling from the Config Server
        tooling_status, tarball = self.client.get_tooling()
        if tooling_status != 200:
            LOGGER.error('Get Tooling returned: %s' % tooling_status)
            raise AAErrorGetTooling('Get Tooling returned: %s' %
                                    tooling_status)

        self.tooling = Tooling(tarball, **tool_dir)

    def run(self):
        '''
        Main agent loop, called by main() in /usr/bin/audrey
        '''
        # 0 means don't run again
        # -1 is non zero so initial runs will happen
        config_status = -1
        provides_status = -1

        max_retry = MAX_RETRY
        loop_count = 60
        services = []

        # Process the Requires and Provides parameters until the HTTP status
        # from the get_configs and the get_params both return 200
        while config_status or provides_status:

            LOGGER.debug('Config Parameter status: ' + str(config_status))
            LOGGER.debug('Return Parameter status: ' + str(provides_status))

            # Get the Required Configs from the Config Server
            if config_status:
                config_status, configs = self.client.get_configs()

                # Configure the system with the provided Required Configs
                if config_status == 200:
                    services = Service.parse_require_config(
                        configs, self.tooling)
                    self.tooling.invoke_tooling(services)
                    # don't do any more config status work
                    # now that the tooling has run
                    config_status = 0
                else:
                    LOGGER.info(
                        'No configuration parameters provided. status: ' + \
                        str(config_status))

            # Get the requested provides from the Config Server
            if provides_status:
                get_status = self.client.get_provides()[0]

                # Gather the values from the system for the requested provides
                if get_status == 200:
                    params_values = Provides().generate_cs_str()
                else:
                    params_values = '|&|'

                # Put the requested provides with values to the Config Server
                provides_status = self.client.put_provides(params_values)[0]
                if provides_status == 200:
                    # don't operate on params anymore, all have been provided.
                    provides_status = 0

            # Retry a number of times if 404 HTTP Not Found is returned.
            if config_status == 404 or provides_status == 404:
                LOGGER.error('404 from Config Server.')
                LOGGER.error('Required Config status: %s' % config_status)
                LOGGER.info('Return Parameter status: %s' % provides_status)

                max_retry -= 1
                if max_retry < 0:
                    raise AAError('Too many 404 Config Server responses.')

            if loop_count:
                loop_count -= 1
                sleep(SLEEP_SECS)
            else:
                break