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:
commit
ad0e834457
@ -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
|
@ -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/
|
||||
|
@ -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
|
||||
|
@ -20,6 +20,7 @@ $DEVSTACK_DIR/stack.sh
|
||||
# Executing post-stack actions
|
||||
#===============================================================================
|
||||
source ./post-stack.sh no-localrc
|
||||
source ./start-keero.sh no-localrc
|
||||
#===============================================================================
|
||||
|
||||
|
||||
|
25
Deployment/devstack-scripts/start-keero.sh
Normal file
25
Deployment/devstack-scripts/start-keero.sh
Normal 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"
|
||||
#===================
|
@ -18,5 +18,6 @@ $DEVSTACK_DIR/unstack.sh
|
||||
# Executing post-unstack actions
|
||||
#===============================================================================
|
||||
source ./post-unstack.sh no-localrc
|
||||
source ./stop-keero.sh no-localrc
|
||||
#===============================================================================
|
||||
|
||||
|
12
Deployment/devstack-scripts/stop-keero.sh
Normal file
12
Deployment/devstack-scripts/stop-keero.sh
Normal 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
|
||||
#==========================
|
@ -40,4 +40,3 @@ xml_code_engine.XmlCodeEngine.register_function(
|
||||
|
||||
xml_code_engine.XmlCodeEngine.register_function(
|
||||
prepare_user_data, "prepare_user_data")
|
||||
|
||||
|
@ -21,7 +21,6 @@ class CommandDispatcher(command.CommandBase):
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def has_pending_commands(self):
|
||||
result = False
|
||||
for command in self._command_map.values():
|
||||
|
@ -48,4 +48,4 @@ class Context(object):
|
||||
return str(self._data)
|
||||
if self._parent:
|
||||
return str(self._parent)
|
||||
return str({})
|
||||
return str({})
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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]
|
||||
|
||||
|
@ -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!'
|
||||
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'
|
||||
}
|
||||
|
0
conductor/tools/with_venv.sh
Normal file → Executable file
0
conductor/tools/with_venv.sh
Normal file → Executable file
@ -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
|
||||
return filter
|
||||
|
@ -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'
|
||||
|
@ -9,18 +9,23 @@ log = logging.getLogger(__name__)
|
||||
|
||||
class Controller(object):
|
||||
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']:
|
||||
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 <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)
|
||||
|
||||
@ -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 <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['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())
|
||||
return wsgi.Resource(Controller())
|
||||
|
@ -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 <Id: {0}, Body: {1}>'.format(environment_id, body)))
|
||||
log.debug(_('Environments:Update <Id: {0}, Body: {1}>'.
|
||||
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())
|
||||
return wsgi.Resource(Controller())
|
||||
|
@ -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)
|
||||
super(API, self).__init__(mapper)
|
||||
|
@ -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 <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()
|
||||
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 <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.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 <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()
|
||||
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 <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()
|
||||
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 <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()
|
||||
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 <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()
|
||||
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())
|
||||
return wsgi.Resource(Controller())
|
||||
|
@ -11,16 +11,19 @@ class Controller(object):
|
||||
def index(self, request, 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']:
|
||||
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 <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)
|
||||
|
||||
@ -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 <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['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())
|
||||
return wsgi.Resource(Controller())
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
||||
|
||||
|
@ -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'])
|
||||
|
@ -2,4 +2,4 @@ import uuid
|
||||
|
||||
|
||||
def generate_uuid():
|
||||
return str(uuid.uuid4()).replace('-', '')
|
||||
return str(uuid.uuid4()).replace('-', '')
|
||||
|
@ -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)
|
||||
return cls(**values)
|
||||
|
@ -9,4 +9,4 @@ sql_connection_opt = cfg.StrOpt('sql_connection',
|
||||
'Default: %(default)s')
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opt(sql_connection_opt)
|
||||
CONF.register_opt(sql_connection_opt)
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
24
portas/portas/tests/sanity_tests.py
Normal file
24
portas/portas/tests/sanity_tests.py
Normal 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
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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')
|
||||
|
@ -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 == []
|
49
tests/deploy.sh
Normal file → Executable file
49
tests/deploy.sh
Normal file → Executable file
@ -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 "*$*"
|
||||
|
@ -1,4 +1,4 @@
|
||||
[server]
|
||||
address=http://172.18.124.101
|
||||
user=admin
|
||||
password=AkvareL707
|
||||
password=swordfish
|
@ -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()
|
||||
|
@ -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
|
||||
|
192
tests/selenium/page.py
Normal file
192
tests/selenium/page.py
Normal 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()
|
@ -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()
|
||||
|
@ -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()
|
||||
unittest2.main()
|
||||
|
Loading…
x
Reference in New Issue
Block a user