[PATCH 1/2] swtiching shell env setup and renaming config_server* things to csclient things
Dan Radez
dradez at redhat.com
Tue Feb 7 03:42:10 UTC 2012
---
agent/bin/audrey | 2 +-
agent/src/audrey/agent.py.in | 8 +-
agent/src/audrey/config_server/__init__.py | 269 --------------------
agent/src/audrey/config_server/client.py | 216 ----------------
agent/src/audrey/config_server/service.py | 48 ----
agent/src/audrey/config_server/tooling.py | 268 -------------------
agent/src/audrey/csclient/__init__.py | 269 ++++++++++++++++++++
agent/src/audrey/csclient/client.py | 216 ++++++++++++++++
agent/src/audrey/csclient/service.py | 48 ++++
agent/src/audrey/csclient/tooling.py | 268 +++++++++++++++++++
agent/test_audrey_agent.py | 5 +-
agent/tests/agent.py | 14 +-
agent/tests/config_server.py | 301 ----------------------
agent/tests/config_server_client.py | 101 --------
agent/tests/csclient.py | 381 ++++++++++++++++++++++++++++
15 files changed, 1196 insertions(+), 1218 deletions(-)
delete mode 100644 agent/src/audrey/config_server/__init__.py
delete mode 100644 agent/src/audrey/config_server/client.py
delete mode 100644 agent/src/audrey/config_server/service.py
delete mode 100644 agent/src/audrey/config_server/tooling.py
create mode 100644 agent/src/audrey/csclient/__init__.py
create mode 100644 agent/src/audrey/csclient/client.py
create mode 100644 agent/src/audrey/csclient/service.py
create mode 100644 agent/src/audrey/csclient/tooling.py
delete mode 100644 agent/tests/config_server.py
delete mode 100644 agent/tests/config_server_client.py
create mode 100644 agent/tests/csclient.py
diff --git a/agent/bin/audrey b/agent/bin/audrey
index 3cb939b..3fde1e4 100755
--- a/agent/bin/audrey
+++ b/agent/bin/audrey
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
'''
*
* Copyright [2011] [Red Hat, Inc.]
diff --git a/agent/src/audrey/agent.py.in b/agent/src/audrey/agent.py.in
index cf8a5ce..a7f3dd3 100644
--- a/agent/src/audrey/agent.py.in
+++ b/agent/src/audrey/agent.py.in
@@ -47,10 +47,10 @@ from time import sleep
from audrey import ASError
from audrey import user_data
-from audrey.config_server.client import CSClient
-from audrey.config_server.tooling import ConfigTooling
-from audrey.config_server import parse_require_config
-from audrey.config_server import generate_provides
+from audrey.csclient.client import CSClient
+from audrey.csclient.tooling import ConfigTooling
+from audrey.csclient import parse_require_config
+from audrey.csclient import generate_provides
from audrey import setup_logging
SLEEP_SECS = 10
diff --git a/agent/src/audrey/config_server/__init__.py b/agent/src/audrey/config_server/__init__.py
deleted file mode 100644
index ea0448b..0000000
--- a/agent/src/audrey/config_server/__init__.py
+++ /dev/null
@@ -1,269 +0,0 @@
-'''
-*
-* Copyright [2011] [Red Hat, Inc.]
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*
-'''
-
-import os
-import logging
-import base64
-import urllib
-
-from collections import deque
-
-from audrey import ASError
-from audrey.config_server.service import ServiceParams
-from audrey.shell import run_cmd
-from audrey.shell import get_system_info
-
-logger = logging.getLogger('Audrey')
-
-#
-# Methods used to parse the CS<->AS text based API
-#
-def _common_validate_message(src):
- '''
- Perform validation of the text message sent from the Config Server.
- '''
-
- if not src.startswith('|') or not src.endswith('|'):
- raise ASError(('Invalid start and end characters: %s') % (src))
-
-def gen_env(serv_name, param_val):
- '''
- Description:
- Generate the os environment variables from the required config string.
-
- Input:
- serv_name - A service name
- e.g.:
- jon_agent_config
-
- param_val - A parameter name&val pair. The value is base64 encoded.
- e.g.:
- jon_server_ip&MTkyLjE2OC4wLjE=
-
- Output:
- Set environment variables of the form:
- <name>=<value>
- e.g.:
- jon_server_ip=base64.b64decode('MTkyLjE2OC4wLjE=')
- jon_server_ip='192.168.0.1
-
- Raises ASError when encountering an error.
-
- '''
- logger.debug('Invoked gen_env()')
-
- # If the param_val is missing treat as an exception.
- if param_val == '':
- raise ASError(('Missing parameter name. %s') % \
- (str(param_val)))
-
- # If serv_name is not blank an extra "_" must be added to
- # the environment variable name.
- if serv_name != '':
- serv_name = serv_name + '_'
-
- name_val = param_val.split('&')
- var_name = 'AUDREY_VAR_' + serv_name + name_val[0]
- os.environ[var_name] = \
- base64.b64decode(name_val[1])
-
- # Get what was set and log it.
- cmd = ['/usr/bin/printenv', var_name]
- ret = run_cmd(cmd)
- logger.debug(var_name + '=' + str(ret['out'].strip()))
-
-def parse_require_config(src):
- '''
- 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
-
- _common_validate_message(src)
-
- # Message specific validation
- if src == '||':
- # special case indicating no required config needed.
- return []
-
- if src.find('|service|') != 0:
- raise ASError(('|service| is not the first tag found. %s') % (src))
-
-
- src_q = deque(src.split('|'))
-
- # remove leading and trailing elements from the src_q since they are
- # empty strings generated by the split('|') because of the leading
- # and trailing '|'
- token = src_q.popleft()
- token = src_q.pop()
-
- while True:
- try:
- token = src_q.popleft()
- if token == 'service':
- token = src_q.popleft() # next token is service name
-
- # Raise an error if the service name is invalid.
- if token.find('&') != -1 or \
- token == 'service' or \
- token == 'parameters':
- raise ASError(('ERROR invalid service name: %s') % \
- (str(token)))
-
- new = ServiceParams(token)
- services.append(new)
- elif token == 'parameters' or token == '':
- pass
- else: # token is a name&value pair.
- if token.find('&') == -1:
- raise ASError(('ERROR name&val: %s missing delimiter') % \
- (str(token)))
- if new:
- new.add_param(token)
- gen_env(new.name, token)
- else:
- raise ASError(('ERROR missing service tag %s') % \
- (str(src)))
- except IndexError:
- break
-
- return services
-
-def parse_provides_params(src):
- '''
- 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:
- - a list of parameter names.
- '''
-
- _common_validate_message(src)
-
- # Message specific validation
- if src == '||':
- # special case indicating no provides parameters requested.
- return ['']
-
- params_str = src[src.find('|')+1:len(src)-1]
-
- return params_str.split('&')
-
-def generate_provides(src):
- '''
- Description:
- Generate the provides parameters list.
- Uses parse_provides_params()
-
- Input:
- The provides parameters string obtained from the Config Server.
-
- Returns:
- A string to send back to the Config Server with prifix
- 'audrey_data='<url encoded return data>'
-
- The return portion will be delimited with an | and an &
-
- To ensure all the data is transmitted the entire string will be
- terminated with an "|".
-
- This will be a continuous text string (no CR or New Line).
-
- Data portion Format:
- |name1&val1|name2&val...|nameN$valN|
-
- e.g.:
- |ipaddress&<b64/10.118.46.205>|virtual&<b64/xenu>|
-
- The return string format:
- "audrey_data=<url encoded data portion>"
-
-
- '''
- logger.info('Invoked generate_provides()')
-
- provides_dict = {}
- params_list = parse_provides_params(src)
-
- system_info_dict = get_system_info()
-
- for param in params_list:
- try:
- provides_dict.update( \
- {param:base64.b64encode(system_info_dict[param])})
- except KeyError:
- # A specified parameter is not found. Provide value ''
- provides_dict.update({param:''})
-
-
- # Create string to send to Config Server
- provides_list = ['']
- for key in provides_dict.keys():
- provides_list.append(str(key) + '&' + str(provides_dict[key]))
- provides_list.append('')
-
- return urllib.urlencode({'audrey_data':'|'.join(provides_list)})
diff --git a/agent/src/audrey/config_server/client.py b/agent/src/audrey/config_server/client.py
deleted file mode 100644
index 33283c4..0000000
--- a/agent/src/audrey/config_server/client.py
+++ /dev/null
@@ -1,216 +0,0 @@
-'''
-*
-* Copyright [2011] [Red Hat, Inc.]
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*
-'''
-
-import shutil
-import logging
-import tempfile
-import oauth2 as oauth
-
-from audrey import ASError
-
-logger = logging.getLogger('Audrey')
-
-TOOLING_URL = 'files'
-CONFIGS_URL = 'configs'
-PARAMS_URL = 'params'
-
-class CSClient(object):
- '''
- Description:
- Client interface to Config Server (CS)
- '''
-
- def __init__(self, endpoint, oauth_key, oauth_secret, **kwargs):
- '''
- Description:
- Set initial state so it can be tracked. Valuable for
- testing and debugging.
- '''
-
- self.version = 1
- self.cs_endpoint = endpoint
- self.cs_oauth_key = oauth_key
- self.cs_oauth_secret = oauth_secret
- self.cs_params = ''
- self.cs_configs = ''
- self.tmpdir = ''
- self.tarball = ''
-
- # create an oauth client for communication with the cs
- consumer = oauth.Consumer(self.cs_oauth_key, self.cs_oauth_secret)
- # 2 legged auth, token unnessesary
- token = None #oauth.Token('access-key-here','access-key-secret-here')
- client = oauth.Client(consumer, token)
- self.http = client
-
- def __del__(self):
- '''
- Description:
- Class destructor
- '''
- try:
- shutil.rmtree(self.tmpdir)
- except OSError:
- pass # ignore any errors when attempting to remove the temp dir.
-
- def __str__(self):
- '''
- Description:
- Called by the str() function and by the print statement to
- produce the informal string representation of an object.
- '''
- return('\n<Instance of: %s\n' \
- '\tVersion: %s\n' \
- '\tConfig Server Endpoint: %s\n' \
- '\tConfig Server oAuth Key: %s\n' \
- '\tConfig Server oAuth Secret: %s\n' \
- '\tConfig Server Params: %s\n' \
- '\tConfig Server Configs: %s\n' \
- '\tTemporary Directory: %s\n' \
- '\tTarball Name: %s\n' \
- 'eot>' %
- (self.__class__.__name__,
- str(self.version),
- str(self.cs_endpoint),
- str(self.cs_oauth_key),
- str(self.cs_oauth_secret),
- str(self.cs_params),
- str(self.cs_configs),
- str(self.tmpdir),
- str(self.tarball),
- ))
-
- def _cs_url(self, url_type):
- '''
- Description:
- Generate the Config Server (CS) URL.
- '''
- endpoint = '%s/%s' % (self.cs_endpoint.lower(), url_type)
- if url_type != 'version':
- endpoint = '%s/%s/%s' % (endpoint, self.version, self.cs_oauth_key)
- return endpoint
-
- def _get(self, url, headers=None):
- '''
- Description:
- Issue the http get to the the Config Server.
- '''
- try:
- return self.http.request(url, method='GET', headers=headers)
- except Exception, e:
- return (e, None)
-
- def _put(self, url, body=None, headers=None):
- '''
- Description:
- Issue the http put to the the Config Server.
- '''
- try:
- return self.http.request(url, method='PUT',
- body=body, headers=headers)
- except Exception, e:
- return (e, None)
-
- def _validate_http_status(self, status):
- '''
- Description:
- Confirm the http status is one of:
- 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
- '''
- if (status != 200) and (status != 202) and (status != 404):
- raise ASError(('Invalid HTTP status code: %s') % \
- (str(status)))
-
- # Public interfaces
- def get_cs_configs(self):
- '''
- Description:
- get the required configuration from the Config Server.
- '''
- logger.info('Invoked CSClient.get_cs_configs()')
- url = self._cs_url(CONFIGS_URL)
- headers = {'Accept': 'text/plain'}
-
- response, body = self._get(url, headers=headers)
- self.cs_configs = body
- self._validate_http_status(response.status)
-
- return response.status, body
-
- def get_cs_params(self):
- '''
- Description:
- get the provides parameters from the Config Server.
- '''
- logger.info('Invoked CSClient.get_cs_params()')
- url = self._cs_url(PARAMS_URL)
- headers = {'Accept': 'text/plain'}
-
- response, body = self._get(url, headers=headers)
- self.cs_params = body
- self._validate_http_status(response.status)
-
- return response.status, body
-
- def put_cs_params_values(self, params_values):
- '''
- Description:
- put the provides parameters to the Config Server.
- '''
- logger.info('Invoked CSClient.put_cs_params_values()')
- url = self._cs_url(PARAMS_URL)
- headers = {'Content-Type': 'application/x-www-form-urlencoded'}
-
- response, body = self._put(url, body=params_values, headers=headers)
- return response.status, body
-
- def get_cs_tooling(self):
- '''
- Description:
- get any optional user supplied tooling which is
- provided as a tarball
- '''
- logger.info('Invoked CSClient.get_cs_tooling()')
- url = self._cs_url(TOOLING_URL)
- headers = {'Accept': 'content-disposition'}
-
- tarball = ''
- response, body = self._get(url, headers=headers)
- self._validate_http_status(response.status)
-
- # Parse the file name burried in the response header
- # at: response['content-disposition']
- # as: 'attachment; tarball="tarball.tgz"'
- if (response.status == 200) or (response.status == 202):
- tarball = response['content-disposition']. \
- lstrip('attachment; filename=').replace('"','')
-
- # Create the temporary tarfile
- try:
- self.tmpdir = tempfile.mkdtemp()
- self.tarball = self.tmpdir + '/' + tarball
- f = open(self.tarball, 'w')
- f.write(body)
- f.close()
- except IOError, (errno, strerror):
- raise ASError(('File not found or not a tar file: %s ' + \
- 'Error: %s %s') % (self.tarball, errno, strerror))
-
- return response.status, self.tarball
diff --git a/agent/src/audrey/config_server/service.py b/agent/src/audrey/config_server/service.py
deleted file mode 100644
index 22b0aac..0000000
--- a/agent/src/audrey/config_server/service.py
+++ /dev/null
@@ -1,48 +0,0 @@
-'''
-*
-* Copyright [2011] [Red Hat, Inc.]
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*
-'''
-
-class ServiceParams(object):
- '''
- Description:
- Used for storing a service and all of it's associated parameters
- as provided by the Config Server in the "required" parameters
- API message.
-
- services = [
- ServiceParams('serviceA', ['n&v', 'n&v', 'n&v',...]),
- ServiceParams('serviceB', ['n&v', 'n&v', 'n&v',...]),
- ServiceParams('serviceB', ['n&v', 'n&v', 'n&v',...]),
- ]
-
- This structure aids in tracking the parsed required config
- parameters which is useful when doing UNITTESTing.
-
- '''
- def __init__(self, name=None):
- if name == None:
- name = ''
- self.name = name # string
- self.params = [] # start with an empty list
- def add_param(self, param):
- '''
- Description:
- Add a parameter provided by the Config Server to the list.
- '''
- self.params.append(param)
- def __repr__(self):
- return repr((self.name, self.params))
diff --git a/agent/src/audrey/config_server/tooling.py b/agent/src/audrey/config_server/tooling.py
deleted file mode 100644
index 0878372..0000000
--- a/agent/src/audrey/config_server/tooling.py
+++ /dev/null
@@ -1,268 +0,0 @@
-'''
-*
-* Copyright [2011] [Red Hat, Inc.]
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*
-'''
-
-import os
-import logging
-import tarfile
-
-from audrey import ASError
-
-TOOLING_DIR = '/var/audrey/tooling/'
-
-logger = logging.getLogger('Audrey')
-
-class ConfigTooling(object):
- '''
- TBD - Consider making this class derived from dictionary or a mutable
- mapping.
-
- Description:
- Interface to configuration tooling:
- - Getting optional user supplied tooling from CS
- - Verify and Unpack optional user supplied tooling retrieved
- from CS
- - Is tooling for a given service user supplied
- - Is tooling for a given service Red Hat supplied
- - Find tooling for a given service Red Hat supplied
- - List tooling for services and indicate if it is user or Red
- Hat supplied.
- '''
-
- def __init__(self, tool_dir=TOOLING_DIR):
- '''
- Description:
- Set initial state so it can be tracked. Valuable for
- testing and debugging.
- '''
- self.tool_dir = tool_dir
- self.user_dir = tool_dir + 'user/'
- self.log = tool_dir + 'log'
- self.tarball = ''
-
- # Create the extraction destination
- try:
- os.makedirs(self.user_dir)
- except OSError, (errno, strerror):
- if errno is 17: # File exists
- pass
- else:
- raise ASError(('Failed to create directory %s. ' + \
- 'Error: %s') % (self.user_dir, strerror))
-
- self.ct_logger = logging.getLogger('ConfigTooling')
- self.ct_logger.addHandler(logging.FileHandler(self.log))
-
- def __str__(self):
- '''
- Description:
- Called by the str() function and by the print statement to
- produce the informal string representation of an object.
- '''
- return('\n<Instance of: %s\n' \
- '\tTooling Dir: %s\n' \
- '\tUnpack User Tooling Tarball Dir: %s\n' \
- '\tLog File: %s\n' \
- '\ttarball Name: %s\n' \
- 'eot>' %
- (self.__class__.__name__,
- str(self.tool_dir),
- str(self.user_dir),
- str(self.log),
- str(self.tarball),
- ))
-
- def log_info(self, log_str):
- '''
- Description:
- Used for logging the commands that have been executed
- along with their output and return codes.
-
- Simply logs the provided input string.
- '''
- self.ct_logger.info(log_str)
-
- def log_error(self, log_str):
- '''
- Description:
- Used for logging errors encountered when attempting to
- execute the service command.
-
- Simply logs the provided input string.
- '''
- self.ct_logger.error(log_str)
-
- def invoke_tooling(self, services):
- '''
- Description:
- Invoke the configuration tooling for the specified services.
-
- Input:
- services - A list of ServiceParams objects.
-
- '''
-
- # For now invoke them all. Later versions will invoke the service
- # based on the required params from the Config Server.
- logger.debug('Invoked ConfigTooling.invoke_tooling()')
- logger.debug(str(services))
- for service in services:
-
- try:
- top_level, tooling_path = self.find_tooling(service.name)
- except ASError:
- # No tooling found. Try the next service.
- continue
-
- cmd = [tooling_path]
- cmd_dir = os.path.dirname(tooling_path)
- ret = run_cmd(cmd, cmd_dir)
- self.log_info('Execute Tooling command: ' + ' '.join(cmd))
-
- retcode = ret['subproc'].returncode
- if retcode == 0:
- # Command successed, log the output.
- self.log_info('return code: ' + str(retcode))
- self.log_info('\n\tStart Output of: ' + ' '.join(cmd) + \
- ' >>>\n' + \
- str(ret['out']) + \
- '\n\t<<< End Output')
- else:
- # Command failed, log the errors.
- self.log_info('\n\tStart Output of: ' + ' '.join(cmd) + \
- ' >>>\n' + \
- str(ret['out']) + \
- '\n\t<<< End Output')
- self.log_error('error code: ' + str(retcode))
- self.log_error('error msg: ' + str(ret['err']))
-
- # If tooling was provided at the top level only run it once
- # for all services listed in the required config params.
- if top_level:
- break
-
- def unpack_tooling(self, tarball):
- '''
- Description:
- Methods used to untar the user provided tarball
-
- Perform validation of the text message sent from the
- Config Server. Validate, open and write out the contents
- of the user provided tarball.
- '''
- logger.info('Invoked unpack_tooling()')
- logger.debug('tarball: ' + str(tarball) + \
- 'Target Direcory: ' + str(self.user_dir))
-
- self.tarball = tarball
-
- # Validate the specified tarfile.
- try:
- if not tarfile.is_tarfile(self.tarball):
- # If file exists but is not a tar file force IOError.
- raise IOError
- except IOError, (errno, strerror):
- raise ASError(('File was not found or is not a tar file: %s ' + \
- 'Error: %s %s') % (self.tarball, errno, strerror))
-
- # Attempt to extract the contents from the specified tarfile.
- #
- # If tarfile access or content is bad report to the user to aid
- # problem resolution.
- try:
- tarf = tarfile.open(self.tarball)
- tarf.extractall(path=self.user_dir)
- tarf.close()
- except IOError, (errno, strerror):
- raise ASError(('Failed to access tar file %s. Error: %s') % \
- (self.tarball, strerror))
- # Capture and report errors with the tarfile
- except (tarfile.TarError, tarfile.ReadError, tarfile.CompressionError, \
- tarfile.StreamError, tarfile.ExtractError), (strerror):
- raise ASError(('Failed to access tar file %s. Error: %s') % \
- (self.tarball, strerror))
-
- def is_user_supplied(self):
- '''
- Description:
- Is the the configuration tooling for the specified service
- supplied by the user?
-
- TBD: Take in a service_name and evaluate.
- def is_user_supplied(self, service_name):
- '''
- return True
-
- def is_rh_supplied(self):
- '''
- Description:
- Is the the configuration tooling for the specified service
- supplied by Red Hat?
-
- TBD: Take in a service_name and evaluate.
- def is_rh_supplied(self, service_name):
- '''
- return False
-
- def find_tooling(self, service_name):
- '''
- Description:
- Given a service name return the path to the configuration
- tooling.
-
- Search for the service start executable in the user
- tooling directory.
- self.tool_dir + '/user/<service name>/start'
-
- If not found there search for the it in the documented directory
- here built in tooling should be placed.
- self.tool_dir + '/AUDREY_TOOLING/<service name>/start'
-
- If not found there search for the it in the Red Hat tooling
- directory.
- self.tool_dir + '/REDHAT/<service name>/start'
-
- If not found there raise an error.
-
- Returns:
- return 1 - True if top level tooling found, False otherwise.
- return 2 - path to tooling
- '''
-
- top_path = self.tool_dir + 'user/start'
- if os.access(top_path, os.X_OK):
- return True, top_path
-
- service_user_path = self.tool_dir + 'user/' + \
- service_name + '/start'
- if os.access(service_user_path, os.X_OK):
- return False, service_user_path
-
- service_redhat_path = self.tool_dir + 'AUDREY_TOOLING/' + \
- service_name + '/start'
- if os.access(service_redhat_path, os.X_OK):
- return False, service_redhat_path
-
- service_redhat_path = self.tool_dir + 'REDHAT/' + \
- service_name + '/start'
- if os.access(service_redhat_path, os.X_OK):
- return False, service_redhat_path
-
- # No tooling found. Raise an error.
- raise ASError(('No configuration tooling found for service: %s') % \
- (service_name))
-
diff --git a/agent/src/audrey/csclient/__init__.py b/agent/src/audrey/csclient/__init__.py
new file mode 100644
index 0000000..021fd54
--- /dev/null
+++ b/agent/src/audrey/csclient/__init__.py
@@ -0,0 +1,269 @@
+'''
+*
+* Copyright [2011] [Red Hat, Inc.]
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+'''
+
+import os
+import logging
+import base64
+import urllib
+
+from collections import deque
+
+from audrey import ASError
+from audrey.csclient.service import ServiceParams
+from audrey.shell import run_cmd
+from audrey.shell import get_system_info
+
+logger = logging.getLogger('Audrey')
+
+#
+# Methods used to parse the CS<->AS text based API
+#
+def _common_validate_message(src):
+ '''
+ Perform validation of the text message sent from the Config Server.
+ '''
+
+ if not src.startswith('|') or not src.endswith('|'):
+ raise ASError(('Invalid start and end characters: %s') % (src))
+
+def gen_env(serv_name, param_val):
+ '''
+ Description:
+ Generate the os environment variables from the required config string.
+
+ Input:
+ serv_name - A service name
+ e.g.:
+ jon_agent_config
+
+ param_val - A parameter name&val pair. The value is base64 encoded.
+ e.g.:
+ jon_server_ip&MTkyLjE2OC4wLjE=
+
+ Output:
+ Set environment variables of the form:
+ <name>=<value>
+ e.g.:
+ jon_server_ip=base64.b64decode('MTkyLjE2OC4wLjE=')
+ jon_server_ip='192.168.0.1
+
+ Raises ASError when encountering an error.
+
+ '''
+ logger.debug('Invoked gen_env()')
+
+ # If the param_val is missing treat as an exception.
+ if param_val == '':
+ raise ASError(('Missing parameter name. %s') % \
+ (str(param_val)))
+
+ # If serv_name is not blank an extra "_" must be added to
+ # the environment variable name.
+ if serv_name != '':
+ serv_name = serv_name + '_'
+
+ name_val = param_val.split('&')
+ var_name = 'AUDREY_VAR_' + serv_name + name_val[0]
+ os.environ[var_name] = \
+ base64.b64decode(name_val[1])
+
+ # Get what was set and log it.
+ cmd = ['/usr/bin/printenv', var_name]
+ ret = run_cmd(cmd)
+ logger.debug(var_name + '=' + str(ret['out'].strip()))
+
+def parse_require_config(src):
+ '''
+ 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
+
+ _common_validate_message(src)
+
+ # Message specific validation
+ if src == '||':
+ # special case indicating no required config needed.
+ return []
+
+ if src.find('|service|') != 0:
+ raise ASError(('|service| is not the first tag found. %s') % (src))
+
+
+ src_q = deque(src.split('|'))
+
+ # remove leading and trailing elements from the src_q since they are
+ # empty strings generated by the split('|') because of the leading
+ # and trailing '|'
+ token = src_q.popleft()
+ token = src_q.pop()
+
+ while True:
+ try:
+ token = src_q.popleft()
+ if token == 'service':
+ token = src_q.popleft() # next token is service name
+
+ # Raise an error if the service name is invalid.
+ if token.find('&') != -1 or \
+ token == 'service' or \
+ token == 'parameters':
+ raise ASError(('ERROR invalid service name: %s') % \
+ (str(token)))
+
+ new = ServiceParams(token)
+ services.append(new)
+ elif token == 'parameters' or token == '':
+ pass
+ else: # token is a name&value pair.
+ if token.find('&') == -1:
+ raise ASError(('ERROR name&val: %s missing delimiter') % \
+ (str(token)))
+ if new:
+ new.add_param(token)
+ gen_env(new.name, token)
+ else:
+ raise ASError(('ERROR missing service tag %s') % \
+ (str(src)))
+ except IndexError:
+ break
+
+ return services
+
+def parse_provides_params(src):
+ '''
+ 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:
+ - a list of parameter names.
+ '''
+
+ _common_validate_message(src)
+
+ # Message specific validation
+ if src == '||':
+ # special case indicating no provides parameters requested.
+ return ['']
+
+ params_str = src[src.find('|')+1:len(src)-1]
+
+ return params_str.split('&')
+
+def generate_provides(src):
+ '''
+ Description:
+ Generate the provides parameters list.
+ Uses parse_provides_params()
+
+ Input:
+ The provides parameters string obtained from the Config Server.
+
+ Returns:
+ A string to send back to the Config Server with prifix
+ 'audrey_data='<url encoded return data>'
+
+ The return portion will be delimited with an | and an &
+
+ To ensure all the data is transmitted the entire string will be
+ terminated with an "|".
+
+ This will be a continuous text string (no CR or New Line).
+
+ Data portion Format:
+ |name1&val1|name2&val...|nameN$valN|
+
+ e.g.:
+ |ipaddress&<b64/10.118.46.205>|virtual&<b64/xenu>|
+
+ The return string format:
+ "audrey_data=<url encoded data portion>"
+
+
+ '''
+ logger.info('Invoked generate_provides()')
+
+ provides_dict = {}
+ params_list = parse_provides_params(src)
+
+ system_info_dict = get_system_info()
+
+ for param in params_list:
+ try:
+ provides_dict.update( \
+ {param:base64.b64encode(system_info_dict[param])})
+ except KeyError:
+ # A specified parameter is not found. Provide value ''
+ provides_dict.update({param:''})
+
+
+ # Create string to send to Config Server
+ provides_list = ['']
+ for key in provides_dict.keys():
+ provides_list.append(str(key) + '&' + str(provides_dict[key]))
+ provides_list.append('')
+
+ return urllib.urlencode({'audrey_data':'|'.join(provides_list)})
diff --git a/agent/src/audrey/csclient/client.py b/agent/src/audrey/csclient/client.py
new file mode 100644
index 0000000..33283c4
--- /dev/null
+++ b/agent/src/audrey/csclient/client.py
@@ -0,0 +1,216 @@
+'''
+*
+* Copyright [2011] [Red Hat, Inc.]
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+'''
+
+import shutil
+import logging
+import tempfile
+import oauth2 as oauth
+
+from audrey import ASError
+
+logger = logging.getLogger('Audrey')
+
+TOOLING_URL = 'files'
+CONFIGS_URL = 'configs'
+PARAMS_URL = 'params'
+
+class CSClient(object):
+ '''
+ Description:
+ Client interface to Config Server (CS)
+ '''
+
+ def __init__(self, endpoint, oauth_key, oauth_secret, **kwargs):
+ '''
+ Description:
+ Set initial state so it can be tracked. Valuable for
+ testing and debugging.
+ '''
+
+ self.version = 1
+ self.cs_endpoint = endpoint
+ self.cs_oauth_key = oauth_key
+ self.cs_oauth_secret = oauth_secret
+ self.cs_params = ''
+ self.cs_configs = ''
+ self.tmpdir = ''
+ self.tarball = ''
+
+ # create an oauth client for communication with the cs
+ consumer = oauth.Consumer(self.cs_oauth_key, self.cs_oauth_secret)
+ # 2 legged auth, token unnessesary
+ token = None #oauth.Token('access-key-here','access-key-secret-here')
+ client = oauth.Client(consumer, token)
+ self.http = client
+
+ def __del__(self):
+ '''
+ Description:
+ Class destructor
+ '''
+ try:
+ shutil.rmtree(self.tmpdir)
+ except OSError:
+ pass # ignore any errors when attempting to remove the temp dir.
+
+ def __str__(self):
+ '''
+ Description:
+ Called by the str() function and by the print statement to
+ produce the informal string representation of an object.
+ '''
+ return('\n<Instance of: %s\n' \
+ '\tVersion: %s\n' \
+ '\tConfig Server Endpoint: %s\n' \
+ '\tConfig Server oAuth Key: %s\n' \
+ '\tConfig Server oAuth Secret: %s\n' \
+ '\tConfig Server Params: %s\n' \
+ '\tConfig Server Configs: %s\n' \
+ '\tTemporary Directory: %s\n' \
+ '\tTarball Name: %s\n' \
+ 'eot>' %
+ (self.__class__.__name__,
+ str(self.version),
+ str(self.cs_endpoint),
+ str(self.cs_oauth_key),
+ str(self.cs_oauth_secret),
+ str(self.cs_params),
+ str(self.cs_configs),
+ str(self.tmpdir),
+ str(self.tarball),
+ ))
+
+ def _cs_url(self, url_type):
+ '''
+ Description:
+ Generate the Config Server (CS) URL.
+ '''
+ endpoint = '%s/%s' % (self.cs_endpoint.lower(), url_type)
+ if url_type != 'version':
+ endpoint = '%s/%s/%s' % (endpoint, self.version, self.cs_oauth_key)
+ return endpoint
+
+ def _get(self, url, headers=None):
+ '''
+ Description:
+ Issue the http get to the the Config Server.
+ '''
+ try:
+ return self.http.request(url, method='GET', headers=headers)
+ except Exception, e:
+ return (e, None)
+
+ def _put(self, url, body=None, headers=None):
+ '''
+ Description:
+ Issue the http put to the the Config Server.
+ '''
+ try:
+ return self.http.request(url, method='PUT',
+ body=body, headers=headers)
+ except Exception, e:
+ return (e, None)
+
+ def _validate_http_status(self, status):
+ '''
+ Description:
+ Confirm the http status is one of:
+ 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
+ '''
+ if (status != 200) and (status != 202) and (status != 404):
+ raise ASError(('Invalid HTTP status code: %s') % \
+ (str(status)))
+
+ # Public interfaces
+ def get_cs_configs(self):
+ '''
+ Description:
+ get the required configuration from the Config Server.
+ '''
+ logger.info('Invoked CSClient.get_cs_configs()')
+ url = self._cs_url(CONFIGS_URL)
+ headers = {'Accept': 'text/plain'}
+
+ response, body = self._get(url, headers=headers)
+ self.cs_configs = body
+ self._validate_http_status(response.status)
+
+ return response.status, body
+
+ def get_cs_params(self):
+ '''
+ Description:
+ get the provides parameters from the Config Server.
+ '''
+ logger.info('Invoked CSClient.get_cs_params()')
+ url = self._cs_url(PARAMS_URL)
+ headers = {'Accept': 'text/plain'}
+
+ response, body = self._get(url, headers=headers)
+ self.cs_params = body
+ self._validate_http_status(response.status)
+
+ return response.status, body
+
+ def put_cs_params_values(self, params_values):
+ '''
+ Description:
+ put the provides parameters to the Config Server.
+ '''
+ logger.info('Invoked CSClient.put_cs_params_values()')
+ url = self._cs_url(PARAMS_URL)
+ headers = {'Content-Type': 'application/x-www-form-urlencoded'}
+
+ response, body = self._put(url, body=params_values, headers=headers)
+ return response.status, body
+
+ def get_cs_tooling(self):
+ '''
+ Description:
+ get any optional user supplied tooling which is
+ provided as a tarball
+ '''
+ logger.info('Invoked CSClient.get_cs_tooling()')
+ url = self._cs_url(TOOLING_URL)
+ headers = {'Accept': 'content-disposition'}
+
+ tarball = ''
+ response, body = self._get(url, headers=headers)
+ self._validate_http_status(response.status)
+
+ # Parse the file name burried in the response header
+ # at: response['content-disposition']
+ # as: 'attachment; tarball="tarball.tgz"'
+ if (response.status == 200) or (response.status == 202):
+ tarball = response['content-disposition']. \
+ lstrip('attachment; filename=').replace('"','')
+
+ # Create the temporary tarfile
+ try:
+ self.tmpdir = tempfile.mkdtemp()
+ self.tarball = self.tmpdir + '/' + tarball
+ f = open(self.tarball, 'w')
+ f.write(body)
+ f.close()
+ except IOError, (errno, strerror):
+ raise ASError(('File not found or not a tar file: %s ' + \
+ 'Error: %s %s') % (self.tarball, errno, strerror))
+
+ return response.status, self.tarball
diff --git a/agent/src/audrey/csclient/service.py b/agent/src/audrey/csclient/service.py
new file mode 100644
index 0000000..22b0aac
--- /dev/null
+++ b/agent/src/audrey/csclient/service.py
@@ -0,0 +1,48 @@
+'''
+*
+* Copyright [2011] [Red Hat, Inc.]
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+'''
+
+class ServiceParams(object):
+ '''
+ Description:
+ Used for storing a service and all of it's associated parameters
+ as provided by the Config Server in the "required" parameters
+ API message.
+
+ services = [
+ ServiceParams('serviceA', ['n&v', 'n&v', 'n&v',...]),
+ ServiceParams('serviceB', ['n&v', 'n&v', 'n&v',...]),
+ ServiceParams('serviceB', ['n&v', 'n&v', 'n&v',...]),
+ ]
+
+ This structure aids in tracking the parsed required config
+ parameters which is useful when doing UNITTESTing.
+
+ '''
+ def __init__(self, name=None):
+ if name == None:
+ name = ''
+ self.name = name # string
+ self.params = [] # start with an empty list
+ def add_param(self, param):
+ '''
+ Description:
+ Add a parameter provided by the Config Server to the list.
+ '''
+ self.params.append(param)
+ def __repr__(self):
+ return repr((self.name, self.params))
diff --git a/agent/src/audrey/csclient/tooling.py b/agent/src/audrey/csclient/tooling.py
new file mode 100644
index 0000000..0878372
--- /dev/null
+++ b/agent/src/audrey/csclient/tooling.py
@@ -0,0 +1,268 @@
+'''
+*
+* Copyright [2011] [Red Hat, Inc.]
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+'''
+
+import os
+import logging
+import tarfile
+
+from audrey import ASError
+
+TOOLING_DIR = '/var/audrey/tooling/'
+
+logger = logging.getLogger('Audrey')
+
+class ConfigTooling(object):
+ '''
+ TBD - Consider making this class derived from dictionary or a mutable
+ mapping.
+
+ Description:
+ Interface to configuration tooling:
+ - Getting optional user supplied tooling from CS
+ - Verify and Unpack optional user supplied tooling retrieved
+ from CS
+ - Is tooling for a given service user supplied
+ - Is tooling for a given service Red Hat supplied
+ - Find tooling for a given service Red Hat supplied
+ - List tooling for services and indicate if it is user or Red
+ Hat supplied.
+ '''
+
+ def __init__(self, tool_dir=TOOLING_DIR):
+ '''
+ Description:
+ Set initial state so it can be tracked. Valuable for
+ testing and debugging.
+ '''
+ self.tool_dir = tool_dir
+ self.user_dir = tool_dir + 'user/'
+ self.log = tool_dir + 'log'
+ self.tarball = ''
+
+ # Create the extraction destination
+ try:
+ os.makedirs(self.user_dir)
+ except OSError, (errno, strerror):
+ if errno is 17: # File exists
+ pass
+ else:
+ raise ASError(('Failed to create directory %s. ' + \
+ 'Error: %s') % (self.user_dir, strerror))
+
+ self.ct_logger = logging.getLogger('ConfigTooling')
+ self.ct_logger.addHandler(logging.FileHandler(self.log))
+
+ def __str__(self):
+ '''
+ Description:
+ Called by the str() function and by the print statement to
+ produce the informal string representation of an object.
+ '''
+ return('\n<Instance of: %s\n' \
+ '\tTooling Dir: %s\n' \
+ '\tUnpack User Tooling Tarball Dir: %s\n' \
+ '\tLog File: %s\n' \
+ '\ttarball Name: %s\n' \
+ 'eot>' %
+ (self.__class__.__name__,
+ str(self.tool_dir),
+ str(self.user_dir),
+ str(self.log),
+ str(self.tarball),
+ ))
+
+ def log_info(self, log_str):
+ '''
+ Description:
+ Used for logging the commands that have been executed
+ along with their output and return codes.
+
+ Simply logs the provided input string.
+ '''
+ self.ct_logger.info(log_str)
+
+ def log_error(self, log_str):
+ '''
+ Description:
+ Used for logging errors encountered when attempting to
+ execute the service command.
+
+ Simply logs the provided input string.
+ '''
+ self.ct_logger.error(log_str)
+
+ def invoke_tooling(self, services):
+ '''
+ Description:
+ Invoke the configuration tooling for the specified services.
+
+ Input:
+ services - A list of ServiceParams objects.
+
+ '''
+
+ # For now invoke them all. Later versions will invoke the service
+ # based on the required params from the Config Server.
+ logger.debug('Invoked ConfigTooling.invoke_tooling()')
+ logger.debug(str(services))
+ for service in services:
+
+ try:
+ top_level, tooling_path = self.find_tooling(service.name)
+ except ASError:
+ # No tooling found. Try the next service.
+ continue
+
+ cmd = [tooling_path]
+ cmd_dir = os.path.dirname(tooling_path)
+ ret = run_cmd(cmd, cmd_dir)
+ self.log_info('Execute Tooling command: ' + ' '.join(cmd))
+
+ retcode = ret['subproc'].returncode
+ if retcode == 0:
+ # Command successed, log the output.
+ self.log_info('return code: ' + str(retcode))
+ self.log_info('\n\tStart Output of: ' + ' '.join(cmd) + \
+ ' >>>\n' + \
+ str(ret['out']) + \
+ '\n\t<<< End Output')
+ else:
+ # Command failed, log the errors.
+ self.log_info('\n\tStart Output of: ' + ' '.join(cmd) + \
+ ' >>>\n' + \
+ str(ret['out']) + \
+ '\n\t<<< End Output')
+ self.log_error('error code: ' + str(retcode))
+ self.log_error('error msg: ' + str(ret['err']))
+
+ # If tooling was provided at the top level only run it once
+ # for all services listed in the required config params.
+ if top_level:
+ break
+
+ def unpack_tooling(self, tarball):
+ '''
+ Description:
+ Methods used to untar the user provided tarball
+
+ Perform validation of the text message sent from the
+ Config Server. Validate, open and write out the contents
+ of the user provided tarball.
+ '''
+ logger.info('Invoked unpack_tooling()')
+ logger.debug('tarball: ' + str(tarball) + \
+ 'Target Direcory: ' + str(self.user_dir))
+
+ self.tarball = tarball
+
+ # Validate the specified tarfile.
+ try:
+ if not tarfile.is_tarfile(self.tarball):
+ # If file exists but is not a tar file force IOError.
+ raise IOError
+ except IOError, (errno, strerror):
+ raise ASError(('File was not found or is not a tar file: %s ' + \
+ 'Error: %s %s') % (self.tarball, errno, strerror))
+
+ # Attempt to extract the contents from the specified tarfile.
+ #
+ # If tarfile access or content is bad report to the user to aid
+ # problem resolution.
+ try:
+ tarf = tarfile.open(self.tarball)
+ tarf.extractall(path=self.user_dir)
+ tarf.close()
+ except IOError, (errno, strerror):
+ raise ASError(('Failed to access tar file %s. Error: %s') % \
+ (self.tarball, strerror))
+ # Capture and report errors with the tarfile
+ except (tarfile.TarError, tarfile.ReadError, tarfile.CompressionError, \
+ tarfile.StreamError, tarfile.ExtractError), (strerror):
+ raise ASError(('Failed to access tar file %s. Error: %s') % \
+ (self.tarball, strerror))
+
+ def is_user_supplied(self):
+ '''
+ Description:
+ Is the the configuration tooling for the specified service
+ supplied by the user?
+
+ TBD: Take in a service_name and evaluate.
+ def is_user_supplied(self, service_name):
+ '''
+ return True
+
+ def is_rh_supplied(self):
+ '''
+ Description:
+ Is the the configuration tooling for the specified service
+ supplied by Red Hat?
+
+ TBD: Take in a service_name and evaluate.
+ def is_rh_supplied(self, service_name):
+ '''
+ return False
+
+ def find_tooling(self, service_name):
+ '''
+ Description:
+ Given a service name return the path to the configuration
+ tooling.
+
+ Search for the service start executable in the user
+ tooling directory.
+ self.tool_dir + '/user/<service name>/start'
+
+ If not found there search for the it in the documented directory
+ here built in tooling should be placed.
+ self.tool_dir + '/AUDREY_TOOLING/<service name>/start'
+
+ If not found there search for the it in the Red Hat tooling
+ directory.
+ self.tool_dir + '/REDHAT/<service name>/start'
+
+ If not found there raise an error.
+
+ Returns:
+ return 1 - True if top level tooling found, False otherwise.
+ return 2 - path to tooling
+ '''
+
+ top_path = self.tool_dir + 'user/start'
+ if os.access(top_path, os.X_OK):
+ return True, top_path
+
+ service_user_path = self.tool_dir + 'user/' + \
+ service_name + '/start'
+ if os.access(service_user_path, os.X_OK):
+ return False, service_user_path
+
+ service_redhat_path = self.tool_dir + 'AUDREY_TOOLING/' + \
+ service_name + '/start'
+ if os.access(service_redhat_path, os.X_OK):
+ return False, service_redhat_path
+
+ service_redhat_path = self.tool_dir + 'REDHAT/' + \
+ service_name + '/start'
+ if os.access(service_redhat_path, os.X_OK):
+ return False, service_redhat_path
+
+ # No tooling found. Raise an error.
+ raise ASError(('No configuration tooling found for service: %s') % \
+ (service_name))
+
diff --git a/agent/test_audrey_agent.py b/agent/test_audrey_agent.py
index abfdcbf..570795b 100644
--- a/agent/test_audrey_agent.py
+++ b/agent/test_audrey_agent.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
'''
*
* Copyright [2011] [Red Hat, Inc.]
@@ -30,8 +30,7 @@ sys.path.append('src')
####
from tests.agent import *
from tests.shell import *
-from tests.config_server_client import *
-from tests.config_server import *
+from tests.csclient import *
from tests.user_data import *
####
diff --git a/agent/tests/agent.py b/agent/tests/agent.py
index 6f1fc68..0ca549d 100644
--- a/agent/tests/agent.py
+++ b/agent/tests/agent.py
@@ -27,7 +27,7 @@ from audrey import ASError
from audrey import setup_logging
from audrey.shell import run_cmd
from audrey.agent import main
-from audrey.config_server import gen_env
+from audrey.csclient import gen_env
from tests.mocks import CLOUD_INFO_FILE
from tests.user_data import _write_file
@@ -39,9 +39,9 @@ class TestAudreyAgent(unittest.TestCase):
def setUp(self):
audrey.user_data_ec2.EC2_USER_DATA_URL='http://169.254.169.254/latest/user-data'
- audrey.config_server.client.TOOLING_URL = 'files'
- audrey.config_server.client.PARAMS_URL = 'params'
- audrey.config_server.client.CONFIGS_URL = 'configs'
+ audrey.csclient.client.TOOLING_URL = 'files'
+ audrey.csclient.client.PARAMS_URL = 'params'
+ audrey.csclient.client.CONFIGS_URL = 'configs'
# make a copy of argv
self.argv = list(sys.argv)
# clean out args before you run me
@@ -110,15 +110,15 @@ class TestAudreyAgent(unittest.TestCase):
def test_404_from_tooling(self):
_write_file(CLOUD_INFO_FILE, 'EC2')
- audrey.config_server.client.TOOLING_URL = 'gimmie-404'
+ audrey.csclient.client.TOOLING_URL = 'gimmie-404'
main()
def test_404_from_params(self):
_write_file(CLOUD_INFO_FILE, 'EC2')
- audrey.config_server.client.PARAMS_URL = 'gimmie-404'
+ audrey.csclient.client.PARAMS_URL = 'gimmie-404'
main()
def test_404_from_configs(self):
_write_file(CLOUD_INFO_FILE, 'EC2')
- audrey.config_server.client.CONFIGS_URL = 'gimmie-404'
+ audrey.csclient.client.CONFIGS_URL = 'gimmie-404'
self.assertRaises(ASError, main)
diff --git a/agent/tests/config_server.py b/agent/tests/config_server.py
deleted file mode 100644
index c438d10..0000000
--- a/agent/tests/config_server.py
+++ /dev/null
@@ -1,301 +0,0 @@
-'''
-*
-* Copyright [2011] [Red Hat, Inc.]
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*
-'''
-
-import unittest
-import base64
-
-from audrey import ASError
-from audrey.shell import run_cmd
-from audrey.config_server.tooling import ConfigTooling
-from audrey.config_server import parse_require_config
-from audrey.config_server import parse_provides_params
-from audrey.config_server import generate_provides
-
-class TestAudreyStartupConfigTooling(unittest.TestCase):
- '''
- Make sure all the Config tooling is tested
- '''
- def test_is_user_supplied(self):
- ConfigTooling('test_tooling').is_user_supplied()
-
- def test_is_rh_supplied(self):
- ConfigTooling('test_tooling').is_rh_supplied()
-
- def test_empty_find_tooling(self):
- self.assertRaises(ASError, ConfigTooling('test_tooling').find_tooling, '')
-
- def test_fail_to_create_tooling_dir(self):
- self.assertRaises(ASError, ConfigTooling, tool_dir='/not/real/dir')
-
-class TestAudreyStartupRequiredConfig(unittest.TestCase):
- '''
- Class for exercising the parsing of the Required Configs from the CS.
- '''
-
- def test_success_service_n_params(self):
- '''
- Success case:
- - Exercise parse_require_config() with valid input
- '''
- # Establish valid test data:
-
- src = '|service|jon1' + \
- '|parameters|jon_server_ip&' + base64.b64encode('192.168.1.1') + \
- '|jon_server_ip_2&' + base64.b64encode('192.168.1.2') + \
- '|jon_server_ip_3&' + base64.b64encode('192.168.1.3') + \
- '|service|jon2|'
-
- validation_dict = {'AUDREY_VAR_jon1_jon_server_ip' : '192.168.1.1',
- 'AUDREY_VAR_jon1_jon_server_ip_2' : '192.168.1.2',
- 'AUDREY_VAR_jon1_jon_server_ip_3' : '192.168.1.3' }
-
- # Exersise code segment
- services = parse_require_config(src)
-
- # Validate results
- self.assertEqual(services[0].name, 'jon1')
- self.assertEqual(services[1].name, 'jon2')
-
- for service in services:
- for param in service.params:
- name_val = param.split('&')
- env_var = 'AUDREY_VAR_' + service.name + '_' + name_val[0]
- cmd = ['/usr/bin/printenv', env_var]
- ret = run_cmd(cmd)
- self.assertEqual(ret['out'][:-1], \
- validation_dict[env_var])
-
- def test_success_empty_source(self):
- '''
- Success case:
- - Exercise parse_require_config() with valid empty input
- '''
-
- src = '||'
- services = parse_require_config(src)
- self.assertEqual(services, [])
-
- def test_success_empty_service(self):
- '''
- Failure case:
- - Exercise parse_require_config() with valid input
- '''
-
- src = '|service|' + \
- '|parameters|jon_server_ip&' + base64.b64encode('192.168.1.1') + \
- '|jon_server_ip_2&' + base64.b64encode('192.168.1.2') + \
- '|jon_server_ip_3&' + base64.b64encode('192.168.1.3') + \
- '|service|jon2|'
-
- validation_dict = {'AUDREY_VAR_jon_server_ip' : '192.168.1.1',
- 'AUDREY_VAR_jon_server_ip_2' : '192.168.1.2',
- 'AUDREY_VAR_jon_server_ip_3' : '192.168.1.3' }
-
- services = parse_require_config(src)
- self.assertEqual(services[0].name, '')
- self.assertEqual(services[1].name, 'jon2')
-
- for service in services:
- for param in service.params:
- name_val = param.split('&')
- env_var = 'AUDREY_VAR_' + name_val[0]
- cmd = ['/usr/bin/printenv', env_var]
- ret = run_cmd(cmd)
- self.assertEqual(ret['out'][:-1], \
- validation_dict[env_var])
-
- def test_failure_no_services_name(self):
- '''
- Failure case:
- - Exercise parse_require_config() with valid input
-
- The slight difference between this test and test_success_empty_services
- is the success case has an empty service name indicated by "||":
- |service||paramseters
-
- and the failure case has no service name:
- |service|paramseters
-
- '''
-
- # Establish valid test data:
- src = '|service' \
- '|parameters|jon_server_ip&' + base64.b64encode('192.168.1.1') + \
- '|jon_server_ip_2&' + base64.b64encode('192.168.1.2') + \
- '|jon_server_ip_3&' + base64.b64encode('192.168.1.3') + \
- '|service|jon2|'
-
- validation_dict = {'AUDREY_VAR_jon_server_ip' : '192.168.1.1',
- 'AUDREY_VAR_jon_server_ip_2' : '192.168.1.2',
- 'AUDREY_VAR_jon_server_ip_3' : '192.168.1.3' }
-
- self.assertRaises(ASError, parse_require_config, src)
-
- def test_failure_bad_service_name(self):
- '''
- Failure case:
- - Exercise parse_require_config() with valid input
- '''
-
- src = '|service|parameters|'
- self.assertRaises(ASError, parse_require_config, src)
-
- def test_failure_service_tag_not_found(self):
- '''
- Failure Case:
- - |service| not in src to parse_require_config()
- '''
- src = '|notservice|blah|'
- self.assertRaises(ASError, parse_require_config, src)
-
- def test_failure_no_amp_delim(self):
- '''
- Failure Case:
- - no delim in param token
- '''
- src = '|service|blah|parameters|blah|'
- self.assertRaises(ASError, parse_require_config, src)
-
-
-class TestAudreyStartupProvidesParameters(unittest.TestCase):
- '''
- Class for exercising the parsing of the Provides ParametersConfigs
- from the CS.
- '''
-
- def test_success_parameters(self):
- '''
- Success case:
- - Exercise parse_provides_params() and generate_provides()
- with valid input
- '''
-
- src = '|operatingsystem&is_virtual|'
- expected_params_list = ['operatingsystem', 'is_virtual']
-
- params_list = parse_provides_params(src)
- provides = generate_provides(src)
- self.assertEqual(params_list, expected_params_list)
-
- # The values are not validatable because they are unpredictable
- # but all the expected parameters should be returned.
- # Note: %7C is the encoded |, %26 is the encoded &
- self.assertTrue('audrey_data=%7Coperatingsystem' in provides)
- for param in expected_params_list:
- self.assertTrue('%7C' + str(param) in provides)
-
- def test_success_empty_params(self):
- '''
- Success case:
- - Exercise parse_provides_params() and generate_provides()
- with valid demlims but empty input
- '''
- src = '||'
- params_list = parse_provides_params(src)
- provides = generate_provides(src)
- self.assertEqual(params_list, [''])
- self.assertEqual(provides, 'audrey_data=%7C%26%7C')
-
- def test_success_no_params(self):
- '''
- Success case:
- - Exercise parse_provides_params() and generate_provides()
- with valid input
- - Containging an unavailable parameter
- '''
-
- src = '|uptime_days&unavailable_dogs&ipaddress|'
- expected_params_list = ['uptime_days', 'unavailable_dogs', 'ipaddress']
-
- params_list = parse_provides_params(src)
- provides = generate_provides(src)
-
- # Validate results
- self.assertEqual(params_list, expected_params_list)
-
- # The values are not validatable because they are unpredictable
- # but all the expected parameters should be returned.
- # Note: %7C is the encoded |, %26 is the encoded &
- for param in expected_params_list:
- self.assertTrue('%7C' + str(param) in provides)
-
- # Confirm unavailable parameters return an empty string.
- self.assertTrue('%7C' + 'unavailable_dogs' + '%26%7C' in provides)
-
- def test_success_one_parameters(self):
- '''
- Success case:
- - Exercise parse_provides_params() and generate_provides()
- with valid input
- - with only one parameter
- '''
-
- # Establish valid test data:
- src = '|uptime_days|'
- expected_params_list = ['uptime_days']
-
- # Exersise code segment
- params_list = parse_provides_params(src)
- provides = generate_provides(src)
-
- # Validate results
- self.assertEqual(params_list, expected_params_list)
-
- # The values are not validatable because they are unpredictable
- # but all the expected parameters should be returned.
- # Note: %7C is the encoded |, %26 is the encoded &
- for param in expected_params_list:
- self.assertTrue('%7C' + str(param) in provides)
-
- def test_success_one_parameter(self):
- '''
- Success case:
- - Exercise parse_provides_params() and generate_provides()
- with valid input
- - With only one parameter which is unavailable
- '''
-
- src = '|unavailable_dogs|'
- expected_params_list = ['unavailable_dogs']
-
- params_list = parse_provides_params(src)
- provides = generate_provides(src)
- self.assertEqual(params_list, expected_params_list)
-
- # The values are not validatable because they are unpredictable
- # but all the expected parameters should be returned.
- # Note: %7C is the encoded |, %26 is the encoded &
- for param in expected_params_list:
- self.assertTrue('%7C' + str(param) in provides)
-
- # Confirm unavailable parameters return an empty string.
- self.assertTrue('%7C' + 'unavailable_dogs' + '%26%7C' in provides)
-
- def test_failure_missing_delimiter(self):
- '''
- Failure case:
- - Exercise parse_provides_params() and generate_provides()
- with invalid input
- - missing leading delimiter
- '''
-
- src = 'unavailable_dogs|'
- expected_params_list = ['unavailable_dogs']
-
- self.assertRaises(ASError, parse_provides_params, src)
- self.assertRaises(ASError, generate_provides, src)
diff --git a/agent/tests/config_server_client.py b/agent/tests/config_server_client.py
deleted file mode 100644
index 4a75fc0..0000000
--- a/agent/tests/config_server_client.py
+++ /dev/null
@@ -1,101 +0,0 @@
-'''
-*
-* Copyright [2011] [Red Hat, Inc.]
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*
-'''
-
-import unittest
-
-from audrey import ASError
-from audrey.config_server.client import CSClient
-
-from tests.mocks import HttpUnitTest
-
-DUMMY_CS_CONFIG = {'endpoint': 'http://example.com/',
- 'oauth_key': 'oauthConsumer',
- 'oauth_secret': 'oauthSecret',}
-
-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_cs_configs(self):
- '''
- Success case:
- - Exercise get_cs_configs()
- '''
- self.cs_client.get_cs_configs()
-
- def test_success_get_cs_tooling(self):
- '''
- Success case:
- - Exercise get_cs_tooling()
- '''
- self.cs_client.get_cs_tooling()
-
- def test_success_get_cs_params(self):
- '''
- Success case:
- - Exercise get_cs_params()
- '''
- self.cs_client.get_cs_params()
-
- def test_success_get_cs_confs_n_params(self):
- '''
- Success case:
- - Exercise get_cs_configs() and get_cs_params()
- '''
- self.cs_client.get_cs_configs()
- self.cs_client.get_cs_params()
-
-
- def test_success_put_cs_params_values(self):
- '''
- Success case:
- - Exercise put_cs_params_values()
- '''
- self.cs_client.put_cs_params_values('')
-
- def test_error_http_status(self):
- '''
- Success case:
- - Get a 401
- '''
- self.assertRaises(ASError, self.cs_client._validate_http_status, 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')
diff --git a/agent/tests/csclient.py b/agent/tests/csclient.py
new file mode 100644
index 0000000..24b2a13
--- /dev/null
+++ b/agent/tests/csclient.py
@@ -0,0 +1,381 @@
+'''
+*
+* Copyright [2011] [Red Hat, Inc.]
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+'''
+
+import unittest
+import base64
+
+from audrey import ASError
+from audrey.shell import run_cmd
+from audrey.csclient.client import CSClient
+from audrey.csclient.tooling import ConfigTooling
+from audrey.csclient import parse_require_config
+from audrey.csclient import parse_provides_params
+from audrey.csclient import generate_provides
+
+from tests.mocks import HttpUnitTest
+
+DUMMY_CS_CONFIG = {'endpoint': 'http://example.com/',
+ 'oauth_key': 'oauthConsumer',
+ 'oauth_secret': 'oauthSecret',}
+
+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_cs_configs(self):
+ '''
+ Success case:
+ - Exercise get_cs_configs()
+ '''
+ self.cs_client.get_cs_configs()
+
+ def test_success_get_cs_tooling(self):
+ '''
+ Success case:
+ - Exercise get_cs_tooling()
+ '''
+ self.cs_client.get_cs_tooling()
+
+ def test_success_get_cs_params(self):
+ '''
+ Success case:
+ - Exercise get_cs_params()
+ '''
+ self.cs_client.get_cs_params()
+
+ def test_success_get_cs_confs_n_params(self):
+ '''
+ Success case:
+ - Exercise get_cs_configs() and get_cs_params()
+ '''
+ self.cs_client.get_cs_configs()
+ self.cs_client.get_cs_params()
+
+
+ def test_success_put_cs_params_values(self):
+ '''
+ Success case:
+ - Exercise put_cs_params_values()
+ '''
+ self.cs_client.put_cs_params_values('')
+
+ def test_error_http_status(self):
+ '''
+ Success case:
+ - Get a 401
+ '''
+ self.assertRaises(ASError, self.cs_client._validate_http_status, 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')
+
+class TestAudreyStartupConfigTooling(unittest.TestCase):
+ '''
+ Make sure all the Config tooling is tested
+ '''
+ def test_is_user_supplied(self):
+ ConfigTooling('test_tooling').is_user_supplied()
+
+ def test_is_rh_supplied(self):
+ ConfigTooling('test_tooling').is_rh_supplied()
+
+ def test_empty_find_tooling(self):
+ self.assertRaises(ASError, ConfigTooling('test_tooling').find_tooling, '')
+
+ def test_fail_to_create_tooling_dir(self):
+ self.assertRaises(ASError, ConfigTooling, tool_dir='/not/real/dir')
+
+class TestAudreyStartupRequiredConfig(unittest.TestCase):
+ '''
+ Class for exercising the parsing of the Required Configs from the CS.
+ '''
+
+ def test_success_service_n_params(self):
+ '''
+ Success case:
+ - Exercise parse_require_config() with valid input
+ '''
+ # Establish valid test data:
+
+ src = '|service|jon1' + \
+ '|parameters|jon_server_ip&' + base64.b64encode('192.168.1.1') + \
+ '|jon_server_ip_2&' + base64.b64encode('192.168.1.2') + \
+ '|jon_server_ip_3&' + base64.b64encode('192.168.1.3') + \
+ '|service|jon2|'
+
+ validation_dict = {'AUDREY_VAR_jon1_jon_server_ip' : '192.168.1.1',
+ 'AUDREY_VAR_jon1_jon_server_ip_2' : '192.168.1.2',
+ 'AUDREY_VAR_jon1_jon_server_ip_3' : '192.168.1.3' }
+
+ # Exersise code segment
+ services = parse_require_config(src)
+
+ # Validate results
+ self.assertEqual(services[0].name, 'jon1')
+ self.assertEqual(services[1].name, 'jon2')
+
+ for service in services:
+ for param in service.params:
+ name_val = param.split('&')
+ env_var = 'AUDREY_VAR_' + service.name + '_' + name_val[0]
+ cmd = ['/usr/bin/printenv', env_var]
+ ret = run_cmd(cmd)
+ self.assertEqual(ret['out'][:-1], \
+ validation_dict[env_var])
+
+ def test_success_empty_source(self):
+ '''
+ Success case:
+ - Exercise parse_require_config() with valid empty input
+ '''
+
+ src = '||'
+ services = parse_require_config(src)
+ self.assertEqual(services, [])
+
+ def test_success_empty_service(self):
+ '''
+ Failure case:
+ - Exercise parse_require_config() with valid input
+ '''
+
+ src = '|service|' + \
+ '|parameters|jon_server_ip&' + base64.b64encode('192.168.1.1') + \
+ '|jon_server_ip_2&' + base64.b64encode('192.168.1.2') + \
+ '|jon_server_ip_3&' + base64.b64encode('192.168.1.3') + \
+ '|service|jon2|'
+
+ validation_dict = {'AUDREY_VAR_jon_server_ip' : '192.168.1.1',
+ 'AUDREY_VAR_jon_server_ip_2' : '192.168.1.2',
+ 'AUDREY_VAR_jon_server_ip_3' : '192.168.1.3' }
+
+ services = parse_require_config(src)
+ self.assertEqual(services[0].name, '')
+ self.assertEqual(services[1].name, 'jon2')
+
+ for service in services:
+ for param in service.params:
+ name_val = param.split('&')
+ env_var = 'AUDREY_VAR_' + name_val[0]
+ cmd = ['/usr/bin/printenv', env_var]
+ ret = run_cmd(cmd)
+ self.assertEqual(ret['out'][:-1], \
+ validation_dict[env_var])
+
+ def test_failure_no_services_name(self):
+ '''
+ Failure case:
+ - Exercise parse_require_config() with valid input
+
+ The slight difference between this test and test_success_empty_services
+ is the success case has an empty service name indicated by "||":
+ |service||paramseters
+
+ and the failure case has no service name:
+ |service|paramseters
+
+ '''
+
+ # Establish valid test data:
+ src = '|service' \
+ '|parameters|jon_server_ip&' + base64.b64encode('192.168.1.1') + \
+ '|jon_server_ip_2&' + base64.b64encode('192.168.1.2') + \
+ '|jon_server_ip_3&' + base64.b64encode('192.168.1.3') + \
+ '|service|jon2|'
+
+ validation_dict = {'AUDREY_VAR_jon_server_ip' : '192.168.1.1',
+ 'AUDREY_VAR_jon_server_ip_2' : '192.168.1.2',
+ 'AUDREY_VAR_jon_server_ip_3' : '192.168.1.3' }
+
+ self.assertRaises(ASError, parse_require_config, src)
+
+ def test_failure_bad_service_name(self):
+ '''
+ Failure case:
+ - Exercise parse_require_config() with valid input
+ '''
+
+ src = '|service|parameters|'
+ self.assertRaises(ASError, parse_require_config, src)
+
+ def test_failure_service_tag_not_found(self):
+ '''
+ Failure Case:
+ - |service| not in src to parse_require_config()
+ '''
+ src = '|notservice|blah|'
+ self.assertRaises(ASError, parse_require_config, src)
+
+ def test_failure_no_amp_delim(self):
+ '''
+ Failure Case:
+ - no delim in param token
+ '''
+ src = '|service|blah|parameters|blah|'
+ self.assertRaises(ASError, parse_require_config, src)
+
+
+class TestAudreyStartupProvidesParameters(unittest.TestCase):
+ '''
+ Class for exercising the parsing of the Provides ParametersConfigs
+ from the CS.
+ '''
+
+ def test_success_parameters(self):
+ '''
+ Success case:
+ - Exercise parse_provides_params() and generate_provides()
+ with valid input
+ '''
+
+ src = '|operatingsystem&is_virtual|'
+ expected_params_list = ['operatingsystem', 'is_virtual']
+
+ params_list = parse_provides_params(src)
+ provides = generate_provides(src)
+ self.assertEqual(params_list, expected_params_list)
+
+ # The values are not validatable because they are unpredictable
+ # but all the expected parameters should be returned.
+ # Note: %7C is the encoded |, %26 is the encoded &
+ self.assertTrue('audrey_data=%7Coperatingsystem' in provides)
+ for param in expected_params_list:
+ self.assertTrue('%7C' + str(param) in provides)
+
+ def test_success_empty_params(self):
+ '''
+ Success case:
+ - Exercise parse_provides_params() and generate_provides()
+ with valid demlims but empty input
+ '''
+ src = '||'
+ params_list = parse_provides_params(src)
+ provides = generate_provides(src)
+ self.assertEqual(params_list, [''])
+ self.assertEqual(provides, 'audrey_data=%7C%26%7C')
+
+ def test_success_no_params(self):
+ '''
+ Success case:
+ - Exercise parse_provides_params() and generate_provides()
+ with valid input
+ - Containging an unavailable parameter
+ '''
+
+ src = '|uptime_days&unavailable_dogs&ipaddress|'
+ expected_params_list = ['uptime_days', 'unavailable_dogs', 'ipaddress']
+
+ params_list = parse_provides_params(src)
+ provides = generate_provides(src)
+
+ # Validate results
+ self.assertEqual(params_list, expected_params_list)
+
+ # The values are not validatable because they are unpredictable
+ # but all the expected parameters should be returned.
+ # Note: %7C is the encoded |, %26 is the encoded &
+ for param in expected_params_list:
+ self.assertTrue('%7C' + str(param) in provides)
+
+ # Confirm unavailable parameters return an empty string.
+ self.assertTrue('%7C' + 'unavailable_dogs' + '%26%7C' in provides)
+
+ def test_success_one_parameters(self):
+ '''
+ Success case:
+ - Exercise parse_provides_params() and generate_provides()
+ with valid input
+ - with only one parameter
+ '''
+
+ # Establish valid test data:
+ src = '|uptime_days|'
+ expected_params_list = ['uptime_days']
+
+ # Exersise code segment
+ params_list = parse_provides_params(src)
+ provides = generate_provides(src)
+
+ # Validate results
+ self.assertEqual(params_list, expected_params_list)
+
+ # The values are not validatable because they are unpredictable
+ # but all the expected parameters should be returned.
+ # Note: %7C is the encoded |, %26 is the encoded &
+ for param in expected_params_list:
+ self.assertTrue('%7C' + str(param) in provides)
+
+ def test_success_one_parameter(self):
+ '''
+ Success case:
+ - Exercise parse_provides_params() and generate_provides()
+ with valid input
+ - With only one parameter which is unavailable
+ '''
+
+ src = '|unavailable_dogs|'
+ expected_params_list = ['unavailable_dogs']
+
+ params_list = parse_provides_params(src)
+ provides = generate_provides(src)
+ self.assertEqual(params_list, expected_params_list)
+
+ # The values are not validatable because they are unpredictable
+ # but all the expected parameters should be returned.
+ # Note: %7C is the encoded |, %26 is the encoded &
+ for param in expected_params_list:
+ self.assertTrue('%7C' + str(param) in provides)
+
+ # Confirm unavailable parameters return an empty string.
+ self.assertTrue('%7C' + 'unavailable_dogs' + '%26%7C' in provides)
+
+ def test_failure_missing_delimiter(self):
+ '''
+ Failure case:
+ - Exercise parse_provides_params() and generate_provides()
+ with invalid input
+ - missing leading delimiter
+ '''
+
+ src = 'unavailable_dogs|'
+ expected_params_list = ['unavailable_dogs']
+
+ self.assertRaises(ASError, parse_provides_params, src)
+ self.assertRaises(ASError, generate_provides, src)
--
1.7.7.6
More information about the aeolus-devel
mailing list