Merge branch 'master' into conductor-openstack-style

Conflicts:
	conductor/bin/app.py
	conductor/conductor/app.py
	conductor/conductor/commands/cloud_formation.py
	conductor/conductor/commands/windows_agent.py
	conductor/conductor/rabbitmq.py
	conductor/conductor/windows_agent.py
This commit is contained in:
Stan Lagun 2013-03-26 14:46:09 +04:00
commit ad0e834457
45 changed files with 723 additions and 311 deletions

View File

@ -8,3 +8,4 @@ config_drive_cdrom=false
verbose=true verbose=true
logdir=C:\Program Files (x86)\Cloudbase Solutions\Cloudbase-Init\log\ logdir=C:\Program Files (x86)\Cloudbase Solutions\Cloudbase-Init\log\
logfile=cloudbase-init.log logfile=cloudbase-init.log
plugins=cloudbaseinit.plugins.windows.userdata.UserDataPlugin

View File

@ -16,6 +16,7 @@ RABBIT_PASSWORD=$lab_password
SERVICE_PASSWORD=$lab_password SERVICE_PASSWORD=$lab_password
SERVICE_TOKEN=tokentoken SERVICE_TOKEN=tokentoken
ENABLED_SERVICES+=,heat,h-api,h-api-cfn,h-api-cw,h-eng ENABLED_SERVICES+=,heat,h-api,h-api-cfn,h-api-cw,h-eng
ENABLED_SERVICES+=,conductor,portas
LOGFILE=/opt/stack/devstack/stack.sh.log LOGFILE=/opt/stack/devstack/stack.sh.log
SCREEN_LOGDIR=/opt/stack/log/ SCREEN_LOGDIR=/opt/stack/log/

View File

@ -1,6 +1,7 @@
#!/bin/bash #!/bin/bash
DEVSTACK_DIR=/home/stack/devstack DEVSTACK_DIR=/home/stack/devstack
INSTALL_DIR=/opt/stack
MYSQL_DB_TMPFS=true MYSQL_DB_TMPFS=true
MYSQL_DB_TMPFS_SIZE=128M MYSQL_DB_TMPFS_SIZE=128M
@ -8,7 +9,6 @@ MYSQL_DB_TMPFS_SIZE=128M
NOVA_CACHE_TMPFS=true NOVA_CACHE_TMPFS=true
NOVA_CACHE_TMPFS_SIZE=24G NOVA_CACHE_TMPFS_SIZE=24G
#====================================== #======================================
source $DEVSTACK_DIR/openrc admin admin source $DEVSTACK_DIR/openrc admin admin
source ./functions.sh source ./functions.sh

View File

@ -20,6 +20,7 @@ $DEVSTACK_DIR/stack.sh
# Executing post-stack actions # Executing post-stack actions
#=============================================================================== #===============================================================================
source ./post-stack.sh no-localrc source ./post-stack.sh no-localrc
source ./start-keero.sh no-localrc
#=============================================================================== #===============================================================================

View File

@ -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"
#===================

View File

@ -18,5 +18,6 @@ $DEVSTACK_DIR/unstack.sh
# Executing post-unstack actions # Executing post-unstack actions
#=============================================================================== #===============================================================================
source ./post-unstack.sh no-localrc source ./post-unstack.sh no-localrc
source ./stop-keero.sh no-localrc
#=============================================================================== #===============================================================================

View File

@ -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
#==========================

View File

@ -40,4 +40,3 @@ xml_code_engine.XmlCodeEngine.register_function(
xml_code_engine.XmlCodeEngine.register_function( xml_code_engine.XmlCodeEngine.register_function(
prepare_user_data, "prepare_user_data") prepare_user_data, "prepare_user_data")

View File

@ -21,7 +21,6 @@ class CommandDispatcher(command.CommandBase):
return result return result
def has_pending_commands(self): def has_pending_commands(self):
result = False result = False
for command in self._command_map.values(): for command in self._command_map.values():

View File

@ -48,4 +48,4 @@ class Context(object):
return str(self._data) return str(self._data)
if self._parent: if self._parent:
return str(self._parent) return str(self._parent)
return str({}) return str({})

View File

@ -38,6 +38,7 @@ def merge_dicts(dict1, dict2, max_levels=0):
result[key] = value result[key] = value
return result return result
def find(f, seq): def find(f, seq):
"""Return first item in sequence where f(item) == True.""" """Return first item in sequence where f(item) == True."""
index = 0 index = 0

View File

@ -25,12 +25,9 @@ class Reporter(object):
message=msg, message=msg,
key='task-reports') key='task-reports')
def _report_func(context, id, entity, text, **kwargs): def _report_func(context, id, entity, text, **kwargs):
reporter = context['/reporter'] reporter = context['/reporter']
return reporter._report_func(id, entity, text, **kwargs) return reporter._report_func(id, entity, text, **kwargs)
xml_code_engine.XmlCodeEngine.register_function(_report_func, "report") xml_code_engine.XmlCodeEngine.register_function(_report_func, "report")

View File

@ -5,6 +5,7 @@ import re
import xml_code_engine import xml_code_engine
import function_context import function_context
class Workflow(object): class Workflow(object):
def __init__(self, filename, data, command_dispatcher, config, reporter): def __init__(self, filename, data, command_dispatcher, config, reporter):
self._data = data self._data = data
@ -84,7 +85,6 @@ class Workflow(object):
else: else:
return position + suffix.split('.') return position + suffix.split('.')
@staticmethod @staticmethod
def _select_func(context, path='', source=None, **kwargs): def _select_func(context, path='', source=None, **kwargs):
@ -102,7 +102,6 @@ class Workflow(object):
context['/dataSource'], context['/dataSource'],
Workflow._correct_position(path, context)) Workflow._correct_position(path, context))
@staticmethod @staticmethod
def _set_func(path, context, body, engine, target=None, **kwargs): def _set_func(path, context, body, engine, target=None, **kwargs):
body_data = engine.evaluate_content(body, context) body_data = engine.evaluate_content(body, context)

View File

@ -61,7 +61,8 @@ class XmlCodeEngine(object):
return_value = result return_value = result
if len(result) == 0: if len(result) == 0:
return_value = ''.join(parts) return_value = ''.join(parts)
if do_strip: return_value = return_value.strip() if do_strip:
return_value = return_value.strip()
elif len(result) == 1: elif len(result) == 1:
return_value = result[0] return_value = result[0]

View File

@ -3,12 +3,34 @@
$WindowsAgentConfigBase64 = '%WINDOWS_AGENT_CONFIG_BASE64%' $WindowsAgentConfigBase64 = '%WINDOWS_AGENT_CONFIG_BASE64%'
$WindowsAgentConfigFile = "C:\Keero\Agent\WindowsAgent.exe.config" $WindowsAgentConfigFile = "C:\Keero\Agent\WindowsAgent.exe.config"
$NewComputerName = '%INTERNAL_HOSTNAME%'
$RestartRequired = $false
Import-Module CoreFunctions Import-Module CoreFunctions
Stop-Service "Keero Agent" if ( $WindowsAgentConfigBase64 -ne '%WINDOWS_AGENT_CONFIG_BASE64%' ) {
Backup-File $WindowsAgentConfigFile Write-Log "Updating Keero Windows Agent."
Remove-Item $WindowsAgentConfigFile -Force Stop-Service "Keero Agent"
ConvertFrom-Base64String -Base64String $WindowsAgentConfigBase64 -Path $WindowsAgentConfigFile Backup-File $WindowsAgentConfigFile
Exec sc.exe 'config','"Keero Agent"','start=','delayed-auto' Remove-Item $WindowsAgentConfigFile -Force
Start-Service 'Keero Agent' ConvertFrom-Base64String -Base64String $WindowsAgentConfigBase64 -Path $WindowsAgentConfigFile
Write-Log 'All done!' 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'
}

0
conductor/tools/with_venv.sh Normal file → Executable file
View File

View File

@ -75,7 +75,7 @@ class ContextMiddleware(BaseContextMiddleware):
'auth_tok': req.headers.get('X-Auth-Token', deprecated_token), 'auth_tok': req.headers.get('X-Auth-Token', deprecated_token),
'service_catalog': service_catalog, 'service_catalog': service_catalog,
'session': req.headers.get('X-Configuration-Session') 'session': req.headers.get('X-Configuration-Session')
} }
req.context = portas.context.RequestContext(**kwargs) req.context = portas.context.RequestContext(**kwargs)
else: else:
raise webob.exc.HTTPUnauthorized() raise webob.exc.HTTPUnauthorized()
@ -84,4 +84,4 @@ class ContextMiddleware(BaseContextMiddleware):
def factory(cls, global_conf, **local_conf): def factory(cls, global_conf, **local_conf):
def filter(app): def filter(app):
return cls(app) return cls(app)
return filter return filter

View File

@ -27,15 +27,19 @@ def get_env_status(environment_id, session_id):
unit = get_session() unit = get_session()
if not session_id: if not session_id:
session = unit.query(Session).filter( variants = ['open', 'deploying']
Session.environment_id == environment_id and Session.state.in_(['open', 'deploying'])).first() session = unit.query(Session).filter(Session.environment_id ==
environment_id and
Session.state.in_(variants)
).first()
if session: if session:
session_id = session.id session_id = session.id
else: else:
return status return status
session_state = unit.query(Session).get(session_id).state 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': if session_state == 'deployed':
status = 'finished' status = 'finished'
@ -50,13 +54,16 @@ def get_env_status(environment_id, session_id):
def get_statuses(type): def get_statuses(type):
if type in draft['services']: if type in draft['services']:
return [get_service_status(environment_id, session_id, service) for service in services = draft['services'][type]
draft['services'][type]] return [get_service_status(environment_id,
session_id,
service) for service in services]
else: else:
return [] return []
is_inprogress = filter(lambda item: item == 'inprogress', 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: if session_state == 'deploying' and is_inprogress > 1:
status = 'inprogress' status = 'inprogress'
@ -71,10 +78,11 @@ def get_service_status(environment_id, session_id, service):
session_state = unit.query(Session).get(session_id).state session_state = unit.query(Session).get(session_id).state
entities = [u['id'] for u in service['units']] entities = [u['id'] for u in service['units']]
reports_count = unit.query(Status).filter(Status.environment_id == environment_id reports_count = unit.query(Status).filter(
and Status.session_id == session_id Status.environment_id == environment_id
and Status.entity_id.in_(entities)) \ and Status.session_id == session_id
.count() and Status.entity_id.in_(entities)
).count()
if session_state == 'deployed': if session_state == 'deployed':
status = 'finished' status = 'finished'

View File

@ -9,18 +9,23 @@ log = logging.getLogger(__name__)
class Controller(object): class Controller(object):
def index(self, request, environment_id): def index(self, request, environment_id):
log.debug(_('ActiveDirectory:Index <EnvId: {0}>'.format(environment_id))) log.debug(_('ActiveDirectory:Index <EnvId: {0}>'.
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']: 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']} return {'activeDirectories': draft['services']['activeDirectories']}
@utils.verify_session @utils.verify_session
def create(self, request, environment_id, body): def create(self, request, environment_id, body):
log.debug(_('ActiveDirectory:Create <EnvId: {0}, Body: {1}>'.format(environment_id, body))) log.debug(_('ActiveDirectory:Create <EnvId: {0}, Body: {1}>'.
format(environment_id, body)))
draft = get_draft(session_id=request.context.session) draft = get_draft(session_id=request.context.session)
@ -33,7 +38,7 @@ class Controller(object):
for unit in active_directory['units']: for unit in active_directory['units']:
unit_count += 1 unit_count += 1
unit['id'] = uuidutils.generate_uuid() 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 = prepare_draft(draft)
draft['services']['activeDirectories'].append(active_directory) draft['services']['activeDirectories'].append(active_directory)
@ -42,23 +47,25 @@ class Controller(object):
return active_directory return active_directory
def delete(self, request, environment_id, active_directory_id): def delete(self, request, environment_id, active_directory_id):
log.debug(_('ActiveDirectory:Delete <EnvId: {0}, Id: {1}>'.format(environment_id, active_directory_id))) log.debug(_('ActiveDirectory:Delete <EnvId: {0}, Id: {1}>'.
format(environment_id, active_directory_id)))
draft = get_draft(request.context.session) draft = get_draft(request.context.session)
draft['services']['activeDirectories'] = [service for service in draft['services']['activeDirectories'] if items = [service for service in draft['services']['activeDirectories']
service['id'] != active_directory_id] if service['id'] != active_directory_id]
draft['services']['activeDirectories'] = items
save_draft(request.context.session, draft) save_draft(request.context.session, draft)
def prepare_draft(draft): def prepare_draft(draft):
if not draft.has_key('services'): if not 'services' in draft:
draft['services'] = {} draft['services'] = {}
if not draft['services'].has_key('activeDirectories'): if not 'activeDirectories' in draft['services']:
draft['services']['activeDirectories'] = [] draft['services']['activeDirectories'] = []
return draft return draft
def create_resource(): def create_resource():
return wsgi.Resource(Controller()) return wsgi.Resource(Controller())

View File

@ -1,10 +1,16 @@
from amqplib.client_0_8 import Message
import anyjson
import eventlet
from webob import exc from webob import exc
from portas.common import config
from portas.api.v1 import get_env_status from portas.api.v1 import get_env_status
from portas.db.session import get_session from portas.db.session import get_session
from portas.db.models import Environment from portas.db.models import Environment
from portas.openstack.common import wsgi from portas.openstack.common import wsgi
from portas.openstack.common import log as logging 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__) log = logging.getLogger(__name__)
@ -61,7 +67,8 @@ class Controller(object):
return env return env
def update(self, request, environment_id, body): def update(self, request, environment_id, body):
log.debug(_('Environments:Update <Id: {0}, Body: {1}>'.format(environment_id, body))) log.debug(_('Environments:Update <Id: {0}, Body: {1}>'.
format(environment_id, body)))
session = get_session() session = get_session()
environment = session.query(Environment).get(environment_id) environment = session.query(Environment).get(environment_id)
@ -84,8 +91,26 @@ class Controller(object):
with session.begin(): with session.begin():
session.delete(environment) 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 return None
def create_resource(): def create_resource():
return wsgi.Resource(Controller()) return wsgi.Resource(Controller())

View File

@ -16,7 +16,8 @@
# under the License. # under the License.
import routes import routes
from portas.openstack.common import wsgi 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): class API(wsgi.Router):
@ -64,11 +65,13 @@ class API(wsgi.Router):
controller=sessions_resource, controller=sessions_resource,
action='delete', action='delete',
conditions={'method': ['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, controller=sessions_resource,
action='reports', action='reports',
conditions={'method': ['GET']}) conditions={'method': ['GET']})
mapper.connect('/environments/{environment_id}/sessions/{session_id}/deploy', mapper.connect('/environments/{environment_id}/sessions/'
'{session_id}/deploy',
controller=sessions_resource, controller=sessions_resource,
action='deploy', action='deploy',
conditions={'method': ['POST']}) conditions={'method': ['POST']})
@ -82,7 +85,8 @@ class API(wsgi.Router):
controller=activeDirectories_resource, controller=activeDirectories_resource,
action='create', action='create',
conditions={'method': ['POST']}) conditions={'method': ['POST']})
mapper.connect('/environments/{environment_id}/activeDirectories/{active_directory_id}', mapper.connect('/environments/{environment_id}/activeDirectories/'
'{active_directory_id}',
controller=activeDirectories_resource, controller=activeDirectories_resource,
action='delete', action='delete',
conditions={'method': ['DELETE']}) conditions={'method': ['DELETE']})
@ -96,8 +100,9 @@ class API(wsgi.Router):
controller=webServers_resource, controller=webServers_resource,
action='create', action='create',
conditions={'method': ['POST']}) conditions={'method': ['POST']})
mapper.connect('/environments/{environment_id}/webServers/{web_server_id}', mapper.connect('/environments/{environment_id}/webServers/'
'{web_server_id}',
controller=webServers_resource, controller=webServers_resource,
action='delete', action='delete',
conditions={'method': ['DELETE']}) conditions={'method': ['DELETE']})
super(API, self).__init__(mapper) super(API, self).__init__(mapper)

View File

@ -1,7 +1,6 @@
from amqplib.client_0_8 import Message from amqplib.client_0_8 import Message
import anyjson import anyjson
import eventlet import eventlet
from eventlet.semaphore import Semaphore
from webob import exc from webob import exc
from portas.common import config from portas.common import config
from portas.db.models import Session, Status, Environment from portas.db.models import Session, Status, Environment
@ -15,36 +14,34 @@ log = logging.getLogger(__name__)
class Controller(object): 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): def index(self, request, environment_id):
log.debug(_('Session:List <EnvId: {0}>'.format(environment_id))) log.debug(_('Session:List <EnvId: {0}>'.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() unit = get_session()
configuration_sessions = unit.query(Session).filter_by(**filters) configuration_sessions = unit.query(Session).filter_by(**filters)
return {"sessions": [session.to_dict() for session in configuration_sessions if sessions = [session.to_dict() for session in configuration_sessions if
session.environment.tenant_id == request.context.tenant]} session.environment.tenant_id == request.context.tenant]
return {"sessions": sessions}
def configure(self, request, environment_id): def configure(self, request, environment_id):
log.debug(_('Session:Configure <EnvId: {0}>'.format(environment_id))) log.debug(_('Session:Configure <EnvId: {0}>'.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 = Session()
session.update(params) session.update(params)
unit = get_session() unit = get_session()
if unit.query(Session).filter(Session.environment_id == environment_id and Session.state.in_( if unit.query(Session).filter(Session.environment_id == environment_id
['open', 'deploing'])).first(): and
Session.state.in_(['open', 'deploing'])
).first():
log.info('There is already open session for this environment') log.info('There is already open session for this environment')
raise exc.HTTPConflict raise exc.HTTPConflict
@ -58,7 +55,8 @@ class Controller(object):
return session.to_dict() return session.to_dict()
def show(self, request, environment_id, session_id): def show(self, request, environment_id, session_id):
log.debug(_('Session:Show <EnvId: {0}, SessionId: {1}>'.format(environment_id, session_id))) log.debug(_('Session:Show <EnvId: {0}, SessionId: {1}>'.
format(environment_id, session_id)))
unit = get_session() unit = get_session()
session = unit.query(Session).get(session_id) session = unit.query(Session).get(session_id)
@ -70,14 +68,16 @@ class Controller(object):
return session.to_dict() return session.to_dict()
def delete(self, request, environment_id, session_id): def delete(self, request, environment_id, session_id):
log.debug(_('Session:Delete <EnvId: {0}, SessionId: {1}>'.format(environment_id, session_id))) log.debug(_('Session:Delete <EnvId: {0}, SessionId: {1}>'.
format(environment_id, session_id)))
unit = get_session() unit = get_session()
session = unit.query(Session).get(session_id) session = unit.query(Session).get(session_id)
comment = 'Session object in \'deploying\' state could not be deleted'
if session.state == 'deploying': if session.state == 'deploying':
log.info('Session is in \'deploying\' state. Could not be deleted.') log.info(comment)
raise exc.HTTPForbidden(comment='Session object in \'deploying\' state could not be deleted') raise exc.HTTPForbidden(comment=comment)
with unit.begin(): with unit.begin():
unit.delete(session) unit.delete(session)
@ -85,7 +85,8 @@ class Controller(object):
return None return None
def reports(self, request, environment_id, session_id): def reports(self, request, environment_id, session_id):
log.debug(_('Session:Reports <EnvId: {0}, SessionId: {1}>'.format(environment_id, session_id))) log.debug(_('Session:Reports <EnvId: {0}, SessionId: {1}>'.
format(environment_id, session_id)))
unit = get_session() unit = get_session()
statuses = unit.query(Status).filter_by(session_id=session_id) 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]} return {'reports': [status.to_dict() for status in statuses]}
def deploy(self, request, environment_id, session_id): def deploy(self, request, environment_id, session_id):
log.debug(_('Session:Deploy <EnvId: {0}, SessionId: {1}>'.format(environment_id, session_id))) log.debug(_('Session:Deploy <EnvId: {0}, SessionId: {1}>'.
format(environment_id, session_id)))
unit = get_session() unit = get_session()
session = unit.query(Session).get(session_id) session = unit.query(Session).get(session_id)
msg = _('Could not deploy session. Session is already '
'deployed or in deployment state')
if session.state != 'open': 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.state = 'deploying'
session.save(unit) session.save(unit)
with self.write_lock: #Set X-Auth-Token for conductor
self.ch.basic_publish(Message(body=anyjson.serialize(session.description)), 'tasks', 'tasks') 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(): def create_resource():
return wsgi.Resource(Controller()) return wsgi.Resource(Controller())

View File

@ -11,16 +11,19 @@ class Controller(object):
def index(self, request, environment_id): def index(self, request, environment_id):
log.debug(_('WebServer:List <EnvId: {0}>'.format(environment_id))) log.debug(_('WebServer:List <EnvId: {0}>'.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']: 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']} return {'webServers': draft['services']['webServers']}
@utils.verify_session @utils.verify_session
def create(self, request, environment_id, body): def create(self, request, environment_id, body):
log.debug(_('WebServer:Create <EnvId: {0}, Body: {1}>'.format(environment_id, body))) log.debug(_('WebServer:Create <EnvId: {0}, Body: {1}>'.
format(environment_id, body)))
draft = get_draft(session_id=request.context.session) draft = get_draft(session_id=request.context.session)
@ -33,7 +36,7 @@ class Controller(object):
for unit in webServer['units']: for unit in webServer['units']:
unit_count += 1 unit_count += 1
unit['id'] = uuidutils.generate_uuid() 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 = prepare_draft(draft)
draft['services']['webServers'].append(webServer) draft['services']['webServers'].append(webServer)
@ -43,23 +46,26 @@ class Controller(object):
@utils.verify_session @utils.verify_session
def delete(self, request, environment_id, web_server_id): def delete(self, request, environment_id, web_server_id):
log.debug(_('WebServer:Delete <EnvId: {0}, Id: {1}>'.format(environment_id, web_server_id))) log.debug(_('WebServer:Delete <EnvId: {0}, Id: {1}>'.
format(environment_id, web_server_id)))
draft = get_draft(session_id=request.context.session) 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) save_draft(request.context.session, draft)
def prepare_draft(draft): def prepare_draft(draft):
if not draft.has_key('services'): if not 'services' in draft:
draft['services'] = {} draft['services'] = {}
if not draft['services'].has_key('webServers'): if not 'webServers' in draft['services']:
draft['services']['webServers'] = [] draft['services']['webServers'] = []
return draft return draft
def create_resource(): def create_resource():
return wsgi.Resource(Controller()) return wsgi.Resource(Controller())

View File

@ -82,6 +82,7 @@ def parse_args(args=None, usage=None, default_config_files=None):
usage=usage, usage=usage,
default_config_files=default_config_files) default_config_files=default_config_files)
def setup_logging(): def setup_logging():
""" """
Sets up the logging options for a log with supplied name Sets up the logging options for a log with supplied name

View File

@ -45,6 +45,7 @@ class PortasException(Exception):
super(PortasException, self).__init__(message) super(PortasException, self).__init__(message)
class SchemaLoadError(PortasException): class SchemaLoadError(PortasException):
message = _("Unable to load schema: %(reason)s") message = _("Unable to load schema: %(reason)s")
@ -52,5 +53,3 @@ class SchemaLoadError(PortasException):
class InvalidObject(PortasException): class InvalidObject(PortasException):
message = _("Provided object does not match schema " message = _("Provided object does not match schema "
"'%(schema)s': %(reason)s") "'%(schema)s': %(reason)s")

View File

@ -27,14 +27,18 @@ class TaskResultHandlerService(service.Service):
super(TaskResultHandlerService, self).stop() super(TaskResultHandlerService, self).stop()
def _handle_results(self): def _handle_results(self):
connection = amqp.Connection('{0}:{1}'.format(rabbitmq.host, rabbitmq.port), virtual_host=rabbitmq.virtual_host, connection = amqp.Connection('{0}:{1}'.
userid=rabbitmq.userid, password=rabbitmq.password, format(rabbitmq.host, rabbitmq.port),
virtual_host=rabbitmq.virtual_host,
userid=rabbitmq.userid,
password=rabbitmq.password,
ssl=rabbitmq.use_ssl, insist=True) ssl=rabbitmq.use_ssl, insist=True)
ch = connection.channel() ch = connection.channel()
def bind(exchange, queue): def bind(exchange, queue):
if not exchange: 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) ch.queue_declare(queue, durable=True, auto_delete=False)
if not exchange: if not exchange:
ch.queue_bind(queue, exchange, queue) ch.queue_bind(queue, exchange, queue)
@ -43,13 +47,15 @@ class TaskResultHandlerService(service.Service):
bind(conf.reports_exchange, conf.reports_queue) bind(conf.reports_exchange, conf.reports_queue)
ch.basic_consume(conf.results_exchange, callback=handle_result) 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: while ch.callbacks:
ch.wait() ch.wait()
def handle_report(msg): 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 = anyjson.deserialize(msg.body)
params['entity_id'] = params['id'] params['entity_id'] = params['id']
@ -61,7 +67,8 @@ def handle_report(msg):
session = get_session() session = get_session()
#connect with session #connect with session
conf_session = session.query(Session).filter_by( 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 status.session_id = conf_session.id
with session.begin(): with session.begin():
@ -69,9 +76,16 @@ def handle_report(msg):
def handle_result(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) 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() session = get_session()
environment = session.query(Environment).get(environment_result['id']) environment = session.query(Environment).get(environment_result['id'])

View File

@ -2,4 +2,4 @@ import uuid
def generate_uuid(): def generate_uuid():
return str(uuid.uuid4()).replace('-', '') return str(uuid.uuid4()).replace('-', '')

View File

@ -24,7 +24,9 @@ class RequestContext(object):
accesses the system, as well as additional request information. 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.auth_tok = auth_tok
self.user = user self.user = user
self.tenant = tenant self.tenant = tenant
@ -55,4 +57,4 @@ class RequestContext(object):
@classmethod @classmethod
def from_dict(cls, values): def from_dict(cls, values):
return cls(**values) return cls(**values)

View File

@ -9,4 +9,4 @@ sql_connection_opt = cfg.StrOpt('sql_connection',
'Default: %(default)s') 'Default: %(default)s')
CONF = cfg.CONF CONF = cfg.CONF
CONF.register_opt(sql_connection_opt) CONF.register_opt(sql_connection_opt)

View File

@ -11,7 +11,7 @@ Table('environment', meta,
Column('updated', DateTime(), nullable=False), Column('updated', DateTime(), nullable=False),
Column('tenant_id', String(32), nullable=False), Column('tenant_id', String(32), nullable=False),
Column('description', Text(), nullable=False), Column('description', Text(), nullable=False),
) )
Table('service', meta, Table('service', meta,
Column('id', String(32), primary_key=True), Column('id', String(32), primary_key=True),
@ -21,7 +21,7 @@ Table('service', meta,
Column('created', DateTime, nullable=False), Column('created', DateTime, nullable=False),
Column('updated', DateTime, nullable=False), Column('updated', DateTime, nullable=False),
Column('description', Text(), nullable=False), Column('description', Text(), nullable=False),
) )
def upgrade(migrate_engine): def upgrade(migrate_engine):

View File

@ -5,12 +5,13 @@ meta = MetaData()
session = Table('session', meta, session = Table('session', meta,
Column('id', String(32), primary_key=True), 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('created', DateTime, nullable=False),
Column('updated', DateTime, nullable=False), Column('updated', DateTime, nullable=False),
Column('user_id', String(32), nullable=False), Column('user_id', String(32), nullable=False),
Column('state', Text(), nullable=False), Column('state', Text(), nullable=False),
) )
def upgrade(migrate_engine): def upgrade(migrate_engine):

View File

@ -8,10 +8,11 @@ status = Table('status', meta,
Column('created', DateTime, nullable=False), Column('created', DateTime, nullable=False),
Column('updated', DateTime, nullable=False), Column('updated', DateTime, nullable=False),
Column('entity', String(10), 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('session_id', String(32), ForeignKey('session.id')),
Column('text', Text(), nullable=False), Column('text', Text(), nullable=False),
) )
def upgrade(migrate_engine): def upgrade(migrate_engine):

View File

@ -5,14 +5,15 @@ from sqlalchemy.types import String, Text, DateTime
meta = MetaData() meta = MetaData()
service = Table('service', meta, service = Table('service', meta,
Column('id', String(32), primary_key=True), Column('id', String(32), primary_key=True),
Column('name', String(255), nullable=False), Column('name', String(255), nullable=False),
Column('type', String(40), nullable=False), Column('type', String(40), nullable=False),
Column('environment_id', String(32), ForeignKey('environment.id')), Column('environment_id', String(32),
Column('created', DateTime, nullable=False), ForeignKey('environment.id')),
Column('updated', DateTime, nullable=False), Column('created', DateTime, nullable=False),
Column('description', Text(), nullable=False), Column('updated', DateTime, nullable=False),
) Column('description', Text(), nullable=False),
)
def upgrade(migrate_engine): def upgrade(migrate_engine):

View File

@ -83,7 +83,8 @@ class ModelBase(object):
def to_dict(self): def to_dict(self):
dictionary = self.__dict__.copy() 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): class JsonBlob(TypeDecorator):

View File

@ -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

View File

@ -11,8 +11,8 @@ def verify_session(func):
@functools.wraps(func) @functools.wraps(func)
def __inner(self, request, *args, **kwargs): def __inner(self, request, *args, **kwargs):
if hasattr(request, 'context') and request.context.session: if hasattr(request, 'context') and request.context.session:
uw = get_session() uw = get_session().query(Session)
configuration_session = uw.query(Session).get(request.context.session) configuration_session = uw.get(request.context.session)
if configuration_session.state != 'open': if configuration_session.state != 'open':
log.info('Session is already deployed') log.info('Session is already deployed')
@ -22,5 +22,3 @@ def verify_session(func):
raise exc.HTTPUnauthorized raise exc.HTTPUnauthorized
return func(self, request, *args, **kwargs) return func(self, request, *args, **kwargs)
return __inner return __inner

View File

@ -15,6 +15,6 @@
# under the License. # 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') version_info = common_version.VersionInfo('python-portasclient')

View File

@ -1,19 +1,29 @@
import os
import unittest import unittest
import logging import logging
from mock import MagicMock from mock import MagicMock
from mock import patch
from portasclient.client import Client as CommonClient
from portasclient.v1 import Client from portasclient.v1 import Client
import portasclient.v1.environments as environments import portasclient.v1.environments as environments
import portasclient.v1.services as services import portasclient.v1.services as services
import portasclient.v1.sessions as sessions import portasclient.v1.sessions as sessions
import portasclient.shell as shell
import portasclient.common.http as http
LOG = logging.getLogger('Unit tests') LOG = logging.getLogger('Unit tests')
def my_mock(*a, **b): def my_mock(*a, **b):
return [a, b] return [a, b]
api = MagicMock(json_request=my_mock) api = MagicMock(json_request=my_mock)
class SanityUnitTests(unittest.TestCase): class SanityUnitTests(unittest.TestCase):
def test_create_client_instance(self): def test_create_client_instance(self):
@ -25,145 +35,121 @@ class SanityUnitTests(unittest.TestCase):
assert test_client.sessions is not None assert test_client.sessions is not None
assert test_client.activeDirectories is not None assert test_client.activeDirectories is not None
assert test_client.webServers 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): def test_env_manager_list(self):
manager = environments.EnvironmentManager(api) manager = environments.EnvironmentManager(api)
result = manager.list() result = manager.list()
assert result == [] assert result == []
def test_env_manager_create(self): def test_env_manager_create(self):
manager = environments.EnvironmentManager(api) manager = environments.EnvironmentManager(api)
result = manager.create('test') result = manager.create('test')
assert result.headers == {} assert result.headers == {}
assert result.body == {'name': 'test'} assert result.body == {'name': 'test'}
def test_env_manager_delete(self): def test_env_manager_delete(self):
manager = environments.EnvironmentManager(api) manager = environments.EnvironmentManager(api)
result = manager.delete('test') result = manager.delete('test')
assert result is None assert result is None
def test_env_manager_update(self): def test_env_manager_update(self):
manager = environments.EnvironmentManager(api) manager = environments.EnvironmentManager(api)
result = manager.update('1', 'test') result = manager.update('1', 'test')
assert result.body == {'name': 'test'} assert result.body == {'name': 'test'}
def test_env_manager_get(self): def test_env_manager_get(self):
manager = environments.EnvironmentManager(api) manager = environments.EnvironmentManager(api)
result = manager.get('test') result = manager.get('test')
## WTF? ## WTF?
assert result.manager is not None 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): def test_ad_manager_list(self):
manager = services.ActiveDirectoryManager(api) manager = services.ActiveDirectoryManager(api)
result = manager.list('datacenter1') result = manager.list('datacenter1')
assert result == []
result = manager.list('datacenter1', '1')
assert result == [] assert result == []
def test_ad_manager_create(self): def test_ad_manager_create(self):
manager = services.ActiveDirectoryManager(api) manager = services.ActiveDirectoryManager(api)
result = manager.create('datacenter1', 'session1', 'test') result = manager.create('datacenter1', 'session1', 'test')
assert result.headers == {'X-Configuration-Session': 'session1'} assert result.headers == {'X-Configuration-Session': 'session1'}
assert result.body == 'test' assert result.body == 'test'
#@unittest.skip("https://mirantis.jira.com/browse/KEERO-218") #@unittest.skip("https://mirantis.jira.com/browse/KEERO-218")
def test_ad_manager_delete(self): def test_ad_manager_delete(self):
manager = services.ActiveDirectoryManager(api) manager = services.ActiveDirectoryManager(api)
result = manager.delete('datacenter1', 'session1', 'test') result = manager.delete('datacenter1', 'session1', 'test')
assert result is None assert result is None
def test_iis_manager_list(self): def test_iis_manager_list(self):
manager = services.WebServerManager(api) manager = services.WebServerManager(api)
result = manager.list('datacenter1') result = manager.list('datacenter1')
assert result == []
result = manager.list('datacenter1', '1')
assert result == [] assert result == []
def test_iis_manager_create(self): def test_iis_manager_create(self):
manager = services.WebServerManager(api) manager = services.WebServerManager(api)
result = manager.create('datacenter1', 'session1', 'test') result = manager.create('datacenter1', 'session1', 'test')
assert result.headers == {'X-Configuration-Session': 'session1'} assert result.headers == {'X-Configuration-Session': 'session1'}
assert result.body == 'test' assert result.body == 'test'
#@unittest.skip("https://mirantis.jira.com/browse/KEERO-218") #@unittest.skip("https://mirantis.jira.com/browse/KEERO-218")
def test_iis_manager_delete(self): def test_iis_manager_delete(self):
manager = services.WebServerManager(api) manager = services.WebServerManager(api)
result = manager.delete('datacenter1', 'session1', 'test') result = manager.delete('datacenter1', 'session1', 'test')
assert result is None 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): def test_session_manager_list(self):
manager = sessions.SessionManager(api) manager = sessions.SessionManager(api)
result = manager.list('datacenter1') result = manager.list('datacenter1')
assert result == [] assert result == []
def test_session_manager_delete(self): def test_session_manager_delete(self):
manager = sessions.SessionManager(api) manager = sessions.SessionManager(api)
result = manager.delete('datacenter1', 'session1') result = manager.delete('datacenter1', 'session1')
assert result is None assert result is None
def test_session_manager_get(self): def test_session_manager_get(self):
manager = sessions.SessionManager(api) manager = sessions.SessionManager(api)
result = manager.get('datacenter1', 'session1') result = manager.get('datacenter1', 'session1')
# WTF? # WTF?
assert result.manager is not None assert result.manager is not None
def test_session_manager_configure(self): def test_session_manager_configure(self):
manager = sessions.SessionManager(api) manager = sessions.SessionManager(api)
result = manager.configure('datacenter1') result = manager.configure('datacenter1')
assert result.headers == {} assert result.headers == {}
def test_session_manager_deploy(self): def test_session_manager_deploy(self):
manager = sessions.SessionManager(api) manager = sessions.SessionManager(api)
result = manager.deploy('datacenter1', '1') result = manager.deploy('datacenter1', '1')
assert result is None assert result is None
#@unittest.skip("https://mirantis.jira.com/browse/KEERO-219") #@unittest.skip("https://mirantis.jira.com/browse/KEERO-219")
def test_session_manager_reports(self): def test_session_manager_reports(self):
manager = sessions.SessionManager(api) manager = sessions.SessionManager(api)
result = manager.reports('datacenter1', '1') result = manager.reports('datacenter1', '1')
assert result == [] assert result == []

49
tests/deploy.sh Normal file → Executable file
View File

@ -24,10 +24,20 @@ expect "*$*"
send -- "./unstack.sh\n" send -- "./unstack.sh\n"
expect "*$*" expect "*$*"
send -- "./stack.sh\n" send -- "./stack.sh\n"
expect "*Would you like to start it now?*" expect "*/usr/bin/service: 123: exec: status: not found*"
send -- "y\n" send -- "y\n"
expect "*stack.sh completed*" 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" send -- "source openrc admin admin\n"
expect "*$*" expect "*$*"
@ -37,7 +47,7 @@ expect "*$*"
send -- "nova keypair-add keero-linux-keys > heat_key.priv\n" send -- "nova keypair-add keero-linux-keys > heat_key.priv\n"
expect "*$*" 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 "*$*" expect "*$*"
send -- "cd ~/keero\n" send -- "cd ~/keero\n"
@ -50,33 +60,22 @@ send -- "cp -Rf ~/keero/dashboard/windc /opt/stack/horizon/openstack_dashboard/d
expect "*$*" expect "*$*"
send -- "cp -f ~/keero/dashboard/api/windc.py /opt/stack/horizon/openstack_dashboard/api/\n" send -- "cp -f ~/keero/dashboard/api/windc.py /opt/stack/horizon/openstack_dashboard/api/\n"
expect "*$*" expect "*$*"
send -- "cp -Rf ~/keero/dashboard/windcclient /opt/stack/horizon/\n" send -- "cd ~/keero/python-portasclient\n"
expect "*$*" expect "*$*"
send -- "cd ~/keero/windc\n" send -- "sudo python setup.py install\n"
expect "*$*" expect "*$*"
send -- "rm -rf windc.sqlite\n" send -- "cd ~/keero/portas\n"
expect "*$*" 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 "*$*" expect "*$*"
send -- "logout\n" send -- "logout\n"
expect "*#*" 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 "*$*"

View File

@ -1,4 +1,4 @@
[server] [server]
address=http://172.18.124.101 address=http://172.18.124.101
user=admin user=admin
password=AkvareL707 password=swordfish

View File

@ -1,56 +1,54 @@
import re import re
from login_page import LoginPage
from services_page import ServicesPage from services_page import ServicesPage
import page
class DataCentersPage(): class DataCentersPage(page.Page):
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()
def create_data_center(self, name): def create_data_center(self, name):
button_text = 'Create Windows Data Center' self.Refresh()
self.page.find_element_by_link_text(button_text).click()
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']" xpath = "//input[@value='Create']"
button = self.page.find_element_by_xpath(xpath) self.Button(xpath).Click()
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)
def delete_data_center(self, name): def delete_data_center(self, name):
datacenter = self.find_data_center(name) self.Refresh()
link = datacenter.get_attribute('href')
link = self.Link(name).Address()
datacenter_id = re.search('windc/(\S+)', link).group(0)[6:-1] datacenter_id = re.search('windc/(\S+)', link).group(0)[6:-1]
xpath = ".//*[@id='windc__row__%s']/td[3]/div/a[2]" % datacenter_id xpath = ".//*[@id='windc__row__%s']/td[4]/div/a[2]" % datacenter_id
more_button = self.page.find_element_by_xpath(xpath) 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 self.Button("Delete Data Center").Click()
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
def select_data_center(self, name): def select_data_center(self, name):
datacenter = self.page.find_data_center(name) self.Link(name).Click()
datacenter.click() page = ServicesPage(self.driver)
self.page = ServicesPage(self.page) return page
return self.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()

View File

@ -1,8 +1,8 @@
import ConfigParser import ConfigParser
from selenium import webdriver import page
class LoginPage(): class LoginPage(page.Page):
def login(self): def login(self):
config = ConfigParser.RawConfigParser() config = ConfigParser.RawConfigParser()
@ -11,20 +11,11 @@ class LoginPage():
user = config.get('server', 'user') user = config.get('server', 'user')
password = config.get('server', 'password') password = config.get('server', 'password')
page = webdriver.Firefox() self.Open(url)
page.set_page_load_timeout(30)
page.implicitly_wait(30) self.EditBox('username').Set(user)
page.get(url) self.EditBox('password').Set(password)
name = page.find_element_by_name('username')
pwd = page.find_element_by_name('password')
xpath = "//button[@type='submit']" xpath = "//button[@type='submit']"
button = page.find_element_by_xpath(xpath) self.Button(xpath).Click()
name.clear() return self
name.send_keys(user)
pwd.clear()
pwd.send_keys(password)
button.click()
return page

192
tests/selenium/page.py Normal file
View File

@ -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()

View File

@ -1,57 +1,32 @@
import ConfigParser import page
from selenium import webdriver import re
class ServicesPage(): class ServicesPage(page.Page):
page = None
def __init__(self, page):
self.page = page
def create_service(self, service_type, parameters): def create_service(self, service_type, parameters):
self.Refresh()
button_id = 'services__action_CreateService' self.Button('services__action_CreateService').Click()
button = self.page.find_element_by_id(button_id) self.DropDownList('0-service').Set(service_type)
button.click() 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: self.Button("//input[@value='Create']").Click()
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)
def delete_service(self, name): def delete_service(self, name):
service = self.find_data_center(name) self.Refresh()
link = service.get_attribute('href')
link = self.Link(name).Address()
service_id = re.search('windc/(\S+)', link).group(0)[6:-1] service_id = re.search('windc/(\S+)', link).group(0)[6:-1]
xpath = ".//*[@id='services__row__%s']/td[5]/div/a[2]" % service_id xpath = ".//*[@id='services__row__%s']/td[5]/div/a[2]" % service_id
more_button = self.page.find_element_by_xpath(xpath) self.Button(xpath).Click()
more_button.click()
delete_button_id = "services__row_%s__action_delete" % datacenter_id button_id = "services__row_%s__action_delete" % service_id
delete_button = self.page.find_element_by_id(delete_button_id) self.Button(button_id).Click()
delete_button.click() self.Button("Delete Service").Click()
self.page.find_element_by_link_text("Delete Service").click()
return self.page

View File

@ -1,54 +1,155 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import untitest2 import unittest2
from login_page import LoginPage
from datacenters_page import DataCentersPage from datacenters_page import DataCentersPage
from selenium import webdriver
class SanityTests(unittest2.TestCase): class SanityTests(unittest2.TestCase):
def setUp(self): @classmethod
self.page = DataCentersPage() 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): @classmethod
self.page.close() def tearDownClass(self):
self.page.driver.close()
def test_01_create_data_center(self): def test_01_create_data_center(self):
self.page.create_data_center('dc1') 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): def test_02_delete_data_center(self):
self.page.delete_data_center('dc1') 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): def test_03_create_data_centers(self):
for i in range(1, 20): for i in range(1, 10):
name = 'datacenter' + str(i) name = 'datacenter' + str(i)
self.page.create_data_center(name) 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): def test_04_delete_data_centers(self):
self.page.delete_data_center('datacenter1') self.page.delete_data_center('datacenter1')
self.page.delete_data_center('datacenter20') self.page.delete_data_center('datacenter9')
assert self.page.find_data_center('datacenter1') is None assert not self.page.Link('datacenter1').isPresented()
assert self.page.find_data_center('datacenter20') is None assert not self.page.Link('datacenter9').isPresented()
for i in range(2, 19): for i in range(2, 9):
name = 'datacenter' + str(i) 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): def test_05_create_service_ad(self):
name = 'dc001.local' name = 'dc001.local'
self.page.create_data_center(name) self.page.Navigate('Windows Data Centers')
self.select_data_center(name) self.page.create_data_center('test05')
self.page = self.page.select_data_center('test05')
ad_parameters = {'1-dc_name': name, ad_parameters = {'1-dc_name': name,
'1-dc_count': 1, '1-dc_count': 1,
'1-adm_password': 'AkvareL707!', '1-adm_password': 'AkvareL707!',
'1-recovery_password': 'AkvareL707!'} '1-recovery_password': 'AkvareL707!'}
self.page.create_service('Active Directory', ad_parameters) 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__': if __name__ == '__main__':
unittest2.main() unittest2.main()