diff --git a/Deployment/cloudbase-init/config/cloudbase-init.conf b/Deployment/cloudbase-init/config/cloudbase-init.conf index 57d1faa..0b167a0 100644 --- a/Deployment/cloudbase-init/config/cloudbase-init.conf +++ b/Deployment/cloudbase-init/config/cloudbase-init.conf @@ -8,3 +8,4 @@ config_drive_cdrom=false verbose=true logdir=C:\Program Files (x86)\Cloudbase Solutions\Cloudbase-Init\log\ logfile=cloudbase-init.log +plugins=cloudbaseinit.plugins.windows.userdata.UserDataPlugin \ No newline at end of file diff --git a/Deployment/devstack-scripts/devstack.localrc b/Deployment/devstack-scripts/devstack.localrc index 758db5f..3295cdf 100644 --- a/Deployment/devstack-scripts/devstack.localrc +++ b/Deployment/devstack-scripts/devstack.localrc @@ -16,6 +16,7 @@ RABBIT_PASSWORD=$lab_password SERVICE_PASSWORD=$lab_password SERVICE_TOKEN=tokentoken ENABLED_SERVICES+=,heat,h-api,h-api-cfn,h-api-cw,h-eng +ENABLED_SERVICES+=,conductor,portas LOGFILE=/opt/stack/devstack/stack.sh.log SCREEN_LOGDIR=/opt/stack/log/ diff --git a/Deployment/devstack-scripts/localrc b/Deployment/devstack-scripts/localrc index 2658117..13ce3cd 100644 --- a/Deployment/devstack-scripts/localrc +++ b/Deployment/devstack-scripts/localrc @@ -1,6 +1,7 @@ #!/bin/bash DEVSTACK_DIR=/home/stack/devstack +INSTALL_DIR=/opt/stack MYSQL_DB_TMPFS=true MYSQL_DB_TMPFS_SIZE=128M @@ -8,7 +9,6 @@ MYSQL_DB_TMPFS_SIZE=128M NOVA_CACHE_TMPFS=true NOVA_CACHE_TMPFS_SIZE=24G - #====================================== source $DEVSTACK_DIR/openrc admin admin source ./functions.sh diff --git a/Deployment/devstack-scripts/start-devstack.sh b/Deployment/devstack-scripts/start-devstack.sh index 305200d..e206d87 100644 --- a/Deployment/devstack-scripts/start-devstack.sh +++ b/Deployment/devstack-scripts/start-devstack.sh @@ -20,6 +20,7 @@ $DEVSTACK_DIR/stack.sh # Executing post-stack actions #=============================================================================== source ./post-stack.sh no-localrc +source ./start-keero.sh no-localrc #=============================================================================== diff --git a/Deployment/devstack-scripts/start-keero.sh b/Deployment/devstack-scripts/start-keero.sh new file mode 100644 index 0000000..0c4aa5e --- /dev/null +++ b/Deployment/devstack-scripts/start-keero.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +if [[ -z "$1" ]] ; then + source ./localrc +fi + +die_if_not_set INSTALL_DIR + +# Starting Portas +#================ +if [[ ! -d "$INSTALL_DIR/portas" ]] ; then + mkdir -p "$INSTALL_DIR/portas" +fi + +cp "$INSTALL_DIR/keero/portas/etc" "$INSTALL_DIR/portas/etc" + +screen_it portas "cd $INSTALL_DIR/portas && portas-api --config-file=$INSTALL_DIR/portas/etc/portas-api.conf" +#================ + + + +# Starting Conductor +#=================== +screen_it conductor "cd $INSTALL_DIR/keero/conductor && bash ./tools/with_venv.sh ./bin/app.py" +#=================== diff --git a/Deployment/devstack-scripts/stop-devstack.sh b/Deployment/devstack-scripts/stop-devstack.sh index af35304..32b3d36 100644 --- a/Deployment/devstack-scripts/stop-devstack.sh +++ b/Deployment/devstack-scripts/stop-devstack.sh @@ -18,5 +18,6 @@ $DEVSTACK_DIR/unstack.sh # Executing post-unstack actions #=============================================================================== source ./post-unstack.sh no-localrc +source ./stop-keero.sh no-localrc #=============================================================================== diff --git a/Deployment/devstack-scripts/stop-keero.sh b/Deployment/devstack-scripts/stop-keero.sh new file mode 100644 index 0000000..bf8af5f --- /dev/null +++ b/Deployment/devstack-scripts/stop-keero.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +if [[ -z "$1" ]] ; then + source ./localrc +fi + +# Stopping Keero components +#========================== +for serv in conductor portas ; do + screen -S $SCREEN_NAME -p $serv -X kill +done +#========================== diff --git a/conductor/conductor/cloud_formation.py b/conductor/conductor/cloud_formation.py index e7894bf..7303801 100644 --- a/conductor/conductor/cloud_formation.py +++ b/conductor/conductor/cloud_formation.py @@ -40,4 +40,3 @@ xml_code_engine.XmlCodeEngine.register_function( xml_code_engine.XmlCodeEngine.register_function( prepare_user_data, "prepare_user_data") - diff --git a/conductor/conductor/commands/dispatcher.py b/conductor/conductor/commands/dispatcher.py index 624ce30..8208856 100644 --- a/conductor/conductor/commands/dispatcher.py +++ b/conductor/conductor/commands/dispatcher.py @@ -21,7 +21,6 @@ class CommandDispatcher(command.CommandBase): return result - def has_pending_commands(self): result = False for command in self._command_map.values(): diff --git a/conductor/conductor/function_context.py b/conductor/conductor/function_context.py index 237f23e..e27b6db 100644 --- a/conductor/conductor/function_context.py +++ b/conductor/conductor/function_context.py @@ -48,4 +48,4 @@ class Context(object): return str(self._data) if self._parent: return str(self._parent) - return str({}) \ No newline at end of file + return str({}) diff --git a/conductor/conductor/helpers.py b/conductor/conductor/helpers.py index 4128e16..435a35b 100644 --- a/conductor/conductor/helpers.py +++ b/conductor/conductor/helpers.py @@ -38,6 +38,7 @@ def merge_dicts(dict1, dict2, max_levels=0): result[key] = value return result + def find(f, seq): """Return first item in sequence where f(item) == True.""" index = 0 diff --git a/conductor/conductor/reporting.py b/conductor/conductor/reporting.py index 1f37b8c..cf47440 100644 --- a/conductor/conductor/reporting.py +++ b/conductor/conductor/reporting.py @@ -25,12 +25,9 @@ class Reporter(object): message=msg, key='task-reports') + def _report_func(context, id, entity, text, **kwargs): reporter = context['/reporter'] return reporter._report_func(id, entity, text, **kwargs) xml_code_engine.XmlCodeEngine.register_function(_report_func, "report") - - - - diff --git a/conductor/conductor/workflow.py b/conductor/conductor/workflow.py index a39a7da..46d1d3d 100644 --- a/conductor/conductor/workflow.py +++ b/conductor/conductor/workflow.py @@ -5,6 +5,7 @@ import re import xml_code_engine import function_context + class Workflow(object): def __init__(self, filename, data, command_dispatcher, config, reporter): self._data = data @@ -84,7 +85,6 @@ class Workflow(object): else: return position + suffix.split('.') - @staticmethod def _select_func(context, path='', source=None, **kwargs): @@ -102,7 +102,6 @@ class Workflow(object): context['/dataSource'], Workflow._correct_position(path, context)) - @staticmethod def _set_func(path, context, body, engine, target=None, **kwargs): body_data = engine.evaluate_content(body, context) diff --git a/conductor/conductor/xml_code_engine.py b/conductor/conductor/xml_code_engine.py index fe676b0..42c18a1 100644 --- a/conductor/conductor/xml_code_engine.py +++ b/conductor/conductor/xml_code_engine.py @@ -61,7 +61,8 @@ class XmlCodeEngine(object): return_value = result if len(result) == 0: return_value = ''.join(parts) - if do_strip: return_value = return_value.strip() + if do_strip: + return_value = return_value.strip() elif len(result) == 1: return_value = result[0] diff --git a/conductor/data/init.ps1 b/conductor/data/init.ps1 index 620792c..12c2453 100644 --- a/conductor/data/init.ps1 +++ b/conductor/data/init.ps1 @@ -3,12 +3,34 @@ $WindowsAgentConfigBase64 = '%WINDOWS_AGENT_CONFIG_BASE64%' $WindowsAgentConfigFile = "C:\Keero\Agent\WindowsAgent.exe.config" +$NewComputerName = '%INTERNAL_HOSTNAME%' + +$RestartRequired = $false + Import-Module CoreFunctions -Stop-Service "Keero Agent" -Backup-File $WindowsAgentConfigFile -Remove-Item $WindowsAgentConfigFile -Force -ConvertFrom-Base64String -Base64String $WindowsAgentConfigBase64 -Path $WindowsAgentConfigFile -Exec sc.exe 'config','"Keero Agent"','start=','delayed-auto' -Start-Service 'Keero Agent' -Write-Log 'All done!' \ No newline at end of file +if ( $WindowsAgentConfigBase64 -ne '%WINDOWS_AGENT_CONFIG_BASE64%' ) { + Write-Log "Updating Keero Windows Agent." + Stop-Service "Keero Agent" + Backup-File $WindowsAgentConfigFile + Remove-Item $WindowsAgentConfigFile -Force + ConvertFrom-Base64String -Base64String $WindowsAgentConfigBase64 -Path $WindowsAgentConfigFile + Exec sc.exe 'config','"Keero Agent"','start=','delayed-auto' + Write-Log "Service has been updated." +} + +if ( $NewComputerName -ne '%INTERNAL_HOSTNAME%' ) { + Write-Log "Renaming computer ..." + Rename-Computer -NewName $NewComputerName | Out-Null + Write-Log "New name assigned, restart required." + $RestartRequired = $true +} + +Write-Log 'All done!' +if ( $RestartRequired ) { + Write-Log "Restarting computer ..." + Restart-Computer -Force +} +else { + Start-Service 'Keero Agent' +} diff --git a/conductor/tools/with_venv.sh b/conductor/tools/with_venv.sh old mode 100644 new mode 100755 diff --git a/portas/portas/api/middleware/context.py b/portas/portas/api/middleware/context.py index cd0d6a7..fc81c5c 100644 --- a/portas/portas/api/middleware/context.py +++ b/portas/portas/api/middleware/context.py @@ -75,7 +75,7 @@ class ContextMiddleware(BaseContextMiddleware): 'auth_tok': req.headers.get('X-Auth-Token', deprecated_token), 'service_catalog': service_catalog, 'session': req.headers.get('X-Configuration-Session') - } + } req.context = portas.context.RequestContext(**kwargs) else: raise webob.exc.HTTPUnauthorized() @@ -84,4 +84,4 @@ class ContextMiddleware(BaseContextMiddleware): def factory(cls, global_conf, **local_conf): def filter(app): return cls(app) - return filter \ No newline at end of file + return filter diff --git a/portas/portas/api/v1/__init__.py b/portas/portas/api/v1/__init__.py index 6fa090e..2c08d56 100644 --- a/portas/portas/api/v1/__init__.py +++ b/portas/portas/api/v1/__init__.py @@ -27,15 +27,19 @@ def get_env_status(environment_id, session_id): unit = get_session() if not session_id: - session = unit.query(Session).filter( - Session.environment_id == environment_id and Session.state.in_(['open', 'deploying'])).first() + variants = ['open', 'deploying'] + session = unit.query(Session).filter(Session.environment_id == + environment_id and + Session.state.in_(variants) + ).first() if session: session_id = session.id else: return status session_state = unit.query(Session).get(session_id).state - reports_count = unit.query(Status).filter_by(environment_id=environment_id, session_id=session_id).count() + reports_count = unit.query(Status).filter_by(environment_id=environment_id, + session_id=session_id).count() if session_state == 'deployed': status = 'finished' @@ -50,13 +54,16 @@ def get_env_status(environment_id, session_id): def get_statuses(type): if type in draft['services']: - return [get_service_status(environment_id, session_id, service) for service in - draft['services'][type]] + services = draft['services'][type] + return [get_service_status(environment_id, + session_id, + service) for service in services] else: return [] is_inprogress = filter(lambda item: item == 'inprogress', - get_statuses('activeDirectories') + get_statuses('webServers')) + get_statuses('activeDirectories') + + get_statuses('webServers')) if session_state == 'deploying' and is_inprogress > 1: status = 'inprogress' @@ -71,10 +78,11 @@ def get_service_status(environment_id, session_id, service): session_state = unit.query(Session).get(session_id).state entities = [u['id'] for u in service['units']] - reports_count = unit.query(Status).filter(Status.environment_id == environment_id - and Status.session_id == session_id - and Status.entity_id.in_(entities)) \ - .count() + reports_count = unit.query(Status).filter( + Status.environment_id == environment_id + and Status.session_id == session_id + and Status.entity_id.in_(entities) + ).count() if session_state == 'deployed': status = 'finished' diff --git a/portas/portas/api/v1/active_directories.py b/portas/portas/api/v1/active_directories.py index 2b57a67..ade7a6f 100644 --- a/portas/portas/api/v1/active_directories.py +++ b/portas/portas/api/v1/active_directories.py @@ -9,18 +9,23 @@ log = logging.getLogger(__name__) class Controller(object): def index(self, request, environment_id): - log.debug(_('ActiveDirectory:Index '.format(environment_id))) + log.debug(_('ActiveDirectory:Index '. + format(environment_id))) - draft = prepare_draft(get_draft(environment_id, request.context.session)) + draft = prepare_draft(get_draft(environment_id, + request.context.session)) for dc in draft['services']['activeDirectories']: - dc['status'] = get_service_status(environment_id, request.context.session, dc) + dc['status'] = get_service_status(environment_id, + request.context.session, + dc) return {'activeDirectories': draft['services']['activeDirectories']} @utils.verify_session def create(self, request, environment_id, body): - log.debug(_('ActiveDirectory:Create '.format(environment_id, body))) + log.debug(_('ActiveDirectory:Create '. + format(environment_id, body))) draft = get_draft(session_id=request.context.session) @@ -33,7 +38,7 @@ class Controller(object): for unit in active_directory['units']: unit_count += 1 unit['id'] = uuidutils.generate_uuid() - unit['name'] = 'dc{0}{1}'.format(unit_count, active_directory['id'][:4]) + unit['name'] = 'dc{0}'.format(unit_count) draft = prepare_draft(draft) draft['services']['activeDirectories'].append(active_directory) @@ -42,23 +47,25 @@ class Controller(object): return active_directory def delete(self, request, environment_id, active_directory_id): - log.debug(_('ActiveDirectory:Delete '.format(environment_id, active_directory_id))) + log.debug(_('ActiveDirectory:Delete '. + format(environment_id, active_directory_id))) draft = get_draft(request.context.session) - draft['services']['activeDirectories'] = [service for service in draft['services']['activeDirectories'] if - service['id'] != active_directory_id] + items = [service for service in draft['services']['activeDirectories'] + if service['id'] != active_directory_id] + draft['services']['activeDirectories'] = items save_draft(request.context.session, draft) def prepare_draft(draft): - if not draft.has_key('services'): + if not 'services' in draft: draft['services'] = {} - if not draft['services'].has_key('activeDirectories'): + if not 'activeDirectories' in draft['services']: draft['services']['activeDirectories'] = [] return draft def create_resource(): - return wsgi.Resource(Controller()) \ No newline at end of file + return wsgi.Resource(Controller()) diff --git a/portas/portas/api/v1/environments.py b/portas/portas/api/v1/environments.py index e8c82ee..5e85fa7 100644 --- a/portas/portas/api/v1/environments.py +++ b/portas/portas/api/v1/environments.py @@ -1,10 +1,16 @@ +from amqplib.client_0_8 import Message +import anyjson +import eventlet from webob import exc +from portas.common import config from portas.api.v1 import get_env_status from portas.db.session import get_session from portas.db.models import Environment from portas.openstack.common import wsgi from portas.openstack.common import log as logging +amqp = eventlet.patcher.import_patched('amqplib.client_0_8') +rabbitmq = config.CONF.rabbitmq log = logging.getLogger(__name__) @@ -61,7 +67,8 @@ class Controller(object): return env def update(self, request, environment_id, body): - log.debug(_('Environments:Update '.format(environment_id, body))) + log.debug(_('Environments:Update '. + format(environment_id, body))) session = get_session() environment = session.query(Environment).get(environment_id) @@ -84,8 +91,26 @@ class Controller(object): with session.begin(): session.delete(environment) + #preparing data for removal from conductor + env = environment.description + env['services'] = [] + env['deleted'] = True + + connection = amqp.Connection('{0}:{1}'. + format(rabbitmq.host, rabbitmq.port), + virtual_host=rabbitmq.virtual_host, + userid=rabbitmq.userid, + password=rabbitmq.password, + ssl=rabbitmq.use_ssl, insist=True) + channel = connection.channel() + channel.exchange_declare('tasks', 'direct', durable=True, + auto_delete=False) + + channel.basic_publish(Message(body=anyjson.serialize(env)), 'tasks', + 'tasks') + return None def create_resource(): - return wsgi.Resource(Controller()) \ No newline at end of file + return wsgi.Resource(Controller()) diff --git a/portas/portas/api/v1/router.py b/portas/portas/api/v1/router.py index 5a18601..ed0da25 100644 --- a/portas/portas/api/v1/router.py +++ b/portas/portas/api/v1/router.py @@ -16,7 +16,8 @@ # under the License. import routes from portas.openstack.common import wsgi -from portas.api.v1 import environments, sessions, active_directories, webservers +from portas.api.v1 import (environments, sessions, + active_directories, webservers) class API(wsgi.Router): @@ -64,11 +65,13 @@ class API(wsgi.Router): controller=sessions_resource, action='delete', conditions={'method': ['DELETE']}) - mapper.connect('/environments/{environment_id}/sessions/{session_id}/reports', + mapper.connect('/environments/{environment_id}/sessions/' + '{session_id}/reports', controller=sessions_resource, action='reports', conditions={'method': ['GET']}) - mapper.connect('/environments/{environment_id}/sessions/{session_id}/deploy', + mapper.connect('/environments/{environment_id}/sessions/' + '{session_id}/deploy', controller=sessions_resource, action='deploy', conditions={'method': ['POST']}) @@ -82,7 +85,8 @@ class API(wsgi.Router): controller=activeDirectories_resource, action='create', conditions={'method': ['POST']}) - mapper.connect('/environments/{environment_id}/activeDirectories/{active_directory_id}', + mapper.connect('/environments/{environment_id}/activeDirectories/' + '{active_directory_id}', controller=activeDirectories_resource, action='delete', conditions={'method': ['DELETE']}) @@ -96,8 +100,9 @@ class API(wsgi.Router): controller=webServers_resource, action='create', conditions={'method': ['POST']}) - mapper.connect('/environments/{environment_id}/webServers/{web_server_id}', + mapper.connect('/environments/{environment_id}/webServers/' + '{web_server_id}', controller=webServers_resource, action='delete', conditions={'method': ['DELETE']}) - super(API, self).__init__(mapper) \ No newline at end of file + super(API, self).__init__(mapper) diff --git a/portas/portas/api/v1/sessions.py b/portas/portas/api/v1/sessions.py index dd12cea..5fb30c0 100644 --- a/portas/portas/api/v1/sessions.py +++ b/portas/portas/api/v1/sessions.py @@ -1,7 +1,6 @@ from amqplib.client_0_8 import Message import anyjson import eventlet -from eventlet.semaphore import Semaphore from webob import exc from portas.common import config from portas.db.models import Session, Status, Environment @@ -15,36 +14,34 @@ log = logging.getLogger(__name__) class Controller(object): - def __init__(self): - self.write_lock = Semaphore(1) - connection = amqp.Connection('{0}:{1}'.format(rabbitmq.host, rabbitmq.port), virtual_host=rabbitmq.virtual_host, - userid=rabbitmq.userid, password=rabbitmq.password, - ssl=rabbitmq.use_ssl, insist=True) - self.ch = connection.channel() - self.ch.exchange_declare('tasks', 'direct', durable=True, auto_delete=False) - def index(self, request, environment_id): log.debug(_('Session:List '.format(environment_id))) - filters = {'environment_id': environment_id, 'user_id': request.context.user} + filters = {'environment_id': environment_id, + 'user_id': request.context.user} unit = get_session() configuration_sessions = unit.query(Session).filter_by(**filters) - return {"sessions": [session.to_dict() for session in configuration_sessions if - session.environment.tenant_id == request.context.tenant]} + sessions = [session.to_dict() for session in configuration_sessions if + session.environment.tenant_id == request.context.tenant] + return {"sessions": sessions} def configure(self, request, environment_id): log.debug(_('Session:Configure '.format(environment_id))) - params = {'environment_id': environment_id, 'user_id': request.context.user, 'state': 'open'} + params = {'environment_id': environment_id, + 'user_id': request.context.user, + 'state': 'open'} session = Session() session.update(params) unit = get_session() - if unit.query(Session).filter(Session.environment_id == environment_id and Session.state.in_( - ['open', 'deploing'])).first(): + if unit.query(Session).filter(Session.environment_id == environment_id + and + Session.state.in_(['open', 'deploing']) + ).first(): log.info('There is already open session for this environment') raise exc.HTTPConflict @@ -58,7 +55,8 @@ class Controller(object): return session.to_dict() def show(self, request, environment_id, session_id): - log.debug(_('Session:Show '.format(environment_id, session_id))) + log.debug(_('Session:Show '. + format(environment_id, session_id))) unit = get_session() session = unit.query(Session).get(session_id) @@ -70,14 +68,16 @@ class Controller(object): return session.to_dict() def delete(self, request, environment_id, session_id): - log.debug(_('Session:Delete '.format(environment_id, session_id))) + log.debug(_('Session:Delete '. + format(environment_id, session_id))) unit = get_session() session = unit.query(Session).get(session_id) + comment = 'Session object in \'deploying\' state could not be deleted' if session.state == 'deploying': - log.info('Session is in \'deploying\' state. Could not be deleted.') - raise exc.HTTPForbidden(comment='Session object in \'deploying\' state could not be deleted') + log.info(comment) + raise exc.HTTPForbidden(comment=comment) with unit.begin(): unit.delete(session) @@ -85,7 +85,8 @@ class Controller(object): return None def reports(self, request, environment_id, session_id): - log.debug(_('Session:Reports '.format(environment_id, session_id))) + log.debug(_('Session:Reports '. + format(environment_id, session_id))) unit = get_session() statuses = unit.query(Status).filter_by(session_id=session_id) @@ -93,20 +94,37 @@ class Controller(object): return {'reports': [status.to_dict() for status in statuses]} def deploy(self, request, environment_id, session_id): - log.debug(_('Session:Deploy '.format(environment_id, session_id))) + log.debug(_('Session:Deploy '. + format(environment_id, session_id))) unit = get_session() session = unit.query(Session).get(session_id) + msg = _('Could not deploy session. Session is already ' + 'deployed or in deployment state') if session.state != 'open': - log.warn(_('Could not deploy session. Session is already deployed or in deployment state')) + log.warn(msg) session.state = 'deploying' session.save(unit) - with self.write_lock: - self.ch.basic_publish(Message(body=anyjson.serialize(session.description)), 'tasks', 'tasks') + #Set X-Auth-Token for conductor + env = session.description + env['token'] = request.context.auth_token + + connection = amqp.Connection('{0}:{1}'. + format(rabbitmq.host, rabbitmq.port), + virtual_host=rabbitmq.virtual_host, + userid=rabbitmq.userid, + password=rabbitmq.password, + ssl=rabbitmq.use_ssl, insist=True) + channel = connection.channel() + channel.exchange_declare('tasks', 'direct', durable=True, + auto_delete=False) + + channel.basic_publish(Message(body=anyjson.serialize(env)), 'tasks', + 'tasks') def create_resource(): - return wsgi.Resource(Controller()) \ No newline at end of file + return wsgi.Resource(Controller()) diff --git a/portas/portas/api/v1/webservers.py b/portas/portas/api/v1/webservers.py index d1af6bc..ddc73c3 100644 --- a/portas/portas/api/v1/webservers.py +++ b/portas/portas/api/v1/webservers.py @@ -11,16 +11,19 @@ class Controller(object): def index(self, request, environment_id): log.debug(_('WebServer:List '.format(environment_id))) - draft = prepare_draft(get_draft(environment_id, request.context.session)) + draft = prepare_draft(get_draft(environment_id, + request.context.session)) for dc in draft['services']['webServers']: - dc['status'] = get_service_status(environment_id, request.context.session, dc) + dc['status'] = get_service_status(environment_id, + request.context.session, dc) return {'webServers': draft['services']['webServers']} @utils.verify_session def create(self, request, environment_id, body): - log.debug(_('WebServer:Create '.format(environment_id, body))) + log.debug(_('WebServer:Create '. + format(environment_id, body))) draft = get_draft(session_id=request.context.session) @@ -33,7 +36,7 @@ class Controller(object): for unit in webServer['units']: unit_count += 1 unit['id'] = uuidutils.generate_uuid() - unit['name'] = 'iis{0}{1}'.format(unit_count, webServer['id'][:3]) + unit['name'] = 'iis{0}'.format(unit_count) draft = prepare_draft(draft) draft['services']['webServers'].append(webServer) @@ -43,23 +46,26 @@ class Controller(object): @utils.verify_session def delete(self, request, environment_id, web_server_id): - log.debug(_('WebServer:Delete '.format(environment_id, web_server_id))) + log.debug(_('WebServer:Delete '. + format(environment_id, web_server_id))) draft = get_draft(session_id=request.context.session) - draft['services']['webServers'] = [service for service in draft['services']['webServers'] if - service['id'] != web_server_id] + + elements = [service for service in draft['services']['webServers'] if + service['id'] != web_server_id] + draft['services']['webServers'] = elements save_draft(request.context.session, draft) def prepare_draft(draft): - if not draft.has_key('services'): + if not 'services' in draft: draft['services'] = {} - if not draft['services'].has_key('webServers'): + if not 'webServers' in draft['services']: draft['services']['webServers'] = [] return draft def create_resource(): - return wsgi.Resource(Controller()) \ No newline at end of file + return wsgi.Resource(Controller()) diff --git a/portas/portas/common/config.py b/portas/portas/common/config.py index c0ea7d0..58a6378 100644 --- a/portas/portas/common/config.py +++ b/portas/portas/common/config.py @@ -82,6 +82,7 @@ def parse_args(args=None, usage=None, default_config_files=None): usage=usage, default_config_files=default_config_files) + def setup_logging(): """ Sets up the logging options for a log with supplied name diff --git a/portas/portas/common/exception.py b/portas/portas/common/exception.py index 59bb0ee..7f338e8 100644 --- a/portas/portas/common/exception.py +++ b/portas/portas/common/exception.py @@ -45,6 +45,7 @@ class PortasException(Exception): super(PortasException, self).__init__(message) + class SchemaLoadError(PortasException): message = _("Unable to load schema: %(reason)s") @@ -52,5 +53,3 @@ class SchemaLoadError(PortasException): class InvalidObject(PortasException): message = _("Provided object does not match schema " "'%(schema)s': %(reason)s") - - diff --git a/portas/portas/common/service.py b/portas/portas/common/service.py index 143a358..238304e 100644 --- a/portas/portas/common/service.py +++ b/portas/portas/common/service.py @@ -27,14 +27,18 @@ class TaskResultHandlerService(service.Service): super(TaskResultHandlerService, self).stop() def _handle_results(self): - connection = amqp.Connection('{0}:{1}'.format(rabbitmq.host, rabbitmq.port), virtual_host=rabbitmq.virtual_host, - userid=rabbitmq.userid, password=rabbitmq.password, + connection = amqp.Connection('{0}:{1}'. + format(rabbitmq.host, rabbitmq.port), + virtual_host=rabbitmq.virtual_host, + userid=rabbitmq.userid, + password=rabbitmq.password, ssl=rabbitmq.use_ssl, insist=True) ch = connection.channel() def bind(exchange, queue): if not exchange: - ch.exchange_declare(exchange, 'direct', durable=True, auto_delete=False) + ch.exchange_declare(exchange, 'direct', durable=True, + auto_delete=False) ch.queue_declare(queue, durable=True, auto_delete=False) if not exchange: ch.queue_bind(queue, exchange, queue) @@ -43,13 +47,15 @@ class TaskResultHandlerService(service.Service): bind(conf.reports_exchange, conf.reports_queue) ch.basic_consume(conf.results_exchange, callback=handle_result) - ch.basic_consume(conf.reports_exchange, callback=handle_report, no_ack=True) + ch.basic_consume(conf.reports_exchange, callback=handle_report, + no_ack=True) while ch.callbacks: ch.wait() def handle_report(msg): - log.debug(_('Got report message from orchestration engine:\n{0}'.format(msg.body))) + log.debug(_('Got report message from orchestration engine:\n{0}'. + format(msg.body))) params = anyjson.deserialize(msg.body) params['entity_id'] = params['id'] @@ -61,7 +67,8 @@ def handle_report(msg): session = get_session() #connect with session conf_session = session.query(Session).filter_by( - **{'environment_id': status.environment_id, 'state': 'deploying'}).first() + **{'environment_id': status.environment_id, + 'state': 'deploying'}).first() status.session_id = conf_session.id with session.begin(): @@ -69,9 +76,16 @@ def handle_report(msg): def handle_result(msg): - log.debug(_('Got result message from orchestration engine:\n{0}'.format(msg.body))) + log.debug(_('Got result message from ' + 'orchestration engine:\n{0}'.format(msg.body))) environment_result = anyjson.deserialize(msg.body) + if environment_result['deleted']: + log.debug(_('Result for environment {0} is dropped. ' + 'Environment is deleted'.format(environment_result['id']))) + + msg.channel.basic_ack(msg.delivery_tag) + return session = get_session() environment = session.query(Environment).get(environment_result['id']) diff --git a/portas/portas/common/uuidutils.py b/portas/portas/common/uuidutils.py index f3513ac..cf00651 100644 --- a/portas/portas/common/uuidutils.py +++ b/portas/portas/common/uuidutils.py @@ -2,4 +2,4 @@ import uuid def generate_uuid(): - return str(uuid.uuid4()).replace('-', '') \ No newline at end of file + return str(uuid.uuid4()).replace('-', '') diff --git a/portas/portas/context.py b/portas/portas/context.py index 9fb9c56..c541829 100644 --- a/portas/portas/context.py +++ b/portas/portas/context.py @@ -24,7 +24,9 @@ class RequestContext(object): accesses the system, as well as additional request information. """ - def __init__(self, auth_tok=None, user=None, tenant=None, roles=None, service_catalog=None, session=None): + def __init__(self, auth_tok=None, user=None, tenant=None, + roles=None, service_catalog=None, session=None): + self.auth_tok = auth_tok self.user = user self.tenant = tenant @@ -55,4 +57,4 @@ class RequestContext(object): @classmethod def from_dict(cls, values): - return cls(**values) \ No newline at end of file + return cls(**values) diff --git a/portas/portas/db/__init__.py b/portas/portas/db/__init__.py index 75fd934..2070644 100644 --- a/portas/portas/db/__init__.py +++ b/portas/portas/db/__init__.py @@ -9,4 +9,4 @@ sql_connection_opt = cfg.StrOpt('sql_connection', 'Default: %(default)s') CONF = cfg.CONF -CONF.register_opt(sql_connection_opt) \ No newline at end of file +CONF.register_opt(sql_connection_opt) diff --git a/portas/portas/db/migrate_repo/versions/001_add_initial_tables.py b/portas/portas/db/migrate_repo/versions/001_add_initial_tables.py index f2262f2..52a6d16 100644 --- a/portas/portas/db/migrate_repo/versions/001_add_initial_tables.py +++ b/portas/portas/db/migrate_repo/versions/001_add_initial_tables.py @@ -11,7 +11,7 @@ Table('environment', meta, Column('updated', DateTime(), nullable=False), Column('tenant_id', String(32), nullable=False), Column('description', Text(), nullable=False), -) + ) Table('service', meta, Column('id', String(32), primary_key=True), @@ -21,7 +21,7 @@ Table('service', meta, Column('created', DateTime, nullable=False), Column('updated', DateTime, nullable=False), Column('description', Text(), nullable=False), -) + ) def upgrade(migrate_engine): diff --git a/portas/portas/db/migrate_repo/versions/002_add_session_table.py b/portas/portas/db/migrate_repo/versions/002_add_session_table.py index d9ca59c..30f89c8 100644 --- a/portas/portas/db/migrate_repo/versions/002_add_session_table.py +++ b/portas/portas/db/migrate_repo/versions/002_add_session_table.py @@ -5,12 +5,13 @@ meta = MetaData() session = Table('session', meta, Column('id', String(32), primary_key=True), - Column('environment_id', String(32), ForeignKey('environment.id')), + Column('environment_id', String(32), + ForeignKey('environment.id')), Column('created', DateTime, nullable=False), Column('updated', DateTime, nullable=False), Column('user_id', String(32), nullable=False), Column('state', Text(), nullable=False), -) + ) def upgrade(migrate_engine): diff --git a/portas/portas/db/migrate_repo/versions/003_add_status_table.py b/portas/portas/db/migrate_repo/versions/003_add_status_table.py index 6bacb9c..1ee86b2 100644 --- a/portas/portas/db/migrate_repo/versions/003_add_status_table.py +++ b/portas/portas/db/migrate_repo/versions/003_add_status_table.py @@ -8,10 +8,11 @@ status = Table('status', meta, Column('created', DateTime, nullable=False), Column('updated', DateTime, nullable=False), Column('entity', String(10), nullable=False), - Column('environment_id', String(32), ForeignKey('environment.id')), + Column('environment_id', String(32), + ForeignKey('environment.id')), Column('session_id', String(32), ForeignKey('session.id')), Column('text', Text(), nullable=False), -) + ) def upgrade(migrate_engine): diff --git a/portas/portas/db/migrate_repo/versions/005_remove_obsolete_service_table.py b/portas/portas/db/migrate_repo/versions/005_remove_obsolete_service_table.py index 2692829..8c3bfc1 100644 --- a/portas/portas/db/migrate_repo/versions/005_remove_obsolete_service_table.py +++ b/portas/portas/db/migrate_repo/versions/005_remove_obsolete_service_table.py @@ -5,14 +5,15 @@ from sqlalchemy.types import String, Text, DateTime meta = MetaData() service = Table('service', meta, - Column('id', String(32), primary_key=True), - Column('name', String(255), nullable=False), - Column('type', String(40), nullable=False), - Column('environment_id', String(32), ForeignKey('environment.id')), - Column('created', DateTime, nullable=False), - Column('updated', DateTime, nullable=False), - Column('description', Text(), nullable=False), -) + Column('id', String(32), primary_key=True), + Column('name', String(255), nullable=False), + Column('type', String(40), nullable=False), + Column('environment_id', String(32), + ForeignKey('environment.id')), + Column('created', DateTime, nullable=False), + Column('updated', DateTime, nullable=False), + Column('description', Text(), nullable=False), + ) def upgrade(migrate_engine): diff --git a/portas/portas/db/models.py b/portas/portas/db/models.py index 412d1fb..9491bab 100644 --- a/portas/portas/db/models.py +++ b/portas/portas/db/models.py @@ -83,7 +83,8 @@ class ModelBase(object): def to_dict(self): dictionary = self.__dict__.copy() - return {k: v for k, v in dictionary.iteritems() if k != '_sa_instance_state'} + return {k: v for k, v in dictionary.iteritems() + if k != '_sa_instance_state'} class JsonBlob(TypeDecorator): diff --git a/portas/portas/tests/sanity_tests.py b/portas/portas/tests/sanity_tests.py new file mode 100644 index 0000000..d6258f2 --- /dev/null +++ b/portas/portas/tests/sanity_tests.py @@ -0,0 +1,24 @@ +import unittest2 +from mock import MagicMock + +import portas.api.v1.router as router + +def my_mock(link, controller, action, conditions): + return [link, controller, action, conditions] + +def func_mock(): + return True + +class SanityUnitTests(unittest2.TestCase): + + def test_api(self): + router.webservers = MagicMock(create_resource=func_mock) + router.sessions = MagicMock(create_resource=func_mock) + router.active_directories = MagicMock(create_resource=func_mock) + router.environments = MagicMock(create_resource=func_mock) + mapper = MagicMock(connect=my_mock) + + object = router.API(mapper) + + assert object._router is not None + \ No newline at end of file diff --git a/portas/portas/utils.py b/portas/portas/utils.py index 393c6fb..30ae919 100644 --- a/portas/portas/utils.py +++ b/portas/portas/utils.py @@ -11,8 +11,8 @@ def verify_session(func): @functools.wraps(func) def __inner(self, request, *args, **kwargs): if hasattr(request, 'context') and request.context.session: - uw = get_session() - configuration_session = uw.query(Session).get(request.context.session) + uw = get_session().query(Session) + configuration_session = uw.get(request.context.session) if configuration_session.state != 'open': log.info('Session is already deployed') @@ -22,5 +22,3 @@ def verify_session(func): raise exc.HTTPUnauthorized return func(self, request, *args, **kwargs) return __inner - - diff --git a/python-portasclient/portasclient/version.py b/python-portasclient/portasclient/version.py index 53823b3..c140d8d 100644 --- a/python-portasclient/portasclient/version.py +++ b/python-portasclient/portasclient/version.py @@ -15,6 +15,6 @@ # under the License. -from portas.openstack.common import version as common_version +from portasclient.openstack.common import version as common_version version_info = common_version.VersionInfo('python-portasclient') diff --git a/python-portasclient/tests/test_sanity.py b/python-portasclient/tests/test_sanity.py index 89d3628..87da75d 100644 --- a/python-portasclient/tests/test_sanity.py +++ b/python-portasclient/tests/test_sanity.py @@ -1,19 +1,29 @@ +import os import unittest import logging from mock import MagicMock +from mock import patch +from portasclient.client import Client as CommonClient from portasclient.v1 import Client import portasclient.v1.environments as environments import portasclient.v1.services as services import portasclient.v1.sessions as sessions +import portasclient.shell as shell +import portasclient.common.http as http + + LOG = logging.getLogger('Unit tests') + def my_mock(*a, **b): return [a, b] + api = MagicMock(json_request=my_mock) + class SanityUnitTests(unittest.TestCase): def test_create_client_instance(self): @@ -25,145 +35,121 @@ class SanityUnitTests(unittest.TestCase): assert test_client.sessions is not None assert test_client.activeDirectories is not None assert test_client.webServers is not None + + def test_common_client(self): + endpoint = 'http://localhost:8001' + test_client = CommonClient('1', endpoint=endpoint, token='1', timeout=10) + + assert test_client.environments is not None + assert test_client.sessions is not None + assert test_client.activeDirectories is not None + assert test_client.webServers is not None def test_env_manager_list(self): - manager = environments.EnvironmentManager(api) - result = manager.list() - assert result == [] def test_env_manager_create(self): - manager = environments.EnvironmentManager(api) - result = manager.create('test') - assert result.headers == {} assert result.body == {'name': 'test'} def test_env_manager_delete(self): - manager = environments.EnvironmentManager(api) - result = manager.delete('test') - assert result is None def test_env_manager_update(self): - manager = environments.EnvironmentManager(api) - result = manager.update('1', 'test') - assert result.body == {'name': 'test'} def test_env_manager_get(self): - manager = environments.EnvironmentManager(api) - result = manager.get('test') ## WTF? assert result.manager is not None + + def test_env(self): + environment = environments.Environment(api, api) + assert environment.data() is not None def test_ad_manager_list(self): - manager = services.ActiveDirectoryManager(api) - result = manager.list('datacenter1') - + assert result == [] + result = manager.list('datacenter1', '1') assert result == [] def test_ad_manager_create(self): - manager = services.ActiveDirectoryManager(api) - result = manager.create('datacenter1', 'session1', 'test') - assert result.headers == {'X-Configuration-Session': 'session1'} assert result.body == 'test' #@unittest.skip("https://mirantis.jira.com/browse/KEERO-218") def test_ad_manager_delete(self): - manager = services.ActiveDirectoryManager(api) - result = manager.delete('datacenter1', 'session1', 'test') - assert result is None def test_iis_manager_list(self): - manager = services.WebServerManager(api) - result = manager.list('datacenter1') - + assert result == [] + result = manager.list('datacenter1', '1') assert result == [] def test_iis_manager_create(self): - manager = services.WebServerManager(api) - result = manager.create('datacenter1', 'session1', 'test') - assert result.headers == {'X-Configuration-Session': 'session1'} assert result.body == 'test' #@unittest.skip("https://mirantis.jira.com/browse/KEERO-218") def test_iis_manager_delete(self): - manager = services.WebServerManager(api) - result = manager.delete('datacenter1', 'session1', 'test') - assert result is None + def test_service_ad(self): + service_ad = services.ActiveDirectory(api, api) + assert service_ad.data() is not None + + def test_service_iis(self): + service_iis = services.ActiveDirectory(api, api) + assert service_iis.data() is not None + def test_session_manager_list(self): - manager = sessions.SessionManager(api) - result = manager.list('datacenter1') - assert result == [] def test_session_manager_delete(self): - manager = sessions.SessionManager(api) - result = manager.delete('datacenter1', 'session1') - assert result is None def test_session_manager_get(self): - manager = sessions.SessionManager(api) - result = manager.get('datacenter1', 'session1') # WTF? assert result.manager is not None def test_session_manager_configure(self): - manager = sessions.SessionManager(api) - result = manager.configure('datacenter1') - assert result.headers == {} def test_session_manager_deploy(self): - manager = sessions.SessionManager(api) - result = manager.deploy('datacenter1', '1') - assert result is None #@unittest.skip("https://mirantis.jira.com/browse/KEERO-219") def test_session_manager_reports(self): - manager = sessions.SessionManager(api) - result = manager.reports('datacenter1', '1') - assert result == [] \ No newline at end of file diff --git a/tests/deploy.sh b/tests/deploy.sh old mode 100644 new mode 100755 index e57207d..cad10a2 --- a/tests/deploy.sh +++ b/tests/deploy.sh @@ -24,10 +24,20 @@ expect "*$*" send -- "./unstack.sh\n" expect "*$*" send -- "./stack.sh\n" -expect "*Would you like to start it now?*" +expect "*/usr/bin/service: 123: exec: status: not found*" send -- "y\n" expect "*stack.sh completed*" +send -- "sudo rabbitmq-plugins enable rabbitmq_management\n" +expect "*$*" +send -- "sudo service rabbitmq-server restart\n" +expect "*$*" +send -- "sudo rabbitmqctl add_user keero keero\n" +expect "*$*" +send -- "sudo rabbitmqctl set_user_tags keero administrator\n" +expect "*$*" + + send -- "source openrc admin admin\n" expect "*$*" @@ -37,7 +47,7 @@ expect "*$*" send -- "nova keypair-add keero-linux-keys > heat_key.priv\n" expect "*$*" -send -- "glance image-create --name 'ws-2012-full-agent' --is-public true --container-format ovf --disk-format qcow2 < ws-2012-full-agent.qcow2\n" +send -- "glance image-create --name 'ws-2012-full' --is-public true --container-format ovf --disk-format qcow2 < ws-2012-full.qcow2\n" expect "*$*" send -- "cd ~/keero\n" @@ -50,33 +60,22 @@ send -- "cp -Rf ~/keero/dashboard/windc /opt/stack/horizon/openstack_dashboard/d expect "*$*" send -- "cp -f ~/keero/dashboard/api/windc.py /opt/stack/horizon/openstack_dashboard/api/\n" expect "*$*" -send -- "cp -Rf ~/keero/dashboard/windcclient /opt/stack/horizon/\n" +send -- "cd ~/keero/python-portasclient\n" expect "*$*" -send -- "cd ~/keero/windc\n" +send -- "sudo python setup.py install\n" expect "*$*" -send -- "rm -rf windc.sqlite\n" +send -- "cd ~/keero/portas\n" expect "*$*" -send -- "./tools/with_venv.sh ./bin/windc-api --config-file=./etc/windc-api-paste.ini --dbsync\n" +send -- "./tools/with_venv.sh ./bin/portas-api --config-file=./etc/portas-api.conf & > ~/APIservice.log\n" +sleep 10 +send -- "\n" +expect "*$*" +send -- "cd ~/keero/conductor\n" +expect "*$*" +send -- "./tools/with_venv.sh ./bin/app.py & > ~/conductor.log\n" +sleep 10 +send -- "\n" expect "*$*" send -- "logout\n" expect "*#*" -send -- "rabbitmq-plugins enable rabbitmq_management\n" -expect "*#*" -send -- "service rabbitmq-server restart\n" -expect "*#*" -send -- "rabbitmqctl add_user keero keero\n" -expect "*#*" -send -- "rabbitmqctl set_user_tags keero administrator\n" -expect "*#*" - -send -- "su - stack\n" -expect "*$*" -send -- "cd /opt/stack/devstack\n" -expect "*$*" -send -- "source openrc admin admin\n" -expect "*$*" -send -- "cd /opt/stack/keero/windc\n" -expect "*$*" -send -- "sudo ./tools/with_venv.sh ./bin/windc-api --config-file=./etc/windc-api-paste.ini > /opt/stack/tests_windc_daemon.log &\n" -expect "*$*" diff --git a/tests/selenium/conf.ini b/tests/selenium/conf.ini index 8b43288..437aeba 100644 --- a/tests/selenium/conf.ini +++ b/tests/selenium/conf.ini @@ -1,4 +1,4 @@ [server] address=http://172.18.124.101 user=admin -password=AkvareL707 \ No newline at end of file +password=swordfish \ No newline at end of file diff --git a/tests/selenium/datacenters_page.py b/tests/selenium/datacenters_page.py index e48a9c1..03e4e26 100644 --- a/tests/selenium/datacenters_page.py +++ b/tests/selenium/datacenters_page.py @@ -1,56 +1,54 @@ import re -from login_page import LoginPage from services_page import ServicesPage +import page -class DataCentersPage(): - page = None - - def __init__(self): - start_page = LoginPage() - self.page = start_page.login() - self.page.find_element_by_link_text('Project').click() - self.page.find_element_by_link_text('Windows Data Centers').click() +class DataCentersPage(page.Page): def create_data_center(self, name): - button_text = 'Create Windows Data Center' - self.page.find_element_by_link_text(button_text).click() + self.Refresh() - name_field = self.page.find_element_by_id('id_name') + self.Button('Create Windows Data Center').Click() + + self.EditBox('id_name').Set(name) xpath = "//input[@value='Create']" - button = self.page.find_element_by_xpath(xpath) - - name_field.clear() - name_field.send_keys(name) - - button.click() - - return self.page - - def find_data_center(self, name): - return self.page.find_element_by_link_text(name) + self.Button(xpath).Click() def delete_data_center(self, name): - datacenter = self.find_data_center(name) - link = datacenter.get_attribute('href') + self.Refresh() + + link = self.Link(name).Address() datacenter_id = re.search('windc/(\S+)', link).group(0)[6:-1] - xpath = ".//*[@id='windc__row__%s']/td[3]/div/a[2]" % datacenter_id - more_button = self.page.find_element_by_xpath(xpath) + xpath = ".//*[@id='windc__row__%s']/td[4]/div/a[2]" % datacenter_id + self.Button(xpath).Click() - more_button.click() + button_id = "windc__row_%s__action_delete" % datacenter_id + self.Button(button_id).Click() - delete_button_id = "windc__row_%s__action_delete" % datacenter_id - delete_button = self.page.find_element_by_id(delete_button_id) - - delete_button.click() - - self.page.find_element_by_link_text("Delete Data Center").click() - - return self.page + self.Button("Delete Data Center").Click() def select_data_center(self, name): - datacenter = self.page.find_data_center(name) - datacenter.click() - self.page = ServicesPage(self.page) - return self.page + self.Link(name).Click() + page = ServicesPage(self.driver) + return page + + def deploy_data_center(self, name): + self.Refresh() + + link = self.Link(name).Address() + datacenter_id = re.search('windc/(\S+)', link).group(0)[6:-1] + + xpath = ".//*[@id='windc__row__%s']/td[4]/div/a[2]" % datacenter_id + self.Button(xpath).Click() + + button_id = "windc__row_%s__action_deploy" % datacenter_id + self.Button(button_id).Click() + + def get_datacenter_status(self, name): + self.Navigate('Windows Data Centers') + link = self.Link(name).Address() + datacenter_id = re.search('windc/(\S+)', link).group(0)[6:-1] + + xpath = ".//*[@id='windc__row__%s']/td[3]" % datacenter_id + return self.TableCell(xpath).Text() diff --git a/tests/selenium/login_page.py b/tests/selenium/login_page.py index f0f357c..b22c9b1 100644 --- a/tests/selenium/login_page.py +++ b/tests/selenium/login_page.py @@ -1,8 +1,8 @@ import ConfigParser -from selenium import webdriver +import page -class LoginPage(): +class LoginPage(page.Page): def login(self): config = ConfigParser.RawConfigParser() @@ -11,20 +11,11 @@ class LoginPage(): user = config.get('server', 'user') password = config.get('server', 'password') - page = webdriver.Firefox() - page.set_page_load_timeout(30) - page.implicitly_wait(30) - page.get(url) - name = page.find_element_by_name('username') - pwd = page.find_element_by_name('password') + self.Open(url) + + self.EditBox('username').Set(user) + self.EditBox('password').Set(password) xpath = "//button[@type='submit']" - button = page.find_element_by_xpath(xpath) + self.Button(xpath).Click() - name.clear() - name.send_keys(user) - pwd.clear() - pwd.send_keys(password) - - button.click() - - return page + return self diff --git a/tests/selenium/page.py b/tests/selenium/page.py new file mode 100644 index 0000000..43ed7cc --- /dev/null +++ b/tests/selenium/page.py @@ -0,0 +1,192 @@ +import logging +from selenium.webdriver.support.ui import Select +logging.basicConfig() +LOG = logging.getLogger(' Page object: ') + + +class TableCellClass: + table = None + + def __init__(self, obj): + if not obj: + LOG.error('TableCell does not found') + self.table = obj + + def Text(self): + if self.table: + return self.table.text() + else: + return '' + + +class ButtonClass: + button = None + + def __init__(self, obj): + if not obj: + LOG.error('Button does not found') + self.button = obj + + def Click(self): + if self.button: + self.button.click() + + def isPresented(self): + if self.button: + return True + return False + + +class LinkClass: + link = None + + def __init__(self, obj): + if not obj: + LOG.error('Link does not found') + self.link = obj + + def Click(self): + if self.link: + self.link.click() + + def isPresented(self): + if self.link: + return True + return False + + def Address(self): + if self.link: + return self.link.get_attribute('href') + else: + return '' + + +class EditBoxClass: + + def __init__(self, obj): + if not obj: + LOG.error('EditBox does not found') + self.edit = obj + + def isPresented(self): + if self.edit: + return True + return False + + def Set(self, value): + if self.edit: + self.edit.clear() + self.edit.send_keys(value) + + def Text(self): + if self.edit: + return self.edit.get_text() + else: + return '' + + +class DropDownListClass: + select = None + + def __init__(self, obj): + if not obj: + LOG.error('DropDownList does not found') + self.select = obj + + def isPresented(self): + if self.select is not None: + return True + return False + + def Set(self, value): + if self.select: + Select(self.select).select_by_visible_text(value) + + def Text(self): + if self.select: + return self.select.get_text() + else: + return '' + + +error_msg = """ + Object with parameter: %s + does not found on page. + """ + + +class Page: + + driver = None + timeout = 30 + + def __init__(self, driver): + driver.set_page_load_timeout(30) + driver.implicitly_wait(0.01) + self.driver = driver + + def _find_element(self, parameter): + obj = None + k = 0 + while (obj is None and k < self.timeout): + k += 1 + try: + obj = self.driver.find_element_by_name(parameter) + return obj + except: + pass + try: + obj = self.driver.find_element_by_id(parameter) + return obj + except: + pass + try: + obj = self.driver.find_element_by_xpath(parameter) + return obj + except: + pass + try: + obj = self.driver.find_element_by_partial_link_text(parameter) + return obj + except: + pass + + LOG.error(error_msg % parameter) + return None + + def Open(self, url): + self.driver.get(url) + + def Refresh(self): + self.driver.refresh() + + def TableCell(self, name): + obj = self._find_element(name) + table = TableCellClass(obj) + return table + + def Button(self, name): + obj = self._find_element(name) + button = ButtonClass(obj) + return button + + def Link(self, name): + obj = self._find_element(name) + link = LinkClass(obj) + return link + + def EditBox(self, name): + obj = self._find_element(name) + edit = EditBoxClass(obj) + return edit + + def DropDownList(self, name): + obj = self._find_element(name) + select = DropDownListClass(obj) + return select + + def Navigate(self, path): + steps = path.split(':') + + for step in steps: + self.Button(step).Click() diff --git a/tests/selenium/services_page.py b/tests/selenium/services_page.py index fb43043..772b9df 100644 --- a/tests/selenium/services_page.py +++ b/tests/selenium/services_page.py @@ -1,57 +1,32 @@ -import ConfigParser -from selenium import webdriver +import page +import re -class ServicesPage(): - page = None - - def __init__(self, page): - self.page = page +class ServicesPage(page.Page): def create_service(self, service_type, parameters): + self.Refresh() - button_id = 'services__action_CreateService' - button = self.page.find_element_by_id(button_id) - button.click() + self.Button('services__action_CreateService').Click() + self.DropDownList('0-service').Set(service_type) + self.Button('wizard_goto_step').Click() - self.select_type_of_service(service_type) + for key in parameters: + self.EditBox(key).Set(parameters[key]) - for parameter in parameters: - field = self.page.find_element_by_name(parameter.key) - field.clear() - field.send_keys(parameter.value) - - xpath = "//input[@value='Deploy']" - deploy_button = self.page.find_element_by_xpath(xpath) - deploy_button.click() - - return page - - def select_type_of_service(self, service_type): - type_field = self.page.find_element_by_name('0-service') - type_field.select_by_visible_text(service_type) - next_button = self.page.find_element_by_name('wizard_goto_step') - next_button.click() - return self.page - - def find_service(self, name): - return self.page.find_element_by_link_text(name) + self.Button("//input[@value='Create']").Click() def delete_service(self, name): - service = self.find_data_center(name) - link = service.get_attribute('href') + self.Refresh() + + link = self.Link(name).Address() service_id = re.search('windc/(\S+)', link).group(0)[6:-1] xpath = ".//*[@id='services__row__%s']/td[5]/div/a[2]" % service_id - more_button = self.page.find_element_by_xpath(xpath) - more_button.click() + self.Button(xpath).Click() - delete_button_id = "services__row_%s__action_delete" % datacenter_id - delete_button = self.page.find_element_by_id(delete_button_id) + button_id = "services__row_%s__action_delete" % service_id + self.Button(button_id).Click() - delete_button.click() - - self.page.find_element_by_link_text("Delete Service").click() - - return self.page + self.Button("Delete Service").Click() diff --git a/tests/selenium/test.py b/tests/selenium/test.py index 6c8389c..b1c2b60 100644 --- a/tests/selenium/test.py +++ b/tests/selenium/test.py @@ -1,54 +1,155 @@ # -*- coding: utf-8 -*- -import untitest2 +import unittest2 +from login_page import LoginPage from datacenters_page import DataCentersPage +from selenium import webdriver class SanityTests(unittest2.TestCase): - def setUp(self): - self.page = DataCentersPage() + @classmethod + def setUpClass(self): + driver = webdriver.Firefox() + self.page = LoginPage(driver) + self.page.login() + self.page.Navigate('Project:Windows Data Centers') + self.page = DataCentersPage(driver) - def tearDown(self): - self.page.close() + @classmethod + def tearDownClass(self): + self.page.driver.close() def test_01_create_data_center(self): self.page.create_data_center('dc1') - assert self.page.find_data_center('dc1') is not None + assert self.page.Link('dc1').isPresented() def test_02_delete_data_center(self): self.page.delete_data_center('dc1') - assert self.page.find_data_center('dc1') is None + assert not self.page.Link('dc1').isPresented() def test_03_create_data_centers(self): - for i in range(1, 20): + for i in range(1, 10): name = 'datacenter' + str(i) self.page.create_data_center(name) - assert self.page.find_data_center(name) is not None + assert self.page.Link(name).isPresented() def test_04_delete_data_centers(self): self.page.delete_data_center('datacenter1') - self.page.delete_data_center('datacenter20') - assert self.page.find_data_center('datacenter1') is None - assert self.page.find_data_center('datacenter20') is None + self.page.delete_data_center('datacenter9') + assert not self.page.Link('datacenter1').isPresented() + assert not self.page.Link('datacenter9').isPresented() - for i in range(2, 19): + for i in range(2, 9): name = 'datacenter' + str(i) - assert self.page.find_data_center(name) is not None + assert self.page.Link(name).isPresented() def test_05_create_service_ad(self): name = 'dc001.local' - self.page.create_data_center(name) - self.select_data_center(name) - + self.page.Navigate('Windows Data Centers') + self.page.create_data_center('test05') + self.page = self.page.select_data_center('test05') + ad_parameters = {'1-dc_name': name, '1-dc_count': 1, '1-adm_password': 'AkvareL707!', '1-recovery_password': 'AkvareL707!'} self.page.create_service('Active Directory', ad_parameters) - - assert self.page.find_service(name) is not None + + assert self.page.Link(name).isPresented() + + def test_06_create_service_ad_two_instances(self): + name = 'dc002.local' + self.page.Navigate('Windows Data Centers') + self.page.create_data_center('test06') + self.page = self.page.select_data_center('test06') + + ad_parameters = {'1-dc_name': name, + '1-dc_count': 2, + '1-adm_password': 'P@ssw0rd2', + '1-recovery_password': 'P@ssw0rd'} + self.page.create_service('Active Directory', ad_parameters) + + assert self.page.Link(name).isPresented() + + def test_07_create_service_ad_with_iis(self): + ad_name = 'dc003.local' + self.page.Navigate('Windows Data Centers') + self.page.create_data_center('test07') + self.page = self.page.select_data_center('test07') + + ad_parameters = {'1-dc_name': ad_name, + '1-dc_count': 3, + '1-adm_password': 'P@ssw0rd', + '1-recovery_password': 'P@ssw0rd2'} + self.page.create_service('Active Directory', ad_parameters) + + assert self.page.Link(ad_name).isPresented() + + iis_name = 'iis_server1' + iis_parameters = {'1-iis_name': iis_name, + '1-adm_password': 'P@ssw0rd', + '1-iis_domain': 'dc003.local', + '1-domain_user_name': 'Administrator', + '1-domain_user_password': 'P@ssw0rd'} + self.page.create_service('Internet Information Services', + iis_parameters) + + assert self.page.Link(iis_name).isPresented() + + iis_name = 'iis_server2' + iis_parameters = {'1-iis_name': iis_name, + '1-adm_password': 'P@ssw0rd', + '1-iis_domain': 'dc003.local', + '1-domain_user_name': 'Administrator', + '1-domain_user_password': 'P@ssw0rd'} + self.page.create_service('Internet Information Services', + iis_parameters) + + assert self.page.Link(iis_name).isPresented() + + iis_name = 'iis_server3' + iis_parameters = {'1-iis_name': iis_name, + '1-adm_password': 'P@ssw0rd', + '1-iis_domain': 'dc003.local', + '1-domain_user_name': 'Administrator', + '1-domain_user_password': 'P@ssw0rd'} + self.page.create_service('Internet Information Services', + iis_parameters) + + assert self.page.Link(iis_name).isPresented() + + def test_08_deploy_data_center(self): + ad_name = 'AD.net' + self.page.Navigate('Windows Data Centers') + self.page.create_data_center('test08') + self.page = self.page.select_data_center('test08') + + ad_parameters = {'1-dc_name': ad_name, + '1-dc_count': 2, + '1-adm_password': 'P@ssw0rd', + '1-recovery_password': 'P@ssw0rd2'} + self.page.create_service('Active Directory', ad_parameters) + + assert self.page.Link(ad_name).isPresented() + + iis_parameters = {'1-iis_name': 'iis_server', + '1-adm_password': 'P@ssw0rd', + '1-iis_domain': 'AD.net', + '1-domain_user_name': 'Administrator', + '1-domain_user_password': 'P@ssw0rd'} + self.page.create_service('Internet Information Services', + iis_parameters) + + assert self.page.Link('iis_server').isPresented() + + self.page.Navigate('Windows Data Centers') + self.page = DataCentersPage(self.page.driver) + self.page.deploy_data_center('test08') + + status = self.page.get_datacenter_status('test08') + assert 'Deploy in progress' in status if __name__ == '__main__': - unittest2.main() \ No newline at end of file + unittest2.main()