Merge ssh://gerrit.mirantis.com:29418/keero/keero into feature-orchestration

This commit is contained in:
Stan Lagun 2013-03-05 19:30:26 +04:00
commit 9d0b7e39fe
16 changed files with 389 additions and 33 deletions

View File

@ -35,27 +35,35 @@ def windcclient(request):
% (request.user.token, url))
return windc_client.Client(endpoint=url, token=None)
def datacenters_create(request, parameters):
name = parameters.get('name', '')
return windcclient(request).datacenters.create(name)
def datacenters_delete(request, datacenter_id):
return windcclient(request).datacenters.delete(datacenter_id)
def datacenters_get(request, datacenter_id):
return windcclient(request).datacenters.get(datacenter_id)
def datacenters_list(request):
return windcclient(request).datacenters.list()
def services_create(request, datacenter, parameters):
return windcclient(request).services.create(datacenter, parameters)
def services_list(request, datacenter):
return windcclient(request).services.list(datacenter)
def services_get(request, datacenter, service_id):
return windcclient(request).services.get(datacenter, service_id)
def services_delete(request, datacenter, service_id):
return windcclient(request).services.delete(datacenter, service_id)

View File

@ -20,19 +20,30 @@
import logging
from django import forms
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from openstack_dashboard import api
from horizon import exceptions
from horizon import forms
from horizon import exceptions
from horizon import messages
import pdb
LOG = logging.getLogger(__name__)
class WizardFormServiceType(forms.Form):
_type = forms.ChoiceField(label=_("Service Type"))
class WizardFormConfiguration(forms.Form):
subject = forms.CharField(max_length=100)
sender = forms.CharField(max_length=1)
class UpdateWinDC(forms.SelfHandlingForm):
tenant_id = forms.CharField(widget=forms.HiddenInput)
data_center = forms.CharField(widget=forms.HiddenInput)

View File

@ -81,8 +81,7 @@ class DeleteDataCenter(tables.BatchAction):
return True
def action(self, request, datacenter_id):
datacenter = api.windc.datacenters_get(request, datacenter_id)
api.windc.datacenters_delete(request, datacenter)
api.windc.datacenters_delete(request, datacenter_id)
class DeleteService(tables.BatchAction):
@ -101,9 +100,8 @@ class DeleteService(tables.BatchAction):
link = request.__dict__['META']['HTTP_REFERER']
datacenter_id = re.search('windc/(\S+)', link).group(0)[6:-1]
##############
datacenter = api.windc.datacenters_get(request, datacenter_id)
api.windc.services_delete(request, datacenter, service_id)
api.windc.services_delete(request, datacenter_id, service_id)
class EditService(tables.LinkAction):
@ -116,6 +114,16 @@ class EditService(tables.LinkAction):
return True
class Wizard(tables.LinkAction):
name = "wizard"
verbose_name = _("Wizard")
url = "horizon:project:windc:update"
classes = ("ajax-modal", "btn-edit")
def allowed(self, request, instance):
return True
class ShowDataCenterServices(tables.LinkAction):
name = "edit"
verbose_name = _("Services")
@ -144,18 +152,36 @@ class WinDCTable(tables.DataTable):
name = "windc"
verbose_name = _("Windows Data Centers")
row_class = UpdateRow
table_actions = (CreateDataCenter,)
row_actions = (ShowDataCenterServices,DeleteDataCenter)
table_actions = (CreateDataCenter, Wizard)
row_actions = (ShowDataCenterServices, DeleteDataCenter)
STATUS_DISPLAY_CHOICES = (
("create", "Deploy"),
)
class WinServicesTable(tables.DataTable):
name = tables.Column('dc_name', verbose_name=_('Name'))
STATUS_CHOICES = (
(None, True),
("deployed", True),
("active", True),
("error", False),
)
name = tables.Column('dc_name', verbose_name=_('Name'),
link=("horizon:project:windc:service_details"),)
_type = tables.Column('type', verbose_name=_('Type'))
status = tables.Column('status', verbose_name=_('Status'))
status = tables.Column('status', verbose_name=_('Status'),
status=True,
status_choices=STATUS_CHOICES,
display_choices=STATUS_DISPLAY_CHOICES)
class Meta:
name = "services"
verbose_name = _("Services")
row_class = UpdateRow
status_columns = ['status']
table_actions = (CreateService,)
row_actions = (EditService, DeleteService)

View File

@ -1,2 +1,3 @@
{% load i18n %}
<p>{% blocktrans %}You can deploy few domain controllers with one name.{% endblocktrans %}</p>
<p>{% blocktrans %}You can deploy few Active Directory services with one domain name.{% endblocktrans %}</p>
<p>{% blocktrans %}The DNS service will automatically created for each Active Directory.{% endblocktrans %}</p>

View File

@ -0,0 +1,28 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block modal-header %}{% trans "Create Service" %}{% endblock %}
{% block modal-body %}
<p>Step {{ wizard.steps.step1 }} of {{ wizard.steps.count }}</p>
<form action="" method="post">{% csrf_token %}
<table>
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{{ form }}
{% endfor %}
{% else %}
{{ wizard.form }}
{% endif %}
{{ wizard.form.forms }}
</table>
{% if wizard.steps.prev %}
<button name="wizard_goto_step" class="btn btn-small" type="submit" value="{{ wizard.steps.prev }}">{% trans "Back" %}</button>
<input type="submit" class="btn btn-primary pull-right" value="{% trans 'Deploy' %}"/>
{% else %}
<button name="wizard_goto_step" class="btn btn-small" type="submit" value="{{ wizard.steps.next }}">{% trans "Next" %}</button>
<input type="submit" class="btn btn-primary pull-right" value="{% trans 'Deploy' %}"/>
{% endif %}
</form>
{% endblock %}

View File

@ -20,8 +20,10 @@
from django.conf.urls.defaults import patterns, url
from .views import IndexView, CreateWinDCView, WinServices, CreateWinServiceView
from .views import IndexView, WinServices, \
CreateWinDCView, CreateWinServiceView
from .views import Wizard
from .forms import WizardFormServiceType, WizardFormConfiguration
VIEW_MOD = 'openstack_dashboard.dashboards.project.windc.views'
@ -29,6 +31,11 @@ urlpatterns = patterns(VIEW_MOD,
url(r'^$', IndexView.as_view(), name='index'),
url(r'^create$', CreateWinServiceView.as_view(), name='create'),
url(r'^create_dc$', CreateWinDCView.as_view(), name='create_dc'),
url(r'^(?P<domain_controller_id>[^/]+)/$', WinServices.as_view(),
name='services')
url(r'^(?P<data_center_id>[^/]+)/$', WinServices.as_view(),
name='services'),
url(r'^update$',
Wizard.as_view([WizardFormServiceType, WizardFormConfiguration]),
name='update'),
url(r'^(?P<service_id>[^/]+)/$', WinServices.as_view(),
name='service_details')
)

View File

@ -15,7 +15,8 @@
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations# under the License.
# License for the specific language governing permissions and limitations
# under the License.
"""
Views for managing instances.
@ -24,24 +25,42 @@ import logging
from django import http
from django import shortcuts
from django.views import generic
from django.core.urlresolvers import reverse, reverse_lazy
from django.utils.datastructures import SortedDict
from django.utils.translation import ugettext_lazy as _
from django.contrib.formtools.wizard.views import SessionWizardView
from horizon import exceptions
from horizon import forms
from horizon import tabs
from horizon import tables
from horizon import workflows
from horizon.forms.views import ModalFormMixin
from openstack_dashboard import api
from .tables import WinDCTable, WinServicesTable
from .workflows import CreateWinService, CreateWinDC
from .forms import WizardFormServiceType, WizardFormConfiguration
import pdb
LOG = logging.getLogger(__name__)
class Wizard(ModalFormMixin, SessionWizardView, generic.FormView):
template_name = 'project/windc/services_tabs.html'
def done(self, form_list, **kwargs):
#do_something_with_the_form_data(form_list)
return HttpResponseRedirect('/')
def get_form(self, step=None, data=None, files=None):
form = super(Wizard, self).get_form(step, data, files)
print step
print data
return form
class IndexView(tables.DataTableView):
table_class = WinDCTable
template_name = 'project/windc/index.html'
@ -69,7 +88,7 @@ class WinServices(tables.DataTableView):
def get_data(self):
try:
dc_id = self.kwargs['domain_controller_id']
dc_id = self.kwargs['data_center_id']
datacenter = api.windc.datacenters_get(self.request, dc_id)
self.dc_name = datacenter.name
services = api.windc.services_list(self.request, datacenter)
@ -91,6 +110,7 @@ class CreateWinDCView(workflows.WorkflowView):
initial['user_id'] = self.request.user.id
return initial
class CreateWinServiceView(workflows.WorkflowView):
workflow_class = CreateWinService
template_name = "project/windc/create.html"

View File

@ -84,11 +84,7 @@ class ConfigureWinDCAction(workflows.Action):
dc_name = forms.CharField(label=_("Domain Name"),
required=False)
#dc_net_name = forms.CharField(label=_("Domain NetBIOS Name"),
# required=False,
# help_text=_("A NetBIOS name of new domain."))
dc_count = forms.IntegerField(label=_("Domain Controllers Count"),
dc_count = forms.IntegerField(label=_("Instances Count"),
required=True,
min_value=1,
max_value=100,
@ -108,7 +104,7 @@ class ConfigureWinDCAction(workflows.Action):
"Recovery Mode."))
class Meta:
name = _("Domain Controllers")
name = _("Active Directory")
help_text_template = ("project/windc/_dc_help.html")
@ -169,7 +165,7 @@ class CreateWinService(workflows.Workflow):
default_steps = (SelectProjectUser,
ConfigureWinDC,
ConfigureWinIIS)
def format_status_message(self, message):
dc_name = self.context.get('dc_name', 'noname')
return message % dc_name
@ -192,7 +188,6 @@ class CreateWinService(workflows.Workflow):
return False
class CreateWinDC(workflows.Workflow):
slug = "create"
name = _("Create Windows Data Center")

View File

@ -35,9 +35,9 @@ class DCManager(base.Manager):
body.update(extra)
return self._create('/datacenters', body, 'datacenter')
def delete(self, datacenter):
return self._delete("/datacenters/%s" % base.getid(datacenter))
def delete(self, datacenter_id):
return self._delete("/datacenters/%s" % datacenter_id)
def get(self, datacenter):
return self._get("/datacenters/%s" % base.getid(datacenter),
def get(self, datacenter_id):
return self._get("/datacenters/%s" % datacenter_id,
'datacenter')

View File

@ -37,10 +37,9 @@ class DCServiceManager(base.Manager):
return self._create("/datacenters/%s/services" % base.getid(datacenter),
body, 'service')
def delete(self, datacenter, service):
def delete(self, datacenter_id, service_id):
return self._delete("/datacenters/%s/services/%s" % \
(base.getid(datacenter),
base.getid(service)))
(datacenter_id, service_id))
def get(self, datacenter, service):
return self._get("/datacenters/%s/services/%s" % \

82
tests/deploy.sh Normal file
View File

@ -0,0 +1,82 @@
#!/usr/bin/expect -d
# The following directories should be created for this script:
# /opt/stack/devstack
# /opt/stack/keero
# the ssh key should be in directory /opt/stack/.ssh/
# the iso file with windows should be in directory /opt/stack/
set timeout 1200
send_user "\n\nStart to login to the test bed...\n\n"
spawn /usr/bin/ssh [lindex $argv 0]@[lindex $argv 1]
expect "password"
send -- "EVYiMCVZX9\n"
expect "*#*"
send -- "su - stack\n"
expect "*$*"
send -- "sudo killall python\n"
expect "*$*"
send -- "cd ~/devstack\n"
expect "*$*"
send -- "./unstack.sh\n"
expect "*$*"
send -- "./stack.sh\n"
expect "*Would you like to start it now?*"
send -- "y\n"
expect "*stack.sh completed*"
send -- "source openrc admin admin\n"
expect "*$*"
send -- "cd ~\n"
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"
expect "*$*"
send -- "cd ~/keero\n"
expect "*$*"
send -- "git pull\n"
expect "/.ssh/id_rsa"
send -- "swordfish\n"
expect "*$*"
send -- "cp -Rf ~/keero/dashboard/windc /opt/stack/horizon/openstack_dashboard/dashboards/project\n"
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"
expect "*$*"
send -- "cd ~/keero/windc\n"
expect "*$*"
send -- "rm -rf windc.sqlite\n"
expect "*$*"
send -- "./tools/with_venv.sh ./bin/windc-api --config-file=./etc/windc-api-paste.ini --dbsync\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 "*$*"

4
tests/selenium/conf.ini Normal file
View File

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

View File

@ -0,0 +1,55 @@
import re
from login_page import LoginPage
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()
def create_data_center(self, name):
button_text = 'Create Windows Data Center'
self.page.find_element_by_link_text(button_text).click()
name_field = self.page.find_element_by_id('id_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)
def delete_data_center(self, name):
datacenter = self.find_data_center(name)
link = datacenter.get_attribute('href')
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)
more_button.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
def select_data_center(self, name):
datacenter = self.page.find_data_center(name)
datacenter.click()
return self.page

View File

@ -0,0 +1,30 @@
import ConfigParser
from selenium import webdriver
class LoginPage():
def login(self):
config = ConfigParser.RawConfigParser()
config.read('conf.ini')
url = config.get('server', 'address')
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')
xpath = "//button[@type='submit']"
button = page.find_element_by_xpath(xpath)
name.clear()
name.send_keys(user)
pwd.clear()
pwd.send_keys(password)
button.click()
return page

View File

@ -0,0 +1,55 @@
import ConfigParser
from selenium import webdriver
class ServicesPage():
page = None
def __init__(self, page):
self.page = page
def create_service(self, service_type, parameters):
button_id = 'services__action_CreateService'
button = self.page.find_element_by_id(button_id)
button.click()
self.select_type_of_service(service_type)
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):
tab = find_element_by_link_text(service_type)
tab.click()
return self.page
def find_service(self, name):
return self.page.find_element_by_link_text(name)
def delete_service(self, name):
service = self.find_data_center(name)
link = service.get_attribute('href')
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()
delete_button_id = "services__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 Service").click()
return self.page

35
tests/selenium/test.py Normal file
View File

@ -0,0 +1,35 @@
import untitests
from datacenters_page import DataCentersPage
class SanityTests():
def setUp(self):
self.page = DataCentersPage()
def tearDown(self):
self.page.close()
def test_01_create_data_center(self):
self.page.create_data_center('dc1')
assert self.page.find_data_center('dc1') is not None
def test_02_delete_data_center(self):
page.delete_data_center('dc1')
assert self.page.find_data_center('dc1') is None
def test_03_create_data_centers(self):
for i in range(1, 20):
name = 'datacenter' + str(i)
self.page.create_data_center(name)
assert self.page.find_data_center(name) is not None
def test_04_delete_data_centers(self):
page.delete_data_center('datacenter1')
page.delete_data_center('datacenter20')
assert self.page.find_data_center('datacenter1') is None
assert self.page.find_data_center('datacenter20') is None
for i in range(2, 19):
name = 'datacenter' + str(i)
assert self.page.find_data_center(name) is not None