Cherry-pick the following change-ids from release-0.2.

I08b8b7391e591d0131af8c4fe074ddaf0222eddc
I9eab7412b67b1cf83025688dba8d879aa0be3b01
I487019103d4ad00c993a6e2df0962dea68c4c86b
I8d8463e79675778f6a8dc01623c81227d06e8d4b
I6609d418150053ee4953a2ae9dba80f76da03fb2
Ic496352384412e53860ee48d696d2794b06ad2ca
I2ae97838ac45eac65032f71f9f6e9445974f0236
I35279696b2aee81739a5c4cb7657e4a9fc4f52e2
Ia6b32e14213b147881b242710ca130a4dea3cdb2
I25ff8448c3cde53aa69905ba5f3736a37e7ed692
Icc1651aeed02784b8a3bde792550efb067af19c9
Ibd5529e1b1754b12fcfade77361dc393cc1780ab
Ice7a3cca2f7931c6f9089f207eddb6f1f802bf4e
I283a67e437d1495257e4c39b18e558072875ffea
I10d4bfd2a0a6a35dbb3e9fca437ceae9e853cc1b
I6c48f803b57399604211ad9a9faee44d87d4dd6d
I7ae888d2708ca438f7e5c94a53e7ddbcc134f4f6
I6b7c54e3700b3b2511f87dfb107d91a13dee9d9b
I2e5c1a4edea567fc2aa31541e7fbc582fc63b885
Ia2d2eaa17c6f8c3e53da4745b03a2b27ff6738fe
I20f03d21c09d090b7edb3360da385e12cad28102
I932674e2b789be54c4366a9a2b81ceaa0c37a133
I64000e26965e8fabb883e5b1294b7639bdade849
I2093b4ed5c33340904698e0d0f01128ece7a2f2b
I2c1e5e156bba1c4c4ed1cd5fb643410c24055706
Iebfe935385a3eeab2ea258bd8dad2ccb70ab14ef
Id7b215a610c0c3375055fb1ff7c75e384fab2d3a
Ie74f5a48ce90e3dab33cd3fc8b57a366433d7fe5
I136eb043e915d4028d5256efbb071c61906a3f69
I4fc13f58f679893b68abbc87c2650743c0258260
Idde251b1c7b1a31cadb5f416f753e365fe572961
Icd385908dd620837b0f4c779059ff540e10263df
I29e4ef69087a78a85d9ea323590e70b0fc8ad642
I2f284035fc656a48c1c7525724a34a3b75f72c64
Ie46f5d27f21f6ddfc1e2cb86e081bea14e1099f4

Change-Id: I96cb8ee13e2905fab9ba41b6dea1ae84b74c7765
This commit is contained in:
Igor Yozhikov 2013-08-20 18:28:43 +04:00 committed by Timur Sufiev
parent 271e8de71d
commit e9f38c707d
29 changed files with 1246 additions and 1130 deletions

View File

@ -1,6 +1,6 @@
recursive-include muranodashboard/static *
recursive-include muranodashboard/templates *
include muranodashboard/panel/services/*.yaml
include muranodashboard/services/*.yaml
include tools/pip-requires
include run_tests.sh
include ChangeLog

View File

@ -53,8 +53,8 @@ class RadioColumn(Column):
class NodeDataGrid(DataGrid):
name = Column('Node', sortable=False)
is_sync = CheckColumn('Sync')
is_primary = RadioColumn('Primary')
isSync = CheckColumn('Sync')
isMaster = RadioColumn('Primary')
def __init__(self, request, data):
self.pk = PK()
@ -68,7 +68,7 @@ class NodeDataGrid(DataGrid):
super(NodeDataGrid, self).__init__(request, FakeQuerySet(
Node, items=items), optimize_sorts=False)
self.default_sort = []
self.default_columns = ['name', 'is_sync', 'is_primary']
self.default_columns = ['name', 'isSync', 'isMaster']
# hack
def load_state(self, render_context=None):

View File

@ -73,6 +73,5 @@ class FakeQuerySet(EmptyQuerySet):
class Node(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=20)
is_primary = models.BooleanField(default=False)
is_sync = models.BooleanField(default=False)
is_async = models.BooleanField(default=False)
isMaster = models.BooleanField(default=False)
isSync = models.BooleanField(default=False)

View File

@ -21,6 +21,7 @@ from muranoclient.common.exceptions import HTTPForbidden, HTTPNotFound
from openstack_dashboard.api.base import url_for
from muranoclient.v1.client import Client
from muranodashboard.panel.services import get_service_name
from consts import STATUS_ID_READY, STATUS_ID_NEW
log = logging.getLogger(__name__)
@ -57,7 +58,7 @@ def get_status_messages_for_service(request, service_id, environment_id):
result = '\n'
#TODO: Add updated time to logs
if deployments:
for deployment in deployments:
for deployment in reversed(deployments):
reports = muranoclient(request).deployments.reports(environment_id,
deployment.id,
service_id)
@ -156,8 +157,9 @@ def environments_list(request):
if service:
environments[index].has_services = True
break
if environments[index].status == u'ready':
environments[index].status = u'new'
if not environments[index].has_services:
if environments[index].status == STATUS_ID_READY:
environments[index].status = STATUS_ID_NEW
return environments
@ -207,6 +209,7 @@ def get_environment_name(request, environment_id):
def get_environment_data(request, environment_id, *args):
"""
For given list of environment attributes return a values
:return list
"""
session_id = Session.get(request, environment_id)
@ -234,7 +237,7 @@ def services_list(request, environment_id):
for service_item in environment.services:
service_data = service_item
service_data['full_service_name'] = get_service_name(
service_data['slug'])
service_data['type'])
if service_data['id'] in reports and reports[service_data['id']]:
last_operation = str(reports[service_data['id']].text)
@ -324,6 +327,6 @@ def get_deployment_descr(request, environment_id, deployment_id):
if 'services' in descr:
for service in descr['services']:
service['full_service_name'] = get_service_name(
service['slug'])
service['type'])
return descr
return None

View File

@ -20,16 +20,6 @@ from muranodashboard.panel.services import iterate_over_service_forms, \
log = logging.getLogger(__name__)
# has to be returned to all forms
# def validate_hostname_template(template, instance_count):
# if template and instance_count > 1:
# if not '#' in template:
# raise forms.ValidationError(
# _('Incrementation symbol "#" is '
# 'required in the Hostname template'))
#
#
class WizardFormServiceType(forms.Form):
service = forms.ChoiceField(label=_('Service Type'),

View File

@ -1,115 +0,0 @@
name: Active Directory
type: activeDirectory
description: >-
<strong> The Active Directory Service </strong>
includes one primary and optionally a few secondary
Domain Controllers, with DNS
unitTemplates:
- isMaster: true
recoveryPassword: $recoveryPassword
location: west-dc
- isMaster: false
recoveryPassword: $recoveryPassword
forms:
- - name: configuration
type: string
hidden: true
initial: standalone
- name: name
type: string
label: Domain Name
description: >-
Enter a desired name for a new domain. This name should fit
in standard Windows domain name requirements: it should contain
only A-Z, a-z, 0-9, and (-) and should not end with a dash.
DNS server will be automatically set up on each of the Domain
Controller instances.
attributeNames: [name, domain]
minLength: 2
maxLength: 64
regexpValidator: '^([a-zA-Z\d][a-zA-Z\d-]*[a-zA-Z\d]\.){1,}[a-zA-Z\d][a-zA-Z\d-]*[a-zA-Z\d]$'
errorMessages:
invalid: >-
Only letters, numbers and dashes in the middle are
allowed. Period characters are allowed only when they are
used to delimit the components of domain style
names. Single-level domain is not appropriate.
helpText: >-
Just letters, numbers and dashes are allowed.
A dot can be used to create subdomains
- name: dcInstances
type: instance
label: Instance Count
description: >-
You can create several Active Directory instances by setting
instance number larger than one. One primary Domain Controller
and a few secondary DCs will be created.
attributeNames: units
minValue: 1
maxValue: 100
initial: 1
helpText: Enter an integer value between 1 and 100
- name: adminAccountName
type: string
label: Account Name
initial: Administrator
regexpValidator: '^[-\w]+$'
errorMessages:
invalid: 'Just letters, numbers, underscores and hyphens are allowed.'
- name: adminPassword
type: password
label: Administrator password
descriptionTitle: Passwords
description: >-
"Windows requires strong password for service administration.
Your password should have at least one letter in each
register, a number and a special character. Password length should be
a minimum of 8 characters.
Once you forget your password you won't be able to
operate the service until recovery password would be entered. So it's
better for Recovery and Administrator password to be different."
- name: recoveryPassword
type: password
label: Recovery password
attributeNames: false
- name: unitNamingPattern
type: string
label: Hostname template
description: >-
"For your convenience all instance hostnames can be named
in the same way. Enter a name and use # character for incrementation.
For example, host# turns into host1, host2, etc. Please follow Windows
hostname restrictions."
required: false
#regexpValidator: '^(([a-zA-Z0-9#][a-zA-Z0-9-#]*[a-zA-Z0-9#])\.)*([A-Za-z0-9#]|[A-Za-z0-9#][A-Za-z0-9-#]*[A-Za-z0-9#])$'
helpText: Optional field for a machine hostname template
- - name: title
type: string
required: false
hidden: true
attributeNames: false
descriptionTitle: Instance Configuration
description: Specify some instance parameters on which service would be created.
- name: flavor
type: flavor
label: Instance flavor
description: >-
Select registered in Openstack flavor. Consider that service performance
depends on this parameter.
required: false
- name: osImage
type: image
label: Instance image
description: >-
Select valid image for a service. Image should already be prepared and
registered in glance.
required: false
- name: availabilityZone
type: azone
label: Availability zone
description: Select availability zone where service would be installed.
required: false

View File

@ -1,92 +0,0 @@
name: Internet Information Services
type: webServer
description: >-
<strong> The Internet Information Service </strong>
sets up an IIS server and joins it into an existing domain
unitTemplates:
- {}
forms:
- - name: title
type: string
required: false
hidden: true
attributeNames: false
description: Standalone IIS Server
- name: name
type: string
label: Service Name
description: >-
Enter a desired name for a service. Just A-Z, a-z, 0-9, dash and
underline are allowed.
minLength: 2
maxLength: 64
regexpValidator: '^[-\w]+$'
errorMessages:
invalid: Just letters, numbers, underscores and hyphens are allowed.
helpText: Just letters, numbers, underscores and hyphens are allowed.
- name: dcInstances
type: instance
hidden: true
attributeNames: units
initial: 1
- name: adminPassword
type: password
label: Administrator password
descriptionTitle: Passwords
description: >-
"Windows requires strong password for service administration.
Your password should have at least one letter in each
register, a number and a special character. Password length should be
a minimum of 8 characters.
Once you forget your password you won't be able to
operate the service until recovery password would be entered. So it's
better for Recovery and Administrator password to be different."
- name: domain
type: domain
label: Domain
required: false
description: >-
Service can be joined to the Active Directory domain. If you want to
create an AD domain create the AD Service first.
helpText: Optional field for a domain to which service can be joined
- name: unitNamingPattern
type: string
label: Hostname template
description: >-
"For your convenience all instance hostnames can be named
in the same way. Enter a name and use # character for incrementation.
For example, host# turns into host1, host2, etc. Please follow Windows
hostname restrictions."
required: false
#regexpValidator: '^(([a-zA-Z0-9#][a-zA-Z0-9-#]*[a-zA-Z0-9#])\.)*([A-Za-z0-9#]|[A-Za-z0-9#][A-Za-z0-9-#]*[A-Za-z0-9#])$'
helpText: Optional field for a machine hostname template
- - name: title
type: string
required: false
hidden: true
attributeNames: false
descriptionTitle: Instance Configuration
description: Specify some instance parameters on which service would be created.
- name: flavor
type: flavor
label: Instance flavor
description: >-
Select registered in Openstack flavor. Consider that service performance
depends on this parameter.
required: false
- name: osImage
type: image
label: Instance image
description: >-
Select valid image for a service. Image should already be prepared and
registered in glance.
required: false
- name: availabilityZone
type: azone
label: Availability zone
description: Select availability zone where service would be installed.
required: false

View File

@ -1,101 +0,0 @@
name: ASP.NET Application
type: aspNetApp
description: >-
<strong> The ASP.NET Application Service </strong> installs
custom application onto one IIS Web Server
unitTemplates:
- {}
forms:
- - name: title
type: string
required: false
hidden: true
attributeNames: false
description: ASP.NET application will be installed onto one IISWeb Server
- name: name
type: string
label: Service Name
description: >-
Enter a desired name for a service. Just A-Z, a-z, 0-9, dash and
underline are allowed.
minLength: 2
maxLength: 64
regexpValidator: '^[-\w]+$'
errorMessages:
invalid: Just letters, numbers, underscores and hyphens are allowed.
helpText: Just letters, numbers, underscores and hyphens are allowed.
- name: dcInstances
type: instance
hidden: true
attributeNames: units
initial: 1
- name: adminPassword
type: password
label: Administrator password
descriptionTitle: Passwords
description: >-
"Windows requires strong password for service administration.
Your password should have at least one letter in each
register, a number and a special character. Password length should be
a minimum of 8 characters.
Once you forget your password you won't be able to
operate the service until recovery password would be entered. So it's
better for Recovery and Administrator password to be different."
- name: domain
type: domain
label: Domain
required: false
description: >-
Service can be joined to the Active Directory domain. If you want to
create an AD domain create the AD Service first.
helpText: Optional field for a domain to which service can be joined
- name: repository
type: string
label: Git repository
description: >-
URL of a git repository with the application you want to deploy.
regexpValidator: '/(\w+://)(.+@)*([\w\d\.]+)(:[\d]+)?/*(.*)/i'
errorMessages:
invalid: Enter correct git repository url
helpText: Enter a valid git repository URL
- name: unitNamingPattern
type: string
label: Hostname template
description: >-
"For your convenience all instance hostnames can be named
in the same way. Enter a name and use # character for incrementation.
For example, host# turns into host1, host2, etc. Please follow Windows
hostname restrictions."
required: false
#regexpValidator: '^(([a-zA-Z0-9#][a-zA-Z0-9-#]*[a-zA-Z0-9#])\.)*([A-Za-z0-9#]|[A-Za-z0-9#][A-Za-z0-9-#]*[A-Za-z0-9#])$'
helpText: Optional field for a machine hostname template
- - name: title
type: string
required: false
hidden: true
attributeNames: false
descriptionTitle: Instance Configuration
description: Specify some instance parameters on which service would be created.
- name: flavor
type: flavor
label: Instance flavor
description: >-
Select registered in Openstack flavor. Consider that service performance
depends on this parameter.
required: false
- name: osImage
type: image
label: Instance image
description: >-
Select valid image for a service. Image should already be prepared and
registered in glance.
required: false
- name: availabilityZone
type: azone
label: Availability zone
description: Select availability zone where service would be installed.
required: false

View File

@ -1,109 +0,0 @@
name: Internet Information Services Web Farm
type: webServerFarm
description: >-
<strong> The IIS Farm Service </strong> sets up a load-balanced set of IIS servers
unitTemplates:
- {}
forms:
- - name: title
type: string
hidden: true
required: false
attributeNames: false
description: A load-balanced array of IIS servers
- name: name
type: string
label: Service Name
description: >-
Enter a desired name for a service. Just A-Z, a-z, 0-9, dash and
underline are allowed.
minLength: 2
maxLength: 64
regexpValidator: '^[-\w]+$'
errorMessages:
invalid: Just letters, numbers, underscores and hyphens are allowed.
helpText: Just letters, numbers, underscores and hyphens are allowed.
- name: username
type: string
hidden: true
initial: Administrator
attributeNames: credentials.username
- name: adminPassword
type: password
attributeNames: [adminPassword, credentials.password]
label: Administrator password
descriptionTitle: Passwords
description: >-
"Windows requires strong password for service administration.
Your password should have at least one letter in each
register, a number and a special character. Password length should be
a minimum of 8 characters.
Once you forget your password you won't be able to
operate the service until recovery password would be entered. So it's
better for Recovery and Administrator password to be different."
- name: domain
type: domain
label: Domain
required: false
description: >-
Service can be joined to the Active Directory domain. If you want to
create an AD domain create the AD Service first.
helpText: Optional field for a domain to which service can be joined
- name: dcInstances
type: instance
minValue: 1
maxValue: 100
attributeNames: [units, instanceCount]
initial: 1
label: Instance Count
description: Several instances with IIS Service can be created at one time.
helpText: Enter an integer value between 1 and 100
- name: loadBalancerPort
type: integer
label: Load Balancer port
minValue: 1
maxValue: 65536
initial: 80
description: Specify port number where Load Balancer will be running
helpText: Enter an integer value from 1 to 65536
- name: unitNamingPattern
type: string
label: Hostname template
description: >-
"For your convenience all instance hostnames can be named
in the same way. Enter a name and use # character for incrementation.
For example, host# turns into host1, host2, etc. Please follow Windows
hostname restrictions."
required: false
#regexpValidator: '^(([a-zA-Z0-9#][a-zA-Z0-9-#]*[a-zA-Z0-9#])\.)*([A-Za-z0-9#]|[A-Za-z0-9#][A-Za-z0-9-#]*[A-Za-z0-9#])$'
helpText: Optional field for a machine hostname template
- - name: title
type: string
required: false
hidden: true
attributeNames: false
descriptionTitle: Instance Configuration
description: Specify some instance parameters on which service would be created.
- name: flavor
type: flavor
label: Instance flavor
description: >-
Select registered in Openstack flavor. Consider that service performance
depends on this parameter.
required: false
- name: osImage
type: image
label: Instance image
description: >-
Select valid image for a service. Image should already be prepared and
registered in glance.
required: false
- name: availabilityZone
type: azone
label: Availability zone
description: Select availability zone where service would be installed.
required: false

View File

@ -1,117 +0,0 @@
name: ASP.NET Application Web Farm
type: aspNetAppFarm
description: >-
<strong> The ASP.NET Farm Service </strong> installs a custom application
on a load-balanced array of IIS servers
unitTemplates:
- {}
forms:
- - name: title
type: string
required: false
hidden: true
attributeNames: false
description: >-
The ASP.NET application will be installed on a number of IIS Web
Servers, and load balancing will be configured.
- name: name
type: string
label: Service Name
description: >-
Enter a desired name for a service. Just A-Z, a-z, 0-9, dash and
underline are allowed.
minLength: 2
maxLength: 64
regexpValidator: '^[-\w]+$'
errorMessages:
invalid: Just letters, numbers, underscores and hyphens are allowed.
helpText: Just letters, numbers, underscores and hyphens are allowed.
- name: dcInstances
type: instance
hidden: true
attributeNames: units
initial: 1
- name: username
type: string
hidden: true
initial: Administrator
attributeNames: credentials.username
- name: adminPassword
type: password
attributeNames: [adminPassword, credentials.password]
label: Administrator password
descriptionTitle: Passwords
description: >-
"Windows requires strong password for service administration.
Your password should have at least one letter in each
register, a number and a special character. Password length should be
a minimum of 8 characters.
Once you forget your password you won't be able to
operate the service until recovery password would be entered. So it's
better for Recovery and Administrator password to be different."
- name: domain
type: domain
label: Domain
required: false
description: >-
Service can be joined to the Active Directory domain. If you want to
create an AD domain create the AD Service first.
helpText: Optional field for a domain to which service can be joined
- name: repository
type: string
label: Git repository
description: >-
URL of a git repository with the application you want to deploy.
regexpValidator: '/(\w+://)(.+@)*([\w\d\.]+)(:[\d]+)?/*(.*)/i'
errorMessages:
invalid: Enter correct git repository url
helpText: Enter a valid git repository URL
- name: loadBalancerPort
type: integer
label: Load Balancer port
minValue: 1
maxValue: 65536
initial: 80
description: Specify port number where Load Balancer will be running
helpText: Enter an integer value from 1 to 65536
- name: unitNamingPattern
type: string
label: Hostname template
description: >-
"For your convenience all instance hostnames can be named
in the same way. Enter a name and use # character for incrementation.
For example, host# turns into host1, host2, etc. Please follow Windows
hostname restrictions."
required: false
#regexpValidator: '^(([a-zA-Z0-9#][a-zA-Z0-9-#]*[a-zA-Z0-9#])\.)*([A-Za-z0-9#]|[A-Za-z0-9#][A-Za-z0-9-#]*[A-Za-z0-9#])$'
helpText: Optional field for a machine hostname template
- - name: title
type: string
required: false
hidden: true
attributeNames: false
descriptionTitle: Instance Configuration
description: Specify some instance parameters on which service would be created.
- name: flavor
type: flavor
label: Instance flavor
description: >-
Select registered in Openstack flavor. Consider that service performance
depends on this parameter.
required: false
- name: osImage
type: image
label: Instance image
description: >-
Select valid image for a service. Image should already be prepared and
registered in glance.
required: false
- name: availabilityZone
type: azone
label: Availability zone
description: Select availability zone where service would be installed.
required: false

View File

@ -1,111 +0,0 @@
name: MS SQL Server
type: msSqlServer
description: >-
<strong> The MS SQL Service </strong> installs an instance of
Microsoft SQL Server
unitTemplates:
- {}
forms:
- - name: title
type: string
required: false
hidden: true
attributeNames: false
description: MS SQL Server
# temporaryHack
widgetMedia:
js: [muranodashboard/js/mixed-mode.js]
- name: name
type: string
label: Service Name
description: >-
Enter a desired name for a service. Just A-Z, a-z, 0-9, dash and
underline are allowed.
minLength: 2
maxLength: 64
regexpValidator: '^[-\w]+$'
errorMessages:
invalid: Just letters, numbers, underscores and hyphens are allowed.
helpText: Just letters, numbers, underscores and hyphens are allowed.
- name: dcInstances
type: instance
hidden: true
attributeNames: units
initial: 1
- name: adminPassword
type: password
label: Administrator password
descriptionTitle: Passwords
description: >-
"Windows requires strong password for service administration.
Your password should have at least one letter in each
register, a number and a special character. Password length should be
a minimum of 8 characters.
Once you forget your password you won't be able to
operate the service until recovery password would be entered. So it's
better for Recovery and Administrator password to be different."
- name: domain
type: domain
label: Domain
required: false
description: >-
Service can be joined to the Active Directory domain. If you want to
create an AD domain create the AD Service first.
helpText: Optional field for a domain to which service can be joined
- name: mixedModeAuth
type: boolean
label: Mixed-mode Authentication
initial: true
required: false
description: >-
Mixed authentication mode allows the use of Windows
credentials but supplements them with local SQL Server user
accounts that the administrator may create and maintain within
SQL Server. If this mode is on SA password is required
- name: saPassword
type: password
label: SA Password
description: Set system administrator password for the MS SQL Server.
helpText: SQL server System Administrator account
required: $mixedModeAuth
- name: unitNamingPattern
type: string
label: Hostname template
description: >-
"For your convenience all instance hostnames can be named
in the same way. Enter a name and use # character for incrementation.
For example, host# turns into host1, host2, etc. Please follow Windows
hostname restrictions."
required: false
#regexpValidator: '^(([a-zA-Z0-9#][a-zA-Z0-9-#]*[a-zA-Z0-9#])\.)*([A-Za-z0-9#]|[A-Za-z0-9#][A-Za-z0-9-#]*[A-Za-z0-9#])$'
helpText: Optional field for a machine hostname template
- - name: title
type: string
required: false
hidden: true
attributeNames: false
descriptionTitle: Instance Configuration
description: Specify some instance parameters on which service would be created.
- name: flavor
type: flavor
label: Instance flavor
description: >-
Select registered in Openstack flavor. Consider that service performance
depends on this parameter.
required: false
- name: osImage
type: image
label: Instance image
description: >-
Select valid image for a service. Image should already be prepared and
registered in glance.
required: false
- name: availabilityZone
type: azone
label: Availability zone
description: Select availability zone where service would be installed.
required: false

View File

@ -1,193 +0,0 @@
name: MS SQL Server Cluster
type: msSqlClusterServer
description: >-
<strong> The MS SQL Failover Cluster </strong> installs
Microsoft SQL Failover Cluster Server
unitTemplates:
- {}
forms:
- - name: title
type: string
required: false
hidden: true
attributeNames: false
description: MS SQL Failover Cluster
# temporaryHack
widgetMedia:
js: [muranodashboard/js/mixed-mode.js, muranodashboard/js/external-ad.js]
- name: name
type: string
label: Service Name
description: >-
Enter a desired name for a service. Just A-Z, a-z, 0-9, dash and
underline are allowed.
minLength: 2
maxLength: 64
regexpValidator: '^[-\w]+$'
errorMessages:
invalid: Just letters, numbers, underscores and hyphens are allowed.
helpText: Just letters, numbers, underscores and hyphens are allowed.
- name: adminPassword
type: password
label: Administrator password
descriptionTitle: Passwords
description: >-
"Windows requires strong password for service administration.
Your password should have at least one letter in each
register, a number and a special character. Password length should be
a minimum of 8 characters.
Once you forget your password you won't be able to
operate the service until recovery password would be entered. So it's
better for Recovery and Administrator password to be different."
- name: externalAD
type: boolean
label: Active Directory is configured by the System Administrator
widgetAttrs: # temporary hack
class: external-ad
required: false
- name: domainAdminUserName
type: string
label: Active Directory User
required: $externalAD
regexpValidator: '^[-\w]+$'
errorMessages:
invalid: 'Just letters, numbers, underscores and hyphens are allowed.'
- name: domainAdminPassword
type: password
label: Active Directory Password
required: $externalAD
- name: domain
type: domain
label: Domain
required: false
description: >-
Service can be joined to the Active Directory domain. If you want to
create an AD domain create the AD Service first.
helpText: Optional field for a domain to which service can be joined
- name: mixedModeAuth
type: boolean
label: Mixed-mode Authentication
initial: true
required: false
description: >-
Mixed authentication mode allows the use of Windows
credentials but supplements them with local SQL Server user
accounts that the administrator may create and maintain within
SQL Server. If this mode is on SA password is required
- name: saPassword
type: password
label: SA Password
description: Set system administrator password for the MS SQL Server.
helpText: SQL server System Administrator account
required: $mixedModeAuth
- name: unitNamingPattern
type: string
label: Hostname template
description: >-
"For your convenience all instance hostnames can be named
in the same way. Enter a name and use # character for incrementation.
For example, host# turns into host1, host2, etc. Please follow Windows
hostname restrictions."
required: false
#regexpValidator: '^(([a-zA-Z0-9#][a-zA-Z0-9-#]*[a-zA-Z0-9#])\.)*([A-Za-z0-9#]|[A-Za-z0-9#][A-Za-z0-9-#]*[A-Za-z0-9#])$'
helpText: Optional field for a machine hostname template
- - name: clusterIP
type: clusterip
label: Cluster Static IP
description: Specify a valid IPv4 fixed IP.
- name: clusterName
type: string
label: Cluster Name
helpText: Service name for new SQL Cluster service
description: >-
Specify a name of a cluster. Just A-Z, a-z, 0-9, dash and underline are allowed.
- name: agGroupName
type: string
label: Availability Group Name
helpText: Name of AG during SQL setup
description: >-
Specify a name of an AG. Just A-Z, a-z, 0-9, dash and underline are allowed.
- name: agListenerName
type: string
label: Availability Group Listener Name
helpText: FQDN name of a new DNS entry for AG Listener endpoint
regexpValidator: '^[-\w]+$'
errorMessages:
invalid: Just letters, numbers, underscores and hyphens are allowed.
description: >-
Specify a name of an AG Listener . Just A-Z, a-z, 0-9, dash and underline are allowed.
- name: agListenerIP
type: clusterip
label: Availability Group Listener IP
description: Specify a valid IPv4 fixed IP.
- name: sqlServiceUserName
type: string
label: SQL User Name
regexpValidator: '^[-\w]+$'
errorMessages:
invalid: Just letters, numbers, underscores and hyphens are allowed.
description: User name that will be created to manage cluster instances.
- name: sqlServicePassword
type: password
label: SQL User Password
description: User password that will be created to manage cluster instances.
- name: dcInstances
type: instance
label: Instance Count
minValue: 2
maxValue: 5
initial: 2
attributeNames: units
helpText: Enter an integer value between 2 and 5
description: Microsoft SQL Failover Cluster includes up to 5 instances.
- - name: nodes
type: datagrid
label: Nodes
description: >-
Configure cluster instances. Cluster node quantity can be set
with 'Add' and 'Remove' buttons. Configure Sync mode by
enabling corresponding checkbox. All other nodes will be in
Async mode. Just 2 nodes are allowed to be Sync. Also one
Master node need to be selected. SQL Failover cluster has
limit of 5 instances.
- name: databases
type: databaselist
label: Database list
description: >-
Specify names for new databases which will be created as part
of service installation. Here should come comma-separated list
of database names, where each name has the following syntax:
first symbol should be latin letter or underscore; subsequent
symbols can be latin letter, numeric, underscore, at sign,
number sign or dollar sign.
helpText: Enter comma separated list of databases that will be created
- - name: title
type: string
required: false
hidden: true
attributeNames: false
descriptionTitle: Instance Configuration
description: Specify some instance parameters on which service would be created.
- name: flavor
type: flavor
label: Instance flavor
description: >-
Select registered in Openstack flavor. Consider that service performance
depends on this parameter.
required: false
- name: osImage
type: image
label: Instance image
description: >-
Select valid image for a service. Image should already be prepared and
registered in glance.
required: false
- name: availabilityZone
type: azone
label: Availability zone
description: Select availability zone where service would be installed.
required: false

View File

@ -11,93 +11,123 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
from django.template.defaultfilters import slugify
from ordereddict import OrderedDict
from yaml.scanner import ScannerError
import yaml
import re
import os
import re
from django.conf import settings
from ordereddict import OrderedDict
import yaml
from yaml.scanner import ScannerError
from django.utils.translation import ugettext_lazy as _
_all_services = OrderedDict()
class Service(object):
def __init__(self, modified_on, **kwargs):
import muranodashboard.panel.services.forms as services
for key, value in kwargs.iteritems():
if key == 'forms':
self.forms = []
for form_data in value:
form_name, form_data = self.extract_form_data(form_data)
self.forms.append(
type(form_name, (services.ServiceConfigurationForm,),
{'service': self,
'fields_template': form_data['fields'],
'validators': form_data.get('validators', [])}))
else:
setattr(self, key, value)
self.modified_on = modified_on
self.cleaned_data = {}
@staticmethod
def extract_form_data(form_data):
form_name = form_data.keys()[0]
return form_name, form_data[form_name]
def update_cleaned_data(self, form, data):
if data:
# match = re.match('^.*-(\d)+$', form.prefix)
# index = int(match.group(1)) if match else None
# if index is not None:
# self.cleaned_data[index] = data
self.cleaned_data[form.__class__.__name__] = data
return self.cleaned_data
def import_all_services():
import muranodashboard.panel.services.helpers as utils
directory = os.path.dirname(__file__)
for fname in sorted(os.listdir(directory)):
from muranodashboard.panel.services.helpers import decamelize
directory = getattr(settings, 'MURANO_SERVICES_DESC_PATH', None)
if directory is None:
directory = os.path.join(os.path.dirname(__file__), '../../services/')
for filename in os.listdir(directory):
if not filename.endswith('.yaml'):
continue
service_file = os.path.join(directory, filename)
modified_on = os.path.getmtime(service_file)
if filename in _all_services:
if _all_services[filename].modified_on >= modified_on:
continue
try:
if fname.endswith('.yaml'):
name = os.path.splitext(fname)[0]
path = os.path.join(directory, fname)
modified_on = os.stat(path).st_mtime
if (not name in _all_services or
_all_services[name][0] < modified_on):
with open(path) as f:
kwargs = dict(
(utils.decamelize(k), v)
for (k, v) in yaml.load(f).iteritems())
_all_services[name] = (modified_on,
type(name, (), kwargs))
except ScannerError:
pass
except OSError:
pass
with open(service_file) as stream:
yaml_desc = yaml.load(stream)
except (OSError, ScannerError):
continue
service = dict((decamelize(k), v) for (k, v) in yaml_desc.iteritems())
_all_services[filename] = Service(modified_on, **service)
def iterate_over_services():
import muranodashboard.panel.services.forms as services
import_all_services()
for id, service_data in _all_services.items():
modified_on, service_cls = service_data
forms = []
for fields in service_cls.forms:
class Form(services.ServiceConfigurationForm):
service = service_cls
fields_template = fields
forms.append(Form)
yield slugify(service_cls.name), service_cls, forms
for service in sorted(_all_services.values(), key=lambda v: v.name):
yield service.type, service, service.forms
def iterate_over_service_forms():
for slug, Service, forms in iterate_over_services():
for step, form in zip(xrange(len(forms)), forms):
yield '{0}-{1}'.format(slug, step), form
for srv_type, service, forms in iterate_over_services():
for step, form in enumerate(forms):
yield '{0}-{1}'.format(srv_type, step), form
def with_service(slug, getter, default):
import_all_services()
match = re.match('(.*)-[0-9]+', slug)
def service_type_from_id(service_id):
match = re.match('(.*)-[0-9]+', service_id)
if match:
slug = match.group(1)
for _slug, Service, forms in iterate_over_services():
if _slug == slug:
return getter(Service)
return match.group(1)
else: # if no number suffix found, it was service_type itself passed in
return service_id
def with_service(service_id, getter, default):
import_all_services()
service_type = service_type_from_id(service_id)
for srv_type, service, forms in iterate_over_services():
if srv_type == service_type:
return getter(service)
return default
def get_service_template(slug):
return with_service(slug, lambda Service: Service.template, '')
def get_service_name(service_id):
return with_service(service_id, lambda service: service.name, '')
def get_service_name(slug):
return with_service(slug, lambda Service: Service.name, '')
def get_service_client(slug):
return with_service(slug, lambda Service: Service.type, None)
def get_service_field_descriptions(slug, index):
def get_descriptions(Service):
form = Service.forms[index]
def get_service_field_descriptions(service_id, index):
def get_descriptions(service):
Form = service.forms[index]
descriptions = []
for field in form:
for field in Form.fields_template:
if 'description' in field:
title = field.get('descriptionTitle', field.get('label', ''))
descriptions.append((title, field['description']))
return descriptions
return with_service(slug, get_descriptions, [])
return with_service(service_id, get_descriptions, [])
def get_service_type(wizard):
@ -107,23 +137,27 @@ def get_service_type(wizard):
def get_service_choices():
return [(slug, Service.name) for slug, Service, forms in
return [(srv_type, service.name) for srv_type, service, forms in
iterate_over_services()]
def get_service_checkers():
import_all_services()
def make_comparator(slug):
def make_comparator(srv_id):
def compare(wizard):
match = re.match('(.*)-[0-9]+', slug)
return match and match.group(1) == get_service_type(wizard)
return service_type_from_id(srv_id) == get_service_type(wizard)
return compare
return [(slug, make_comparator(slug)) for slug, form
return [(srv_id, make_comparator(srv_id)) for srv_id, form
in iterate_over_service_forms()]
def get_service_descriptions():
return [(slug, Service.description) for slug, Service, forms in
iterate_over_services()]
descriptions = []
for srv_type, service, forms in iterate_over_services():
description = getattr(service, 'description', _("<b>Default service \
description</b>. If you want to see here something meaningful, please \
provide `description' field in service markup."))
descriptions.append((srv_type, description))
return descriptions

View File

@ -13,7 +13,6 @@
# under the License.
import re
import ast
import json
from django import forms
from django.core.validators import RegexValidator, validate_ipv4_address
@ -30,16 +29,35 @@ import copy
def with_request(func):
def update(self, initial):
def update(self, initial, **kwargs):
request = initial.get('request')
if request:
func(self, request, initial)
func(self, request, **kwargs)
else:
raise forms.ValidationError("Can't get a request information")
return update
class PasswordField(forms.CharField):
class CustomPropertiesField(object):
@classmethod
def push_properties(cls, kwargs):
props = {}
for key, value in kwargs.iteritems():
if isinstance(value, property):
props[key] = value
for key in props.keys():
del kwargs[key]
if props:
return type('cls_with_props', (cls,), props)
else:
return cls
class CharField(forms.CharField, CustomPropertiesField):
pass
class PasswordField(CharField):
special_characters = '!@#$%^&*()_+|\/.,~?><:{}'
password_re = re.compile('^.*(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[%s]).*$'
% special_characters)
@ -53,7 +71,9 @@ class PasswordField(forms.CharField):
return name + '-clone'
def compare(self, name, form_data):
if self.is_original(): # run compare only for original fields
if self.is_original() and self.required:
# run compare only for original fields
# do not run compare for hidden fields (they are not required)
if form_data.get(name) != form_data.get(self.get_clone_name(name)):
raise forms.ValidationError(_(u"{0}{1} don't match".format(
self.label, pluralize(2))))
@ -84,6 +104,11 @@ class PasswordField(forms.CharField):
help_text=help_text,
widget=self.PasswordInput(render_value=True))
def __deepcopy__(self, memo):
result = super(PasswordField, self).__deepcopy__(memo)
result.error_messages = copy.deepcopy(self.error_messages)
return result
def is_original(self):
return hasattr(self, 'original') and self.original
@ -97,23 +122,41 @@ class PasswordField(forms.CharField):
return field
class InstanceCountField(forms.IntegerField):
class IntegerField(forms.IntegerField, CustomPropertiesField):
pass
class InstanceCountField(IntegerField):
def clean(self, value):
self.value = super(InstanceCountField, self).clean(value)
return self.value
def postclean(self, form, data):
value = []
for dc in range(self.value):
if hasattr(self, 'value'):
templates = form.get_unit_templates(data)
if dc < len(templates) - 1:
value.append(templates[dc])
for dc in range(self.value):
if dc < len(templates) - 1:
template = templates[dc]
else:
template = templates[-1]
value.append(self.interpolate_number(template, dc + 1))
return value
@staticmethod
def interpolate_number(spec, number):
"""Replaces all '#' occurrences with given number."""
def interpolate(spec):
if type(spec) == dict:
return dict((k, interpolate(v)) for (k, v) in spec.iteritems())
elif type(spec) in (str, unicode) and '#' in spec:
return spec.replace('#', '{0}').format(number)
else:
value.append(templates[-1])
return value
return spec
return interpolate(spec)
class DataGridField(forms.MultiValueField):
class DataGridField(forms.MultiValueField, CustomPropertiesField):
def __init__(self, *args, **kwargs):
kwargs['widget'] = DataGridCompound
super(DataGridField, self).__init__(
@ -124,21 +167,22 @@ class DataGridField(forms.MultiValueField):
return data_list[1]
@with_request
def update(self, request, initial):
def update(self, request, **kwargs):
self.widget.update_request(request)
nodes = []
instance_count = initial.get('instance_count')
if instance_count:
for index in xrange(instance_count):
nodes.append({'name': 'node' + str(index + 1),
'is_sync': index < 2,
'is_primary': index == 0})
self.initial = json.dumps(nodes)
# hack to use json string instead of python dict get by YAQL
data = kwargs['form'].service.cleaned_data
if 'clusterConfiguration' in data:
conf = data['clusterConfiguration']
conf['dcInstances'] = json.dumps(conf['dcInstances'])
class DomainChoiceField(forms.ChoiceField):
class ChoiceField(forms.ChoiceField, CustomPropertiesField):
pass
class DomainChoiceField(ChoiceField):
@with_request
def update(self, request, initial):
def update(self, request, **kwargs):
self.choices = [("", "Not in domain")]
link = request.__dict__['META']['HTTP_REFERER']
environment_id = re.search(
@ -149,9 +193,9 @@ class DomainChoiceField(forms.ChoiceField):
[(domain.name, domain.name) for domain in domains])
class FlavorChoiceField(forms.ChoiceField):
class FlavorChoiceField(ChoiceField):
@with_request
def update(self, request, initial):
def update(self, request, **kwargs):
self.choices = [(flavor.name, flavor.name) for flavor in
novaclient(request).flavors.list()]
for flavor in self.choices:
@ -160,33 +204,28 @@ class FlavorChoiceField(forms.ChoiceField):
break
class ImageChoiceField(forms.ChoiceField):
class ImageChoiceField(ChoiceField):
@with_request
def update(self, request, initial):
def update(self, request, **kwargs):
try:
# public filter removed
images, _more = glance.image_list_detailed(request)
except:
images = []
exceptions.handle(request,
_("Unable to retrieve public images."))
exceptions.handle(request, _("Unable to retrieve public images."))
image_mapping, image_choices = {}, []
for image in images:
murano_property = image.properties.get('murano_image_info')
if murano_property:
# convert to dict because
# only string can be stored in image metadata property
try:
murano_json = ast.literal_eval(murano_property)
murano_json = json.loads(murano_property)
except ValueError:
messages.error(request,
_("Invalid value in image metadata"))
messages.error(request, _("Invalid murano image metadata"))
else:
title = murano_json.get('title')
image_id = murano_json.get('id')
if title and image_id:
image_mapping[smart_text(title)] = smart_text(image_id)
title = murano_json.get('title', image.name)
murano_json['name'] = image.name
image_mapping[smart_text(title)] = json.dumps(murano_json)
for name in sorted(image_mapping.keys()):
image_choices.append((image_mapping[name], name))
@ -197,10 +236,14 @@ class ImageChoiceField(forms.ChoiceField):
self.choices = image_choices
def clean(self, value):
value = super(ImageChoiceField, self).clean(value)
return json.loads(value) if value else value
class AZoneChoiceField(forms.ChoiceField):
class AZoneChoiceField(ChoiceField):
@with_request
def update(self, request, initial):
def update(self, request, **kwargs):
try:
availability_zones = novaclient(request).availability_zones.\
list(detailed=False)
@ -211,21 +254,19 @@ class AZoneChoiceField(forms.ChoiceField):
az_choices = [(az.zoneName, az.zoneName)
for az in availability_zones if az.zoneState]
if az_choices:
az_choices.insert(0, ("", _("Select Availability Zone")))
else:
if not az_choices:
az_choices.insert(0, ("", _("No availability zones available")))
self.choices = az_choices
class BooleanField(forms.BooleanField):
class BooleanField(forms.BooleanField, CustomPropertiesField):
def __init__(self, *args, **kwargs):
kwargs['widget'] = forms.CheckboxInput(attrs={'class': 'checkbox'})
super(BooleanField, self).__init__(*args, **kwargs)
class ClusterIPField(forms.CharField):
class ClusterIPField(CharField):
@staticmethod
def validate_cluster_ip(request, ip_ranges):
def perform_checking(ip):
@ -240,6 +281,10 @@ class ClusterIPField(forms.CharField):
request, _("Unable to retrieve information "
"about fixed IP or IP is not valid."),
ignore=True)
except exceptions.NOT_FOUND:
exceptions.handle(
request, _("Could not found fixed ips for ip %s" % (ip,)),
ignore=True)
else:
if ip_info.hostname:
raise forms.ValidationError(
@ -247,7 +292,7 @@ class ClusterIPField(forms.CharField):
return perform_checking
@with_request
def update(self, request, initial):
def update(self, request, **kwargs):
try:
network_list = novaclient(request).networks.list()
ip_ranges = [network.cidr for network in network_list]
@ -261,18 +306,8 @@ class ClusterIPField(forms.CharField):
self.validators = [self.validate_cluster_ip(request, ip_ranges)]
self.error_messages['invalid'] = validate_ipv4_address.message
def postclean(self, form, data):
# hack to compare two IPs
ips = []
for key, field in form.fields.items():
if isinstance(field, ClusterIPField):
ips.append(data.get(key))
if ips[0] == ips[1] and ips[0] is not None:
raise forms.ValidationError(_(
'Listener IP and Cluster Static IP should be different'))
class DatabaseListField(forms.CharField):
class DatabaseListField(CharField):
validate_mssql_identifier = RegexValidator(
re.compile(r'^[a-zA-z_][a-zA-Z0-9_$#@]*$'),
_((u'First symbol should be latin letter or underscore. Subsequent ' +

View File

@ -18,6 +18,8 @@ from django.core.validators import RegexValidator
from django.utils.translation import ugettext_lazy as _
import muranodashboard.panel.services.fields as fields
import muranodashboard.panel.services.helpers as helpers
import yaql
import types
class UpdatableFieldsForm(forms.Form):
@ -39,12 +41,30 @@ class UpdatableFieldsForm(forms.Form):
for name, field in self.fields.iteritems():
if hasattr(field, 'update'):
field.update(self.initial)
field.update(self.initial, form=self)
if not field.required:
field.widget.attrs['placeholder'] = 'Optional'
class ServiceConfigurationForm(UpdatableFieldsForm):
types = {
'string': fields.CharField,
'boolean': fields.BooleanField,
'instance': fields.InstanceCountField,
'clusterip': fields.ClusterIPField,
'domain': fields.DomainChoiceField,
'password': fields.PasswordField,
'integer': fields.IntegerField,
'databaselist': fields.DatabaseListField,
'datagrid': fields.DataGridField,
'flavor': fields.FlavorChoiceField,
'image': fields.ImageChoiceField,
'azone': fields.AZoneChoiceField,
'text': (fields.CharField, forms.Textarea)
}
localizable_keys = set(['label', 'help_text', 'error_messages'])
def __init__(self, *args, **kwargs):
super(ServiceConfigurationForm, self).__init__(*args, **kwargs)
self.attribute_mappings = {}
@ -52,25 +72,9 @@ class ServiceConfigurationForm(UpdatableFieldsForm):
self.initial = kwargs.get('initial', self.initial)
self.update_fields()
EVAL_PREFIX = '$'
types = {
'string': forms.CharField,
'boolean': fields.BooleanField,
'instance': fields.InstanceCountField,
'clusterip': fields.ClusterIPField,
'domain': fields.DomainChoiceField,
'password': fields.PasswordField,
'integer': forms.IntegerField,
'databaselist': fields.DatabaseListField,
'datagrid': fields.DataGridField,
'flavor': fields.FlavorChoiceField,
'image': fields.ImageChoiceField,
'azone': fields.AZoneChoiceField,
'text': (forms.CharField, forms.Textarea)
}
localizable_keys = set(['label', 'help_text', 'error_messages'])
@staticmethod
def get_yaql_expr(expr):
return type(expr) == dict and expr.get('YAQL', None)
def init_attribute_mappings(self, field_name, kwargs):
def set_mapping(name, value):
@ -123,18 +127,6 @@ class ServiceConfigurationForm(UpdatableFieldsForm):
del kwargs['widget_attrs']
return widget
def append_properties(cls, kwargs):
props = {}
for key, value in kwargs.iteritems():
if isinstance(value, property):
props[key] = value
for key in props.keys():
del kwargs[key]
if props:
return type('cls_with_props', (cls,), props)
else:
return cls
def append_field(field_spec):
cls = parse_spec(field_spec['type'], 'type')[1]
widget = None
@ -142,7 +134,7 @@ class ServiceConfigurationForm(UpdatableFieldsForm):
cls, widget = cls
kwargs = parse_spec(field_spec)[1]
kwargs['widget'] = process_widget(kwargs, cls, widget)
cls = append_properties(cls, kwargs)
cls = cls.push_properties(kwargs)
self.init_attribute_mappings(field_spec['name'], kwargs)
self.init_field_descriptions(kwargs)
@ -166,11 +158,25 @@ class ServiceConfigurationForm(UpdatableFieldsForm):
def is_localizable(keys):
return set(keys).intersection(self.localizable_keys)
def make_property(key, spec):
def _get(field):
data_ready, value = self.get_data(spec)
return value if data_ready else field.__dict__[key]
def _set(field, value):
field.__dict__[key] = value
def _del(field):
del field.__dict__[key]
return property(_get, _set, _del)
def parse_spec(spec, keys=[]):
if not type(keys) == list:
keys = [keys]
key = keys and keys[-1] or None
if type(spec) == dict:
if self.get_yaql_expr(spec):
return key, make_property(key, spec)
elif type(spec) == dict:
items = []
for k, v in spec.iteritems():
if not k in ('type', 'name'):
@ -191,69 +197,67 @@ class ServiceConfigurationForm(UpdatableFieldsForm):
return 'widget', forms.HiddenInput
elif key == 'regexp_validator':
return 'validators', [prepare_regexp(spec)]
elif (type(spec) in (str, unicode) and
spec[0] == self.EVAL_PREFIX):
def _get(field):
"""First try to get value from cleaned data, if none
found, use raw data."""
data = getattr(self, 'cleaned_data', None)
value = data and data.get(spec[1:], None)
if value is None:
name = self.add_prefix(spec[1:])
value = self.data.get(name, None)
return value
def _set(field, value):
# doesn't work - why?
# super(field.__class__, field).__setattr__(key, value)
field.__dict__[key] = value
def _del(field):
# doesn't work - why?
# super(field.__class__, field).__delattr__(key)
del field.__dict__[key]
return key, property(_get, _set, _del)
else:
return key, spec
for spec in field_specs:
append_field(spec)
def get_data(self, expr, data=None):
"""First try to get value from cleaned data, if none
found, use raw data."""
context = yaql.create_context()
data = self.service.update_cleaned_data(
self, data or getattr(self, 'cleaned_data', None))
expr = self.get_yaql_expr(expr)
value = data and yaql.parse(expr).evaluate(data, context)
return data != {}, value
def get_unit_templates(self, data):
def parse_spec(spec):
if type(spec) == list:
if self.get_yaql_expr(spec):
data_ready, value = self.get_data(spec, data)
return value
elif isinstance(spec, types.ListType):
return [parse_spec(_spec) for _spec in spec]
elif type(spec) == dict:
elif isinstance(spec, types.DictType):
return dict(
(parse_spec(k), parse_spec(v))
for (k, v) in spec.iteritems())
elif (type(spec) in (str, unicode) and
spec[0] == self.EVAL_PREFIX):
return data.get(spec[1:])
else:
return spec
return [parse_spec(spec) for spec in self.service.unit_templates]
def extract_attributes(self, attributes):
def get_data(name):
def get_attr(name):
if type(name) == dict:
return dict((k, get_data(v)) for (k, v) in name.iteritems())
return dict((k, get_attr(v)) for (k, v) in name.iteritems())
else:
return self.cleaned_data[name]
for attr_name, field_name in self.attribute_mappings.iteritems():
attributes[attr_name] = get_data(field_name)
attributes[attr_name] = get_attr(field_name)
def clean(self):
form_data = self.cleaned_data
if self._errors:
return self.cleaned_data
else:
cleaned_data = super(ServiceConfigurationForm, self).clean()
all_data = self.service.update_cleaned_data(self, cleaned_data)
context = yaql.create_context()
for validator in self.validators:
expr = self.get_yaql_expr(validator['expr'])
if not yaql.parse(expr).evaluate(all_data, context):
raise forms.ValidationError(
_(validator.get('message', '')))
for name, field in self.fields.iteritems():
if isinstance(field, fields.PasswordField):
field.compare(name, form_data)
for name, field in self.fields.iteritems():
if isinstance(field, fields.PasswordField):
field.compare(name, cleaned_data)
if hasattr(field, 'postclean'):
value = field.postclean(self, form_data)
if value:
self.cleaned_data[name] = value
if hasattr(field, 'postclean'):
value = field.postclean(self, cleaned_data)
if value:
cleaned_data[name] = value
return self.cleaned_data
self.service.update_cleaned_data(self, cleaned_data)
return cleaned_data

View File

@ -95,7 +95,7 @@ class DeleteService(tables.DeleteAction):
def allowed(self, request, service=None):
environment_id = self.table.kwargs.get('environment_id')
status = api.get_environment_data(request, environment_id, 'status')
status, = api.get_environment_data(request, environment_id, 'status')
return False if status == STATUS_ID_DEPLOYING else True
@ -132,7 +132,7 @@ class DeployEnvironment(tables.BatchAction):
def action(self, request, environment_id):
try:
api.environment_deploy(request, environment_id)
except:
except Exception:
msg = _('Unable to deploy. Try again later')
redirect = reverse('horizon:project:murano:index')
exceptions.handle(request, msg, redirect=redirect)

View File

@ -39,7 +39,7 @@ from muranoclient.common.exceptions import HTTPUnauthorized, \
CommunicationError, HTTPInternalServerError, HTTPForbidden, HTTPNotFound
from muranodashboard.panel.services import get_service_descriptions, \
get_service_name, get_service_client, get_service_field_descriptions
get_service_name, get_service_field_descriptions
LOG = logging.getLogger(__name__)
@ -54,20 +54,15 @@ class Wizard(ModalFormMixin, SessionWizardView):
args=(environment_id,))
step0_data = form_list[0].cleaned_data
slug = step0_data.get('service', '')
attributes = {'type': get_service_client(slug),
'slug': slug}
service_type = step0_data.get('service', '')
attributes = {'type': service_type}
for form in form_list[1:]:
form.extract_attributes(attributes)
# hack to fill units with data from nodes datagrid
# hack to destringify nodes into units
if 'nodes' in attributes:
units = []
for node in json.loads(attributes['nodes']):
units.append({'isMaster': node['is_primary'],
'isSync': node['is_sync']})
attributes['units'] = units
attributes['units'] = json.loads(attributes['nodes'])
del attributes['nodes']
try:
@ -80,10 +75,11 @@ class Wizard(ModalFormMixin, SessionWizardView):
except Exception:
redirect = reverse("horizon:project:murano:index")
exceptions.handle(self.request,
_('Sorry, you can\'t create service right now.',
redirect=redirect))
_('Sorry, you can\'t create service right now.'),
redirect=redirect)
else:
message = "The %s service successfully created." % slug
message = "The %s service successfully created." % \
get_service_name(service_type)
messages.success(self.request, message)
return HttpResponseRedirect(url)
@ -91,14 +87,6 @@ class Wizard(ModalFormMixin, SessionWizardView):
init_dict = {}
if step != 'service_choice':
init_dict['request'] = self.request
# hack to pass number of nodes from one form to another
if step == 'ms-sql-server-cluster-2':
form_id = 'ms-sql-server-cluster-1'
form_data = self.storage.data['step_data'].get(form_id, {})
instance_count = form_data.get(form_id + '-dcInstances')
if instance_count:
init_dict['instance_count'] = int(instance_count[0])
return self.initial_dict.get(step, init_dict)
def get_context_data(self, form, **kwargs):
@ -106,11 +94,11 @@ class Wizard(ModalFormMixin, SessionWizardView):
context['service_descriptions'] = get_service_descriptions()
if self.steps.index > 0:
data = self.get_cleaned_data_for_step('service_choice')
slug = data['service']
service_type = data['service']
context['field_descriptions'] = get_service_field_descriptions(
slug, self.steps.index - 1)
context.update({'type': get_service_client(slug),
'service_name': get_service_name(slug)})
service_type, self.steps.index - 1)
context.update({'type': service_type,
'service_name': get_service_name(service_type)})
return context

View File

@ -0,0 +1,122 @@
name: Active Directory
type: activeDirectory
description: >-
<strong> The Active Directory Service </strong>
includes one primary and optionally a few secondary
Domain Controllers, with DNS
unitTemplates:
- isMaster: true
recoveryPassword: {YAQL: $.serviceConfiguration.recoveryPassword}
- isMaster: false
recoveryPassword: {YAQL: $.serviceConfiguration.recoveryPassword}
forms:
- serviceConfiguration:
fields:
- name: configuration
type: string
hidden: true
initial: standalone
- name: name
type: string
label: Domain Name
description: >-
Enter a desired name for a new domain. This name should fit to
DNS Domain Name requirements: it should contain
only A-Z, a-z, 0-9, (.) and (-) and should not end with a dash.
DNS server will be automatically set up on each of the Domain
Controller instances. Note: Only first 15 characters or characters
before first period is used as NetBIOS name.
attributeNames: [name, domain]
minLength: 2
maxLength: 255
regexpValidator: '^[0-9A-Za-z](?!--)[0-9A-Za-z\-]{0,13}[0-9A-Za-z]\.[0-9A-Za-z][0-9A-Za-z\-]{0,61}[0-9A-Za-z]$'
errorMessages:
invalid: >-
Only letters, numbers and dashes in the middle are
allowed. Period characters are allowed only when they are
used to delimit the components of domain style
names. Single-level domain is not appropriate.
helpText: >-
Just letters, numbers and dashes are allowed.
A dot can be used to create subdomains
- name: dcInstances
type: instance
label: Instance Count
description: >-
You can create several Active Directory instances by setting
instance number larger than one. One primary Domain Controller
and a few secondary DCs will be created.
attributeNames: units
minValue: 1
maxValue: 100
initial: 1
helpText: Enter an integer value between 1 and 100
- name: adminAccountName
type: string
label: Account Name
initial: Administrator
regexpValidator: '^[-\w]+$'
errorMessages:
invalid: 'Just letters, numbers, underscores and hyphens are allowed.'
- name: adminPassword
type: password
label: Administrator password
descriptionTitle: Passwords
description: >-
Windows requires strong password for service administration.
Your password should have at least one letter in each
register, a number and a special character. Password length should be
a minimum of 7 characters.
Once you forget your password you won't be able to
operate the service until recovery password would be entered. So it's
better for Recovery and Administrator password to be different.
- name: recoveryPassword
type: password
label: Recovery password
attributeNames: false
- name: unitNamingPattern
type: string
label: Hostname template
description: >-
For your convenience all instance hostnames can be named
in the same way. Enter a name and use # character for incrementation.
For example, host# turns into host1, host2, etc. Please follow Windows
hostname restrictions.
required: false
regexpValidator: '^(([a-zA-Z0-9#][a-zA-Z0-9-#]*[a-zA-Z0-9#])\.)*([A-Za-z0-9#]|[A-Za-z0-9#][A-Za-z0-9-#]*[A-Za-z0-9#])$'
helpText: Optional field for a machine hostname template
validators:
# if unitNamingPattern is given and dcInstances > 1, then '#' should occur in unitNamingPattern
- expr: {YAQL: $.serviceConfiguration.dcInstances < 2 or not $.serviceConfiguration.unitNamingPattern.bool() or ('#' in $.serviceConfiguration.unitNamingPattern)}
message: Incrementation symbol "#" is required in the Hostname template
- instanceConfiguration:
fields:
- name: title
type: string
required: false
hidden: true
attributeNames: false
descriptionTitle: Instance Configuration
description: Specify some instance parameters on which service would be created.
- name: flavor
type: flavor
label: Instance flavor
description: >-
Select registered in Openstack flavor. Consider that service performance
depends on this parameter.
required: false
- name: osImage
type: image
label: Instance image
description: >-
Select valid image for a service. Image should already be prepared and
registered in glance.
- name: availabilityZone
type: azone
label: Availability zone
description: Select availability zone where service would be installed.
required: false

View File

@ -0,0 +1,100 @@
name: ASP.NET Application
type: aspNetApp
description: >-
<strong> The ASP.NET Application Service </strong> installs
custom application onto one IIS Web Server
unitTemplates:
- {}
forms:
- serviceConfiguration:
fields:
- name: title
type: string
required: false
hidden: true
attributeNames: false
description: ASP.NET application will be installed onto one IISWeb Server
- name: name
type: string
label: Service Name
description: >-
Enter a desired name for a service. Just A-Z, a-z, 0-9, dash and
underline are allowed.
minLength: 2
maxLength: 64
regexpValidator: '^[-\w]+$'
errorMessages:
invalid: Just letters, numbers, underscores and hyphens are allowed.
helpText: Just letters, numbers, underscores and hyphens are allowed.
- name: dcInstances
type: instance
hidden: true
attributeNames: units
initial: 1
- name: adminPassword
type: password
label: Administrator password
descriptionTitle: Passwords
description: >-
Windows requires strong password for service administration.
Your password should have at least one letter in each
register, a number and a special character. Password length should be
a minimum of 7 characters.
- name: domain
type: domain
label: Domain
required: false
description: >-
Service can be joined to the Active Directory domain. If you want to
create an AD domain create the AD Service first.
helpText: Optional field for a domain to which service can be joined
- name: repository
type: string
label: Git repository
description: >-
URL of a git repository with the application you want to deploy.
regexpValidator: '/(\w+://)(.+@)*([\w\d\.]+)(:[\d]+)?/*(.*)/i'
errorMessages:
invalid: Enter correct git repository url
helpText: Enter a valid git repository URL
- name: unitNamingPattern
type: string
label: Hostname template
description: >-
For your convenience all instance hostnames can be named
in the same way. Enter a name and use # character for incrementation.
For example, host# turns into host1, host2, etc. Please follow Windows
hostname restrictions.
required: false
regexpValidator: '^(([a-zA-Z0-9#][a-zA-Z0-9-#]*[a-zA-Z0-9#])\.)*([A-Za-z0-9#]|[A-Za-z0-9#][A-Za-z0-9-#]*[A-Za-z0-9#])$'
helpText: Optional field for a machine hostname template
- instanceConfiguration:
fields:
- name: title
type: string
required: false
hidden: true
attributeNames: false
descriptionTitle: Instance Configuration
description: Specify some instance parameters on which service would be created.
- name: flavor
type: flavor
label: Instance flavor
description: >-
Select registered in Openstack flavor. Consider that service performance
depends on this parameter.
required: false
- name: osImage
type: image
label: Instance image
description: >-
Select valid image for a service. Image should already be prepared and
registered in glance.
- name: availabilityZone
type: azone
label: Availability zone
description: Select availability zone where service would be installed.
required: false

View File

@ -0,0 +1,125 @@
name: ASP.NET Application Web Farm
type: aspNetAppFarm
description: >-
<strong> The ASP.NET Farm Service </strong> installs a custom application
on a load-balanced array of IIS servers
unitTemplates:
- {}
forms:
- serviceConfiguration:
fields:
- name: title
type: string
required: false
hidden: true
attributeNames: false
description: >-
The ASP.NET application will be installed on a number of IIS Web
Servers, and load balancing will be configured.
- name: name
type: string
label: Service Name
description: >-
Enter a desired name for a service. Just A-Z, a-z, 0-9, dash and
underline are allowed.
minLength: 2
maxLength: 64
regexpValidator: '^[-\w]+$'
errorMessages:
invalid: Just letters, numbers, underscores and hyphens are allowed.
helpText: Just letters, numbers, underscores and hyphens are allowed.
- name: username
type: string
hidden: true
initial: Administrator
attributeNames: credentials.username
- name: adminPassword
type: password
attributeNames: [adminPassword, credentials.password]
label: Administrator password
descriptionTitle: Passwords
description: >-
Windows requires strong password for service administration.
Your password should have at least one letter in each
register, a number and a special character. Password length should be
a minimum of 7 characters.
- name: domain
type: domain
label: Domain
required: false
description: >-
Service can be joined to the Active Directory domain. If you want to
create an AD domain create the AD Service first.
helpText: Optional field for a domain to which service can be joined
- name: repository
type: string
label: Git repository
description: >-
URL of a git repository with the application you want to deploy.
regexpValidator: '/(\w+://)(.+@)*([\w\d\.]+)(:[\d]+)?/*(.*)/i'
errorMessages:
invalid: Enter correct git repository url
helpText: Enter a valid git repository URL
- name: dcInstances
type: instance
label: Instance Count
description: >-
Several instances with ASP.NET application can be created at one time.
attributeNames: units
minValue: 2
maxValue: 100
initial: 2
helpText: Enter an integer value between 2 and 100
- name: loadBalancerPort
type: integer
label: Load Balancer port
minValue: 1
maxValue: 65536
initial: 80
description: Specify port number where Load Balancer will be running
helpText: Enter an integer value from 1 to 65536
- name: unitNamingPattern
type: string
label: Hostname template
description: >-
For your convenience all instance hostnames can be named
in the same way. Enter a name and use # character for incrementation.
For example, host# turns into host1, host2, etc. Please follow Windows
hostname restrictions.
required: false
regexpValidator: '^(([a-zA-Z0-9#][a-zA-Z0-9-#]*[a-zA-Z0-9#])\.)*([A-Za-z0-9#]|[A-Za-z0-9#][A-Za-z0-9-#]*[A-Za-z0-9#])$'
helpText: Optional field for a machine hostname template
validators:
# if unitNamingPattern is given and dcInstances > 1, then '#' should occur in unitNamingPattern
- expr: {YAQL: not $.serviceConfiguration.unitNamingPattern.bool() or ('#' in $.serviceConfiguration.unitNamingPattern)}
message: Incrementation symbol "#" is required in the Hostname template
- instanceConfiguration:
fields:
- name: title
type: string
required: false
hidden: true
attributeNames: false
descriptionTitle: Instance Configuration
description: Specify some instance parameters on which service would be created.
- name: flavor
type: flavor
label: Instance flavor
description: >-
Select registered in Openstack flavor. Consider that service performance
depends on this parameter.
required: false
- name: osImage
type: image
label: Instance image
description: >-
Select valid image for a service. Image should already be prepared and
registered in glance.
- name: availabilityZone
type: azone
label: Availability zone
description: Select availability zone where service would be installed.
required: false

View File

@ -0,0 +1,214 @@
name: MS SQL Server Cluster
type: msSqlClusterServer
description: >-
<strong> The MS SQL Failover Cluster </strong> installs
Microsoft SQL Failover Cluster Server
unitTemplates:
- isMaster: true
isSync: true
name: 'node#'
- isMaster: false
isSync: true
name: 'node#'
- isMaster: false
isSync: false
name: 'node#'
forms:
- serviceConfiguration:
fields:
- name: title
type: string
required: false
hidden: true
attributeNames: false
description: MS SQL Failover Cluster
# temporaryHack
widgetMedia:
js: [muranodashboard/js/mixed-mode.js, muranodashboard/js/external-ad.js]
- name: name
type: string
label: Service Name
description: >-
Enter a desired name for a service. Just A-Z, a-z, 0-9, dash and
underline are allowed.
minLength: 2
maxLength: 64
regexpValidator: '^[-\w]+$'
errorMessages:
invalid: Just letters, numbers, underscores and hyphens are allowed.
helpText: Just letters, numbers, underscores and hyphens are allowed.
- name: adminPassword
type: password
label: Administrator password
descriptionTitle: Passwords
description: >-
Windows requires strong password for service administration.
Your password should have at least one letter in each
register, a number and a special character. Password length should be
a minimum of 7 characters.
- name: externalAD
type: boolean
label: Active Directory is configured by the System Administrator
widgetAttrs: # temporary hack
class: external-ad
required: false
- name: domainAdminUserName
type: string
label: Active Directory User
required: {YAQL: $.serviceConfiguration.externalAD}
regexpValidator: '^[-\w]+$'
errorMessages:
invalid: 'Just letters, numbers, underscores and hyphens are allowed.'
- name: domainAdminPassword
type: password
label: Active Directory Password
required: {YAQL: $.serviceConfiguration.externalAD}
- name: domain
type: domain
label: Domain
required: {YAQL: not $.serviceConfiguration.externalAD}
description: >-
Service can be joined to the Active Directory domain. If you want to
create an AD domain create the AD Service first.
helpText: Optional field for a domain to which service can be joined
- name: mixedModeAuth
type: boolean
label: Mixed-mode Authentication
initial: true
required: false
description: >-
Mixed authentication mode allows the use of Windows
credentials but supplements them with local SQL Server user
accounts that the administrator may create and maintain within
SQL Server. If this mode is on SA password is required
- name: saPassword
type: password
label: SA Password
description: Set system administrator password for the MS SQL Server.
helpText: SQL server System Administrator account
required: {YAQL: $.serviceConfiguration.mixedModeAuth}
- clusterConfiguration:
fields:
- name: clusterIP
type: clusterip
label: Cluster Static IP
description: Specify a valid IPv4 fixed IP.
- name: clusterName
type: string
label: Cluster Name
helpText: Service name for new SQL Cluster service
description: >-
Specify a name of a cluster. Just A-Z, a-z, 0-9, dash and underline are allowed.
- name: agGroupName
type: string
label: Availability Group Name
helpText: Name of AG during SQL setup
description: >-
Specify a name of an AG. Just A-Z, a-z, 0-9, dash and underline are allowed.
- name: agListenerName
type: string
label: Availability Group Listener Name
helpText: FQDN name of a new DNS entry for AG Listener endpoint
regexpValidator: '^[-\w]+$'
errorMessages:
invalid: Just letters, numbers, underscores and hyphens are allowed.
description: >-
Specify a name of an AG Listener . Just A-Z, a-z, 0-9, dash and underline are allowed.
- name: agListenerIP
type: clusterip
label: Availability Group Listener IP
description: Specify a valid IPv4 fixed IP.
- name: sqlServiceUserName
type: string
label: SQL User Name
regexpValidator: '^[-\w]+$'
errorMessages:
invalid: Just letters, numbers, underscores and hyphens are allowed.
description: User name that will be created to manage cluster instances.
- name: sqlServicePassword
type: password
label: SQL User Password
description: User password that will be created to manage cluster instances.
- name: dcInstances
type: instance
label: Instance Count
minValue: 2
maxValue: 5
initial: 2
attributeNames: false
helpText: Enter an integer value between 2 and 5
description: Microsoft SQL Failover Cluster includes up to 5 instances.
- name: unitNamingPattern
type: string
label: Hostname template
description: >-
For your convenience all instance hostnames can be named
in the same way. Enter a name and use # character for incrementation.
For example, host# turns into host1, host2, etc. Please follow Windows
hostname restrictions.
required: false
regexpValidator: '^(([a-zA-Z0-9#][a-zA-Z0-9-#]*[a-zA-Z0-9#])\.)*([A-Za-z0-9#]|[A-Za-z0-9#][A-Za-z0-9-#]*[A-Za-z0-9#])$'
helpText: Optional field for a machine hostname template
validators:
# if unitNamingPattern is given and dcInstances > 1, then '#' should occur in unitNamingPattern
- expr: {YAQL: not $.clusterConfiguration.unitNamingPattern.bool() or '#' in $.clusterConfiguration.unitNamingPattern}
message: Incrementation symbol "#" is required in the Hostname template
# if IP is not valid on its own, it will be empty - the
# first check is needed to not emit comparison error when
# both IPs are not valid
- expr: {YAQL: not $.clusterConfiguration.clusterIP.bool() or $.clusterConfiguration.clusterIP != $.clusterConfiguration.agListenerIP}
message: Listener IP and Cluster Static IP should be different
- unitsConfiguration:
fields:
- name: nodes
type: datagrid
label: Nodes
initial: {YAQL: $.clusterConfiguration.dcInstances}
description: >-
Configure cluster instances. Cluster node quantity can be set
with 'Add' and 'Remove' buttons. Configure Sync mode by
enabling corresponding checkbox. All other nodes will be in
Async mode. Just 2 nodes are allowed to be Sync. Also one
Master node need to be selected. SQL Failover cluster has
limit of 5 instances.
- name: databases
type: databaselist
label: Database list
description: >-
Specify names for new databases which will be created as part
of service installation. Here should come comma-separated list
of database names, where each name has the following syntax:
first symbol should be latin letter or underscore; subsequent
symbols can be latin letter, numeric, underscore, at sign,
number sign or dollar sign.
helpText: Enter comma separated list of databases that will be created
- instanceConfiguration:
fields:
- name: title
type: string
required: false
hidden: true
attributeNames: false
descriptionTitle: Instance Configuration
description: Specify some instance parameters on which service would be created.
- name: flavor
type: flavor
label: Instance flavor
description: >-
Select registered in Openstack flavor. Consider that service performance
depends on this parameter.
required: false
- name: osImage
type: image
label: Instance image
description: >-
Select valid image for a service. Image should already be prepared and
registered in glance.
- name: availabilityZone
type: azone
label: Availability zone
description: Select availability zone where service would be installed.
required: false

View File

@ -0,0 +1,110 @@
name: MS SQL Server
type: msSqlServer
description: >-
<strong> The MS SQL Service </strong> installs an instance of
Microsoft SQL Server
unitTemplates:
- {}
forms:
- serviceConfiguration:
fields:
- name: title
type: string
required: false
hidden: true
attributeNames: false
description: MS SQL Server
# temporaryHack
widgetMedia:
js: [muranodashboard/js/mixed-mode.js]
- name: name
type: string
label: Service Name
description: >-
Enter a desired name for a service. Just A-Z, a-z, 0-9, dash and
underline are allowed.
minLength: 2
maxLength: 64
regexpValidator: '^[-\w]+$'
errorMessages:
invalid: Just letters, numbers, underscores and hyphens are allowed.
helpText: Just letters, numbers, underscores and hyphens are allowed.
- name: dcInstances
type: instance
hidden: true
attributeNames: units
initial: 1
- name: adminPassword
type: password
label: Administrator password
descriptionTitle: Passwords
description: >-
Windows requires strong password for service administration.
Your password should have at least one letter in each
register, a number and a special character. Password length should be
a minimum of 7 characters.
- name: domain
type: domain
label: Domain
required: false
description: >-
Service can be joined to the Active Directory domain. If you want to
create an AD domain create the AD Service first.
helpText: Optional field for a domain to which service can be joined
- name: mixedModeAuth
type: boolean
label: Mixed-mode Authentication
initial: true
required: false
description: >-
Mixed authentication mode allows the use of Windows
credentials but supplements them with local SQL Server user
accounts that the administrator may create and maintain within
SQL Server. If this mode is on SA password is required
- name: saPassword
type: password
label: SA Password
description: Set system administrator password for the MS SQL Server.
helpText: SQL server System Administrator account
required: {YAQL: $.serviceConfiguration.mixedModeAuth}
- name: unitNamingPattern
type: string
label: Hostname template
description: >-
For your convenience all instance hostnames can be named
in the same way. Enter a name and use # character for incrementation.
For example, host# turns into host1, host2, etc. Please follow Windows
hostname restrictions.
required: false
regexpValidator: '^(([a-zA-Z0-9#][a-zA-Z0-9-#]*[a-zA-Z0-9#])\.)*([A-Za-z0-9#]|[A-Za-z0-9#][A-Za-z0-9-#]*[A-Za-z0-9#])$'
helpText: Optional field for a machine hostname template
- instanceConfiguration:
fields:
- name: title
type: string
required: false
hidden: true
attributeNames: false
descriptionTitle: Instance Configuration
description: Specify some instance parameters on which service would be created.
- name: flavor
type: flavor
label: Instance flavor
description: >-
Select registered in Openstack flavor. Consider that service performance
depends on this parameter.
required: false
- name: osImage
type: image
label: Instance image
description: >-
Select valid image for a service. Image should already be prepared and
registered in glance.
- name: availabilityZone
type: azone
label: Availability zone
description: Select availability zone where service would be installed.
required: false

View File

@ -0,0 +1,91 @@
name: Internet Information Services
type: webServer
description: >-
<strong> The Internet Information Service </strong>
sets up an IIS server and joins it into an existing domain
unitTemplates:
- {}
forms:
- serviceConfiguration:
fields:
- name: title
type: string
required: false
hidden: true
attributeNames: false
description: Standalone IIS Server
- name: name
type: string
label: Service Name
description: >-
Enter a desired name for a service. Just A-Z, a-z, 0-9, dash and
underline are allowed.
minLength: 2
maxLength: 64
regexpValidator: '^[-\w]+$'
errorMessages:
invalid: Just letters, numbers, underscores and hyphens are allowed.
helpText: Just letters, numbers, underscores and hyphens are allowed.
- name: dcInstances
type: instance
hidden: true
attributeNames: units
initial: 1
- name: adminPassword
type: password
label: Administrator password
descriptionTitle: Passwords
description: >-
Windows requires strong password for service administration.
Your password should have at least one letter in each
register, a number and a special character. Password length should be
a minimum of 7 characters.
- name: domain
type: domain
label: Domain
required: false
description: >-
Service can be joined to the Active Directory domain. If you want to
create an AD domain create the AD Service first.
helpText: Optional field for a domain to which service can be joined
- name: unitNamingPattern
type: string
label: Hostname template
description: >-
For your convenience all instance hostnames can be named
in the same way. Enter a name and use # character for incrementation.
For example, host# turns into host1, host2, etc. Please follow Windows
hostname restrictions.
required: false
regexpValidator: '^(([a-zA-Z0-9#][a-zA-Z0-9-#]*[a-zA-Z0-9#])\.)*([A-Za-z0-9#]|[A-Za-z0-9#][A-Za-z0-9-#]*[A-Za-z0-9#])$'
helpText: Optional field for a machine hostname template
- instanceConfiguration:
fields:
- name: title
type: string
required: false
hidden: true
attributeNames: false
descriptionTitle: Instance Configuration
description: Specify some instance parameters on which service would be created.
- name: flavor
type: flavor
label: Instance flavor
description: >-
Select registered in Openstack flavor. Consider that service performance
depends on this parameter.
required: false
- name: osImage
type: image
label: Instance image
description: >-
Select valid image for a service. Image should already be prepared and
registered in glance.
- name: availabilityZone
type: azone
label: Availability zone
description: Select availability zone where service would be installed.
required: false

View File

@ -0,0 +1,112 @@
name: Internet Information Services Web Farm
type: webServerFarm
description: >-
<strong> The IIS Farm Service </strong> sets up a load-balanced set of IIS servers
unitTemplates:
- {}
forms:
- serviceConfiguration:
fields:
- name: title
type: string
hidden: true
required: false
attributeNames: false
description: A load-balanced array of IIS servers
- name: name
type: string
label: Service Name
description: >-
Enter a desired name for a service. Just A-Z, a-z, 0-9, dash and
underline are allowed.
minLength: 2
maxLength: 64
regexpValidator: '^[-\w]+$'
errorMessages:
invalid: Just letters, numbers, underscores and hyphens are allowed.
helpText: Just letters, numbers, underscores and hyphens are allowed.
- name: username
type: string
hidden: true
initial: Administrator
attributeNames: credentials.username
- name: adminPassword
type: password
attributeNames: [adminPassword, credentials.password]
label: Administrator password
descriptionTitle: Passwords
description: >-
Windows requires strong password for service administration.
Your password should have at least one letter in each
register, a number and a special character. Password length should be
a minimum of 7 characters.
- name: domain
type: domain
label: Domain
required: false
description: >-
Service can be joined to the Active Directory domain. If you want to
create an AD domain create the AD Service first.
helpText: Optional field for a domain to which service can be joined
- name: dcInstances
type: instance
minValue: 2
maxValue: 100
attributeNames: [units, instanceCount]
initial: 2
label: Instance Count
description: Several instances with IIS Service can be created at one time.
helpText: Enter an integer value between 2 and 100
- name: loadBalancerPort
type: integer
label: Load Balancer port
minValue: 1
maxValue: 65536
initial: 80
description: Specify port number where Load Balancer will be running
helpText: Enter an integer value from 1 to 65536
- name: unitNamingPattern
type: string
label: Hostname template
description: >-
For your convenience all instance hostnames can be named
in the same way. Enter a name and use # character for incrementation.
For example, host# turns into host1, host2, etc. Please follow Windows
hostname restrictions.
required: false
regexpValidator: '^(([a-zA-Z0-9#][a-zA-Z0-9-#]*[a-zA-Z0-9#])\.)*([A-Za-z0-9#]|[A-Za-z0-9#][A-Za-z0-9-#]*[A-Za-z0-9#])$'
helpText: Optional field for a machine hostname template
validators:
# if unitNamingPattern is given and dcInstances > 1, then '#' should occur in unitNamingPattern
- expr: {YAQL: not $.serviceConfiguration.unitNamingPattern.bool() or ('#' in $.serviceConfiguration.unitNamingPattern)}
message: Incrementation symbol "#" is required in the Hostname template
- instanceConfiguration:
fields:
- name: title
type: string
required: false
hidden: true
attributeNames: false
descriptionTitle: Instance Configuration
description: Specify some instance parameters on which service would be created.
- name: flavor
type: flavor
label: Instance flavor
description: >-
Select registered in Openstack flavor. Consider that service performance
depends on this parameter.
required: false
- name: osImage
type: image
label: Instance image
description: >-
Select valid image for a service. Image should already be prepared and
registered in glance.
- name: availabilityZone
type: azone
label: Availability zone
description: Select availability zone where service would be installed.
required: false

View File

@ -123,8 +123,8 @@ $(function() {
}
data.push({
name: trimLabel($(tr).children().eq(0).text()),
is_sync: getInputVal($(tr).children().eq(1)),
is_primary: getInputVal($(tr).children().eq(2))
isSync: getInputVal($(tr).children().eq(1)),
isMaster: getInputVal($(tr).children().eq(2))
})
});
$('input.gridfield-hidden').val(JSON.stringify(data));

View File

@ -33,8 +33,8 @@
{% endfor %}
{% else %}
<h3>{% trans "Description" %}:</h3>
{% for slug, description in service_descriptions %}
<p class="switchable" id="{{ slug }}">
{% for service_type, description in service_descriptions %}
<p class="switchable" id="{{ service_type }}">
{% autoescape off %} {% blocktrans %}
{{ description }}
{% endblocktrans %} {% endautoescape %}</p>

View File

@ -8,4 +8,5 @@ PyYAML
django-pipeline
django-floppyforms==1.1
ordereddict
yaql==0.2
http://tarballs.openstack.org/python-muranoclient/python-muranoclient-release-0.2.tar.gz#egg=muranoclient-dev

View File

@ -17,14 +17,13 @@
LOGLVL=1
SERVICE_CONTENT_DIRECTORY=`cd $(dirname "$0") && pwd`
DJBLETS_ZIP_URL=https://github.com/tsufiev/djblets/archive
PREREQ_PKGS="wget make git python-pip mysql-connector-python python-devel unzip libffi-devel"
PIPAPPS="pip python-pip pip-python"
PIPCMD=""
SERVICE_SRV_NAME="murano-dashboard"
GIT_CLONE_DIR=`echo $SERVICE_CONTENT_DIRECTORY | sed -e "s/$SERVICE_SRV_NAME//"`
HORIZON_CONFIGS="/opt/stack/horizon/openstack_dashboard/settings.py,/usr/share/openstack-dashboard/openstack_dashboard/settings.py"
NON_PIP_PACKAGES_BASE_URL=https://github.com
# Functions
# Loger function
log()
@ -171,6 +170,14 @@ rebuildstatic()
log "openstack-dashboard manage.py not found, exiting!!!"
exit 1
fi
_old_murano_static="$(dirname $horizon_manage)/openstack_dashboard/static/muranodashboard"
if [ -d "$_old_murano_static" ];then
log "Our staic for \"muranodashboard\" found under \"HORIZON\" STATIC, deleting \"$_old_murano_static\"..."
rm -rf $_old_murano_static
if [ $? -ne 0 ]; then
log "Can't delete \"$_old_murano_static\, WARNING!!!"
fi
fi
log "Rebuilding STATIC...."
python $horizon_manage collectstatic --noinput
if [ $? -ne 0 ]; then
@ -243,28 +250,35 @@ CLONE_FROM_GIT=$1
log "$PIPCMD install \"$TRBL_FILE\" FAILS, exiting!!!"
exit 1
fi
# DJBLETS INSTALL START
DJBLETS_SUFFIX=master.zip
DJBLETS_OUTARCH_FILENAME=djblets-$DJBLETS_SUFFIX
cd $SERVICE_CONTENT_DIRECTORY/dist && wget $DJBLETS_ZIP_URL/$DJBLETS_SUFFIX -O $DJBLETS_OUTARCH_FILENAME
if [ $? -ne 0 ];then
log " Can't download \"$DJBLETS_OUTARCH_FILENAME\", exiting!!!"
exit 1
fi
cd $SERVICE_CONTENT_DIRECTORY/dist && unzip $DJBLETS_OUTARCH_FILENAME
if [ $? -ne 0 ];then
log " Can't unzip \"$SERVICE_CONTENT_DIRECTORY/dist/$DJBLETS_OUTARCH_FILENAME\", exiting!!!"
exit 1
fi
cd $SERVICE_CONTENT_DIRECTORY/dist/djblets-master && python setup.py install
if [ $? -ne 0 ]; then
log "\"$SERVICE_CONTENT_DIRECTORY/dist/djblets-master/setup.py\" python setup FAILS, exiting!"
# NON PIP PACKAGES INSTALL START
for pkg in tsufiev.djblets; do
PACKAGE=${pkg##*.}
OWNER=${pkg%.*}
SUFFIX=master.zip
PACKAGE_OUTARCH_FILENAME=$PACKAGE-$SUFFIX
cd $SERVICE_CONTENT_DIRECTORY/dist && wget $NON_PIP_PACKAGES_BASE_URL/$OWNER/$PACKAGE/archive/$SUFFIX -O $PACKAGE_OUTARCH_FILENAME
if [ $? -ne 0 ];then
log " Can't download \"$PACKAGE_OUTARCH_FILENAME\", exiting!!!"
exit 1
fi
cd $SERVICE_CONTENT_DIRECTORY/dist && unzip $PACKAGE_OUTARCH_FILENAME
if [ $? -ne 0 ];then
log " Can't unzip \"$SERVICE_CONTENT_DIRECTORY/dist/$PACKAGE_OUTARCH_FILENAME\", exiting!!!"
exit 1
fi
cd $SERVICE_CONTENT_DIRECTORY/dist/$PACKAGE-${SUFFIX%.*} && python setup.py install
if [ $? -ne 0 ]; then
log "\"$SERVICE_CONTENT_DIRECTORY/dist/$PACKAGE-${SUFFIX%.*}/setup.py\" python setup FAILS, exiting!"
exit 1
fi
# DJBLETS INSTALL END
fi
done
# NON PIP PACKAGES INSTALL END
else
log "$MRN_CND_SPY not found!"
fi
# add apache to autostart
chkconfig --add httpd
chkconfig httpd on
}
# uninstall

View File

@ -21,7 +21,7 @@ PREREQ_PKGS="wget make git python-pip python-dev python-mysqldb libxml2-dev libx
SERVICE_SRV_NAME="murano-dashboard"
GIT_CLONE_DIR=`echo $SERVICE_CONTENT_DIRECTORY | sed -e "s/$SERVICE_SRV_NAME//"`
HORIZON_CONFIGS="/opt/stack/horizon/openstack_dashboard/settings.py,/usr/share/openstack-dashboard/openstack_dashboard/settings.py"
DJBLETS_ZIP_URL=https://github.com/tsufiev/djblets/archive
NON_PIP_PACKAGES_BASE_URL=https://github.com
# Functions
# Logger function
@ -187,26 +187,30 @@ CLONE_FROM_GIT=$1
log "pip install \"$TRBL_FILE\" FAILS, exiting!!!"
exit 1
fi
# DJBLETS INSTALL START
DJBLETS_SUFFIX=master.zip
DJBLETS_OUTARCH_FILENAME=djblets-$DJBLETS_SUFFIX
cd $SERVICE_CONTENT_DIRECTORY/dist && wget $DJBLETS_ZIP_URL/$DJBLETS_SUFFIX -O $DJBLETS_OUTARCH_FILENAME
if [ $? -ne 0 ];then
log " Can't download \"$DJBLETS_OUTARCH_FILENAME\", exiting!!!"
exit 1
fi
cd $SERVICE_CONTENT_DIRECTORY/dist && unzip $DJBLETS_OUTARCH_FILENAME
if [ $? -ne 0 ];then
log " Can't unzip \"$SERVICE_CONTENT_DIRECTORY/dist/$DJBLETS_OUTARCH_FILENAME\", exiting!!!"
exit 1
fi
cd $SERVICE_CONTENT_DIRECTORY/dist/djblets-master && python setup.py install
if [ $? -ne 0 ]; then
log "\"$SERVICE_CONTENT_DIRECTORY/dist/djblets-master/setup.py\" python setup FAILS, exiting!"
exit 1
# NON PIP PACKAGES INSTALL START
for pkg in tsufiev.djblets; do
PACKAGE=${pkg##*.}
OWNER=${pkg%.*}
SUFFIX=master.zip
PACKAGE_OUTARCH_FILENAME=$PACKAGE-$SUFFIX
cd $SERVICE_CONTENT_DIRECTORY/dist && wget $NON_PIP_PACKAGES_BASE_URL/$OWNER/$PACKAGE/archive/$SUFFIX -O $PACKAGE_OUTARCH_FILENAME
if [ $? -ne 0 ];then
log " Can't download \"$PACKAGE_OUTARCH_FILENAME\", exiting!!!"
exit 1
fi
# DJBLETS INSTALL END
else
cd $SERVICE_CONTENT_DIRECTORY/dist && unzip $PACKAGE_OUTARCH_FILENAME
if [ $? -ne 0 ];then
log " Can't unzip \"$SERVICE_CONTENT_DIRECTORY/dist/$PACKAGE_OUTARCH_FILENAME\", exiting!!!"
exit 1
fi
cd $SERVICE_CONTENT_DIRECTORY/dist/$PACKAGE-${SUFFIX%.*} && python setup.py install
if [ $? -ne 0 ]; then
log "\"$SERVICE_CONTENT_DIRECTORY/dist/$PACKAGE-${SUFFIX%.*}/setup.py\" python setup FAILS, exiting!"
exit 1
fi
done
# NON PIP PACKAGES INSTALL END
else
log "$MRN_CND_SPY not found!"
fi
}
@ -233,7 +237,7 @@ preinst()
dpkg -s $_PKG > /dev/null 2>&1
if [ $? -ne 0 ]; then
log "Package \"$_PKG\" is not installed."
fi
fi
}
# rebuild static
@ -244,6 +248,14 @@ rebuildstatic()
log "openstack-dashboard manage.py not found, exiting!!!"
exit 1
fi
_old_murano_static="$(dirname $horizon_manage)/openstack_dashboard/static/muranodashboard"
if [ -d "$_old_murano_static" ];then
log "Our staic for \"muranodashboard\" found under \"HORIZON\" STATIC, deleting \"$_old_murano_static\"..."
rm -rf $_old_murano_static
if [ $? -ne 0 ]; then
log "Can't delete \"$_old_murano_static\, WARNING!!!"
fi
fi
log "Rebuilding STATIC...."
python $horizon_manage collectstatic --noinput
if [ $? -ne 0 ]; then