mirror of
https://github.com/dkmstr/openuds.git
synced 2024-12-22 13:34:04 +03:00
* Added "Service Pool Group" full logic, so new we can add services pools to groups, so we can visualzize them grouped. (already needs the visualizarion part)
* Finished basic taglist edition (fixed a bug & added the possibility of insert serveral comma separated values at once')
This commit is contained in:
parent
e1b8c43cca
commit
a08fe53383
@ -52,7 +52,7 @@ class Authenticators(ModelHandler):
|
|||||||
# Custom get method "search" that requires authenticator id
|
# Custom get method "search" that requires authenticator id
|
||||||
custom_methods = [('search', True)]
|
custom_methods = [('search', True)]
|
||||||
detail = {'users': Users, 'groups': Groups}
|
detail = {'users': Users, 'groups': Groups}
|
||||||
save_fields = ['name', 'comments', 'priority', 'small_name']
|
save_fields = ['name', 'comments', 'tags', 'priority', 'small_name']
|
||||||
|
|
||||||
table_title = _('Current authenticators')
|
table_title = _('Current authenticators')
|
||||||
table_fields = [
|
table_fields = [
|
||||||
@ -80,7 +80,7 @@ class Authenticators(ModelHandler):
|
|||||||
|
|
||||||
def getGui(self, type_):
|
def getGui(self, type_):
|
||||||
try:
|
try:
|
||||||
return self.addDefaultFields(auths.factory().lookup(type_).guiDescription(), ['name', 'comments', 'priority', 'small_name'])
|
return self.addDefaultFields(auths.factory().lookup(type_).guiDescription(), ['name', 'comments', 'tags', 'priority', 'small_name'])
|
||||||
except:
|
except:
|
||||||
raise NotFound('type not found')
|
raise NotFound('type not found')
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ class Calendars(ModelHandler):
|
|||||||
model = Calendar
|
model = Calendar
|
||||||
detail = {'rules': CalendarRules}
|
detail = {'rules': CalendarRules}
|
||||||
|
|
||||||
save_fields = ['name', 'comments']
|
save_fields = ['name', 'comments', 'tags']
|
||||||
|
|
||||||
table_title = _('Calendars')
|
table_title = _('Calendars')
|
||||||
table_fields = [
|
table_fields = [
|
||||||
@ -73,4 +73,4 @@ class Calendars(ModelHandler):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def getGui(self, type_):
|
def getGui(self, type_):
|
||||||
return self.addDefaultFields([], ['name', 'comments'])
|
return self.addDefaultFields([], ['name', 'comments', 'tags'])
|
||||||
|
@ -78,7 +78,7 @@ class Images(ModelHandler):
|
|||||||
'value': '',
|
'value': '',
|
||||||
'label': ugettext('Image'),
|
'label': ugettext('Image'),
|
||||||
'tooltip': ugettext('Image object'),
|
'tooltip': ugettext('Image object'),
|
||||||
'type': gui.InputField.IMAGE_TYPE,
|
'type': gui.InputField.IMAGECHOICE_TYPE,
|
||||||
'order': 100, # At end
|
'order': 100, # At end
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -53,7 +53,7 @@ class Networks(ModelHandler):
|
|||||||
Implements specific handling for network related requests using GUI
|
Implements specific handling for network related requests using GUI
|
||||||
'''
|
'''
|
||||||
model = Network
|
model = Network
|
||||||
save_fields = ['name', 'net_string']
|
save_fields = ['name', 'net_string', 'tags']
|
||||||
|
|
||||||
table_title = _('Current Networks')
|
table_title = _('Current Networks')
|
||||||
table_fields = [
|
table_fields = [
|
||||||
@ -74,7 +74,7 @@ class Networks(ModelHandler):
|
|||||||
|
|
||||||
def getGui(self, type_):
|
def getGui(self, type_):
|
||||||
return self.addField(
|
return self.addField(
|
||||||
self.addDefaultFields([], ['name']), {
|
self.addDefaultFields([], ['name', 'tags']), {
|
||||||
'name': 'net_string',
|
'name': 'net_string',
|
||||||
'value': '',
|
'value': '',
|
||||||
'label': ugettext('Network range'),
|
'label': ugettext('Network range'),
|
||||||
|
@ -50,7 +50,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class OsManagers(ModelHandler):
|
class OsManagers(ModelHandler):
|
||||||
model = OSManager
|
model = OSManager
|
||||||
save_fields = ['name', 'comments']
|
save_fields = ['name', 'comments', 'tags']
|
||||||
|
|
||||||
table_title = _('Current OS Managers')
|
table_title = _('Current OS Managers')
|
||||||
table_fields = [
|
table_fields = [
|
||||||
@ -89,6 +89,6 @@ class OsManagers(ModelHandler):
|
|||||||
# Gui related
|
# Gui related
|
||||||
def getGui(self, type_):
|
def getGui(self, type_):
|
||||||
try:
|
try:
|
||||||
return self.addDefaultFields(factory().lookup(type_).guiDescription(), ['name', 'comments'])
|
return self.addDefaultFields(factory().lookup(type_).guiDescription(), ['name', 'comments', 'tags'])
|
||||||
except:
|
except:
|
||||||
raise NotFound('type not found')
|
raise NotFound('type not found')
|
||||||
|
@ -56,7 +56,7 @@ class Providers(ModelHandler):
|
|||||||
detail = {'services': DetailServices}
|
detail = {'services': DetailServices}
|
||||||
custom_methods = [('allservices', False), ('service', False), ('maintenance', True)]
|
custom_methods = [('allservices', False), ('service', False), ('maintenance', True)]
|
||||||
|
|
||||||
save_fields = ['name', 'comments']
|
save_fields = ['name', 'comments', 'tags']
|
||||||
|
|
||||||
table_title = _('Service providers')
|
table_title = _('Service providers')
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ from __future__ import unicode_literals
|
|||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
|
||||||
from uds.models import Service, UserService
|
from uds.models import Service, UserService, Tag
|
||||||
|
|
||||||
from uds.core.services import Service as coreService
|
from uds.core.services import Service as coreService
|
||||||
from uds.core.util import log
|
from uds.core.util import log
|
||||||
@ -130,7 +130,9 @@ class Services(DetailHandler): # pylint: disable=too-many-public-methods
|
|||||||
# Extract item db fields
|
# Extract item db fields
|
||||||
# We need this fields for all
|
# We need this fields for all
|
||||||
logger.debug('Saving service {0} / {1}'.format(parent, item))
|
logger.debug('Saving service {0} / {1}'.format(parent, item))
|
||||||
fields = self.readFieldsFromParams(['name', 'comments', 'data_type'])
|
fields = self.readFieldsFromParams(['name', 'comments', 'data_type', 'tags'])
|
||||||
|
tags = fields['tags']
|
||||||
|
del fields['tags']
|
||||||
service = None
|
service = None
|
||||||
try:
|
try:
|
||||||
if item is None: # Create new
|
if item is None: # Create new
|
||||||
@ -139,6 +141,8 @@ class Services(DetailHandler): # pylint: disable=too-many-public-methods
|
|||||||
service = parent.services.get(uuid=processUuid(item))
|
service = parent.services.get(uuid=processUuid(item))
|
||||||
service.__dict__.update(fields)
|
service.__dict__.update(fields)
|
||||||
|
|
||||||
|
service.tags = [Tag.objects.get_or_create(tag=val)[0] for val in tags]
|
||||||
|
|
||||||
service.data = service.getInstance(self._params).serialize()
|
service.data = service.getInstance(self._params).serialize()
|
||||||
service.save()
|
service.save()
|
||||||
except Service.DoesNotExist:
|
except Service.DoesNotExist:
|
||||||
@ -209,7 +213,7 @@ class Services(DetailHandler): # pylint: disable=too-many-public-methods
|
|||||||
parentInstance = parent.getInstance()
|
parentInstance = parent.getInstance()
|
||||||
serviceType = parentInstance.getServiceByType(forType)
|
serviceType = parentInstance.getServiceByType(forType)
|
||||||
service = serviceType(Environment.getTempEnv(), parentInstance) # Instantiate it so it has the opportunity to alter gui description based on parent
|
service = serviceType(Environment.getTempEnv(), parentInstance) # Instantiate it so it has the opportunity to alter gui description based on parent
|
||||||
return self.addDefaultFields(service.guiDescription(service), ['name', 'comments'])
|
return self.addDefaultFields(service.guiDescription(service), ['name', 'comments', 'tags'])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception('getGui')
|
logger.exception('getGui')
|
||||||
raise ResponseError(unicode(e))
|
raise ResponseError(unicode(e))
|
||||||
|
105
server/src/uds/REST/methods/services_pool_groups.py
Normal file
105
server/src/uds/REST/methods/services_pool_groups.py
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright (c) 2014 Virtual Cable S.L.
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
# are permitted provided that the following conditions are met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer.
|
||||||
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer in the documentation
|
||||||
|
# and/or other materials provided with the distribution.
|
||||||
|
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||||
|
# may be used to endorse or promote products derived from this software
|
||||||
|
# without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
'''
|
||||||
|
@itemor: Adolfo Gómez, dkmaster at dkmon dot com
|
||||||
|
'''
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext_lazy as _, ugettext
|
||||||
|
from uds.models import ServicesPoolGroup, Image
|
||||||
|
from uds.core.util.model import processUuid
|
||||||
|
from uds.core.ui.UserInterface import gui
|
||||||
|
from uds.core.ui.images import DEFAULT_THUMB_BASE64
|
||||||
|
|
||||||
|
from uds.REST.model import ModelHandler
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Enclosed methods under /item path
|
||||||
|
|
||||||
|
|
||||||
|
class ServicesPoolGroups(ModelHandler):
|
||||||
|
'''
|
||||||
|
Handles the gallery REST interface
|
||||||
|
'''
|
||||||
|
needs_admin = True
|
||||||
|
|
||||||
|
path = 'gallery'
|
||||||
|
model = ServicesPoolGroup
|
||||||
|
save_fields = ['name', 'comments', 'image_id']
|
||||||
|
|
||||||
|
table_title = _('Services Pool Groups')
|
||||||
|
table_fields = [
|
||||||
|
{'name': {'title': _('Name')}},
|
||||||
|
{'thumb': {'title': _('Image'), 'visible': True, 'type': 'image'}},
|
||||||
|
]
|
||||||
|
|
||||||
|
def beforeSave(self, fields):
|
||||||
|
imgId = fields['image_id']
|
||||||
|
fields['image_id'] = None
|
||||||
|
logger.debug('Image id: {}'.format(imgId))
|
||||||
|
try:
|
||||||
|
if imgId != '-1':
|
||||||
|
image = Image.objects.get(uuid=processUuid(imgId))
|
||||||
|
fields['image_id'] = image.id
|
||||||
|
except Exception:
|
||||||
|
logger.exception('At image recovering')
|
||||||
|
|
||||||
|
# Gui related
|
||||||
|
def getGui(self, type_):
|
||||||
|
g = self.addDefaultFields([], ['name', 'comments'])
|
||||||
|
|
||||||
|
for f in [{
|
||||||
|
'name': 'image_id',
|
||||||
|
'values': [gui.choiceImage(-1, '--------', DEFAULT_THUMB_BASE64)] + gui.sortedChoices([gui.choiceImage(v.uuid, v.name, v.thumb64) for v in Image.objects.all()]),
|
||||||
|
'label': ugettext('Associated Image'),
|
||||||
|
'tooltip': ugettext('Image assocciated with this service'),
|
||||||
|
'type': gui.InputField.IMAGECHOICE_TYPE,
|
||||||
|
'order': 102,
|
||||||
|
}]:
|
||||||
|
self.addField(g, f)
|
||||||
|
|
||||||
|
return g
|
||||||
|
|
||||||
|
def item_as_dict(self, item):
|
||||||
|
return {
|
||||||
|
'id': item.uuid,
|
||||||
|
'name': item.name,
|
||||||
|
'image_id': item.image.uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
def item_as_dict_overview(self, item):
|
||||||
|
return {
|
||||||
|
'id': item.uuid,
|
||||||
|
'name': item.name,
|
||||||
|
'thumb': item.image.thumb64,
|
||||||
|
}
|
@ -33,7 +33,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||||
from uds.models import DeployedService, OSManager, Service, Image
|
from uds.models import DeployedService, OSManager, Service, Image, ServicesPoolGroup
|
||||||
from uds.core.ui.images import DEFAULT_THUMB_BASE64
|
from uds.core.ui.images import DEFAULT_THUMB_BASE64
|
||||||
from uds.core.util.State import State
|
from uds.core.util.State import State
|
||||||
from uds.core.util.model import processUuid
|
from uds.core.util.model import processUuid
|
||||||
@ -64,7 +64,7 @@ class ServicesPools(ModelHandler):
|
|||||||
'changelog': Changelog
|
'changelog': Changelog
|
||||||
}
|
}
|
||||||
|
|
||||||
save_fields = ['name', 'comments', 'service_id', 'osmanager_id', 'image_id', 'initial_srvs', 'cache_l1_srvs', 'cache_l2_srvs', 'max_srvs', 'show_transports']
|
save_fields = ['name', 'comments', 'tags', 'service_id', 'osmanager_id', 'image_id', 'servicesPoolGroup_id', 'initial_srvs', 'cache_l1_srvs', 'cache_l2_srvs', 'max_srvs', 'show_transports']
|
||||||
remove_fields = ['osmanager_id', 'service_id']
|
remove_fields = ['osmanager_id', 'service_id']
|
||||||
|
|
||||||
table_title = _('Service Pools')
|
table_title = _('Service Pools')
|
||||||
@ -73,7 +73,7 @@ class ServicesPools(ModelHandler):
|
|||||||
{'parent': {'title': _('Parent Service')}},
|
{'parent': {'title': _('Parent Service')}},
|
||||||
{'state': {'title': _('status'), 'type': 'dict', 'dict': State.dictionary()}},
|
{'state': {'title': _('status'), 'type': 'dict', 'dict': State.dictionary()}},
|
||||||
{'show_transports': {'title': _('Shows transports'), 'type': 'callback'}},
|
{'show_transports': {'title': _('Shows transports'), 'type': 'callback'}},
|
||||||
{'comments': {'title': _('Comments')}},
|
{'servicesPoolGroup': {'title': _('Services Pool Group')}},
|
||||||
]
|
]
|
||||||
# Field from where to get "class" and prefix for that class, so this will generate "row-state-A, row-state-X, ....
|
# Field from where to get "class" and prefix for that class, so this will generate "row-state-A, row-state-X, ....
|
||||||
table_row_style = {'field': 'state', 'prefix': 'row-state-'}
|
table_row_style = {'field': 'state', 'prefix': 'row-state-'}
|
||||||
@ -93,6 +93,8 @@ class ServicesPools(ModelHandler):
|
|||||||
'service_id': item.service.uuid,
|
'service_id': item.service.uuid,
|
||||||
'provider_id': item.service.provider.uuid,
|
'provider_id': item.service.provider.uuid,
|
||||||
'image_id': item.image.uuid if item.image is not None else None,
|
'image_id': item.image.uuid if item.image is not None else None,
|
||||||
|
'servicesPoolGroup_id': item.servicesPoolGroup.uuid if item.servicesPoolGroup is not None else None,
|
||||||
|
'servicesPoolGroup': item.servicesPoolGroup.name if item.servicesPoolGroup is not None else _('Default'),
|
||||||
'initial_srvs': item.initial_srvs,
|
'initial_srvs': item.initial_srvs,
|
||||||
'cache_l1_srvs': item.cache_l1_srvs,
|
'cache_l1_srvs': item.cache_l1_srvs,
|
||||||
'cache_l2_srvs': item.cache_l2_srvs,
|
'cache_l2_srvs': item.cache_l2_srvs,
|
||||||
@ -116,7 +118,7 @@ class ServicesPools(ModelHandler):
|
|||||||
if Service.objects.count() < 1:
|
if Service.objects.count() < 1:
|
||||||
raise ResponseError(ugettext('Create at least a service before creating a new service pool'))
|
raise ResponseError(ugettext('Create at least a service before creating a new service pool'))
|
||||||
|
|
||||||
g = self.addDefaultFields([], ['name', 'comments'])
|
g = self.addDefaultFields([], ['name', 'comments', 'tags'])
|
||||||
|
|
||||||
for f in [{
|
for f in [{
|
||||||
'name': 'service_id',
|
'name': 'service_id',
|
||||||
@ -141,6 +143,13 @@ class ServicesPools(ModelHandler):
|
|||||||
'tooltip': ugettext('Image assocciated with this service'),
|
'tooltip': ugettext('Image assocciated with this service'),
|
||||||
'type': gui.InputField.IMAGECHOICE_TYPE,
|
'type': gui.InputField.IMAGECHOICE_TYPE,
|
||||||
'order': 102,
|
'order': 102,
|
||||||
|
}, {
|
||||||
|
'name': 'servicesPoolGroup_id',
|
||||||
|
'values': [gui.choiceImage(-1, _('Default'), DEFAULT_THUMB_BASE64)] + gui.sortedChoices([gui.choiceImage(v.uuid, v.name, v.image.thumb64) for v in ServicesPoolGroup.objects.all()]),
|
||||||
|
'label': ugettext('Pool group'),
|
||||||
|
'tooltip': ugettext('Pool group for this pool (for pool clasify on display)'),
|
||||||
|
'type': gui.InputField.IMAGECHOICE_TYPE,
|
||||||
|
'order': 103,
|
||||||
}, {
|
}, {
|
||||||
'name': 'initial_srvs',
|
'name': 'initial_srvs',
|
||||||
'value': '0',
|
'value': '0',
|
||||||
@ -148,7 +157,7 @@ class ServicesPools(ModelHandler):
|
|||||||
'label': ugettext('Initial available services'),
|
'label': ugettext('Initial available services'),
|
||||||
'tooltip': ugettext('Services created initially for this service pool'),
|
'tooltip': ugettext('Services created initially for this service pool'),
|
||||||
'type': gui.InputField.NUMERIC_TYPE,
|
'type': gui.InputField.NUMERIC_TYPE,
|
||||||
'order': 103,
|
'order': 110,
|
||||||
}, {
|
}, {
|
||||||
'name': 'cache_l1_srvs',
|
'name': 'cache_l1_srvs',
|
||||||
'value': '0',
|
'value': '0',
|
||||||
@ -156,7 +165,7 @@ class ServicesPools(ModelHandler):
|
|||||||
'label': ugettext('Services to keep in cache'),
|
'label': ugettext('Services to keep in cache'),
|
||||||
'tooltip': ugettext('Services kept in cache for improved user service assignation'),
|
'tooltip': ugettext('Services kept in cache for improved user service assignation'),
|
||||||
'type': gui.InputField.NUMERIC_TYPE,
|
'type': gui.InputField.NUMERIC_TYPE,
|
||||||
'order': 104,
|
'order': 111,
|
||||||
}, {
|
}, {
|
||||||
'name': 'cache_l2_srvs',
|
'name': 'cache_l2_srvs',
|
||||||
'value': '0',
|
'value': '0',
|
||||||
@ -164,7 +173,7 @@ class ServicesPools(ModelHandler):
|
|||||||
'label': ugettext('Services to keep in L2 cache'),
|
'label': ugettext('Services to keep in L2 cache'),
|
||||||
'tooltip': ugettext('Services kept in cache of level2 for improved service generation'),
|
'tooltip': ugettext('Services kept in cache of level2 for improved service generation'),
|
||||||
'type': gui.InputField.NUMERIC_TYPE,
|
'type': gui.InputField.NUMERIC_TYPE,
|
||||||
'order': 105,
|
'order': 112,
|
||||||
}, {
|
}, {
|
||||||
'name': 'max_srvs',
|
'name': 'max_srvs',
|
||||||
'value': '0',
|
'value': '0',
|
||||||
@ -172,14 +181,14 @@ class ServicesPools(ModelHandler):
|
|||||||
'label': ugettext('Maximum number of services to provide'),
|
'label': ugettext('Maximum number of services to provide'),
|
||||||
'tooltip': ugettext('Maximum number of service (assigned and L1 cache) that can be created for this service'),
|
'tooltip': ugettext('Maximum number of service (assigned and L1 cache) that can be created for this service'),
|
||||||
'type': gui.InputField.NUMERIC_TYPE,
|
'type': gui.InputField.NUMERIC_TYPE,
|
||||||
'order': 106,
|
'order': 113,
|
||||||
}, {
|
}, {
|
||||||
'name': 'show_transports',
|
'name': 'show_transports',
|
||||||
'value': True,
|
'value': True,
|
||||||
'label': ugettext('Show transports'),
|
'label': ugettext('Show transports'),
|
||||||
'tooltip': ugettext('If active, alternative transports for user will be shown'),
|
'tooltip': ugettext('If active, alternative transports for user will be shown'),
|
||||||
'type': gui.InputField.CHECKBOX_TYPE,
|
'type': gui.InputField.CHECKBOX_TYPE,
|
||||||
'order': 107,
|
'order': 120,
|
||||||
}]:
|
}]:
|
||||||
self.addField(g, f)
|
self.addField(g, f)
|
||||||
|
|
||||||
@ -226,6 +235,17 @@ class ServicesPools(ModelHandler):
|
|||||||
except Exception:
|
except Exception:
|
||||||
logger.exception('At image recovering')
|
logger.exception('At image recovering')
|
||||||
|
|
||||||
|
# Servicepool Group
|
||||||
|
spgrpId = fields['servicesPoolGroup_id']
|
||||||
|
fields['servicesPoolGroup_id'] = None
|
||||||
|
logger.debug('servicesPoolGroup_id: {}'.format(spgrpId))
|
||||||
|
try:
|
||||||
|
if imgId != '-1':
|
||||||
|
spgrp = ServicesPoolGroup.objects.get(uuid=processUuid(spgrpId))
|
||||||
|
fields['servicesPoolGroup_id'] = spgrp.id
|
||||||
|
except Exception:
|
||||||
|
logger.exception('At service pool group recovering')
|
||||||
|
|
||||||
except (RequestError, ResponseError):
|
except (RequestError, ResponseError):
|
||||||
raise
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -48,7 +48,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class Transports(ModelHandler):
|
class Transports(ModelHandler):
|
||||||
model = Transport
|
model = Transport
|
||||||
save_fields = ['name', 'comments', 'priority', 'nets_positive']
|
save_fields = ['name', 'comments', 'tags', ' priority', 'nets_positive']
|
||||||
|
|
||||||
table_title = _('Current Transports')
|
table_title = _('Current Transports')
|
||||||
table_fields = [
|
table_fields = [
|
||||||
@ -64,7 +64,7 @@ class Transports(ModelHandler):
|
|||||||
def getGui(self, type_):
|
def getGui(self, type_):
|
||||||
try:
|
try:
|
||||||
return self.addField(
|
return self.addField(
|
||||||
self.addField(self.addDefaultFields(factory().lookup(type_).guiDescription(), ['name', 'comments', 'priority']), {
|
self.addField(self.addDefaultFields(factory().lookup(type_).guiDescription(), ['name', 'comments', 'tags', 'priority']), {
|
||||||
'name': 'nets_positive',
|
'name': 'nets_positive',
|
||||||
'value': True,
|
'value': True,
|
||||||
'label': ugettext('Network access'),
|
'label': ugettext('Network access'),
|
||||||
|
@ -42,6 +42,8 @@ from uds.REST.handlers import Handler, HandlerError
|
|||||||
from uds.core.util import log
|
from uds.core.util import log
|
||||||
from uds.core.util import permissions
|
from uds.core.util import permissions
|
||||||
from uds.core.util.model import processUuid
|
from uds.core.util.model import processUuid
|
||||||
|
from uds.models import Tag
|
||||||
|
|
||||||
|
|
||||||
import fnmatch
|
import fnmatch
|
||||||
import re
|
import re
|
||||||
@ -52,7 +54,7 @@ import logging
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
__updated__ = '2016-02-10'
|
__updated__ = '2016-02-12'
|
||||||
|
|
||||||
|
|
||||||
# a few constants
|
# a few constants
|
||||||
@ -841,6 +843,13 @@ class ModelHandler(BaseModelHandler):
|
|||||||
args = self.readFieldsFromParams(self.save_fields)
|
args = self.readFieldsFromParams(self.save_fields)
|
||||||
logger.debug('Args: {}'.format(args))
|
logger.debug('Args: {}'.format(args))
|
||||||
self.beforeSave(args)
|
self.beforeSave(args)
|
||||||
|
# If tags is in save fields, treat it "specially"
|
||||||
|
if 'tags' in self.save_fields:
|
||||||
|
tags = args['tags']
|
||||||
|
del args['tags']
|
||||||
|
else:
|
||||||
|
tags = None
|
||||||
|
|
||||||
deleteOnError = False
|
deleteOnError = False
|
||||||
if len(self._args) == 0: # create new
|
if len(self._args) == 0: # create new
|
||||||
item = self.model.objects.create(**args)
|
item = self.model.objects.create(**args)
|
||||||
@ -852,6 +861,12 @@ class ModelHandler(BaseModelHandler):
|
|||||||
if v in args:
|
if v in args:
|
||||||
del args[v]
|
del args[v]
|
||||||
item.__dict__.update(args) # Update fields from args
|
item.__dict__.update(args) # Update fields from args
|
||||||
|
|
||||||
|
# Now if tags, update them
|
||||||
|
if tags is not None:
|
||||||
|
logger.debug('Updating tags: {}'.format(tags))
|
||||||
|
item.tags = [ Tag.objects.get_or_create(tag=val)[0] for val in tags]
|
||||||
|
|
||||||
except self.model.DoesNotExist:
|
except self.model.DoesNotExist:
|
||||||
raise NotFound('Item not found')
|
raise NotFound('Item not found')
|
||||||
except IntegrityError: # Duplicate key probably
|
except IntegrityError: # Duplicate key probably
|
||||||
|
34
server/src/uds/migrations/0020_auto_20160212_0330.py
Normal file
34
server/src/uds/migrations/0020_auto_20160212_0330.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models, migrations
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('uds', '0019_auto_20160210_0144'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ServicesPoolGroup',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||||
|
('uuid', models.CharField(default=None, max_length=50, unique=True, null=True)),
|
||||||
|
('name', models.CharField(default='', max_length=128)),
|
||||||
|
('comments', models.CharField(default='', max_length=256)),
|
||||||
|
('image', models.ForeignKey(related_name='servicesPoolsGrou', on_delete=django.db.models.deletion.SET_NULL, blank=True, to='uds.Image', null=True)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
'db_table': 'uds__pools_groups',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='deployedservice',
|
||||||
|
name='servicesPoolGroup',
|
||||||
|
field=models.ForeignKey(related_name='servicesPools', on_delete=django.db.models.deletion.SET_NULL, blank=True, to='uds.ServicesPoolGroup', null=True),
|
||||||
|
),
|
||||||
|
]
|
@ -49,6 +49,7 @@ from uds.models.Service import Service
|
|||||||
from uds.models.Transport import Transport
|
from uds.models.Transport import Transport
|
||||||
from uds.models.Group import Group
|
from uds.models.Group import Group
|
||||||
from uds.models.Image import Image
|
from uds.models.Image import Image
|
||||||
|
from uds.models.ServicesPoolGroup import ServicesPoolGroup
|
||||||
|
|
||||||
from uds.models.Util import NEVER
|
from uds.models.Util import NEVER
|
||||||
from uds.models.Util import getSqlDatetime
|
from uds.models.Util import getSqlDatetime
|
||||||
@ -56,7 +57,7 @@ from uds.models.Util import getSqlDatetime
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
__updated__ = '2016-02-10'
|
__updated__ = '2016-02-12'
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -78,12 +79,16 @@ class DeployedService(UUIDModel, TaggingMixin):
|
|||||||
state_date = models.DateTimeField(default=NEVER)
|
state_date = models.DateTimeField(default=NEVER)
|
||||||
show_transports = models.BooleanField(default=True)
|
show_transports = models.BooleanField(default=True)
|
||||||
image = models.ForeignKey(Image, null=True, blank=True, related_name='deployedServices', on_delete=models.SET_NULL)
|
image = models.ForeignKey(Image, null=True, blank=True, related_name='deployedServices', on_delete=models.SET_NULL)
|
||||||
|
|
||||||
|
servicesPoolGroup = models.ForeignKey(ServicesPoolGroup, null=True, blank=True, related_name='servicesPools', on_delete=models.SET_NULL)
|
||||||
|
|
||||||
initial_srvs = models.PositiveIntegerField(default=0)
|
initial_srvs = models.PositiveIntegerField(default=0)
|
||||||
cache_l1_srvs = models.PositiveIntegerField(default=0)
|
cache_l1_srvs = models.PositiveIntegerField(default=0)
|
||||||
cache_l2_srvs = models.PositiveIntegerField(default=0)
|
cache_l2_srvs = models.PositiveIntegerField(default=0)
|
||||||
max_srvs = models.PositiveIntegerField(default=0)
|
max_srvs = models.PositiveIntegerField(default=0)
|
||||||
current_pub_revision = models.PositiveIntegerField(default=1)
|
current_pub_revision = models.PositiveIntegerField(default=1)
|
||||||
|
|
||||||
|
|
||||||
# Meta service related
|
# Meta service related
|
||||||
meta_pools = models.ManyToManyField('self', symmetrical=False)
|
meta_pools = models.ManyToManyField('self', symmetrical=False)
|
||||||
|
|
||||||
|
70
server/src/uds/models/ServicesPoolGroup.py
Normal file
70
server/src/uds/models/ServicesPoolGroup.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright (c) 2012 Virtual Cable S.L.
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
# are permitted provided that the following conditions are met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer.
|
||||||
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer in the documentation
|
||||||
|
# and/or other materials provided with the distribution.
|
||||||
|
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||||
|
# may be used to endorse or promote products derived from this software
|
||||||
|
# without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
'''
|
||||||
|
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
|
||||||
|
'''
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.db.models import signals
|
||||||
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
|
|
||||||
|
from uds.models.UUIDModel import UUIDModel
|
||||||
|
from uds.models.Image import Image
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
__updated__ = '2016-02-12'
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
|
class ServicesPoolGroup(UUIDModel):
|
||||||
|
'''
|
||||||
|
A deployed service is the Service produced element that is assigned finally to an user (i.e. a Virtual Machine, etc..)
|
||||||
|
'''
|
||||||
|
# pylint: disable=model-missing-unicode
|
||||||
|
name = models.CharField(max_length=128, default='')
|
||||||
|
comments = models.CharField(max_length=256, default='')
|
||||||
|
image = models.ForeignKey(Image, null=True, blank=True, related_name='servicesPoolsGrou', on_delete=models.SET_NULL)
|
||||||
|
|
||||||
|
class Meta(UUIDModel.Meta):
|
||||||
|
'''
|
||||||
|
Meta class to declare the name of the table at database
|
||||||
|
'''
|
||||||
|
db_table = 'uds__pools_groups'
|
||||||
|
app_label = 'uds'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return u"Service Pool group {0}({1})".format(self.name, self.comments)
|
||||||
|
|
@ -65,6 +65,7 @@ from .Group import Group
|
|||||||
# Provisioned services
|
# Provisioned services
|
||||||
from .ServicesPool import DeployedService # Old name, will continue here for a while already
|
from .ServicesPool import DeployedService # Old name, will continue here for a while already
|
||||||
from .ServicesPool import ServicePool # New name
|
from .ServicesPool import ServicePool # New name
|
||||||
|
from .ServicesPoolGroup import ServicesPoolGroup
|
||||||
from .ServicesPoolPublication import DeployedServicePublication
|
from .ServicesPoolPublication import DeployedServicePublication
|
||||||
from .UserService import UserService
|
from .UserService import UserService
|
||||||
from .UserServiceProperty import UserServiceProperty
|
from .UserServiceProperty import UserServiceProperty
|
||||||
@ -101,7 +102,7 @@ from .CalendarRule import CalendarRule
|
|||||||
# Tagging
|
# Tagging
|
||||||
from .Tag import Tag
|
from .Tag import Tag
|
||||||
|
|
||||||
__updated__ = '2016-02-10'
|
__updated__ = '2016-02-12'
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -425,6 +425,7 @@ api.networks = new BasicModelRest("networks")
|
|||||||
api.servicesPools = new BasicModelRest("servicespools")
|
api.servicesPools = new BasicModelRest("servicespools")
|
||||||
api.configuration = new BasicModelRest("config")
|
api.configuration = new BasicModelRest("config")
|
||||||
api.gallery = new BasicModelRest("gallery/images")
|
api.gallery = new BasicModelRest("gallery/images")
|
||||||
|
api.sPoolGroups = new BasicModelRest("gallery/servicespoolgroups")
|
||||||
api.system = new BasicModelRest("system")
|
api.system = new BasicModelRest("system")
|
||||||
api.reports = new BasicModelRest("reports") # Not fully used, but basic usage is common
|
api.reports = new BasicModelRest("reports") # Not fully used, but basic usage is common
|
||||||
api.calendars = new BasicModelRest("calendars")
|
api.calendars = new BasicModelRest("calendars")
|
||||||
|
28
server/src/uds/static/adm/js/gui-d-servicespoolsgroup.coffee
Normal file
28
server/src/uds/static/adm/js/gui-d-servicespoolsgroup.coffee
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
gui.sPoolGroups = new GuiElement(api.sPoolGroups, "spal")
|
||||||
|
gui.sPoolGroups.link = ->
|
||||||
|
gui.doLog 'Executing pool groups'
|
||||||
|
"use strict"
|
||||||
|
|
||||||
|
if api.config.admin is false
|
||||||
|
return
|
||||||
|
|
||||||
|
api.templates.get "services_pool_groups", (tmpl) ->
|
||||||
|
gui.clearWorkspace()
|
||||||
|
gui.appendToWorkspace api.templates.evaluate(tmpl,
|
||||||
|
sPoolGroups: "sPoolGroups-placeholder"
|
||||||
|
)
|
||||||
|
gui.sPoolGroups.table
|
||||||
|
icon: 'sPoolGroups'
|
||||||
|
container: "sPoolGroups-placeholder"
|
||||||
|
rowSelect: "single"
|
||||||
|
buttons: [
|
||||||
|
"new"
|
||||||
|
"edit"
|
||||||
|
"delete"
|
||||||
|
]
|
||||||
|
onNew: gui.methods.typedNew(gui.sPoolGroups, gettext("New services Services Pool Group"), gettext("Services Services Pool Group creation error"))
|
||||||
|
onEdit: gui.methods.typedEdit(gui.sPoolGroups, gettext("Edit services Services Pool Group"), gettext("Services Provider saving error"))
|
||||||
|
onDelete: gui.methods.del(gui.sPoolGroups, gettext("Delete Services Pool Group"), gettext("Services Pool Group error"))
|
||||||
|
return
|
||||||
|
|
||||||
|
return
|
@ -240,6 +240,11 @@
|
|||||||
exec: gui.calendars.link
|
exec: gui.calendars.link
|
||||||
cleanup: true
|
cleanup: true
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
id: "lnk-spoolsgroup"
|
||||||
|
exec: gui.sPoolGroups.link
|
||||||
|
cleanup: true
|
||||||
|
}
|
||||||
]
|
]
|
||||||
$.each sidebarLinks, (index, value) ->
|
$.each sidebarLinks, (index, value) ->
|
||||||
gui.doLog "Adding " + value.id
|
gui.doLog "Adding " + value.id
|
||||||
|
@ -121,6 +121,7 @@
|
|||||||
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-d-servicespools.coffee"></script>
|
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-d-servicespools.coffee"></script>
|
||||||
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-d-config.coffee"></script>
|
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-d-config.coffee"></script>
|
||||||
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-d-gallery.coffee"></script>
|
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-d-gallery.coffee"></script>
|
||||||
|
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-d-servicespoolsgroup.coffee"></script>
|
||||||
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-d-reports.coffee"></script>
|
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-d-reports.coffee"></script>
|
||||||
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-d-calendar.coffee"></script>
|
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-d-calendar.coffee"></script>
|
||||||
<!-- base64 encoding -->
|
<!-- base64 encoding -->
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><img class="icon" src="{{ STATIC_URL }}/adm/img/icons/tools.png"/> <span class="menu-lnk">{% trans 'Tools' %} <b class="caret"></b></span></a>
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><img class="icon" src="{{ STATIC_URL }}/adm/img/icons/tools.png"/> <span class="menu-lnk">{% trans 'Tools' %} <b class="caret"></b></span></a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a class="lnk-gallery" href="#"><img class="icon" src="{{ STATIC_URL }}/adm/img/icons/gallery.png"/> <span class="menu-lnk">{% trans 'Gallery' %}</span></a></li>
|
<li><a class="lnk-gallery" href="#"><img class="icon" src="{{ STATIC_URL }}/adm/img/icons/gallery.png"/> <span class="menu-lnk">{% trans 'Gallery' %}</span></a></li>
|
||||||
|
<li><a class="lnk-spoolsgroup" href="#"><img class="icon" src="{{ STATIC_URL }}/adm/img/icons/groups.png"/> <span class="menu-lnk">{% trans 'Services Pool Group' %}</span></a></li>
|
||||||
<li><a class="lnk-reports" href=""><img class="icon" src="{{ STATIC_URL }}/adm/img/icons/reports.png"/> <span class="menu-lnk">{% trans 'Reports' %}</a></span></li>
|
<li><a class="lnk-reports" href=""><img class="icon" src="{{ STATIC_URL }}/adm/img/icons/reports.png"/> <span class="menu-lnk">{% trans 'Reports' %}</a></span></li>
|
||||||
<li><a class="lnk-configuration" href="#"><img class="icon" src="{{ STATIC_URL }}/adm/img/icons/configuration.png"/> <span class="menu-lnk">{% trans 'Configuration' %}</span></a></li>
|
<li><a class="lnk-configuration" href="#"><img class="icon" src="{{ STATIC_URL }}/adm/img/icons/configuration.png"/> <span class="menu-lnk">{% trans 'Configuration' %}</span></a></li>
|
||||||
<li><a class="lnk-clear_cache" href="#"><img class="icon" src="{{ STATIC_URL }}/adm/img/icons/flush-cache.png"/> <span class="menu-lnk">{% trans 'Flush cache' %}</span></a></li>
|
<li><a class="lnk-clear_cache" href="#"><img class="icon" src="{{ STATIC_URL }}/adm/img/icons/flush-cache.png"/> <span class="menu-lnk">{% trans 'Flush cache' %}</span></a></li>
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<span class="label label-success tag tagadder" id="adder{{ id }}">{% endverbatim %}{% trans 'Add Tag...' %}{% verbatim %}</span>
|
<span class="label label-success tag tagadder" id="adder{{ id }}">{% endverbatim %}{% trans 'Add Tag...' %}{% verbatim %}</span>
|
||||||
{{/ unless }}
|
{{/ unless }}
|
||||||
|
|
||||||
<input id="h{{ id }}" type="hidden" data-uds="list" name="{{ name }}" value="{{ value }}" class="{{ css }}" />
|
<input id="h{{ id }}" type="hidden" data-uds="commaList" name="{{ name }}" value="{{ value }}" class="{{ css }}" />
|
||||||
|
|
||||||
{% endverbatim %}
|
{% endverbatim %}
|
||||||
{% comment %}<script>/*This is a little trick to make IDE recognize sintax hightlight, will not be show on render :-)*/{% endcomment %}
|
{% comment %}<script>/*This is a little trick to make IDE recognize sintax hightlight, will not be show on render :-)*/{% endcomment %}
|
||||||
@ -44,7 +44,9 @@
|
|||||||
// Tagadder never gets destroyed, so we only set the event once
|
// Tagadder never gets destroyed, so we only set the event once
|
||||||
$('#adder{{ id }}').on('click', function(){
|
$('#adder{{ id }}').on('click', function(){
|
||||||
var text = prompt('Enter new tag');
|
var text = prompt('Enter new tag');
|
||||||
addTag(text);
|
$.each( text.split(','), function (idx, value) {
|
||||||
|
addTag(value);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
{{/ unless }}
|
{{/ unless }}
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
{% load i18n html5 static %}
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<h1>{% trans 'UDS Services Pool Groups' %}</h1>
|
||||||
|
<!-- <ol class="breadcrumb">
|
||||||
|
<li><a class="lnk-dashboard" href="#">Dashboard</a></li>
|
||||||
|
<li>{% trans 'Gallery' %}</li>
|
||||||
|
</ol> -->
|
||||||
|
</div>
|
||||||
|
</div><!-- /.row -->
|
||||||
|
{% if messages %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-offset-2 col-md-8">
|
||||||
|
{% for message in messages %}
|
||||||
|
<div class="alert alert-danger alert-dismissable">
|
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||||
|
{{ message }}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% verbatim %}
|
||||||
|
<div class="row">
|
||||||
|
<div id="{{ sPoolGroups }}" class="col-xs-12"></div>
|
||||||
|
</div>
|
||||||
|
{% endverbatim %}
|
Loading…
Reference in New Issue
Block a user