mirror of
https://github.com/dkmstr/openuds.git
synced 2025-10-15 19:35:45 +03:00
Compare commits
93 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a0797ac07f | ||
|
bbcc06f503 | ||
|
6fbf419064 | ||
|
cabc906758 | ||
|
0b7535e76e | ||
|
f536112312 | ||
|
ab2ca6d527 | ||
|
442b78ef2e | ||
|
9cfc348ee6 | ||
|
0f1d1af736 | ||
|
6830d8db4e | ||
|
d8db218c6d | ||
|
81dd4e3b8c | ||
|
9519c7c95b | ||
|
afa7cb8f39 | ||
|
dc640fd400 | ||
|
76c822b015 | ||
|
c7513328eb | ||
|
e5f0fcce69 | ||
|
004acbab9a | ||
|
c566ec47a2 | ||
|
3e1a31954a | ||
|
ec5473d99f | ||
|
4cf62de3fd | ||
|
a43d6af237 | ||
|
f41f431f38 | ||
|
c5af877fce | ||
|
88dd6e7494 | ||
|
e69800ccd2 | ||
|
b1ba02c1f3 | ||
|
cb13ac1617 | ||
|
de051d99ac | ||
|
fa1e6a69e8 | ||
|
417ddc6d8d | ||
|
238f08b7dd | ||
|
739ee728d3 | ||
|
c5b9233d4a | ||
|
22a8933e82 | ||
|
a185baccdd | ||
|
2521f41e76 | ||
|
815de57b86 | ||
|
428ddd493c | ||
|
4de93ddf1f | ||
|
e6b75e3807 | ||
|
e835c018b4 | ||
|
098620cd05 | ||
|
214f9c397b | ||
|
47ae741952 | ||
|
907fad4a55 | ||
|
7c3e289a6b | ||
|
1f5a647ff3 | ||
|
0eebe6a0a5 | ||
|
8aa1607fee | ||
|
0aa1311836 | ||
|
6d49ae6667 | ||
|
e60a4bc8fa | ||
|
6e5b73c3b7 | ||
|
ae4330fc9d | ||
|
b7147661e7 | ||
|
3c07d2f667 | ||
|
f54d87a295 | ||
|
13f97248f6 | ||
|
786945fcbf | ||
|
1070b716a2 | ||
|
ad1bf0fe0b | ||
|
df70dd4fd8 | ||
|
3038c545ce | ||
|
8d3b28e3cb | ||
|
c431e39d4a | ||
|
770f2eef09 | ||
|
e702ff6bca | ||
|
c6cc1f2b43 | ||
|
06f5184ddd | ||
|
d5688116e3 | ||
|
1ec1104356 | ||
|
234eb98f0c | ||
|
fa7ab534a3 | ||
|
ee661461a5 | ||
|
aecf6854a4 | ||
|
34cd90f9a1 | ||
|
201fb8ff9b | ||
|
d071f86c31 | ||
|
1c133eacd0 | ||
|
89ed019788 | ||
|
9930f4323c | ||
|
115beefa8a | ||
|
611f3590e6 | ||
|
59635839cf | ||
|
4853729d7c | ||
|
a8b3b80f75 | ||
|
e5a6916109 | ||
|
6a3093c49d | ||
|
498154ffb1 |
6
actors/.gitignore
vendored
6
actors/.gitignore
vendored
@@ -3,8 +3,8 @@ bin
|
||||
udsactor*.deb
|
||||
udsactor*.build
|
||||
udsactor*.changes
|
||||
/udsactor_1.7.0.dsc
|
||||
/udsactor_1.7.0.tar.xz
|
||||
/udsactor_*.dsc
|
||||
/udsactor_*.tar.xz
|
||||
/udsactor_*_amd64.buildinfo
|
||||
/udsactor*.rpm
|
||||
/udsactor_2.1.0_amd64.buildinfo
|
||||
linux/debian/files
|
||||
|
@@ -69,12 +69,14 @@ install-udsactor:
|
||||
cp scripts/udsactor $(BINDIR)
|
||||
cp scripts/UDSActorConfig-pkexec $(SBINDIR)
|
||||
cp scripts/UDSActorTool-startup $(BINDIR)
|
||||
cp scripts/udsvapp ${BINDIR}
|
||||
|
||||
# Policy to run as administrator
|
||||
cp policy/org.openuds.pkexec.UDSActorConfig.policy $(POLKITDIR)
|
||||
|
||||
# Fix permissions
|
||||
chmod 755 $(BINDIR)/udsactor
|
||||
chmod 755 $(BINDIR)/udsvapp
|
||||
chmod 755 $(BINDIR)/UDSActorTool-startup
|
||||
chmod 755 $(SBINDIR)/UDSActorConfig-pkexec
|
||||
chmod 755 $(LIBDIR)/UDSActorConfig.py
|
||||
@@ -93,4 +95,4 @@ endif
|
||||
uninstall:
|
||||
rm -rf $(LIBDIR)
|
||||
# rm -f $(BINDIR)/udsactor
|
||||
rm -rf $(CFGDIR)
|
||||
rm -rf $(CFGDIR)
|
||||
|
@@ -1,3 +1,9 @@
|
||||
udsactor (2.2.0) stable; urgency=medium
|
||||
|
||||
* Upgraded to 2.2.0 release
|
||||
|
||||
-- Adolfo Gómez García <agomez@virtualcable.es> Thu, 19 Oct 2017 16:44:12 +0200
|
||||
|
||||
udsactor (2.1.0) stable; urgency=medium
|
||||
|
||||
* Fixes for 2.1.0 release
|
||||
|
@@ -3,4 +3,4 @@
|
||||
FOLDER=/usr/share/UDSActor
|
||||
|
||||
cd $FOLDER
|
||||
python -m udsactor.linux.UDSActorService $@
|
||||
python2.7 -m udsactor.linux.UDSActorService $@
|
||||
|
5
actors/linux/scripts/udsvapp
Executable file
5
actors/linux/scripts/udsvapp
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
/usr/bin/udsactor login "$USER"
|
||||
$@
|
||||
/usr/bin/udsactor logout "$USER"
|
@@ -60,6 +60,7 @@ This package provides the required components to allow this machine to work on a
|
||||
/etc/init.d/udsactor
|
||||
/usr/bin/UDSActorTool-startup
|
||||
/usr/bin/udsactor
|
||||
/usr/bin/udsvapp
|
||||
/usr/bin/UDSActorTool
|
||||
/usr/sbin/UDSActorConfig
|
||||
/usr/sbin/UDSActorConfig-pkexec
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python2.7
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 Virtual Cable S.L.
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python2.7
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 Virtual Cable S.L.
|
||||
@@ -295,7 +295,7 @@ class UDSSystemTray(QtGui.QSystemTrayIcon):
|
||||
def quit(self, logoff=False):
|
||||
global doLogoff
|
||||
logger.debug('Quit invoked')
|
||||
if self.stopped is False:
|
||||
if not self.stopped:
|
||||
self.stopped = True
|
||||
try:
|
||||
# If we close Client, send Logoff to Broker
|
||||
@@ -327,7 +327,7 @@ if __name__ == '__main__':
|
||||
sys.exit(1)
|
||||
|
||||
# Sets a default idle duration, but will not be used unless idle is notified from server
|
||||
operations.initIdleDuration(3600 * 10)
|
||||
operations.initIdleDuration(3600 * 16)
|
||||
|
||||
trayIcon.show()
|
||||
|
||||
|
@@ -34,14 +34,14 @@ from __future__ import unicode_literals
|
||||
# On centos, old six release does not includes byte2int, nor six.PY2
|
||||
import six
|
||||
|
||||
VERSION = '2.1.0'
|
||||
VERSION = '2.2.0'
|
||||
|
||||
__title__ = 'udsactor'
|
||||
__version__ = VERSION
|
||||
__build__ = 0x010750
|
||||
__build__ = 0x010755
|
||||
__author__ = 'Adolfo Gómez <dkmaster@dkmon.com>'
|
||||
__license__ = "BSD 3-clause"
|
||||
__copyright__ = "Copyright 2014-2016 VirtualCable S.L.U."
|
||||
__copyright__ = "Copyright 2014-2017 VirtualCable S.L.U."
|
||||
|
||||
|
||||
if not hasattr(six, 'byte2int'):
|
||||
|
@@ -44,6 +44,10 @@ from .utils import exceptionToMessage
|
||||
import socket
|
||||
import time
|
||||
import random
|
||||
import os
|
||||
import subprocess
|
||||
import shlex
|
||||
import stat
|
||||
|
||||
IPC_PORT = 39188
|
||||
|
||||
@@ -86,23 +90,25 @@ class CommonService(object):
|
||||
def reboot(self):
|
||||
self.rebootRequested = True
|
||||
|
||||
def execute(self, cmd, section):
|
||||
import os
|
||||
import subprocess
|
||||
import stat
|
||||
def execute(self, cmdLine, section):
|
||||
cmd = shlex.split(cmdLine, posix=False)
|
||||
|
||||
if os.path.isfile(cmd):
|
||||
if (os.stat(cmd).st_mode & stat.S_IXUSR) != 0:
|
||||
subprocess.call([cmd, ])
|
||||
if os.path.isfile(cmd[0]):
|
||||
if (os.stat(cmd[0]).st_mode & stat.S_IXUSR) != 0:
|
||||
try:
|
||||
res = subprocess.check_call(cmd)
|
||||
except Exception as e:
|
||||
logger.error('Got exception executing: {} - {}'.format(cmdLine, e))
|
||||
return False
|
||||
logger.info('Result of executing cmd was {}'.format(res))
|
||||
return True
|
||||
else:
|
||||
logger.info('{} file exists but it it is not executable (needs execution permission by admin/root)'.format(section))
|
||||
logger.error('{} file exists but it it is not executable (needs execution permission by admin/root)'.format(section))
|
||||
else:
|
||||
logger.info('{} file not found & not executed'.format(section))
|
||||
logger.error('{} file not found & not executed'.format(section))
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def setReady(self):
|
||||
self.api.setReady([(v.mac, v.ip) for v in operations.getNetworkInfo()])
|
||||
|
||||
@@ -158,6 +164,7 @@ class CommonService(object):
|
||||
# Now try to run the "runonce" element
|
||||
runOnce = store.runApplication()
|
||||
if runOnce is not None:
|
||||
logger.info('Executing runOnce app: {}'.format(runOnce))
|
||||
if self.execute(runOnce, 'RunOnce') is True:
|
||||
# operations.reboot()
|
||||
return False
|
||||
|
@@ -1,3 +1,9 @@
|
||||
udsclient (2.2.0) stable; urgency=medium
|
||||
|
||||
* Updated release
|
||||
|
||||
-- Adolfo Gómez García <agomez@virtualcable.es> Thu, 27 Aug 2017 14:18:18 +0200
|
||||
|
||||
udsclient (2.1.0) stable; urgency=medium
|
||||
|
||||
* Updated release
|
||||
|
@@ -10,6 +10,6 @@ Package: udsclient
|
||||
Section: admin
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
Depends: python-paramiko (>=0.8.2), python-qt4 (>=4.9), python-six(>=1.1), python (>=2.7), rdesktop | freerdp-x11, desktop-file-utils, ${misc:Depends}
|
||||
Depends: python-paramiko (>=0.8.2), python-qt4 (>=4.9), python-six(>=1.1), python (>=2.7), freerdp-x11 | rdesktop, desktop-file-utils, ${misc:Depends}
|
||||
Description: Client connector for Universal Desktop Services (UDS) Broker
|
||||
This package provides the required components to allow this machine to connect to services provided by UDS Broker.
|
||||
|
@@ -1,2 +1,2 @@
|
||||
udsclient_2.1.0_all.deb admin optional
|
||||
udsclient_2.1.0_amd64.buildinfo admin optional
|
||||
udsclient_2.2.0_all.deb admin optional
|
||||
udsclient_2.2.0_amd64.buildinfo admin optional
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python2.7
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2017 Virtual Cable S.L.
|
||||
# Copyright (c) 2014-2017 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@@ -43,9 +43,15 @@ from uds import tools
|
||||
from uds import VERSION
|
||||
|
||||
import webbrowser
|
||||
import json
|
||||
import sys
|
||||
import six
|
||||
|
||||
from UDSWindow import Ui_MainWindow
|
||||
|
||||
# Server before this version uses "unsigned" scripts
|
||||
OLD_METHOD_VERSION = '2.4.0'
|
||||
|
||||
class RetryException(Exception):
|
||||
pass
|
||||
|
||||
@@ -57,6 +63,7 @@ class UDSClient(QtGui.QMainWindow):
|
||||
animTimer = None
|
||||
anim = 0
|
||||
animInverted = False
|
||||
serverVersion = 'X.Y.Z' # Will be overwriten on getVersion
|
||||
|
||||
def __init__(self):
|
||||
QtGui.QMainWindow.__init__(self)
|
||||
@@ -146,6 +153,8 @@ class UDSClient(QtGui.QMainWindow):
|
||||
webbrowser.open(data['result']['downloadUrl'])
|
||||
self.closeWindow()
|
||||
return
|
||||
|
||||
self.serverVersion = data['result']['requiredVersion']
|
||||
self.getTransportData()
|
||||
|
||||
except RetryException as e:
|
||||
@@ -172,7 +181,20 @@ class UDSClient(QtGui.QMainWindow):
|
||||
try:
|
||||
self.processError(data)
|
||||
|
||||
script = data['result'].decode('base64').decode('bz2')
|
||||
params = None
|
||||
|
||||
if self.serverVersion <= OLD_METHOD_VERSION:
|
||||
script = data['result'].decode('base64').decode('bz2')
|
||||
else:
|
||||
res = data['result']
|
||||
# We have three elements on result:
|
||||
# * Script
|
||||
# * Signature
|
||||
# * Script data
|
||||
# We test that the Script has correct signature, and them execute it with the parameters
|
||||
script, signature, params = res['script'].decode('base64').decode('bz2'), res['signature'], json.loads(res['params'].decode('base64').decode('bz2'))
|
||||
if tools.verifySignature(script, signature) is False:
|
||||
raise Exception('Invalid UDS code signature. Please, report to administrator')
|
||||
|
||||
self.stopAnim()
|
||||
|
||||
@@ -182,7 +204,14 @@ class UDSClient(QtGui.QMainWindow):
|
||||
QtCore.QTimer.singleShot(3000, self.endScript)
|
||||
self.hide()
|
||||
|
||||
six.exec_(script, globals(), {'parent': self})
|
||||
# if self.serverVersion <= OLD_METHOD_VERSION:
|
||||
# errorString = '<p>The server <b>{}</b> runs an old version of UDS:</p>'.format(host)
|
||||
# errorString += '<p>To avoid security issues, you must approve old UDS Version access.</p>'
|
||||
|
||||
# if QtGui.QMessageBox.warning(None, 'ACCESS Warning', errorString, QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) == QtGui.QMessageBox.No:
|
||||
# raise Exception('Server not approved. Access denied.')
|
||||
|
||||
six.exec_(script, globals(), {'parent': self, 'sp': params})
|
||||
|
||||
except RetryException as e:
|
||||
self.ui.info.setText(six.text_type(e) + ', retrying access...')
|
||||
@@ -224,7 +253,7 @@ def done(data):
|
||||
QtGui.QMessageBox.critical(None, 'Notice', six.text_type(data.data), QtGui.QMessageBox.Ok)
|
||||
sys.exit(0)
|
||||
|
||||
# Ask user to aprobe endpoint
|
||||
# Ask user to approve endpoint
|
||||
def approveHost(host, parentWindow=None):
|
||||
settings = QtCore.QSettings()
|
||||
settings.beginGroup('endpoints')
|
||||
@@ -241,6 +270,7 @@ def approveHost(host, parentWindow=None):
|
||||
settings.endGroup()
|
||||
return approved
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logger.debug('Initializing connector')
|
||||
# Initialize app
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 Virtual Cable S.L.
|
||||
# Copyright (c) 2014-2017 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@@ -34,11 +34,11 @@ from __future__ import unicode_literals
|
||||
# On centos, old six release does not includes byte2int, nor six.PY2
|
||||
import six
|
||||
|
||||
VERSION = '2.1.0'
|
||||
VERSION = '2.2.0'
|
||||
|
||||
__title__ = 'udclient'
|
||||
__version__ = VERSION
|
||||
__build__ = 0x010750
|
||||
__build__ = 0x010760
|
||||
__author__ = 'Adolfo Gómez'
|
||||
__license__ = "BSD 3-clause"
|
||||
__copyright__ = "Copyright 2014-2015 VirtualCable S.L.U."
|
||||
__copyright__ = "Copyright 2014-2017 VirtualCable S.L.U."
|
||||
|
@@ -32,6 +32,8 @@
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from base64 import b64decode
|
||||
|
||||
import tempfile
|
||||
import string
|
||||
import random
|
||||
@@ -48,9 +50,25 @@ _unlinkFiles = []
|
||||
_tasksToWait = []
|
||||
_execBeforeExit = []
|
||||
|
||||
|
||||
sys_fs_enc = sys.getfilesystemencoding() or 'mbcs'
|
||||
|
||||
# Public key for scripts
|
||||
PUBLIC_KEY = '''-----BEGIN PUBLIC KEY-----
|
||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuNURlGjBpqbglkTTg2lh
|
||||
dU5qPbg9Q+RofoDDucGfrbY0pjB9ULgWXUetUWDZhFG241tNeKw+aYFTEorK5P+g
|
||||
ud7h9KfyJ6huhzln9eyDu3k+kjKUIB1PLtA3lZLZnBx7nmrHRody1u5lRaLVplsb
|
||||
FmcnptwYD+3jtJ2eK9ih935DYAkYS4vJFi2FO+npUQdYBZHPG/KwXLjP4oGOuZp0
|
||||
pCTLiCXWGjqh2GWsTECby2upGS/ZNZ1r4Ymp4V2A6DZnN0C0xenHIY34FWYahbXF
|
||||
ZGdr4DFBPdYde5Rb5aVKJQc/pWK0CV7LK6Krx0/PFc7OGg7ItdEuC7GSfPNV/ANt
|
||||
5BEQNF5w2nUUsyN8ziOrNih+z6fWQujAAUZfpCCeV9ekbwXGhbRtdNkbAryE5vH6
|
||||
eCE0iZ+cFsk72VScwLRiOhGNelMQ7mIMotNck3a0P15eaGJVE2JV0M/ag/Cnk0Lp
|
||||
wI1uJQRAVqz9ZAwvF2SxM45vnrBn6TqqxbKnHCeiwstLDYG4fIhBwFxP3iMH9EqV
|
||||
2+QXqdJW/wLenFjmXfxrjTRr+z9aYMIdtIkSpADIlbaJyTtuQpEdWnrlDS2b1IGd
|
||||
Okbm65EebVzOxfje+8dRq9Uqwip8f/qmzFsIIsx3wPSvkKawFwb0G5h2HX5oJrk0
|
||||
nVgtClKcDDlSaBsO875WDR0CAwEAAQ==
|
||||
-----END PUBLIC KEY-----'''
|
||||
|
||||
|
||||
def saveTempFile(content, filename=None):
|
||||
if filename is None:
|
||||
filename = b''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(16))
|
||||
@@ -68,6 +86,7 @@ def saveTempFile(content, filename=None):
|
||||
logger.info('Returning filename')
|
||||
return filename
|
||||
|
||||
|
||||
def readTempFile(filename):
|
||||
if 'win32' in sys.platform:
|
||||
filename = filename.encode('utf-8')
|
||||
@@ -79,6 +98,7 @@ def readTempFile(filename):
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def testServer(host, port, timeOut=4):
|
||||
try:
|
||||
sock = socket.create_connection((host, int(port)), timeOut)
|
||||
@@ -160,3 +180,24 @@ def addExecBeforeExit(fnc):
|
||||
def execBeforeExit():
|
||||
for fnc in _execBeforeExit:
|
||||
fnc.__call__()
|
||||
|
||||
|
||||
def verifySignature(script, signature):
|
||||
'''
|
||||
Verifies with a public key from whom the data came that it was indeed
|
||||
signed by their private key
|
||||
param: public_key_loc Path to public key
|
||||
param: signature String signature to be verified
|
||||
return: Boolean. True if the signature is valid; False otherwise.
|
||||
'''
|
||||
# For signature checking
|
||||
from Crypto.PublicKey import RSA
|
||||
from Crypto.Signature import PKCS1_v1_5
|
||||
from Crypto.Hash import SHA256
|
||||
|
||||
rsakey = RSA.importKey(PUBLIC_KEY)
|
||||
signer = PKCS1_v1_5.new(rsakey)
|
||||
digest = SHA256.new(script) # Script is "binary string" here
|
||||
if signer.verify(digest, b64decode(signature)):
|
||||
return True
|
||||
return False
|
||||
|
@@ -39,9 +39,9 @@ import json
|
||||
import six
|
||||
import osDetector
|
||||
|
||||
|
||||
from .log import logger
|
||||
|
||||
|
||||
class RetryException(Exception):
|
||||
pass
|
||||
|
||||
@@ -65,7 +65,7 @@ class RestRequest(object):
|
||||
logger.debug('Requesting {}'.format(url))
|
||||
|
||||
try:
|
||||
r = requests.get(url, headers={'Content-type': 'application/json', 'User-Agent': osDetector.getOs() + " - UDS Connector " + VERSION })
|
||||
r = requests.get(url, headers={'Content-type': 'application/json', 'User-Agent': osDetector.getOs() + " - UDS Connector " + VERSION }, verify=False)
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
raise Exception('Error connecting to UDS Server at {}'.format(self.restApiUrl[0:-11]))
|
||||
|
||||
|
@@ -1,8 +1,8 @@
|
||||
#!/bin/sh
|
||||
pip install paramiko requests six
|
||||
pip install paramiko requests six pycrypto
|
||||
rm -rf lib
|
||||
mkdir -p lib/python2.7/site-packages
|
||||
for a in requests paramiko pyasn1 cryptography packaging idna asn1crypto six enum ipaddress cffi ; do cp -r /usr/lib/python2.7/site-packages/$a* lib/python2.7/site-packages/; done
|
||||
for a in requests paramiko pyasn1 cryptography packaging idna asn1crypto six enum ipaddress cffi Crypto; do cp -r /usr/lib/python2.7/site-packages/$a* lib/python2.7/site-packages/; done
|
||||
cp src/udsclient bin
|
||||
chmod 755 bin/udsclient
|
||||
mkdir lib/UDSClient
|
||||
|
@@ -65,6 +65,7 @@ def login():
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def logout():
|
||||
global headers
|
||||
h = Http()
|
||||
|
@@ -164,6 +164,7 @@ MIDDLEWARE_CLASSES = (
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'uds.core.util.request.GlobalRequestMiddleware',
|
||||
'uds.core.util.middleware.XUACompatibleMiddleware',
|
||||
'uds.core.util.middleware.RedirectMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
)
|
||||
|
||||
|
@@ -39,6 +39,7 @@ from django.contrib.sessions.backends.db import SessionStore
|
||||
from uds.core.util.Config import GlobalConfig
|
||||
from uds.core.auths.auth import getRootUser
|
||||
from uds.models import Authenticator
|
||||
from uds.core.managers import cryptoManager
|
||||
|
||||
import logging
|
||||
|
||||
@@ -178,7 +179,7 @@ class Handler(object):
|
||||
return self._authToken
|
||||
|
||||
@staticmethod
|
||||
def storeSessionAuthdata(session, id_auth, username, locale, is_admin, staff_member):
|
||||
def storeSessionAuthdata(session, id_auth, username, password, locale, is_admin, staff_member, scrambler):
|
||||
'''
|
||||
Stores the authentication data inside current session
|
||||
:param session: session handler (Djano user session object)
|
||||
@@ -194,12 +195,13 @@ class Handler(object):
|
||||
session['REST'] = {
|
||||
'auth': id_auth,
|
||||
'username': username,
|
||||
'password': cryptoManager().xor(password, scrambler), # Stores "bytes"
|
||||
'locale': locale,
|
||||
'is_admin': is_admin,
|
||||
'staff_member': staff_member
|
||||
}
|
||||
|
||||
def genAuthToken(self, id_auth, username, locale, is_admin, staf_member):
|
||||
def genAuthToken(self, id_auth, username, password, locale, is_admin, staf_member, scrambler):
|
||||
'''
|
||||
Generates the authentication token from a session, that is basically
|
||||
the session key itself
|
||||
@@ -211,7 +213,7 @@ class Handler(object):
|
||||
'''
|
||||
session = SessionStore()
|
||||
session.set_expiry(GlobalConfig.ADMIN_IDLE_TIME.getInt())
|
||||
Handler.storeSessionAuthdata(session, id_auth, username, locale, is_admin, staf_member)
|
||||
Handler.storeSessionAuthdata(session, id_auth, username, password, locale, is_admin, staf_member, scrambler)
|
||||
session.save()
|
||||
self._authToken = session.session_key
|
||||
self._session = session
|
||||
|
@@ -39,12 +39,12 @@ from uds.core.util.State import State
|
||||
from uds.core.util.model import processUuid
|
||||
from uds.core.util import log
|
||||
from uds.core.managers import cryptoManager
|
||||
from uds.core.osmanagers import OSManager
|
||||
from uds.models import TicketStore
|
||||
from uds.REST import Handler
|
||||
from uds.REST import RequestError
|
||||
from uds.models import UserService
|
||||
|
||||
|
||||
import datetime
|
||||
import six
|
||||
|
||||
@@ -52,7 +52,6 @@ import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Actor key, configurable in Security Section of administration interface
|
||||
actorKey = Config.Config.section(Config.SECURITY_SECTION).value('Master Key',
|
||||
cryptoManager().uuid(datetime.datetime.now()).replace('-', ''),
|
||||
@@ -99,8 +98,8 @@ class Actor(Handler):
|
||||
'''
|
||||
# Ensures that key is first parameter
|
||||
# Here, path will be .../actor/ACTION/KEY (probably /rest/actor/KEY/...)
|
||||
logger.debug('{} == {}'.format(self._params.get('key'), actorKey.get(True)))
|
||||
if self._params.get('key') != actorKey.get(True):
|
||||
# logger.debug('{} == {}'.format(self._params.get('key'), actorKey.get()))
|
||||
if self._params.get('key') != actorKey.get():
|
||||
return Actor.result(_('Invalid key'), error=ERR_INVALID_KEY)
|
||||
return None
|
||||
|
||||
@@ -145,6 +144,10 @@ class Actor(Handler):
|
||||
if len(self._args) < 1:
|
||||
raise RequestError('Invalid request')
|
||||
|
||||
if self._args[0] == 'PostThoughGet':
|
||||
self._args = self._args[1:] # Remove first argument
|
||||
return self.post()
|
||||
|
||||
if self._args[0] == 'ticket':
|
||||
return self.getTicket()
|
||||
|
||||
@@ -211,8 +214,22 @@ class Actor(Handler):
|
||||
logger.debug(self._params)
|
||||
data = '\t'.join((self._params.get('message'), six.text_type(self._params.get('level', 10000))))
|
||||
|
||||
osmanager = service.getInstance().osmanager()
|
||||
|
||||
try:
|
||||
res = service.getInstance().osmanager().process(service, message, data, options={'scramble': False})
|
||||
if osmanager is None:
|
||||
if message in ('login', 'logout'):
|
||||
osm = OSManager(None, None) # Dummy os manager, just for using "logging" capability
|
||||
if message == 'login':
|
||||
osm.loggedIn(service)
|
||||
else:
|
||||
osm.loggedOut(service)
|
||||
# Mark for removal...
|
||||
service.release() # Release for removal
|
||||
return 'ok'
|
||||
raise Exception('Unknown message {} for an user service without os manager'.format(message))
|
||||
else:
|
||||
res = osmanager.process(service, message, data, options={'scramble': False})
|
||||
except Exception as e:
|
||||
return Actor.result(six.text_type(e), ERR_OSMANAGER_ERROR)
|
||||
|
||||
|
@@ -44,6 +44,7 @@ from uds.core.managers import cryptoManager, userServiceManager
|
||||
from uds.core.util.Config import GlobalConfig
|
||||
from uds.core.services.Exceptions import ServiceNotReadyError
|
||||
from uds.core import VERSION as UDS_VERSION
|
||||
from uds.core.util import encoders
|
||||
|
||||
import six
|
||||
|
||||
@@ -138,11 +139,9 @@ class Client(Handler):
|
||||
|
||||
userService.setConnectionSource(srcIp, hostname) # Store where we are accessing from so we can notify Service
|
||||
|
||||
transportScript = transportInstance.getUDSTransportScript(userService, transport, ip, self._request.os, self._request.user, password, self._request)
|
||||
transportScript = transportInstance.getEncodedTransportScript(userService, transport, ip, self._request.os, self._request.user, password, self._request)
|
||||
|
||||
logger.debug('Script:\n{}'.format(transportScript))
|
||||
|
||||
return Client.result(result=transportScript.encode('bz2').encode('base64'))
|
||||
return Client.result(result=transportScript)
|
||||
except ServiceNotReadyError as e:
|
||||
# Refresh ticket and make this retrayable
|
||||
TicketStore.revalidate(ticket, 20) # Retry will be in at most 5 seconds
|
||||
|
@@ -36,12 +36,13 @@ from django.utils.translation import ugettext as _
|
||||
|
||||
from uds.REST import Handler
|
||||
from uds.REST import RequestError
|
||||
from uds.models import UserService, DeployedService, Transport
|
||||
from uds.core.util.model import processUuid
|
||||
from uds.core.managers.UserServiceManager import UserServiceManager
|
||||
from uds.core.util import log
|
||||
from uds.core.util.stats import events
|
||||
|
||||
from uds.models import UserService, DeployedService, ServicesPoolGroup
|
||||
from uds.core.managers import userServiceManager
|
||||
from uds.core.managers import cryptoManager
|
||||
from uds.core.ui.images import DEFAULT_THUMB_BASE64
|
||||
from uds.core.util.Config import GlobalConfig
|
||||
from uds.core.services.Exceptions import ServiceNotReadyError
|
||||
from uds.web import errors
|
||||
|
||||
import datetime
|
||||
import six
|
||||
@@ -61,7 +62,7 @@ class Connection(Handler):
|
||||
needs_staff = False
|
||||
|
||||
@staticmethod
|
||||
def result(result=None, error=None):
|
||||
def result(result=None, error=None, errorCode=0, retryable=False):
|
||||
'''
|
||||
Helper method to create a "result" set for connection response
|
||||
:param result: Result value to return (can be None, in which case it is converted to empty string '')
|
||||
@@ -71,7 +72,14 @@ class Connection(Handler):
|
||||
result = result if result is not None else ''
|
||||
res = {'result': result, 'date': datetime.datetime.now()}
|
||||
if error is not None:
|
||||
if isinstance(error, int):
|
||||
error = errors.errorString(error)
|
||||
if errorCode != 0:
|
||||
error += ' (code {0:04X})'.format(errorCode)
|
||||
res['error'] = error
|
||||
|
||||
res['retryable'] = retryable and '1' or '0'
|
||||
|
||||
return res
|
||||
|
||||
def serviceList(self):
|
||||
@@ -90,32 +98,50 @@ class Connection(Handler):
|
||||
if t.validForIp(self._request.ip) and t.getType().providesConnetionInfo():
|
||||
trans.append({'id': t.uuid, 'name': t.name})
|
||||
|
||||
servicePool = svr.deployed_service
|
||||
|
||||
services.append({'id': 'A' + svr.uuid,
|
||||
'name': svr['name'],
|
||||
'name': servicePool.name,
|
||||
'description': servicePool.comments,
|
||||
'visual_name': servicePool.visual_name,
|
||||
'group': servicePool.servicesPoolGroup if servicePool.servicesPoolGroup is not None else ServicesPoolGroup.default().as_dict,
|
||||
'thumb': servicePool.image.thumb64 if servicePool.image is not None else DEFAULT_THUMB_BASE64,
|
||||
'show_transports': servicePool.show_transports,
|
||||
'allow_users_remove': servicePool.allow_users_remove,
|
||||
'maintenance': servicePool.isInMaintenance(),
|
||||
'not_accesible': not servicePool.isAccessAllowed(),
|
||||
'to_be_replaced': False, # Manually assigned will not be autoremoved never
|
||||
'transports': trans,
|
||||
'maintenance': svr.isInMaintenance(),
|
||||
'in_use': svr.in_use})
|
||||
|
||||
logger.debug(services)
|
||||
|
||||
# Now generic user service
|
||||
for svr in availServices:
|
||||
for servicePool in availServices:
|
||||
trans = []
|
||||
for t in svr.transports.all().order_by('priority'):
|
||||
for t in servicePool.transports.all().order_by('priority'):
|
||||
if t.validForIp(self._request.ip) and t.getType().providesConnetionInfo():
|
||||
trans.append({'id': t.uuid, 'name': t.name})
|
||||
|
||||
# Locate if user service has any already assigned user service for this
|
||||
ads = UserServiceManager.manager().getExistingAssignationForUser(svr, self._user)
|
||||
ads = userServiceManager().getExistingAssignationForUser(servicePool, self._user)
|
||||
if ads is None:
|
||||
in_use = False
|
||||
else:
|
||||
in_use = ads.in_use
|
||||
|
||||
services.append({'id': 'F' + svr.uuid,
|
||||
'name': svr.name,
|
||||
services.append({'id': 'F' + servicePool.uuid,
|
||||
'name': servicePool.name,
|
||||
'description': servicePool.comments,
|
||||
'visual_name': servicePool.visual_name,
|
||||
'group': servicePool.servicesPoolGroup if servicePool.servicesPoolGroup is not None else ServicesPoolGroup.default().as_dict,
|
||||
'thumb': servicePool.image.thumb64 if servicePool.image is not None else DEFAULT_THUMB_BASE64,
|
||||
'show_transports': servicePool.show_transports,
|
||||
'allow_users_remove': servicePool.allow_users_remove,
|
||||
'maintenance': servicePool.isInMaintenance(),
|
||||
'not_accesible': not servicePool.isAccessAllowed(),
|
||||
'to_be_replaced': servicePool.toBeReplaced(),
|
||||
'transports': trans,
|
||||
'maintenance': svr.isInMaintenance(),
|
||||
'in_use': in_use})
|
||||
|
||||
logger.debug('Services: {0}'.format(services))
|
||||
@@ -125,71 +151,52 @@ class Connection(Handler):
|
||||
return Connection.result(result=services)
|
||||
|
||||
def connection(self, doNotCheck=False):
|
||||
kind, idService = self._args[0][0], self._args[0][1:]
|
||||
idService = self._args[0]
|
||||
idTransport = self._args[1]
|
||||
try:
|
||||
ip, userService, iads, trans, itrans = userServiceManager().getService(self._user, self._request.ip, idService, idTransport, not doNotCheck)
|
||||
ci = {
|
||||
'username': '',
|
||||
'password': '',
|
||||
'domain': '',
|
||||
'protocol': 'unknown',
|
||||
'ip': ip
|
||||
}
|
||||
ci.update(itrans.getConnectionInfo(userService, self._user, 'UNKNOWN'))
|
||||
return Connection.result(result=ci)
|
||||
except ServiceNotReadyError as e:
|
||||
# Refresh ticket and make this retrayable
|
||||
return Connection.result(error=errors.SERVICE_IN_PREPARATION, errorCode=e.code, retryable=True)
|
||||
except Exception as e:
|
||||
logger.exception("Exception")
|
||||
return Connection.result(error=six.text_type(e))
|
||||
|
||||
logger.debug('Type: {}, Service: {}, Transport: {}'.format(kind, idService, idTransport))
|
||||
def script(self):
|
||||
idService = self._args[0]
|
||||
idTransport = self._args[1]
|
||||
scrambler = self._args[2]
|
||||
hostname = self._args[3]
|
||||
|
||||
try:
|
||||
logger.debug('Kind of service: {0}, idService: {1}'.format(kind, idService))
|
||||
if kind == 'A': # This is an assigned service
|
||||
ads = UserService.objects.get(uuid=processUuid(idService))
|
||||
else:
|
||||
ds = DeployedService.objects.get(uuid=processUuid(idService))
|
||||
# We first do a sanity check for this, if the user has access to this service
|
||||
# If it fails, will raise an exception
|
||||
ds.validateUser(self._user)
|
||||
# Now we have to locate an instance of the service, so we can assign it to user.
|
||||
ads = UserServiceManager.manager().getAssignationForUser(ds, self._user)
|
||||
res = userServiceManager().getService(self._user, self._request.ip, idService, idTransport)
|
||||
logger.debug('Res: {}'.format(res))
|
||||
ip, userService, userServiceInstance, transport, transportInstance = res
|
||||
password = cryptoManager().xor(self.getValue('password'), scrambler).decode('utf-8')
|
||||
|
||||
if ads.isInMaintenance() is True:
|
||||
return Connection.result(error='Service in maintenance')
|
||||
userService.setConnectionSource(self._request.ip, hostname) # Store where we are accessing from so we can notify Service
|
||||
|
||||
logger.debug('Found service: {0}'.format(ads))
|
||||
trans = Transport.objects.get(uuid=processUuid(idTransport))
|
||||
transportScript = transportInstance.getEncodedTransportScript(userService, transport, ip, self._request.os, self._user, password, self._request)
|
||||
|
||||
if trans.validForIp(self._request.ip) is False:
|
||||
return Connection.result(error='Access denied')
|
||||
|
||||
# Test if the service is ready
|
||||
if doNotCheck or ads.isReady():
|
||||
log.doLog(ads, log.INFO, "User {0} from {1} has initiated access".format(self._user.name, self._request.ip), log.WEB)
|
||||
# If ready, show transport for this service, if also ready ofc
|
||||
iads = ads.getInstance()
|
||||
ip = iads.getIp()
|
||||
logger.debug('IP: {}'.format(ip))
|
||||
events.addEvent(ads.deployed_service, events.ET_ACCESS, username=self._user.name, srcip=self._request.ip, dstip=ip, uniqueid=ads.unique_id)
|
||||
if ip is not None:
|
||||
itrans = trans.getInstance()
|
||||
if itrans.providesConnetionInfo() and (doNotCheck or itrans.isAvailableFor(ads, ip)):
|
||||
ads.setConnectionSource(self._request.ip, 'unknown')
|
||||
log.doLog(ads, log.INFO, "User service ready, rendering transport", log.WEB)
|
||||
|
||||
ci = {
|
||||
'username': '',
|
||||
'password': '',
|
||||
'domain': '',
|
||||
'protocol': 'unknown',
|
||||
'ip': ip
|
||||
}
|
||||
ci.update(itrans.getConnectionInfo(ads, self._user, 'UNKNOWN'))
|
||||
|
||||
UserServiceManager.manager().notifyPreconnect(ads, itrans.processedUser(ads, self._user), itrans.protocol)
|
||||
|
||||
return Connection.result(result=ci)
|
||||
else:
|
||||
log.doLog(ads, log.WARN, "User service is not accessible by REST (ip {0})".format(ip), log.TRANSPORT)
|
||||
logger.debug('Transport {} is not accesible for user service {} from {}'.format(trans, ads, self._request.ip))
|
||||
logger.debug("{}, {}".format(itrans.providesConnetionInfo(), itrans.isAvailableFor(ads, ip)))
|
||||
else:
|
||||
logger.debug('Ip not available from user service {0}'.format(ads))
|
||||
else:
|
||||
log.doLog(ads, log.WARN, "User {0} from {1} tried to access, but service was not ready".format(self._user.name, self._request.ip), log.WEB)
|
||||
# Not ready, show message and return to this page in a while
|
||||
return Connection.result(error='Service not ready')
|
||||
return Connection.result(result=transportScript)
|
||||
except ServiceNotReadyError as e:
|
||||
# Refresh ticket and make this retrayable
|
||||
return Connection.result(error=errors.SERVICE_IN_PREPARATION, errorCode=e.code, retryable=True)
|
||||
except Exception as e:
|
||||
logger.exception("Exception")
|
||||
return Connection.result(error=six.text_type(e))
|
||||
|
||||
return password
|
||||
|
||||
def get(self):
|
||||
'''
|
||||
Processes get requests
|
||||
@@ -207,7 +214,13 @@ class Connection(Handler):
|
||||
# Return connection & validate access for service/transport
|
||||
return self.connection()
|
||||
|
||||
if len(self._args) == 3 and self._args[2] == 'skipChecking':
|
||||
return self.connection(True)
|
||||
if len(self._args) == 3:
|
||||
# /connection/idService/idTransport/skipChecking
|
||||
if self._args[2] == 'skipChecking':
|
||||
return self.connection(True)
|
||||
|
||||
if len(self._args) == 4:
|
||||
# /connection/idService/idTransport/scrambler/hostname
|
||||
return self.script()
|
||||
|
||||
raise RequestError('Invalid Request')
|
||||
|
@@ -36,11 +36,14 @@ from uds.core.util.Config import GlobalConfig
|
||||
from uds.core.util.model import processUuid
|
||||
from uds.models import Authenticator
|
||||
from uds.core.auths.auth import authenticate
|
||||
from uds.core import VERSION as UDS_VERSION
|
||||
|
||||
from uds.REST import RequestError
|
||||
from uds.REST import Handler
|
||||
|
||||
import logging
|
||||
import random
|
||||
import string
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -75,6 +78,7 @@ class Login(Handler):
|
||||
if 'authId' not in self._params and 'authSmallName' not in self._params and 'auth' not in self._params:
|
||||
raise RequestError('Invalid parameters (no auth)')
|
||||
|
||||
scrambler = ''.join(random.choice(string.letters + string.digits) for _ in range(32)) # @UndefinedVariable
|
||||
authId = self._params.get('authId', None)
|
||||
authSmallName = self._params.get('authSmallName', None)
|
||||
authName = self._params.get('auth', None)
|
||||
@@ -83,7 +87,7 @@ class Login(Handler):
|
||||
locale = self._params.get('locale', 'en')
|
||||
if authName == 'admin' or authSmallName == 'admin':
|
||||
if GlobalConfig.SUPER_USER_LOGIN.get(True) == username and GlobalConfig.SUPER_USER_PASS.get(True) == password:
|
||||
self.genAuthToken(-1, username, locale, True, True)
|
||||
self.genAuthToken(-1, username, password, locale, True, True, scrambler)
|
||||
return{'result': 'ok', 'token': self.getAuthToken()}
|
||||
else:
|
||||
raise Exception('Invalid credentials')
|
||||
@@ -98,14 +102,14 @@ class Login(Handler):
|
||||
auth = Authenticator.objects.get(small_name=authSmallName)
|
||||
|
||||
if password == '':
|
||||
password = 'xdaf44tgas4xd5ñasdłe4g€@#½|«ð2' # Extrange password if credential leaved empty
|
||||
password = 'xdaf44tgas4xd5ñasdłe4g€@#½|«ð2' # Extrange password if credential left empty
|
||||
|
||||
logger.debug('Auth obj: {0}'.format(auth))
|
||||
user = authenticate(username, password, auth)
|
||||
if user is None: # invalid credentials
|
||||
raise Exception()
|
||||
self.genAuthToken(auth.id, user.name, locale, user.is_admin, user.staff_member)
|
||||
return{'result': 'ok', 'token': self.getAuthToken()}
|
||||
self.genAuthToken(auth.id, user.name, password, locale, user.is_admin, user.staff_member, scrambler)
|
||||
return{'result': 'ok', 'token': self.getAuthToken(), 'version': UDS_VERSION, 'scrambler': scrambler }
|
||||
except:
|
||||
logger.exception('Credentials ')
|
||||
raise Exception('Invalid Credentials (invalid authenticator)')
|
||||
@@ -138,11 +142,13 @@ class Auths(Handler):
|
||||
|
||||
def auths(self):
|
||||
for a in Authenticator.objects.all():
|
||||
if a.getType().isCustom() is False:
|
||||
theType = a.getType()
|
||||
if theType.isCustom() is False and theType.typeType not in ('IP',):
|
||||
yield {
|
||||
'authId': a.id,
|
||||
'authId': a.uuid,
|
||||
'authSmallName': str(a.small_name),
|
||||
'auth': a.name,
|
||||
'type': theType.typeType,
|
||||
}
|
||||
|
||||
def get(self):
|
||||
|
@@ -158,7 +158,10 @@ class Providers(ModelHandler):
|
||||
self.ensureAccess(spType, permissions.PERMISSION_MANAGEMENT, root=True)
|
||||
|
||||
logger.debug('spType: {}'.format(spType))
|
||||
res = spType.test(Environment.getTempEnv(), self._params)
|
||||
|
||||
dct = self._params.copy()
|
||||
dct['_request'] = self._request
|
||||
res = spType.test(Environment.getTempEnv(), dct)
|
||||
if res[0]:
|
||||
return 'ok'
|
||||
else:
|
||||
|
@@ -34,7 +34,6 @@ from __future__ import unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
|
||||
from uds.models import Service, UserService, Tag
|
||||
|
||||
from uds.core.services import Service as coreService
|
||||
@@ -93,7 +92,7 @@ class Services(DetailHandler): # pylint: disable=too-many-public-methods
|
||||
'type': item.data_type,
|
||||
'type_name': _(itemType.name()),
|
||||
'deployed_services_count': item.deployedServices.count(),
|
||||
'user_services_count': UserService.objects.filter(deployed_service__service=item).exclude(state__in=(State.REMOVED, State.ERROR)).count(),
|
||||
'user_services_count': UserService.objects.filter(deployed_service__service=item).exclude(state__in=State.INFO_STATES).count(),
|
||||
'maintenance_mode': item.provider.maintenance_mode,
|
||||
'permission': perm
|
||||
}
|
||||
|
@@ -115,12 +115,11 @@ class AccessCalendars(DetailHandler):
|
||||
CalendarAccess.objects.get(uuid=processUuid(self._args[0])).delete()
|
||||
|
||||
|
||||
|
||||
class ActionsCalendars(DetailHandler):
|
||||
'''
|
||||
Processes the transports detail requests of a Service Pool
|
||||
'''
|
||||
custom_methods = ('execute')
|
||||
custom_methods = ('execute',)
|
||||
|
||||
@staticmethod
|
||||
def as_dict(item):
|
||||
|
@@ -69,7 +69,10 @@ class ServicesPools(ModelHandler):
|
||||
'actions': ActionsCalendars
|
||||
}
|
||||
|
||||
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']
|
||||
save_fields = ['name', 'short_name', 'comments', 'tags', 'service_id',
|
||||
'osmanager_id', 'image_id', 'servicesPoolGroup_id', 'initial_srvs',
|
||||
'cache_l1_srvs', 'cache_l2_srvs', 'max_srvs', 'show_transports',
|
||||
'allow_users_remove', 'ignores_unused']
|
||||
remove_fields = ['osmanager_id', 'service_id']
|
||||
|
||||
table_title = _('Service Pools')
|
||||
@@ -88,7 +91,6 @@ class ServicesPools(ModelHandler):
|
||||
|
||||
custom_methods = [('setFallbackAccess', True), ('actionsList', True)]
|
||||
|
||||
|
||||
def item_as_dict(self, item):
|
||||
# if item does not have an associated service, hide it (the case, for example, for a removed service)
|
||||
# Access from dict will raise an exception, and item will be skipped
|
||||
@@ -110,6 +112,7 @@ class ServicesPools(ModelHandler):
|
||||
val = {
|
||||
'id': item.uuid,
|
||||
'name': item.name,
|
||||
'short_name': item.short_name,
|
||||
'tags': [tag.tag for tag in item.tags.all()],
|
||||
'parent': item.service.name,
|
||||
'parent_type': item.service.data_type,
|
||||
@@ -126,10 +129,12 @@ class ServicesPools(ModelHandler):
|
||||
'cache_l1_srvs': item.cache_l1_srvs,
|
||||
'cache_l2_srvs': item.cache_l2_srvs,
|
||||
'max_srvs': item.max_srvs,
|
||||
'user_services_count': item.userServices.count(),
|
||||
'user_services_count': item.userServices.exclude(state__in=State.INFO_STATES).count(),
|
||||
'user_services_in_preparation': item.userServices.filter(state=State.PREPARING).count(),
|
||||
'restrained': item.isRestrained(),
|
||||
'show_transports': item.show_transports,
|
||||
'allow_users_remove': item.allow_users_remove,
|
||||
'ignores_unused': item.ignores_unused,
|
||||
'fallbackAccess': item.fallbackAccess,
|
||||
'permission': permissions.getEffectivePermission(self._user, item),
|
||||
'info': Services.serviceInfo(item.service),
|
||||
@@ -147,7 +152,7 @@ class ServicesPools(ModelHandler):
|
||||
if Service.objects.count() < 1:
|
||||
raise ResponseError(ugettext('Create at least a service before creating a new service pool'))
|
||||
|
||||
g = self.addDefaultFields([], ['name', 'comments', 'tags'])
|
||||
g = self.addDefaultFields([], ['name', 'short_name', 'comments', 'tags'])
|
||||
|
||||
for f in [{
|
||||
'name': 'service_id',
|
||||
@@ -165,13 +170,37 @@ class ServicesPools(ModelHandler):
|
||||
'type': gui.InputField.CHOICE_TYPE,
|
||||
'rdonly': True,
|
||||
'order': 101,
|
||||
}, {
|
||||
'name': 'show_transports',
|
||||
'value': True,
|
||||
'label': ugettext('Show transports'),
|
||||
'tooltip': ugettext('If active, alternative transports for user will be shown'),
|
||||
'type': gui.InputField.CHECKBOX_TYPE,
|
||||
'order': 110,
|
||||
'tab': ugettext('Advanced'),
|
||||
}, {
|
||||
'name': 'allow_users_remove',
|
||||
'value': False,
|
||||
'label': ugettext('Allow removal by users'),
|
||||
'tooltip': ugettext('If active, the user will be allowed to remove the service "manually". Be care with this, because the user will have the "poser" to delete it\'s own service'),
|
||||
'type': gui.InputField.CHECKBOX_TYPE,
|
||||
'order': 111,
|
||||
'tab': ugettext('Advanced'),
|
||||
}, {
|
||||
'name': 'ignores_unused',
|
||||
'value': False,
|
||||
'label': ugettext('Ignores unused'),
|
||||
'tooltip': ugettext('If active, UDS will not try to detect and remove assigned but not used user services.'),
|
||||
'type': gui.InputField.CHECKBOX_TYPE,
|
||||
'order': 112,
|
||||
'tab': ugettext('Advanced'),
|
||||
}, {
|
||||
'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,
|
||||
'order': 120,
|
||||
'tab': ugettext('Display'),
|
||||
}, {
|
||||
'name': 'servicesPoolGroup_id',
|
||||
@@ -179,7 +208,7 @@ class ServicesPools(ModelHandler):
|
||||
'label': ugettext('Pool group'),
|
||||
'tooltip': ugettext('Pool group for this pool (for pool clasify on display)'),
|
||||
'type': gui.InputField.IMAGECHOICE_TYPE,
|
||||
'order': 103,
|
||||
'order': 121,
|
||||
'tab': ugettext('Display'),
|
||||
}, {
|
||||
'name': 'initial_srvs',
|
||||
@@ -188,7 +217,7 @@ class ServicesPools(ModelHandler):
|
||||
'label': ugettext('Initial available services'),
|
||||
'tooltip': ugettext('Services created initially for this service pool'),
|
||||
'type': gui.InputField.NUMERIC_TYPE,
|
||||
'order': 110,
|
||||
'order': 130,
|
||||
'tab': ugettext('Availability'),
|
||||
}, {
|
||||
'name': 'cache_l1_srvs',
|
||||
@@ -197,7 +226,7 @@ class ServicesPools(ModelHandler):
|
||||
'label': ugettext('Services to keep in cache'),
|
||||
'tooltip': ugettext('Services kept in cache for improved user service assignation'),
|
||||
'type': gui.InputField.NUMERIC_TYPE,
|
||||
'order': 111,
|
||||
'order': 131,
|
||||
'tab': ugettext('Availability'),
|
||||
}, {
|
||||
'name': 'cache_l2_srvs',
|
||||
@@ -206,7 +235,7 @@ class ServicesPools(ModelHandler):
|
||||
'label': ugettext('Services to keep in L2 cache'),
|
||||
'tooltip': ugettext('Services kept in cache of level2 for improved service generation'),
|
||||
'type': gui.InputField.NUMERIC_TYPE,
|
||||
'order': 112,
|
||||
'order': 132,
|
||||
'tab': ugettext('Availability'),
|
||||
}, {
|
||||
'name': 'max_srvs',
|
||||
@@ -215,15 +244,8 @@ class ServicesPools(ModelHandler):
|
||||
'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'),
|
||||
'type': gui.InputField.NUMERIC_TYPE,
|
||||
'order': 113,
|
||||
'order': 133,
|
||||
'tab': ugettext('Availability'),
|
||||
}, {
|
||||
'name': 'show_transports',
|
||||
'value': True,
|
||||
'label': ugettext('Show transports'),
|
||||
'tooltip': ugettext('If active, alternative transports for user will be shown'),
|
||||
'type': gui.InputField.CHECKBOX_TYPE,
|
||||
'order': 120,
|
||||
}]:
|
||||
self.addField(g, f)
|
||||
|
||||
|
@@ -54,7 +54,7 @@ import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
__updated__ = '2017-02-02'
|
||||
__updated__ = '2017-10-25'
|
||||
|
||||
|
||||
# a few constants
|
||||
@@ -123,7 +123,7 @@ class BaseModelHandler(Handler):
|
||||
'label': _('Tags'),
|
||||
'type': 'taglist',
|
||||
'tooltip': _('Tags for this element'),
|
||||
'order': 0 - 101,
|
||||
'order': 0 - 105,
|
||||
})
|
||||
if 'name' in flds:
|
||||
self.addField(gui, {
|
||||
@@ -134,13 +134,23 @@ class BaseModelHandler(Handler):
|
||||
'tooltip': _('Name of this element'),
|
||||
'order': 0 - 100,
|
||||
})
|
||||
if 'short_name' in flds:
|
||||
self.addField(gui, {
|
||||
'name': 'short_name',
|
||||
'type': 'text',
|
||||
'label': _('Short name'),
|
||||
'tooltip': _('Short name for user service visualization'),
|
||||
'required': False,
|
||||
'length': 16,
|
||||
'order': 0 - 95,
|
||||
})
|
||||
if 'comments' in flds:
|
||||
self.addField(gui, {
|
||||
'name': 'comments',
|
||||
'label': _('Comments'),
|
||||
'tooltip': _('Comments for this element'),
|
||||
'length': 256,
|
||||
'order': 0 - 99,
|
||||
'order': 0 - 90,
|
||||
})
|
||||
if 'priority' in flds:
|
||||
self.addField(gui, {
|
||||
@@ -151,7 +161,7 @@ class BaseModelHandler(Handler):
|
||||
'required': True,
|
||||
'value': 1,
|
||||
'length': 4,
|
||||
'order': 0 - 97,
|
||||
'order': 0 - 85,
|
||||
})
|
||||
if 'small_name' in flds:
|
||||
self.addField(gui, {
|
||||
@@ -161,7 +171,7 @@ class BaseModelHandler(Handler):
|
||||
'tooltip': _('Label for this element'),
|
||||
'required': True,
|
||||
'length': 128,
|
||||
'order': 0 - 96,
|
||||
'order': 0 - 80,
|
||||
})
|
||||
|
||||
return gui
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012 Virtual Cable S.L.
|
||||
# Copyright (c) 2012-2016 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@@ -43,8 +43,9 @@ import ldap
|
||||
import ldap.filter
|
||||
import re
|
||||
import logging
|
||||
import six
|
||||
|
||||
__updated__ = '2016-04-18'
|
||||
__updated__ = '2017-10-05'
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -67,6 +68,8 @@ class RegexLdap(auths.Authenticator):
|
||||
groupNameAttr = gui.TextField(length=640, label=_('Group Name Attr'), multiline=2, defvalue='cn', order=11, tooltip=_('Attribute that contains the group name'), required=True, tab=_('Ldap info'))
|
||||
# regex = gui.TextField(length=64, label = _('Regular Exp. for groups'), defvalue = '^(.*)', order = 12, tooltip = _('Regular Expression to extract the group name'), required = True)
|
||||
|
||||
altClass = gui.TextField(length=64, label=_('Alt. class'), defvalue='', order=20, tooltip=_('Class for LDAP objects that will be also checked for groups retrieval (normally empty)'), required=False, tab=_('Advanced'))
|
||||
|
||||
typeName = _('Regex LDAP Authenticator')
|
||||
typeType = 'RegexLdapAuthenticator'
|
||||
typeDescription = _('Regular Expressions LDAP authenticator')
|
||||
@@ -102,7 +105,7 @@ class RegexLdap(auths.Authenticator):
|
||||
self._groupNameAttr = values['groupNameAttr']
|
||||
# self._regex = values['regex']
|
||||
self._userNameAttr = values['userNameAttr']
|
||||
|
||||
self._altClass = values['altClass']
|
||||
else:
|
||||
self._host = None
|
||||
self._port = None
|
||||
@@ -116,6 +119,8 @@ class RegexLdap(auths.Authenticator):
|
||||
self._groupNameAttr = None
|
||||
# self._regex = None
|
||||
self._userNameAttr = None
|
||||
self._altClass = None
|
||||
|
||||
self._connection = None
|
||||
|
||||
def __validateField(self, field, fieldLabel):
|
||||
@@ -181,32 +186,43 @@ class RegexLdap(auths.Authenticator):
|
||||
'username': self._username, 'password': self._password, 'timeout': self._timeout,
|
||||
'ldapBase': self._ldapBase, 'userClass': self._userClass,
|
||||
'userIdAttr': self._userIdAttr, 'groupNameAttr': self._groupNameAttr,
|
||||
'userNameAttr': self._userNameAttr
|
||||
'userNameAttr': self._userNameAttr, 'altClass': self._altClass,
|
||||
}
|
||||
|
||||
def __str__(self):
|
||||
return "Ldap Auth: {0}:{1}@{2}:{3}, base = {4}, userClass = {5}, userIdAttr = {6}, groupNameAttr = {7}, userName attr = {8}".format(
|
||||
return "Ldap Auth: {}:{}@{}:{}, base = {}, userClass = {}, userIdAttr = {}, groupNameAttr = {}, userName attr = {}, altClass={}".format(
|
||||
self._username, self._password, self._host, self._port, self._ldapBase, self._userClass, self._userIdAttr, self._groupNameAttr,
|
||||
self._userNameAttr)
|
||||
self._userNameAttr, self._altClass)
|
||||
|
||||
def marshal(self):
|
||||
return '\t'.join([
|
||||
'v2',
|
||||
self._host, self._port, gui.boolToStr(self._ssl), self._username, self._password, self._timeout,
|
||||
self._ldapBase, self._userClass, self._userIdAttr, self._groupNameAttr, self._userNameAttr
|
||||
'v3',
|
||||
self._host, self._port, gui.boolToStr(self._ssl), self._username, self._password,
|
||||
self._timeout, self._ldapBase, self._userClass, self._userIdAttr,
|
||||
self._groupNameAttr, self._userNameAttr, self._altClass
|
||||
])
|
||||
|
||||
def unmarshal(self, val):
|
||||
data = val.split('\t')
|
||||
if data[0] == 'v1':
|
||||
logger.debug("Data: {0}".format(data[1:]))
|
||||
self._host, self._port, self._ssl, self._username, self._password, self._timeout, self._ldapBase, self._userClass, self._userIdAttr, self._groupNameAttr, _regex, self._userNameAttr = data[1:]
|
||||
self._host, self._port, self._ssl, self._username, self._password, \
|
||||
self._timeout, self._ldapBase, self._userClass, self._userIdAttr, \
|
||||
self._groupNameAttr, _regex, self._userNameAttr = data[1:]
|
||||
self._ssl = gui.strToBool(self._ssl)
|
||||
self._groupNameAttr = self._groupNameAttr + '=' + _regex
|
||||
self._userNameAttr = '\n'.join(self._userNameAttr.split(','))
|
||||
elif data[0] == 'v2':
|
||||
logger.debug("Data v2: {0}".format(data[1:]))
|
||||
self._host, self._port, self._ssl, self._username, self._password, self._timeout, self._ldapBase, self._userClass, self._userIdAttr, self._groupNameAttr, self._userNameAttr = data[1:]
|
||||
self._host, self._port, self._ssl, self._username, self._password, \
|
||||
self._timeout, self._ldapBase, self._userClass, self._userIdAttr, \
|
||||
self._groupNameAttr, self._userNameAttr = data[1:]
|
||||
self._ssl = gui.strToBool(self._ssl)
|
||||
elif data[0] == 'v3':
|
||||
logger.debug("Data v3: {0}".format(data[1:]))
|
||||
self._host, self._port, self._ssl, self._username, self._password, \
|
||||
self._timeout, self._ldapBase, self._userClass, self._userIdAttr, \
|
||||
self._groupNameAttr, self._userNameAttr, self._altClass = data[1:]
|
||||
self._ssl = gui.strToBool(self._ssl)
|
||||
|
||||
def __connection(self, username=None, password=None):
|
||||
@@ -250,17 +266,52 @@ class RegexLdap(auths.Authenticator):
|
||||
|
||||
def __getUser(self, username):
|
||||
try:
|
||||
|
||||
con = self.__connection()
|
||||
filter_ = '(&(objectClass=%s)(%s=%s))' % (self._userClass, self._userIdAttr, ldap.filter.escape_filter_chars(username, 0))
|
||||
filter_ = b'(&(objectClass=%s)(%s=%s))' % (self._userClass, self._userIdAttr, ldap.filter.escape_filter_chars(username, 0))
|
||||
attrlist = [self._userIdAttr.encode('utf-8')] + self.__getAttrsFromField(self._userNameAttr) + self.__getAttrsFromField(self._groupNameAttr)
|
||||
|
||||
logger.debug('Getuser filter_: {0}, attr list: {1}'.format(filter_, attrlist))
|
||||
logger.debug('Getuser filter_: {}, attr list: {}'.format(filter_, attrlist))
|
||||
res = con.search_ext_s(base=self._ldapBase, scope=ldap.SCOPE_SUBTREE,
|
||||
filterstr=filter_, attrlist=attrlist, sizelimit=LDAP_RESULT_LIMIT)[0]
|
||||
|
||||
usr = dict((k, '') for k in attrlist)
|
||||
dct = {k.lower(): v for k, v in res[1].iteritems()}
|
||||
usr.update(dct)
|
||||
usr.update({'dn': res[0], '_id': username})
|
||||
|
||||
# If altClass
|
||||
if self._altClass is not None and self._altClass != '':
|
||||
logger.debug('Has alt class {}'.format(self._altClass))
|
||||
filter_ = b'(&(objectClass=%s)(%s=%s))' % (self._altClass, self._userIdAttr, ldap.filter.escape_filter_chars(username, 0))
|
||||
logger.debug('Get Alternate list filter: {}, attrlist: {}'.format(filter_, attrlist))
|
||||
# Get alternate class objects
|
||||
res = con.search_ext_s(base=self._ldapBase, scope=ldap.SCOPE_SUBTREE,
|
||||
filterstr=filter_, attrlist=attrlist, sizelimit=LDAP_RESULT_LIMIT)
|
||||
|
||||
for r in res:
|
||||
if r[0] is None:
|
||||
continue
|
||||
logger.debug('*** Item: {}'.format(r))
|
||||
|
||||
for k, v in six.iteritems(r[1]):
|
||||
kl = k.lower()
|
||||
# If already exists the field
|
||||
if kl in usr:
|
||||
# Convert existint to list, so we can add a new value
|
||||
if not isinstance(usr[kl], (list, tuple)):
|
||||
usr[kl] = [usr[kl]]
|
||||
|
||||
# Convert values to list, if not list
|
||||
if not isinstance(v, (list, tuple)):
|
||||
v = [v]
|
||||
|
||||
# Now append to existing values
|
||||
for x in v:
|
||||
usr[kl].append(x)
|
||||
else:
|
||||
usr[kl] = v
|
||||
|
||||
logger.debug('Usr: {0}'.format(usr))
|
||||
return usr
|
||||
except Exception:
|
||||
|
@@ -36,7 +36,7 @@ from django.utils.translation import ugettext as _
|
||||
from uds.core.ui.UserInterface import UserInterface
|
||||
from uds.core import Environmentable
|
||||
from uds.core import Serializable
|
||||
import base64
|
||||
from uds.core.util import encoders
|
||||
import os.path
|
||||
import sys
|
||||
import logging
|
||||
@@ -95,9 +95,6 @@ class Module(UserInterface, Environmentable, Serializable):
|
||||
module.
|
||||
'''
|
||||
# : Which coded to use to encode module by default.
|
||||
# : This overrides the Environmentable and Serializable Attribute, but in all cases we are using 'base64'
|
||||
CODEC = 'base64' # Can be zip, hez, bzip, base64, uuencoded
|
||||
|
||||
# : Basic name used to provide the administrator an "huma readable" form for the module
|
||||
typeName = 'Base Module'
|
||||
# : Internal type name, used by system to locate this module
|
||||
@@ -174,7 +171,7 @@ class Module(UserInterface, Environmentable, Serializable):
|
||||
data = file_.read()
|
||||
file_.close()
|
||||
if inBase64 == True:
|
||||
return base64.encodestring(data)
|
||||
return encoders.encode(data, 'base64')
|
||||
else:
|
||||
return data
|
||||
|
||||
|
@@ -32,6 +32,8 @@
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from uds.core.util import encoders
|
||||
|
||||
|
||||
class Serializable(object):
|
||||
'''
|
||||
@@ -42,8 +44,6 @@ class Serializable(object):
|
||||
- Initialize the object with default values
|
||||
- Read values from seralized data
|
||||
'''
|
||||
# Codify codec constant
|
||||
CODEC = 'base64' # Can be zip, hez, bzip, base64, uuencoded
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
@@ -79,17 +79,11 @@ class Serializable(object):
|
||||
def serialize(self):
|
||||
'''
|
||||
Serializes and "obfuscates' the data.
|
||||
|
||||
The codec used to encode the string is obtained from the instance CODEC, so derived classes can
|
||||
overwrite this attribute to set another codec
|
||||
'''
|
||||
return self.marshal().encode(self.CODEC)
|
||||
return encoders.encode(self.marshal(), 'base64')
|
||||
|
||||
def unserialize(self, str_):
|
||||
'''
|
||||
des-obfuscates the data and then de-serializes it via unmarshal method
|
||||
|
||||
The codec used to decode the string is obtained from the instance CODEC, so derived classes can
|
||||
overwrite this attribute to set another codec
|
||||
'''
|
||||
return self.unmarshal(str_.decode(self.CODEC))
|
||||
return self.unmarshal(encoders.decode(str_, 'base64'))
|
||||
|
@@ -39,5 +39,5 @@ from uds.core.Environment import Environmentable
|
||||
from uds.core.Serializable import Serializable
|
||||
from uds.core.BaseModule import Module
|
||||
|
||||
VERSION = '2.1.0-DEVEL'
|
||||
VERSION_STAMP = '20161001-DEVEL'
|
||||
VERSION = '2.2.0-DEVEL'
|
||||
VERSION_STAMP = '20170901-DEVEL'
|
||||
|
@@ -53,7 +53,7 @@ from uds.models import User
|
||||
import logging
|
||||
import six
|
||||
|
||||
__updated__ = '2016-04-15'
|
||||
__updated__ = '2017-11-22'
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
authLogger = logging.getLogger('authLog')
|
||||
@@ -106,7 +106,9 @@ def webLoginRequired(admin=False):
|
||||
Decorator to set protection to access page
|
||||
Look for samples at uds.core.web.views
|
||||
'''
|
||||
|
||||
def decorator(view_func):
|
||||
|
||||
@wraps(view_func, assigned=available_attrs(view_func))
|
||||
def _wrapped_view(request, *args, **kwargs):
|
||||
'''
|
||||
@@ -124,7 +126,9 @@ def webLoginRequired(admin=False):
|
||||
return HttpResponseForbidden(_('Forbidden'))
|
||||
|
||||
return view_func(request, *args, **kwargs)
|
||||
|
||||
return _wrapped_view
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
@@ -134,6 +138,7 @@ def trustedSourceRequired(view_func):
|
||||
Decorator to set protection to access page
|
||||
look for sample at uds.dispatchers.pam
|
||||
'''
|
||||
|
||||
@wraps(view_func)
|
||||
def _wrapped_view(request, *args, **kwargs):
|
||||
'''
|
||||
@@ -143,6 +148,7 @@ def trustedSourceRequired(view_func):
|
||||
if net.ipInNetwork(request.ip, GlobalConfig.TRUSTED_SOURCES.get(True)) is False:
|
||||
return HttpResponseForbidden()
|
||||
return view_func(request, *args, **kwargs)
|
||||
|
||||
return _wrapped_view
|
||||
|
||||
|
||||
@@ -253,7 +259,7 @@ def authInfoUrl(authenticator):
|
||||
Helper method, so we can get the info url for an authenticator
|
||||
'''
|
||||
from django.core.urlresolvers import reverse
|
||||
if isinstance(authenticator, unicode) or isinstance(authenticator, str):
|
||||
if isinstance(authenticator, (six.text_type, six.binary_type)):
|
||||
name = authenticator
|
||||
else:
|
||||
name = authenticator.name
|
||||
@@ -281,7 +287,7 @@ def webLogin(request, response, user, password):
|
||||
request.session[USER_KEY] = user.id
|
||||
request.session[PASS_KEY] = CryptoManager.manager().xor(password, cookie) # Stores "bytes"
|
||||
# Ensures that this user will have access through REST api if logged in through web interface
|
||||
REST.Handler.storeSessionAuthdata(request.session, manager_id, user.name, get_language(), user.is_admin, user.staff_member)
|
||||
REST.Handler.storeSessionAuthdata(request.session, manager_id, user.name, password, get_language(), user.is_admin, user.staff_member, cookie)
|
||||
return True
|
||||
|
||||
|
||||
@@ -331,7 +337,7 @@ def authLogLogin(request, authenticator, userName, logStr=''):
|
||||
try:
|
||||
user = authenticator.users.get(name=userName)
|
||||
log.doLog(user, level,
|
||||
'{0} from {1} where os is {3}'.format(logStr, request.ip, request.os['OS']), log.WEB
|
||||
'{} from {} where OS is {}'.format(logStr, request.ip, request.os['OS']), log.WEB
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
@@ -37,6 +37,7 @@ from django.db.models import Q
|
||||
from uds.models import DelayedTask as dbDelayedTask
|
||||
from uds.models import getSqlDatetime
|
||||
from uds.core.Environment import Environment
|
||||
from uds.core.util import encoders
|
||||
from socket import gethostname
|
||||
from pickle import loads, dumps
|
||||
from datetime import timedelta
|
||||
@@ -44,7 +45,7 @@ import threading
|
||||
import time
|
||||
import logging
|
||||
|
||||
__updated__ = '2016-03-07'
|
||||
__updated__ = '2017-11-15'
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -53,6 +54,7 @@ class DelayedTaskThread(threading.Thread):
|
||||
'''
|
||||
Class responsible of executing a delayed task in its own thread
|
||||
'''
|
||||
|
||||
def __init__(self, taskInstance):
|
||||
super(DelayedTaskThread, self).__init__()
|
||||
self._taskInstance = taskInstance
|
||||
@@ -68,7 +70,6 @@ class DelayedTaskRunner(object):
|
||||
'''
|
||||
Delayed task runner class
|
||||
'''
|
||||
CODEC = 'base64' # Can be zip, hez, bzip, base64, uuencoded
|
||||
# How often tasks r checked
|
||||
granularity = 2
|
||||
|
||||
@@ -106,7 +107,7 @@ class DelayedTaskRunner(object):
|
||||
try:
|
||||
with transaction.atomic(): # Encloses
|
||||
task = dbDelayedTask.objects.select_for_update().filter(filt).order_by('execution_time')[0] # @UndefinedVariable
|
||||
taskInstanceDump = task.instance.decode(self.CODEC)
|
||||
taskInstanceDump = encoders.decode(task.instance, 'base64')
|
||||
task.delete()
|
||||
taskInstance = loads(taskInstanceDump)
|
||||
except Exception:
|
||||
@@ -123,7 +124,7 @@ class DelayedTaskRunner(object):
|
||||
now = getSqlDatetime()
|
||||
exec_time = now + timedelta(seconds=delay)
|
||||
cls = instance.__class__
|
||||
instanceDump = dumps(instance).encode(self.CODEC)
|
||||
instanceDump = encoders.encode(dumps(instance), 'base64')
|
||||
typeName = str(cls.__module__ + '.' + cls.__name__)
|
||||
|
||||
logger.debug('Inserting delayed task {0} with {1} bytes ({2})'.format(typeName, len(instanceDump), exec_time))
|
||||
|
@@ -33,6 +33,7 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from uds.core.util import encoders
|
||||
from Crypto.PublicKey import RSA
|
||||
from OpenSSL import crypto
|
||||
from Crypto.Random import atfork
|
||||
@@ -40,7 +41,6 @@ import hashlib
|
||||
import array
|
||||
import uuid
|
||||
import datetime
|
||||
import codecs
|
||||
import random
|
||||
import string
|
||||
|
||||
@@ -57,8 +57,6 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CryptoManager(object):
|
||||
CODEC = 'base64'
|
||||
|
||||
instance = None
|
||||
|
||||
def __init__(self):
|
||||
@@ -77,7 +75,7 @@ class CryptoManager(object):
|
||||
value = value.encode('utf-8')
|
||||
|
||||
atfork()
|
||||
return six.text_type(codecs.encode(self._rsa.encrypt(value, six.b(''))[0], CryptoManager.CODEC))
|
||||
return encoders.encode((self._rsa.encrypt(value, six.b(''))[0]), 'base64', asText=True)
|
||||
|
||||
def decrypt(self, value):
|
||||
if isinstance(value, six.text_type):
|
||||
@@ -85,7 +83,7 @@ class CryptoManager(object):
|
||||
# import inspect
|
||||
try:
|
||||
atfork()
|
||||
return six.text_type(self._rsa.decrypt(value.decode(CryptoManager.CODEC)).decode('utf-8'))
|
||||
return six.text_type(self._rsa.decrypt(encoders.decode(value, 'base64')).decode('utf-8'))
|
||||
except Exception:
|
||||
logger.exception('Decripting: {0}'.format(value))
|
||||
# logger.error(inspect.stack())
|
||||
|
@@ -51,7 +51,7 @@ import requests
|
||||
import json
|
||||
import logging
|
||||
|
||||
__updated__ = '2017-03-22'
|
||||
__updated__ = '2017-11-17'
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
traceLogger = logging.getLogger('traceLog')
|
||||
@@ -428,6 +428,28 @@ class UserServiceManager(object):
|
||||
|
||||
# All done
|
||||
|
||||
def requestLogoff(self, uService):
|
||||
'''
|
||||
Ask client to logoff user
|
||||
'''
|
||||
url = uService.getCommsUrl()
|
||||
if url is None:
|
||||
logger.error('Can\'t connect with actor (no actor or legacy actor)')
|
||||
return
|
||||
url += '/logoff'
|
||||
|
||||
try:
|
||||
r = requests.post(url, data=json.dumps({}), headers={'content-type': 'application/json'}, verify=False, timeout=4)
|
||||
r = json.loads(r.content)
|
||||
logger.debug('Sent logoff to client using {}: {}'.format(url, r))
|
||||
# In fact we ignore result right now
|
||||
except Exception as e:
|
||||
# TODO: Right now, this is an "experimental" feature, not supported on Apps (but will)
|
||||
pass
|
||||
# logger.info('Logoff requested but service was not listening: {}'.format(e, url))
|
||||
|
||||
# All done
|
||||
|
||||
def checkForRemoval(self, uService):
|
||||
'''
|
||||
This method is used by UserService when a request for setInUse(False) is made
|
||||
@@ -455,16 +477,15 @@ class UserServiceManager(object):
|
||||
UserServiceOpChecker.makeUnique(uService, ui, state)
|
||||
except Exception as e:
|
||||
logger.exception('Unhandled exception on notyfyReady: {}'.format(e))
|
||||
UserService.setState(State.ERROR)
|
||||
uService.setState(State.ERROR)
|
||||
return
|
||||
|
||||
def getService(self, user, srcIp, idService, idTransport, doTest=True):
|
||||
'''
|
||||
Get service info from
|
||||
'''
|
||||
def locateUserService(self, user, idService, create=False):
|
||||
kind, idService = idService[0], idService[1:]
|
||||
|
||||
logger.debug('Kind of service: {0}, idService: {1}'.format(kind, idService))
|
||||
userService = None
|
||||
|
||||
if kind == 'A': # This is an assigned service
|
||||
logger.debug('Getting A service {}'.format(idService))
|
||||
userService = UserService.objects.get(uuid=idService)
|
||||
@@ -474,10 +495,21 @@ class UserServiceManager(object):
|
||||
# We first do a sanity check for this, if the user has access to this service
|
||||
# If it fails, will raise an exception
|
||||
ds.validateUser(user)
|
||||
|
||||
# Now we have to locate an instance of the service, so we can assign it to user.
|
||||
userService = self.getAssignationForUser(ds, user)
|
||||
if create: # getAssignation, if no assignation is found, tries to create one
|
||||
userService = self.getAssignationForUser(ds, user)
|
||||
else: # Sometimes maybe we only need to locate the existint user service
|
||||
userService = self.getExistingAssignationForUser(ds, user)
|
||||
|
||||
logger.debug('Found service: {0}'.format(userService))
|
||||
return userService
|
||||
|
||||
def getService(self, user, srcIp, idService, idTransport, doTest=True):
|
||||
'''
|
||||
Get service info from
|
||||
'''
|
||||
userService = self.locateUserService(user, idService, create=True)
|
||||
|
||||
if userService.isInMaintenance() is True:
|
||||
raise ServiceInMaintenanceMode()
|
||||
@@ -507,12 +539,10 @@ class UserServiceManager(object):
|
||||
if user is not None:
|
||||
userName = user.name
|
||||
|
||||
|
||||
if doTest is False:
|
||||
# traceLogger.info('GOT service "{}" for user "{}" with transport "{}" (NOT TESTED)'.format(userService.name, userName, trans.name))
|
||||
return (None, userService, None, trans, None)
|
||||
|
||||
|
||||
serviceNotReadyCode = 0x0001
|
||||
ip = 'unknown'
|
||||
# Test if the service is ready
|
||||
@@ -522,6 +552,7 @@ class UserServiceManager(object):
|
||||
# If ready, show transport for this service, if also ready ofc
|
||||
iads = userService.getInstance()
|
||||
ip = iads.getIp()
|
||||
userService.logIP(ip) # Update known ip
|
||||
|
||||
if self.checkUuid(userService) is False: # Machine is not what is expected
|
||||
serviceNotReadyCode = 0x0004
|
||||
@@ -539,8 +570,9 @@ class UserServiceManager(object):
|
||||
traceLogger.info('READY on service "{}" for user "{}" with transport "{}" (ip:{})'.format(userService.name, userName, trans.name, ip))
|
||||
return (ip, userService, iads, trans, itrans)
|
||||
else:
|
||||
log.doLog(userService, log.WARN, "User service is not accessible (ip {0})".format(ip), log.TRANSPORT)
|
||||
logger.debug('Transport is not ready for user service {0}'.format(userService))
|
||||
message = itrans.getCustomAvailableErrorMsg(userService, ip)
|
||||
log.doLog(userService, log.WARN, message, log.TRANSPORT)
|
||||
logger.debug('Transport is not ready for user service {}: {}'.format(userService, message))
|
||||
else:
|
||||
logger.debug('Ip not available from user service {0}'.format(userService))
|
||||
else:
|
||||
|
@@ -42,7 +42,7 @@ from uds.core import Module
|
||||
|
||||
import six
|
||||
|
||||
__updated__ = '2016-10-03'
|
||||
__updated__ = '2017-10-02'
|
||||
|
||||
STORAGE_KEY = 'osmk'
|
||||
|
||||
@@ -204,6 +204,9 @@ class OSManager(Module):
|
||||
knownUserIP = userService.src_ip + ':' + userService.src_hostname
|
||||
knownUserIP = knownUserIP if knownUserIP != ':' else 'unknown'
|
||||
|
||||
if userName is None:
|
||||
userName = 'unknown'
|
||||
|
||||
addEvent(userService.deployed_service, ET_LOGIN, fld1=userName, fld2=knownUserIP, fld3=serviceIp, fld4=fullUserName)
|
||||
|
||||
log.doLog(userService, log.INFO, "User {0} has logged in".format(userName), log.OSMANAGER)
|
||||
@@ -247,6 +250,9 @@ class OSManager(Module):
|
||||
knownUserIP = userService.src_ip + ':' + userService.src_hostname
|
||||
knownUserIP = knownUserIP if knownUserIP != ':' else 'unknown'
|
||||
|
||||
if userName is None:
|
||||
userName = 'unknown'
|
||||
|
||||
addEvent(userService.deployed_service, ET_LOGOUT, fld1=userName, fld2=knownUserIP, fld3=serviceIp, fld4=fullUserName)
|
||||
|
||||
log.doLog(userService, log.INFO, "User {0} has logged out".format(userName), log.OSMANAGER)
|
||||
|
@@ -35,13 +35,15 @@ from __future__ import unicode_literals
|
||||
from django.utils.translation import ugettext, ugettext_noop as _
|
||||
|
||||
from uds.core.ui.UserInterface import UserInterface
|
||||
from uds.core.util import encoders
|
||||
|
||||
import datetime
|
||||
import six
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
__updated__ = '2015-06-21'
|
||||
__updated__ = '2017-11-15'
|
||||
|
||||
|
||||
class Report(UserInterface):
|
||||
@@ -126,7 +128,7 @@ class Report(UserInterface):
|
||||
'''
|
||||
data = self.generate()
|
||||
if self.encoded:
|
||||
return data.encode('base64').replace('\n', '')
|
||||
return encoders.encode(data, 'base64', asText=True).replace('\n', '')
|
||||
else:
|
||||
return data
|
||||
|
||||
|
@@ -36,7 +36,7 @@ from uds.core import Environmentable
|
||||
from uds.core import Serializable
|
||||
from uds.core.util.State import State
|
||||
|
||||
__updated__ = '2016-02-26'
|
||||
__updated__ = '2017-09-29'
|
||||
|
||||
|
||||
class UserDeployment(Environmentable, Serializable):
|
||||
|
@@ -37,16 +37,18 @@ from django.utils.translation import ugettext_noop as _
|
||||
from uds.core.util import OsDetector
|
||||
from uds.core import Module
|
||||
from uds.core.transports import protocols
|
||||
from uds.core.util import encoders
|
||||
|
||||
import logging
|
||||
|
||||
__updated__ = '2017-08-02'
|
||||
__updated__ = '2017-11-15'
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
DIRECT_GROUP = _('Direct')
|
||||
TUNNELED_GROUP = _('Tunneled')
|
||||
|
||||
|
||||
class Transport(Module):
|
||||
'''
|
||||
An OS Manager is responsible for communication the service the different actions to take (i.e. adding a windows machine to a domain)
|
||||
@@ -113,6 +115,13 @@ class Transport(Module):
|
||||
'''
|
||||
return False
|
||||
|
||||
def getCustomAvailableErrorMsg(self, userService, ip):
|
||||
'''
|
||||
Returns a customized error message, that will be used when a service fails to check "isAvailableFor"
|
||||
Override this in yours transports if needed
|
||||
'''
|
||||
return "Not accessible (using service ip {0})".format(ip)
|
||||
|
||||
@classmethod
|
||||
def supportsProtocol(cls, protocol):
|
||||
if isinstance(protocol, (list, tuple)):
|
||||
@@ -181,6 +190,14 @@ from __future__ import unicode_literals
|
||||
raise Exception('The transport {transport.name} is not supported on your platform.')
|
||||
'''.format(service=userService, transport=transport)
|
||||
|
||||
def getEncodedTransportScript(self, userService, transport, ip, os, user, password, request):
|
||||
"""
|
||||
Encodes the script so the client can understand it
|
||||
"""
|
||||
script = self.getUDSTransportScript(userService, transport, ip, os, user, password, request)
|
||||
logger.debug('Transport script: {}'.format(script))
|
||||
return encoders.encode(encoders.encode(script, 'bz2'), 'base64', asText=True).replace('\n', '')
|
||||
|
||||
def getLink(self, userService, transport, ip, os, user, password, request):
|
||||
'''
|
||||
Must override if transport does provides its own link
|
||||
|
@@ -87,6 +87,7 @@ class gui(object):
|
||||
PARAMETERS_TAB = ugettext_noop('Parameters')
|
||||
CREDENTIALS_TAB = ugettext_noop('Credentials')
|
||||
TUNNEL_TAB = ugettext_noop('Tunnel')
|
||||
DISPLAY_TAB = ugettext_noop('Display')
|
||||
|
||||
# : Static Callbacks simple registry
|
||||
callbacks = {}
|
||||
@@ -347,6 +348,7 @@ class gui(object):
|
||||
tooltip = _('Other info'), rdonly = True)
|
||||
|
||||
'''
|
||||
|
||||
def __init__(self, **options):
|
||||
super(self.__class__, self).__init__(**options)
|
||||
self._type(gui.InputField.TEXT_TYPE)
|
||||
@@ -375,6 +377,7 @@ class gui(object):
|
||||
defvalue = '443', order = 1, tooltip = _('Port (usually 443)'),
|
||||
required = True)
|
||||
'''
|
||||
|
||||
def __init__(self, **options):
|
||||
super(self.__class__, self).__init__(**options)
|
||||
minValue = options.get('minValue', '987654321')
|
||||
@@ -463,6 +466,7 @@ class gui(object):
|
||||
required = True)
|
||||
|
||||
'''
|
||||
|
||||
def __init__(self, **options):
|
||||
super(self.__class__, self).__init__(**options)
|
||||
self._type(gui.InputField.PASSWORD_TYPE)
|
||||
@@ -498,6 +502,7 @@ class gui(object):
|
||||
self.hidden.setDefValue(self.parent().serialize())
|
||||
|
||||
'''
|
||||
|
||||
def __init__(self, **options):
|
||||
super(self.__class__, self).__init__(**options)
|
||||
self._isSerializable = options.get('serializable', '') != ''
|
||||
@@ -525,6 +530,7 @@ class gui(object):
|
||||
tooltip = _('If checked, will use a ssl connection'))
|
||||
|
||||
'''
|
||||
|
||||
def __init__(self, **options):
|
||||
super(self.__class__, self).__init__(**options)
|
||||
self._type(gui.InputField.CHECKBOX_TYPE)
|
||||
@@ -628,6 +634,7 @@ class gui(object):
|
||||
ev = gui.HiddenField() # ....
|
||||
|
||||
'''
|
||||
|
||||
def __init__(self, **options):
|
||||
super(self.__class__, self).__init__(**options)
|
||||
self._data['values'] = options.get('values', [])
|
||||
@@ -647,6 +654,7 @@ class gui(object):
|
||||
self._data['values'] = values
|
||||
|
||||
class ImageChoiceField(InputField):
|
||||
|
||||
def __init__(self, **options):
|
||||
super(self.__class__, self).__init__(**options)
|
||||
self._data['values'] = options.get('values', [])
|
||||
@@ -692,6 +700,7 @@ class gui(object):
|
||||
{'id': '1', 'text': 'datastore1' } ]
|
||||
)
|
||||
'''
|
||||
|
||||
def __init__(self, **options):
|
||||
super(self.__class__, self).__init__(**options)
|
||||
self._data['values'] = options.get('values', [])
|
||||
@@ -750,6 +759,7 @@ class gui(object):
|
||||
'''
|
||||
Image field
|
||||
'''
|
||||
|
||||
def __init__(self, **options):
|
||||
super(self.__class__, self).__init__(**options)
|
||||
self._type(gui.InputField.TEXT_TYPE)
|
||||
@@ -758,6 +768,7 @@ class gui(object):
|
||||
'''
|
||||
Informational field (no input is done)
|
||||
'''
|
||||
|
||||
def __init__(self, **options):
|
||||
super(self.__class__, self).__init__(**options)
|
||||
self._type(gui.InputField.INFO_TYPE)
|
||||
@@ -768,6 +779,7 @@ class UserInterfaceType(type):
|
||||
Metaclass definition for moving the user interface descriptions to a usable
|
||||
better place
|
||||
'''
|
||||
|
||||
def __new__(cls, classname, bases, classDict):
|
||||
newClassDict = {}
|
||||
_gui = {}
|
||||
|
@@ -32,6 +32,7 @@
|
||||
'''
|
||||
|
||||
from uds.core.Serializable import Serializable
|
||||
from uds.core.util import encoders
|
||||
import pickle
|
||||
import timeit
|
||||
|
||||
@@ -67,11 +68,6 @@ class AutoAttributes(Serializable):
|
||||
Access attrs as "self._attr1, self._attr2"
|
||||
'''
|
||||
|
||||
# : This codec is not intended to override Serializable codec
|
||||
# : Serializable codec is for encoding marshaled data,
|
||||
# : while this codec is for encoding pickled data from autoattributes
|
||||
ACODEC = 'zip'
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.declare(**kwargs)
|
||||
|
||||
@@ -93,13 +89,17 @@ class AutoAttributes(Serializable):
|
||||
self.dict = d
|
||||
|
||||
def marshal(self):
|
||||
return '\2'.join(['%s\1%s' % (k, pickle.dumps(v)) for k, v in self.dict.iteritems()]).encode(AutoAttributes.ACODEC)
|
||||
return encoders.encode('\2'.join(['%s\1%s' % (k, pickle.dumps(v)) for k, v in self.dict.iteritems()]), 'bz2')
|
||||
|
||||
def unmarshal(self, data):
|
||||
if data == '': # Can be empty
|
||||
return
|
||||
# We keep original data (maybe incomplete)
|
||||
for pair in data.decode(AutoAttributes.ACODEC).split('\2'):
|
||||
try:
|
||||
data = encoders.decode(data, 'bz2')
|
||||
except Exception: # With old zip encoding
|
||||
data = encoders.decode(data, 'zip')
|
||||
for pair in data.split('\2'):
|
||||
k, v = pair.split('\1')
|
||||
self.dict[k] = pickle.loads(str(v))
|
||||
|
||||
|
@@ -34,6 +34,7 @@ from __future__ import unicode_literals
|
||||
from django.db import transaction
|
||||
import uds.models.Cache
|
||||
from uds.models import getSqlDatetime
|
||||
from uds.core.util import encoders
|
||||
from datetime import datetime, timedelta
|
||||
import hashlib
|
||||
import logging
|
||||
@@ -47,7 +48,6 @@ class Cache(object):
|
||||
misses = 0
|
||||
|
||||
DEFAULT_VALIDITY = 60
|
||||
CODEC = 'base64' # Can be zip, hez, bzip, base64, uuencoded
|
||||
|
||||
def __init__(self, owner):
|
||||
self._owner = owner.encode('utf-8')
|
||||
@@ -67,7 +67,7 @@ class Cache(object):
|
||||
expired = now > c.created + timedelta(seconds=c.validity)
|
||||
if expired:
|
||||
return defValue
|
||||
val = pickle.loads(c.value.decode(Cache.CODEC))
|
||||
val = pickle.loads(encoders.decode(c.value, 'base64'))
|
||||
Cache.hits += 1
|
||||
return val
|
||||
except uds.models.Cache.DoesNotExist: # @UndefinedVariable
|
||||
@@ -97,7 +97,7 @@ class Cache(object):
|
||||
if validity is None:
|
||||
validity = Cache.DEFAULT_VALIDITY
|
||||
key = self.__getKey(skey)
|
||||
value = pickle.dumps(value).encode(Cache.CODEC)
|
||||
value = encoders.encode(pickle.dumps(value), 'base64')
|
||||
now = getSqlDatetime()
|
||||
try:
|
||||
uds.models.Cache.objects.create(owner=self._owner, key=key, value=value, created=now, validity=validity) # @UndefinedVariable
|
||||
|
@@ -50,6 +50,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FileStorage(Storage):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._base_url = getattr(settings, 'FILE_STORAGE', '/files')
|
||||
if self._base_url[-1] != '/':
|
||||
@@ -60,6 +61,7 @@ class FileStorage(Storage):
|
||||
try:
|
||||
cache = caches[cacheName]
|
||||
except:
|
||||
logger.info('No cache for FileStorage configured.')
|
||||
cache = None
|
||||
|
||||
self.cache = cache
|
||||
@@ -73,7 +75,6 @@ class FileStorage(Storage):
|
||||
|
||||
Storage.__init__(self, *args, **kwargs)
|
||||
|
||||
|
||||
def get_valid_name(self, name):
|
||||
return name.replace('\\', os.path.sep)
|
||||
|
||||
@@ -119,7 +120,6 @@ class FileStorage(Storage):
|
||||
return
|
||||
self.cache.delete(self._getKey(name))
|
||||
|
||||
|
||||
def _open(self, name, mode='rb'):
|
||||
f = six.BytesIO(self._dbFileForReadOnly(name).data)
|
||||
f.name = name
|
||||
@@ -173,7 +173,9 @@ class FileStorage(Storage):
|
||||
except DBFile.DoesNotExist:
|
||||
return None
|
||||
|
||||
|
||||
class CompressorFileStorage(FileStorage):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
FileStorage.__init__(self, *args, **dict(kwargs, owner='compressor'))
|
||||
|
||||
|
@@ -34,6 +34,7 @@ from __future__ import unicode_literals
|
||||
|
||||
from django.db import transaction
|
||||
from uds.models import Storage as dbStorage
|
||||
from uds.core.util import encoders
|
||||
import hashlib
|
||||
import logging
|
||||
import pickle
|
||||
@@ -42,7 +43,6 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Storage(object):
|
||||
CODEC = 'base64' # Can be zip, hez, bzip, base64, uuencoded
|
||||
|
||||
def __init__(self, owner):
|
||||
self._owner = owner.encode('utf-8')
|
||||
@@ -57,7 +57,7 @@ class Storage(object):
|
||||
key = self.__getKey(skey)
|
||||
if isinstance(data, unicode):
|
||||
data = data.encode('utf-8')
|
||||
data = data.encode(Storage.CODEC)
|
||||
data = encoders.encode(data, 'base64')
|
||||
attr1 = '' if attr1 is None else attr1
|
||||
try:
|
||||
dbStorage.objects.create(owner=self._owner, key=key, data=data, attr1=attr1) # @UndefinedVariable
|
||||
@@ -79,7 +79,7 @@ class Storage(object):
|
||||
key = self.__getKey(skey)
|
||||
logger.debug('Accesing to {0} {1}'.format(skey, key))
|
||||
c = dbStorage.objects.get(pk=key) # @UndefinedVariable
|
||||
val = c.data.decode(Storage.CODEC)
|
||||
val = encoders.decode(c.data, 'base64')
|
||||
|
||||
if fromPickle:
|
||||
return val
|
||||
@@ -103,7 +103,7 @@ class Storage(object):
|
||||
|
||||
def getPickleByAttr1(self, attr1):
|
||||
try:
|
||||
return pickle.loads(dbStorage.objects.get(owner=self._owner, attr1=attr1).data.decode(Storage.CODEC)) # @UndefinedVariable
|
||||
return pickle.loads(encoders.decode(dbStorage.objects.get(owner=self._owner, attr1=attr1).data, 'base64')) # @UndefinedVariable
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
@@ -133,7 +133,7 @@ class Storage(object):
|
||||
query = dbStorage.objects.filter(owner=self._owner, attr1=attr1) # @UndefinedVariable
|
||||
|
||||
for v in query:
|
||||
yield v.data.decode(Storage.CODEC)
|
||||
yield encoders.decode(v.data, 'base64')
|
||||
|
||||
def filter(self, attr1):
|
||||
if attr1 is None:
|
||||
@@ -142,7 +142,7 @@ class Storage(object):
|
||||
query = dbStorage.objects.filter(owner=self._owner, attr1=attr1) # @UndefinedVariable
|
||||
|
||||
for v in query: # @UndefinedVariable
|
||||
yield (v.key, v.data.decode(Storage.CODEC), v.attr1)
|
||||
yield (v.key, encoders.decode(v.data, 'base64'), v.attr1)
|
||||
|
||||
def filterPickle(self, attr1=None):
|
||||
for v in self.filter(attr1):
|
||||
@@ -150,7 +150,6 @@ class Storage(object):
|
||||
|
||||
@staticmethod
|
||||
def delete(owner=None):
|
||||
logger.info("Deleting storage items")
|
||||
if owner is None:
|
||||
objects = dbStorage.objects.all() # @UndefinedVariable
|
||||
else:
|
||||
|
@@ -46,8 +46,7 @@ import six
|
||||
import bitarray
|
||||
import logging
|
||||
|
||||
__updated__ = '2016-10-31'
|
||||
|
||||
__updated__ = '2017-12-12'
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -113,14 +112,14 @@ class CalendarChecker(object):
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def _updateEvents(self, checkFrom, startEvent=True):
|
||||
|
||||
next_event = None
|
||||
for rule in self.calendar.rules.all():
|
||||
if rule.start > checkFrom or (rule.end is not None and rule.end < checkFrom.date()):
|
||||
# logger.debug('RULE: start = {}, checkFrom = {}, end'.format(rule.start.date(), checkFrom.date()))
|
||||
if rule.end is not None and rule.end < checkFrom.date():
|
||||
continue
|
||||
|
||||
# logger.debug('Rule in check interval...')
|
||||
if startEvent:
|
||||
event = rule.as_rrule().after(checkFrom) # At start
|
||||
else:
|
||||
@@ -162,7 +161,7 @@ class CalendarChecker(object):
|
||||
Returns next event for this interval
|
||||
Returns a list of two elements. First is datetime of event begining, second is timedelta of duration
|
||||
'''
|
||||
logger.debug('Obtainint nextEvent')
|
||||
logger.debug('Obtaining nextEvent')
|
||||
if checkFrom is None:
|
||||
checkFrom = getSqlDatetime()
|
||||
|
||||
@@ -173,7 +172,7 @@ class CalendarChecker(object):
|
||||
next_event = CalendarChecker.cache.get(cacheKey, None)
|
||||
if next_event is None:
|
||||
logger.debug('Regenerating cached nextEvent')
|
||||
next_event = self._updateEvents(checkFrom - offset, startEvent) # We substract on checkin, so we can take into account for next execution the "offset" on start & end (just the inverse of current, so we substract it)
|
||||
next_event = self._updateEvents(checkFrom + offset, startEvent) # We substract on checkin, so we can take into account for next execution the "offset" on start & end (just the inverse of current, so we substract it)
|
||||
if next_event is not None:
|
||||
next_event += offset
|
||||
CalendarChecker.cache.put(cacheKey, next_event, 3600)
|
||||
@@ -183,7 +182,5 @@ class CalendarChecker(object):
|
||||
|
||||
return next_event
|
||||
|
||||
|
||||
def debug(self):
|
||||
|
||||
return "Calendar checker for {}".format(self.calendar)
|
||||
|
55
server/src/uds/core/util/encoders.py
Normal file
55
server/src/uds/core/util/encoders.py
Normal file
@@ -0,0 +1,55 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2017 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.
|
||||
|
||||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
import six
|
||||
import codecs
|
||||
|
||||
|
||||
def __toBinary(data):
|
||||
if isinstance(data, six.text_type):
|
||||
return data.encode('utf8')
|
||||
return data
|
||||
|
||||
|
||||
def encode(data, encoder, asText=False):
|
||||
res = codecs.encode(__toBinary(data), encoder)
|
||||
if asText:
|
||||
return res.decode('utf8')
|
||||
return res
|
||||
|
||||
|
||||
def decode(data, encoder, asText=False):
|
||||
res = codecs.decode(__toBinary(data), encoder)
|
||||
if asText:
|
||||
return res.decode('utf8')
|
||||
return res
|
@@ -27,6 +27,8 @@
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from uds.core.util.Config import GlobalConfig
|
||||
from django.http import HttpResponseRedirect
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -42,3 +44,31 @@ class XUACompatibleMiddleware(object):
|
||||
if response.get('content-type', '').startswith('text/html'):
|
||||
response['X-UA-Compatible'] = 'IE=edge'
|
||||
return response
|
||||
|
||||
class RedirectMiddleware(object):
|
||||
NO_REDIRECT = [
|
||||
'rest',
|
||||
'pam',
|
||||
'guacamole',
|
||||
]
|
||||
|
||||
def process_request(self, request):
|
||||
full_path = request.get_full_path()
|
||||
redirect = True
|
||||
for nr in RedirectMiddleware.NO_REDIRECT:
|
||||
if full_path.startswith('/' + nr):
|
||||
redirect = False
|
||||
break
|
||||
|
||||
if GlobalConfig.REDIRECT_TO_HTTPS.getBool() and request.is_secure() is False and redirect:
|
||||
if request.method == 'POST':
|
||||
url = request.build_absolute_uri(GlobalConfig.LOGIN_URL.get())
|
||||
else:
|
||||
url = request.build_absolute_uri(full_path)
|
||||
url = url.replace('http://', 'https://')
|
||||
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
@staticmethod
|
||||
def registerException(path):
|
||||
RedirectMiddleware.NO_REDIRECT.append(path)
|
||||
|
@@ -55,8 +55,8 @@ class AssignedAndUnused(Job):
|
||||
def run(self):
|
||||
since_state = getSqlDatetime() - timedelta(seconds=self.frecuency)
|
||||
for ds in DeployedService.objects.all():
|
||||
# Skips checking deployed services in maintenance mode
|
||||
if ds.isInMaintenance() is True:
|
||||
# Skips checking deployed services in maintenance mode or ignores assigned and unused
|
||||
if ds.isInMaintenance() is True or ds.ignores_unused:
|
||||
continue
|
||||
# If do not needs os manager, this is
|
||||
if ds.osmanager is not None:
|
||||
|
@@ -34,7 +34,6 @@ from __future__ import unicode_literals
|
||||
|
||||
from django.conf.urls import patterns
|
||||
|
||||
|
||||
urlpatterns = patterns(__package__,
|
||||
(r'^guacamole/(?P<tunnelId>.+)$', 'views.guacamole'),
|
||||
)
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,22 +1,24 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
#
|
||||
# Translators:
|
||||
# Abdel Baaddi <abaaddi@virtualcable.es>, 2016-2017
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: OpenUDS\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-06-06 13:15+0200\n"
|
||||
"PO-Revision-Date: 2017-02-10 11:02+0000\n"
|
||||
"POT-Creation-Date: 2017-12-21 19:09+0100\n"
|
||||
"PO-Revision-Date: 2017-10-04 11:09+0000\n"
|
||||
"Last-Translator: Adolfo Gómez <dkmaster@dkmon.com>\n"
|
||||
"Language-Team: Arabic (http://www.transifex.com/openuds/openuds/language/ar/)\n"
|
||||
"Language-Team: Arabic (http://www.transifex.com/openuds/openuds/language/"
|
||||
"ar/)\n"
|
||||
"Language: ar\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ar\n"
|
||||
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
|
||||
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
|
||||
"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
|
||||
|
||||
#: static/tmp_js/api-tools.js:322
|
||||
msgid "Sunday"
|
||||
@@ -94,7 +96,7 @@ msgstr "تشرين الثاني/نوفمبر"
|
||||
msgid "December"
|
||||
msgstr "كانون الأول/ديسمبر"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:10 static/tmp_js/gui-d-services.js:10
|
||||
#: static/tmp_js/gui-d-authenticators.js:10 static/tmp_js/gui-d-services.js:31
|
||||
msgid "Test"
|
||||
msgstr "اختبار"
|
||||
|
||||
@@ -108,14 +110,14 @@ msgstr "قبول"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:151
|
||||
#: static/tmp_js/gui-d-connectivity.js:43 static/tmp_js/gui-d-osmanagers.js:39
|
||||
#: static/tmp_js/gui-d-services.js:86 static/tmp_js/gui-d-services.js:128
|
||||
#: static/tmp_js/gui-d-services.js:108 static/tmp_js/gui-d-services.js:150
|
||||
#: static/tmp_js/gui-d-servicespools.js:220
|
||||
msgid "Error accessing data"
|
||||
msgstr "خطأ في الوصول إلى البيانات"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:168
|
||||
#: static/tmp_js/gui-d-authenticators.js:395
|
||||
#: static/tmp_js/gui-d-services.js:108
|
||||
#: static/tmp_js/gui-d-services.js:130
|
||||
msgid "Information"
|
||||
msgstr "معلومات"
|
||||
|
||||
@@ -241,8 +243,8 @@ msgid "days"
|
||||
msgstr "أيام"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:23
|
||||
msgid "Dayly"
|
||||
msgstr "يومياً"
|
||||
msgid "Daily"
|
||||
msgstr "اليومية"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:24
|
||||
msgid "week"
|
||||
@@ -516,87 +518,95 @@ msgstr "حدث خطأ أثناء إنشاء التقرير"
|
||||
msgid "Error obtaining report description"
|
||||
msgstr "خطأ في الحصول على وصف التقرير"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:63
|
||||
#: static/tmp_js/gui-d-services.js:85
|
||||
msgid "In Maintenance"
|
||||
msgstr "في وضع الصيانة"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:65
|
||||
#: static/tmp_js/gui-d-services.js:87
|
||||
msgid "Active"
|
||||
msgstr "نشيط"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:125
|
||||
#: static/tmp_js/gui-d-services.js:147
|
||||
msgid "Service information"
|
||||
msgstr "معلومات الخدمة"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:183
|
||||
#: static/tmp_js/gui-d-services.js:205
|
||||
msgid "Edit service"
|
||||
msgstr "تحرير خدمة"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:183
|
||||
#: static/tmp_js/gui-d-services.js:205
|
||||
msgid "Service creation error"
|
||||
msgstr "خطأ في إنشاء خدمة"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:184
|
||||
#: static/tmp_js/gui-d-services.js:206
|
||||
msgid "New service"
|
||||
msgstr "خدمة جديدة"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:184
|
||||
#: static/tmp_js/gui-d-services.js:206
|
||||
msgid "Service saving error"
|
||||
msgstr "خطأ في حفظ الخدمة"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:185
|
||||
#: static/tmp_js/gui-d-services.js:207
|
||||
msgid "Delete service"
|
||||
msgstr "حذف الخدمة"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:185
|
||||
#: static/tmp_js/gui-d-services.js:207
|
||||
msgid "Service deletion error"
|
||||
msgstr "خطأ في حذف الخدمة"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:201 static/tmp_js/gui-d-services.js:224
|
||||
#: static/tmp_js/gui-d-services.js:235
|
||||
msgid "Delete user service"
|
||||
msgstr "حذف خدمة المستخدم"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:235
|
||||
msgid "User service deletion error"
|
||||
msgstr "خطأ في حذف خدمة المستخدم"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:246 static/tmp_js/gui-d-services.js:269
|
||||
msgid "Maintenance"
|
||||
msgstr "صيانة"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:210
|
||||
#: static/tmp_js/gui-d-services.js:255
|
||||
msgid "Maintenance Mode"
|
||||
msgstr "وضع الصيانة"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:210
|
||||
#: static/tmp_js/gui-d-services.js:255
|
||||
msgid "Enter Maintenance Mode?"
|
||||
msgstr "هل تريد دخول وضع الصيانة ؟"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:210
|
||||
#: static/tmp_js/gui-d-services.js:255
|
||||
msgid "Exit Maintenance Mode?"
|
||||
msgstr "هل تريد الخروج من وضع الصيانة؟"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:229
|
||||
#: static/tmp_js/gui-d-services.js:274
|
||||
msgid "Enter maintenance Mode"
|
||||
msgstr "دخول وضع الصيانة"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:232
|
||||
#: static/tmp_js/gui-d-services.js:277
|
||||
msgid "Exit Maintenance Mode"
|
||||
msgstr "إنهاء وضع الصيانة"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:240
|
||||
#: static/tmp_js/gui-d-services.js:285
|
||||
msgid "New services provider"
|
||||
msgstr "موفر خدمات جديد"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:240
|
||||
#: static/tmp_js/gui-d-services.js:285
|
||||
msgid "Services provider creation error"
|
||||
msgstr "خطأ في إنشاء موفر الخدمات"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:241
|
||||
#: static/tmp_js/gui-d-services.js:286
|
||||
msgid "Edit services provider"
|
||||
msgstr "تحرير موفر الخدمات"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:241
|
||||
#: static/tmp_js/gui-d-services.js:286
|
||||
msgid "Services Provider saving error"
|
||||
msgstr "خطأ في حفظ موفر الخدمات "
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:242
|
||||
#: static/tmp_js/gui-d-services.js:287
|
||||
msgid "Delete services provider"
|
||||
msgstr "حذف موفر الخدمات"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:242
|
||||
#: static/tmp_js/gui-d-services.js:287
|
||||
msgid "Services Provider deletion error"
|
||||
msgstr "خطأ في حذف موفر الخدمات"
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,21 +1,22 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
#
|
||||
# Translators:
|
||||
# Adolfo Gómez <dkmaster at dkmon dot com>, 2012
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: OpenUDS\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-06-06 13:15+0200\n"
|
||||
"PO-Revision-Date: 2017-09-20 01:16+0000\n"
|
||||
"POT-Creation-Date: 2017-12-21 19:09+0100\n"
|
||||
"PO-Revision-Date: 2017-10-04 11:09+0000\n"
|
||||
"Last-Translator: Adolfo Gómez <dkmaster@dkmon.com>\n"
|
||||
"Language-Team: German (http://www.transifex.com/openuds/openuds/language/de/)\n"
|
||||
"Language-Team: German (http://www.transifex.com/openuds/openuds/language/"
|
||||
"de/)\n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: de\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: static/tmp_js/api-tools.js:322
|
||||
@@ -94,7 +95,7 @@ msgstr "November"
|
||||
msgid "December"
|
||||
msgstr "Dezember"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:10 static/tmp_js/gui-d-services.js:10
|
||||
#: static/tmp_js/gui-d-authenticators.js:10 static/tmp_js/gui-d-services.js:31
|
||||
msgid "Test"
|
||||
msgstr "Test"
|
||||
|
||||
@@ -108,14 +109,14 @@ msgstr "Akzeptieren"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:151
|
||||
#: static/tmp_js/gui-d-connectivity.js:43 static/tmp_js/gui-d-osmanagers.js:39
|
||||
#: static/tmp_js/gui-d-services.js:86 static/tmp_js/gui-d-services.js:128
|
||||
#: static/tmp_js/gui-d-services.js:108 static/tmp_js/gui-d-services.js:150
|
||||
#: static/tmp_js/gui-d-servicespools.js:220
|
||||
msgid "Error accessing data"
|
||||
msgstr "Fehler beim Zugriff auf Daten"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:168
|
||||
#: static/tmp_js/gui-d-authenticators.js:395
|
||||
#: static/tmp_js/gui-d-services.js:108
|
||||
#: static/tmp_js/gui-d-services.js:130
|
||||
msgid "Information"
|
||||
msgstr "Informationen"
|
||||
|
||||
@@ -241,8 +242,8 @@ msgid "days"
|
||||
msgstr "Tagen"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:23
|
||||
msgid "Dayly"
|
||||
msgstr "Täglich"
|
||||
msgid "Daily"
|
||||
msgstr "Tägliche"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:24
|
||||
msgid "week"
|
||||
@@ -516,87 +517,95 @@ msgstr "Fehler beim Erstellen des Berichts"
|
||||
msgid "Error obtaining report description"
|
||||
msgstr "Herbeiführende Bericht Fehlerbeschreibung."
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:63
|
||||
#: static/tmp_js/gui-d-services.js:85
|
||||
msgid "In Maintenance"
|
||||
msgstr "In der Pflege"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:65
|
||||
#: static/tmp_js/gui-d-services.js:87
|
||||
msgid "Active"
|
||||
msgstr "Aktive"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:125
|
||||
#: static/tmp_js/gui-d-services.js:147
|
||||
msgid "Service information"
|
||||
msgstr "Service-Informationen"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:183
|
||||
#: static/tmp_js/gui-d-services.js:205
|
||||
msgid "Edit service"
|
||||
msgstr "Bearbeiten service"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:183
|
||||
#: static/tmp_js/gui-d-services.js:205
|
||||
msgid "Service creation error"
|
||||
msgstr "Fehler beim Erstellen von Service"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:184
|
||||
#: static/tmp_js/gui-d-services.js:206
|
||||
msgid "New service"
|
||||
msgstr "Neuer service"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:184
|
||||
#: static/tmp_js/gui-d-services.js:206
|
||||
msgid "Service saving error"
|
||||
msgstr "Service Fehler speichern"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:185
|
||||
#: static/tmp_js/gui-d-services.js:207
|
||||
msgid "Delete service"
|
||||
msgstr "Dienst löschen"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:185
|
||||
#: static/tmp_js/gui-d-services.js:207
|
||||
msgid "Service deletion error"
|
||||
msgstr "Dienstfehler löschen"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:201 static/tmp_js/gui-d-services.js:224
|
||||
#: static/tmp_js/gui-d-services.js:235
|
||||
msgid "Delete user service"
|
||||
msgstr "Löschen Sie Benutzerservice"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:235
|
||||
msgid "User service deletion error"
|
||||
msgstr "Benutzer Dienstfehler löschen"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:246 static/tmp_js/gui-d-services.js:269
|
||||
msgid "Maintenance"
|
||||
msgstr "Wartung"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:210
|
||||
#: static/tmp_js/gui-d-services.js:255
|
||||
msgid "Maintenance Mode"
|
||||
msgstr "Wartungsmodus"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:210
|
||||
#: static/tmp_js/gui-d-services.js:255
|
||||
msgid "Enter Maintenance Mode?"
|
||||
msgstr "Geben Sie im Wartungsmodus?"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:210
|
||||
#: static/tmp_js/gui-d-services.js:255
|
||||
msgid "Exit Maintenance Mode?"
|
||||
msgstr "Beenden Wartungsmodus zu?"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:229
|
||||
#: static/tmp_js/gui-d-services.js:274
|
||||
msgid "Enter maintenance Mode"
|
||||
msgstr "Geben Sie Wartung Modus"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:232
|
||||
#: static/tmp_js/gui-d-services.js:277
|
||||
msgid "Exit Maintenance Mode"
|
||||
msgstr "Ausfahrt-Wartungsmodus"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:240
|
||||
#: static/tmp_js/gui-d-services.js:285
|
||||
msgid "New services provider"
|
||||
msgstr "Neuer Anbieter für Dienste"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:240
|
||||
#: static/tmp_js/gui-d-services.js:285
|
||||
msgid "Services provider creation error"
|
||||
msgstr "Fehler beim Erstellen der Provider-Dienste"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:241
|
||||
#: static/tmp_js/gui-d-services.js:286
|
||||
msgid "Edit services provider"
|
||||
msgstr "Dienstleister bearbeiten"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:241
|
||||
#: static/tmp_js/gui-d-services.js:286
|
||||
msgid "Services Provider saving error"
|
||||
msgstr "Speichern von Fehler-Dienstleister"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:242
|
||||
#: static/tmp_js/gui-d-services.js:287
|
||||
msgid "Delete services provider"
|
||||
msgstr "Löschen-Dienstleister"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:242
|
||||
#: static/tmp_js/gui-d-services.js:287
|
||||
msgid "Services Provider deletion error"
|
||||
msgstr "Service Provider löschen Fehler"
|
||||
|
||||
@@ -747,7 +756,8 @@ msgstr "Bei der Erstellung zu veröffentlichen"
|
||||
|
||||
#: static/tmp_js/gui-d-servicespools.js:450
|
||||
msgid "If selected, will initiate the publication inmediatly after creation"
|
||||
msgstr "Wenn ausgewählt, wird die Publikation Inmediatly nach Erstellung einleiten."
|
||||
msgstr ""
|
||||
"Wenn ausgewählt, wird die Publikation Inmediatly nach Erstellung einleiten."
|
||||
|
||||
#: static/tmp_js/gui-d-servicespools.js:462 static/tmp_js/gui.js:50
|
||||
msgid "Edit"
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-06-06 13:15+0200\n"
|
||||
"POT-Creation-Date: 2017-12-21 19:09+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -93,7 +93,7 @@ msgstr ""
|
||||
msgid "December"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:10 static/tmp_js/gui-d-services.js:10
|
||||
#: static/tmp_js/gui-d-authenticators.js:10 static/tmp_js/gui-d-services.js:31
|
||||
msgid "Test"
|
||||
msgstr ""
|
||||
|
||||
@@ -107,14 +107,14 @@ msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:151
|
||||
#: static/tmp_js/gui-d-connectivity.js:43 static/tmp_js/gui-d-osmanagers.js:39
|
||||
#: static/tmp_js/gui-d-services.js:86 static/tmp_js/gui-d-services.js:128
|
||||
#: static/tmp_js/gui-d-services.js:108 static/tmp_js/gui-d-services.js:150
|
||||
#: static/tmp_js/gui-d-servicespools.js:220
|
||||
msgid "Error accessing data"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:168
|
||||
#: static/tmp_js/gui-d-authenticators.js:395
|
||||
#: static/tmp_js/gui-d-services.js:108
|
||||
#: static/tmp_js/gui-d-services.js:130
|
||||
msgid "Information"
|
||||
msgstr ""
|
||||
|
||||
@@ -240,7 +240,7 @@ msgid "days"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:23
|
||||
msgid "Dayly"
|
||||
msgid "Daily"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:24
|
||||
@@ -515,87 +515,95 @@ msgstr ""
|
||||
msgid "Error obtaining report description"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:63
|
||||
#: static/tmp_js/gui-d-services.js:85
|
||||
msgid "In Maintenance"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:65
|
||||
#: static/tmp_js/gui-d-services.js:87
|
||||
msgid "Active"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:125
|
||||
#: static/tmp_js/gui-d-services.js:147
|
||||
msgid "Service information"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:183
|
||||
#: static/tmp_js/gui-d-services.js:205
|
||||
msgid "Edit service"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:183
|
||||
#: static/tmp_js/gui-d-services.js:205
|
||||
msgid "Service creation error"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:184
|
||||
#: static/tmp_js/gui-d-services.js:206
|
||||
msgid "New service"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:184
|
||||
#: static/tmp_js/gui-d-services.js:206
|
||||
msgid "Service saving error"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:185
|
||||
#: static/tmp_js/gui-d-services.js:207
|
||||
msgid "Delete service"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:185
|
||||
#: static/tmp_js/gui-d-services.js:207
|
||||
msgid "Service deletion error"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:201 static/tmp_js/gui-d-services.js:224
|
||||
#: static/tmp_js/gui-d-services.js:235
|
||||
msgid "Delete user service"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:235
|
||||
msgid "User service deletion error"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:246 static/tmp_js/gui-d-services.js:269
|
||||
msgid "Maintenance"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:210
|
||||
#: static/tmp_js/gui-d-services.js:255
|
||||
msgid "Maintenance Mode"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:210
|
||||
#: static/tmp_js/gui-d-services.js:255
|
||||
msgid "Enter Maintenance Mode?"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:210
|
||||
#: static/tmp_js/gui-d-services.js:255
|
||||
msgid "Exit Maintenance Mode?"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:229
|
||||
#: static/tmp_js/gui-d-services.js:274
|
||||
msgid "Enter maintenance Mode"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:232
|
||||
#: static/tmp_js/gui-d-services.js:277
|
||||
msgid "Exit Maintenance Mode"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:240
|
||||
#: static/tmp_js/gui-d-services.js:285
|
||||
msgid "New services provider"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:240
|
||||
#: static/tmp_js/gui-d-services.js:285
|
||||
msgid "Services provider creation error"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:241
|
||||
#: static/tmp_js/gui-d-services.js:286
|
||||
msgid "Edit services provider"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:241
|
||||
#: static/tmp_js/gui-d-services.js:286
|
||||
msgid "Services Provider saving error"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:242
|
||||
#: static/tmp_js/gui-d-services.js:287
|
||||
msgid "Delete services provider"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:242
|
||||
#: static/tmp_js/gui-d-services.js:287
|
||||
msgid "Services Provider deletion error"
|
||||
msgstr ""
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
#
|
||||
# Translators:
|
||||
# , 2014
|
||||
# Adolfo Gómez <dkmaster@dkmon.com>, 2014-2015,2017
|
||||
@@ -9,18 +9,20 @@
|
||||
# blafuente <blafuente@virtualcable.es>, 2014-2015
|
||||
# Javier <jgonzalez@virtualcable.es>, 2014
|
||||
# Javier <jgonzalez@virtualcable.es>, 2014
|
||||
# Javier <jgonzalez@virtualcable.es>, 2014
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: OpenUDS\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-06-06 13:15+0200\n"
|
||||
"PO-Revision-Date: 2017-09-20 01:16+0000\n"
|
||||
"POT-Creation-Date: 2017-12-21 19:09+0100\n"
|
||||
"PO-Revision-Date: 2017-10-05 08:49+0000\n"
|
||||
"Last-Translator: Adolfo Gómez <dkmaster@dkmon.com>\n"
|
||||
"Language-Team: Spanish (http://www.transifex.com/openuds/openuds/language/es/)\n"
|
||||
"Language-Team: Spanish (http://www.transifex.com/openuds/openuds/language/"
|
||||
"es/)\n"
|
||||
"Language: es\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: es\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: static/tmp_js/api-tools.js:322
|
||||
@@ -99,7 +101,7 @@ msgstr "Noviembre"
|
||||
msgid "December"
|
||||
msgstr "Diciembre"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:10 static/tmp_js/gui-d-services.js:10
|
||||
#: static/tmp_js/gui-d-authenticators.js:10 static/tmp_js/gui-d-services.js:31
|
||||
msgid "Test"
|
||||
msgstr "Prueba"
|
||||
|
||||
@@ -113,14 +115,14 @@ msgstr "Aceptar"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:151
|
||||
#: static/tmp_js/gui-d-connectivity.js:43 static/tmp_js/gui-d-osmanagers.js:39
|
||||
#: static/tmp_js/gui-d-services.js:86 static/tmp_js/gui-d-services.js:128
|
||||
#: static/tmp_js/gui-d-services.js:108 static/tmp_js/gui-d-services.js:150
|
||||
#: static/tmp_js/gui-d-servicespools.js:220
|
||||
msgid "Error accessing data"
|
||||
msgstr "Error de acceso a datos"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:168
|
||||
#: static/tmp_js/gui-d-authenticators.js:395
|
||||
#: static/tmp_js/gui-d-services.js:108
|
||||
#: static/tmp_js/gui-d-services.js:130
|
||||
msgid "Information"
|
||||
msgstr "Información"
|
||||
|
||||
@@ -246,7 +248,7 @@ msgid "days"
|
||||
msgstr "días"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:23
|
||||
msgid "Dayly"
|
||||
msgid "Daily"
|
||||
msgstr "Diario"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:24
|
||||
@@ -521,87 +523,95 @@ msgstr "Error al crear el informe"
|
||||
msgid "Error obtaining report description"
|
||||
msgstr "Error al obtener la descripción del informe"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:63
|
||||
#: static/tmp_js/gui-d-services.js:85
|
||||
msgid "In Maintenance"
|
||||
msgstr "En mantenimiento"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:65
|
||||
#: static/tmp_js/gui-d-services.js:87
|
||||
msgid "Active"
|
||||
msgstr "Activo"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:125
|
||||
#: static/tmp_js/gui-d-services.js:147
|
||||
msgid "Service information"
|
||||
msgstr "Información sobre el servicio"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:183
|
||||
#: static/tmp_js/gui-d-services.js:205
|
||||
msgid "Edit service"
|
||||
msgstr "Editar servicio"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:183
|
||||
#: static/tmp_js/gui-d-services.js:205
|
||||
msgid "Service creation error"
|
||||
msgstr "Error al crear servicio"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:184
|
||||
#: static/tmp_js/gui-d-services.js:206
|
||||
msgid "New service"
|
||||
msgstr "Nuevo servicio"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:184
|
||||
#: static/tmp_js/gui-d-services.js:206
|
||||
msgid "Service saving error"
|
||||
msgstr "Error al guardar servicio"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:185
|
||||
#: static/tmp_js/gui-d-services.js:207
|
||||
msgid "Delete service"
|
||||
msgstr "Eliminar servicio"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:185
|
||||
#: static/tmp_js/gui-d-services.js:207
|
||||
msgid "Service deletion error"
|
||||
msgstr "Error al eliminar servicio"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:201 static/tmp_js/gui-d-services.js:224
|
||||
#: static/tmp_js/gui-d-services.js:235
|
||||
msgid "Delete user service"
|
||||
msgstr "Eliminar servicio de usuario"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:235
|
||||
msgid "User service deletion error"
|
||||
msgstr "Error en la eliminación del servicio de usuario"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:246 static/tmp_js/gui-d-services.js:269
|
||||
msgid "Maintenance"
|
||||
msgstr "Mantenimiento"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:210
|
||||
#: static/tmp_js/gui-d-services.js:255
|
||||
msgid "Maintenance Mode"
|
||||
msgstr "Modo de mantenimiento"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:210
|
||||
#: static/tmp_js/gui-d-services.js:255
|
||||
msgid "Enter Maintenance Mode?"
|
||||
msgstr "¿Entrar en modo mantenimiento?"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:210
|
||||
#: static/tmp_js/gui-d-services.js:255
|
||||
msgid "Exit Maintenance Mode?"
|
||||
msgstr "Salir del modo mantenimiento"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:229
|
||||
#: static/tmp_js/gui-d-services.js:274
|
||||
msgid "Enter maintenance Mode"
|
||||
msgstr "Entrar en modo mantenimiento"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:232
|
||||
#: static/tmp_js/gui-d-services.js:277
|
||||
msgid "Exit Maintenance Mode"
|
||||
msgstr "Salir del modo mantenimiento"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:240
|
||||
#: static/tmp_js/gui-d-services.js:285
|
||||
msgid "New services provider"
|
||||
msgstr "Nuevo proveedor de servicios"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:240
|
||||
#: static/tmp_js/gui-d-services.js:285
|
||||
msgid "Services provider creation error"
|
||||
msgstr "Error en la creación del proveedor de servicios"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:241
|
||||
#: static/tmp_js/gui-d-services.js:286
|
||||
msgid "Edit services provider"
|
||||
msgstr "Editar proveedor de servicios"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:241
|
||||
#: static/tmp_js/gui-d-services.js:286
|
||||
msgid "Services Provider saving error"
|
||||
msgstr "Error al guardar proveedor de servicios"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:242
|
||||
#: static/tmp_js/gui-d-services.js:287
|
||||
msgid "Delete services provider"
|
||||
msgstr "Eliminar proveedor de servicios"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:242
|
||||
#: static/tmp_js/gui-d-services.js:287
|
||||
msgid "Services Provider deletion error"
|
||||
msgstr "Error al eliminar el proveedor de servicios"
|
||||
|
||||
@@ -752,7 +762,9 @@ msgstr "Publicar en la creación"
|
||||
|
||||
#: static/tmp_js/gui-d-servicespools.js:450
|
||||
msgid "If selected, will initiate the publication inmediatly after creation"
|
||||
msgstr "Si se selecciona, se iniciará la publicación inmediatamente después de la creación"
|
||||
msgstr ""
|
||||
"Si se selecciona, se iniciará la publicación inmediatamente después de la "
|
||||
"creación"
|
||||
|
||||
#: static/tmp_js/gui-d-servicespools.js:462 static/tmp_js/gui.js:50
|
||||
msgid "Edit"
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,22 +1,24 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
#
|
||||
# Translators:
|
||||
# Adolfo Gómez <dkmaster@dkmon.com>, 2015
|
||||
# Lanmedia Comunicaciones <jetxaniz@lanmedia.es>, 2016-2017
|
||||
# Naroa Uriarte <nuriarte@lanmedia.es>, 2017
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: OpenUDS\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-06-06 13:15+0200\n"
|
||||
"PO-Revision-Date: 2017-05-10 08:47+0000\n"
|
||||
"POT-Creation-Date: 2017-12-21 19:09+0100\n"
|
||||
"PO-Revision-Date: 2017-12-05 15:08+0000\n"
|
||||
"Last-Translator: Lanmedia Comunicaciones <jetxaniz@lanmedia.es>\n"
|
||||
"Language-Team: Basque (http://www.transifex.com/openuds/openuds/language/eu/)\n"
|
||||
"Language-Team: Basque (http://www.transifex.com/openuds/openuds/language/"
|
||||
"eu/)\n"
|
||||
"Language: eu\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: eu\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: static/tmp_js/api-tools.js:322
|
||||
@@ -95,7 +97,7 @@ msgstr "Azaroa"
|
||||
msgid "December"
|
||||
msgstr "Abendua"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:10 static/tmp_js/gui-d-services.js:10
|
||||
#: static/tmp_js/gui-d-authenticators.js:10 static/tmp_js/gui-d-services.js:31
|
||||
msgid "Test"
|
||||
msgstr "Proba"
|
||||
|
||||
@@ -105,18 +107,18 @@ msgstr "Bilaketa errorea"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:51 static/tmp_js/gui-form.js:318
|
||||
msgid "Accept"
|
||||
msgstr "onar ezazu"
|
||||
msgstr "Onartu"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:151
|
||||
#: static/tmp_js/gui-d-connectivity.js:43 static/tmp_js/gui-d-osmanagers.js:39
|
||||
#: static/tmp_js/gui-d-services.js:86 static/tmp_js/gui-d-services.js:128
|
||||
#: static/tmp_js/gui-d-services.js:108 static/tmp_js/gui-d-services.js:150
|
||||
#: static/tmp_js/gui-d-servicespools.js:220
|
||||
msgid "Error accessing data"
|
||||
msgstr "Datuetara sartzerakoan errorea"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:168
|
||||
#: static/tmp_js/gui-d-authenticators.js:395
|
||||
#: static/tmp_js/gui-d-services.js:108
|
||||
#: static/tmp_js/gui-d-services.js:130
|
||||
msgid "Information"
|
||||
msgstr "Informazioa"
|
||||
|
||||
@@ -143,7 +145,7 @@ msgstr "Talde Berria"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:369
|
||||
msgid "Search groups"
|
||||
msgstr "Bilatu Taldeak"
|
||||
msgstr "Taldeak bilatu"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:369
|
||||
#: static/tmp_js/gui-permissions.js:36 static/tmp_js/gui-permissions.js:85
|
||||
@@ -160,7 +162,7 @@ msgstr "Taldeak gordetzerakoan errorea"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:390
|
||||
msgid "Delete group"
|
||||
msgstr "Taldea Ezabatu"
|
||||
msgstr "Taldea ezabatu"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:390
|
||||
msgid "Group deletion error"
|
||||
@@ -190,12 +192,12 @@ msgstr "Erabiltzaile berria"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:623
|
||||
msgid "Search users"
|
||||
msgstr "Bilatu erabiltzaileak"
|
||||
msgstr "Erabiltzaileak bilatu"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:623
|
||||
#: static/tmp_js/gui-permissions.js:33 static/tmp_js/gui-permissions.js:82
|
||||
msgid "User"
|
||||
msgstr "erabiltzaile"
|
||||
msgstr "Erabiltzailea"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:623
|
||||
msgid "Users found"
|
||||
@@ -203,7 +205,7 @@ msgstr "Aurkitutako erabiltzaileak"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:637
|
||||
msgid "Delete user"
|
||||
msgstr "Erabiltzailea Ezabatu"
|
||||
msgstr "Erabiltzailea ezabatu"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:637
|
||||
msgid "User deletion error"
|
||||
@@ -235,15 +237,15 @@ msgstr "Autentifikatzailea ezabatzerakoan errorea"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:23
|
||||
msgid "day"
|
||||
msgstr "Eguna"
|
||||
msgstr "eguna"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:23
|
||||
msgid "days"
|
||||
msgstr "Egunak"
|
||||
msgstr "egun"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:23
|
||||
msgid "Dayly"
|
||||
msgstr "Egunero"
|
||||
msgid "Daily"
|
||||
msgstr "egunero"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:24
|
||||
msgid "week"
|
||||
@@ -315,7 +317,7 @@ msgstr "Arau hau indarrean egongo da"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:153
|
||||
msgid "of any week"
|
||||
msgstr "Edozein astekoa"
|
||||
msgstr "Edozein asteko"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:161
|
||||
msgid "from"
|
||||
@@ -331,23 +333,23 @@ msgstr "Arte"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:167
|
||||
msgid "starting at"
|
||||
msgstr "Hasita"
|
||||
msgstr "hasita"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:170
|
||||
msgid "and will remain valid for "
|
||||
msgstr "eta baliozko izanten jarraituko du"
|
||||
msgstr "eta baliozko izaten jarraituko du"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:172
|
||||
msgid "with no duration"
|
||||
msgstr "Iraupenik Gabe"
|
||||
msgstr "Iraupenik gabe"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:208
|
||||
msgid "New rule"
|
||||
msgstr "Arau barria"
|
||||
msgstr "Arau berria"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:208
|
||||
msgid "Edit rule"
|
||||
msgstr "Editatu Araua"
|
||||
msgstr "Araua editatu"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:209
|
||||
msgid "Save"
|
||||
@@ -375,11 +377,11 @@ msgstr "Egutegi berria"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:356
|
||||
msgid "Calendar creation error"
|
||||
msgstr "Egutegia ezabatzerakoan errorea"
|
||||
msgstr "Egutegia sortzerakoan errorea"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:357
|
||||
msgid "Edit calendar"
|
||||
msgstr "Editatu egutegia"
|
||||
msgstr "Egutegia editatu"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:357
|
||||
msgid "Calendar saving error"
|
||||
@@ -387,7 +389,7 @@ msgstr "Egutegia gordetzerakoan errorea"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:358
|
||||
msgid "Delete calendar"
|
||||
msgstr "Egutegia Ezabatu"
|
||||
msgstr "Egutegia ezabatu"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:358
|
||||
msgid "Calendar deletion error"
|
||||
@@ -395,7 +397,7 @@ msgstr "Egutegia ezabatzerakoan errorea"
|
||||
|
||||
#: static/tmp_js/gui-d-config.js:56
|
||||
msgid "Configuration saved"
|
||||
msgstr "konfigurazioa gordeta"
|
||||
msgstr "Konfigurazioa gordeta"
|
||||
|
||||
#: static/tmp_js/gui-d-config.js:60
|
||||
msgid "No changes has been made"
|
||||
@@ -411,7 +413,7 @@ msgstr "Garraioa sortzerakoan errorea"
|
||||
|
||||
#: static/tmp_js/gui-d-connectivity.js:64
|
||||
msgid "Edit transport"
|
||||
msgstr "Editatu Garraioa"
|
||||
msgstr "Garraioa editatu"
|
||||
|
||||
#: static/tmp_js/gui-d-connectivity.js:64
|
||||
msgid "Transport saving error"
|
||||
@@ -419,7 +421,7 @@ msgstr "Garraioa gordetzerakoan errorea"
|
||||
|
||||
#: static/tmp_js/gui-d-connectivity.js:65
|
||||
msgid "Delete transport"
|
||||
msgstr "Garraioa Ezabatu"
|
||||
msgstr "Garraioa ezabatu"
|
||||
|
||||
#: static/tmp_js/gui-d-connectivity.js:65
|
||||
msgid "Transport deletion error"
|
||||
@@ -435,7 +437,7 @@ msgstr "Sarea sortzerakoan errorea"
|
||||
|
||||
#: static/tmp_js/gui-d-connectivity.js:73
|
||||
msgid "Edit network"
|
||||
msgstr "Editatu sarea"
|
||||
msgstr "Sarea editatu"
|
||||
|
||||
#: static/tmp_js/gui-d-connectivity.js:73
|
||||
msgid "Network saving error"
|
||||
@@ -443,7 +445,7 @@ msgstr "Sarea gordetzerakoan errorea"
|
||||
|
||||
#: static/tmp_js/gui-d-connectivity.js:74
|
||||
msgid "Delete network"
|
||||
msgstr "Sarea Ezabatu"
|
||||
msgstr "Sarea ezabatu"
|
||||
|
||||
#: static/tmp_js/gui-d-connectivity.js:74
|
||||
msgid "Network deletion error"
|
||||
@@ -451,15 +453,15 @@ msgstr "Sarea ezabatzerakoan errorea"
|
||||
|
||||
#: static/tmp_js/gui-d-dashboard.js:40
|
||||
msgid "Staff member"
|
||||
msgstr "Langile Kidea"
|
||||
msgstr "Langile kidea"
|
||||
|
||||
#: static/tmp_js/gui-d-gallery.js:17
|
||||
msgid "New image"
|
||||
msgstr "Irudi Berria"
|
||||
msgstr "Irudi berria"
|
||||
|
||||
#: static/tmp_js/gui-d-gallery.js:17
|
||||
msgid "Edit image"
|
||||
msgstr "Irudia Editatu"
|
||||
msgstr "Irudia editatu"
|
||||
|
||||
#: static/tmp_js/gui-d-gallery.js:18
|
||||
msgid "Upload"
|
||||
@@ -471,11 +473,11 @@ msgstr "Irudi bat aukeratu behar duzu"
|
||||
|
||||
#: static/tmp_js/gui-d-gallery.js:34
|
||||
msgid "Image is too big (max. upload size is 256Kb)"
|
||||
msgstr "Irudi haundiegia (Gehienezko tamaina 256Kb)"
|
||||
msgstr "Irudi handiegia (gehienezko tamaina 256Kb)"
|
||||
|
||||
#: static/tmp_js/gui-d-gallery.js:80
|
||||
msgid "Delete Image"
|
||||
msgstr "Irudia Ezabatu"
|
||||
msgstr "Irudia ezabatu"
|
||||
|
||||
#: static/tmp_js/gui-d-gallery.js:80
|
||||
msgid "Image deletion error"
|
||||
@@ -483,7 +485,7 @@ msgstr "Irudia ezabatzerakoan errorea"
|
||||
|
||||
#: static/tmp_js/gui-d-osmanagers.js:42
|
||||
msgid "New OSManager"
|
||||
msgstr "SE Zuzendari barria"
|
||||
msgstr "SE Zuzendari berria"
|
||||
|
||||
#: static/tmp_js/gui-d-osmanagers.js:42
|
||||
msgid "OSManager creation error"
|
||||
@@ -491,7 +493,7 @@ msgstr "SE Zuzendaria sortzerakoan errorea"
|
||||
|
||||
#: static/tmp_js/gui-d-osmanagers.js:43
|
||||
msgid "Edit OSManager"
|
||||
msgstr "Editatu SE zuzendaria"
|
||||
msgstr "SE Zuzendaria editatu"
|
||||
|
||||
#: static/tmp_js/gui-d-osmanagers.js:43
|
||||
msgid "OSManager saving error"
|
||||
@@ -499,15 +501,15 @@ msgstr "SE Zuzendaria gordetzerakoan errorea"
|
||||
|
||||
#: static/tmp_js/gui-d-osmanagers.js:44
|
||||
msgid "Delete OSManager"
|
||||
msgstr "SE Zuzendaria Ezabatu"
|
||||
msgstr "SE Zuzendaria ezabatu"
|
||||
|
||||
#: static/tmp_js/gui-d-osmanagers.js:44
|
||||
msgid "OSManager deletion error"
|
||||
msgstr "SE zuzendaria ezabatzerakoan errorea"
|
||||
msgstr "SE Zuzendaria ezabatzerakoan errorea"
|
||||
|
||||
#: static/tmp_js/gui-d-reports.js:21 static/tmp_js/gui-d-reports.js:38
|
||||
msgid "Generate report"
|
||||
msgstr "Txostena atera"
|
||||
msgstr "Txostena sortu"
|
||||
|
||||
#: static/tmp_js/gui-d-reports.js:59
|
||||
msgid "Error creating report"
|
||||
@@ -517,87 +519,95 @@ msgstr "Txostena sortzerakoan errorea"
|
||||
msgid "Error obtaining report description"
|
||||
msgstr "Txostenaren deskripzioa lortzerakoan errorea"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:63
|
||||
#: static/tmp_js/gui-d-services.js:85
|
||||
msgid "In Maintenance"
|
||||
msgstr "Mantentze lanetan"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:65
|
||||
#: static/tmp_js/gui-d-services.js:87
|
||||
msgid "Active"
|
||||
msgstr "Aktibo"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:125
|
||||
#: static/tmp_js/gui-d-services.js:147
|
||||
msgid "Service information"
|
||||
msgstr "Zerbitzu informazioa"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:183
|
||||
#: static/tmp_js/gui-d-services.js:205
|
||||
msgid "Edit service"
|
||||
msgstr "Zerbitzua editatu"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:183
|
||||
#: static/tmp_js/gui-d-services.js:205
|
||||
msgid "Service creation error"
|
||||
msgstr "Zerbitzua sortzerakoan errorea"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:184
|
||||
#: static/tmp_js/gui-d-services.js:206
|
||||
msgid "New service"
|
||||
msgstr "Zerbitzu berria"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:184
|
||||
#: static/tmp_js/gui-d-services.js:206
|
||||
msgid "Service saving error"
|
||||
msgstr "Zerbitzua gordetzerakoa errorea"
|
||||
msgstr "Zerbitzua gordetzerakoan errorea"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:185
|
||||
#: static/tmp_js/gui-d-services.js:207
|
||||
msgid "Delete service"
|
||||
msgstr "Zerbitzua Ezabatu"
|
||||
msgstr "Zerbitzua ezabatu"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:185
|
||||
#: static/tmp_js/gui-d-services.js:207
|
||||
msgid "Service deletion error"
|
||||
msgstr "Zerbitzua ezabatzerakoan errorea"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:201 static/tmp_js/gui-d-services.js:224
|
||||
#: static/tmp_js/gui-d-services.js:235
|
||||
msgid "Delete user service"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:235
|
||||
msgid "User service deletion error"
|
||||
msgstr ""
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:246 static/tmp_js/gui-d-services.js:269
|
||||
msgid "Maintenance"
|
||||
msgstr "Mantentze"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:210
|
||||
#: static/tmp_js/gui-d-services.js:255
|
||||
msgid "Maintenance Mode"
|
||||
msgstr "Mantentze-modua"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:210
|
||||
#: static/tmp_js/gui-d-services.js:255
|
||||
msgid "Enter Maintenance Mode?"
|
||||
msgstr "Mantentze-moduan jarri?"
|
||||
msgstr "Mantentze-moduan sartu?"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:210
|
||||
#: static/tmp_js/gui-d-services.js:255
|
||||
msgid "Exit Maintenance Mode?"
|
||||
msgstr "Mantentze-modutik irten?"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:229
|
||||
#: static/tmp_js/gui-d-services.js:274
|
||||
msgid "Enter maintenance Mode"
|
||||
msgstr "Mantentze-moduan jarri"
|
||||
msgstr "Mantentze-moduan sartu"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:232
|
||||
#: static/tmp_js/gui-d-services.js:277
|
||||
msgid "Exit Maintenance Mode"
|
||||
msgstr "Mantentze-modutik irten"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:240
|
||||
#: static/tmp_js/gui-d-services.js:285
|
||||
msgid "New services provider"
|
||||
msgstr "Zerbitzu hornitzaile barria"
|
||||
msgstr "Zerbitzu hornitzaile berria"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:240
|
||||
#: static/tmp_js/gui-d-services.js:285
|
||||
msgid "Services provider creation error"
|
||||
msgstr "Zerbitzu hornitzailea sortzerakoan errorea"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:241
|
||||
#: static/tmp_js/gui-d-services.js:286
|
||||
msgid "Edit services provider"
|
||||
msgstr "Zerbitzu hornitzailea editatu"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:241
|
||||
#: static/tmp_js/gui-d-services.js:286
|
||||
msgid "Services Provider saving error"
|
||||
msgstr "Zerbitzu hornitzailea gordetzerakoan errorea"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:242
|
||||
#: static/tmp_js/gui-d-services.js:287
|
||||
msgid "Delete services provider"
|
||||
msgstr "Zerbitzu Hornitzailea Ezabatu"
|
||||
msgstr "Zerbitzu Hornitzailea ezabatu"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:242
|
||||
#: static/tmp_js/gui-d-services.js:287
|
||||
msgid "Services Provider deletion error"
|
||||
msgstr "Zerbitzu hornitzailea ezabatzerakoan errorea"
|
||||
|
||||
@@ -607,7 +617,7 @@ msgstr "Ekin orain"
|
||||
|
||||
#: static/tmp_js/gui-d-servicespools-actions.js:62
|
||||
msgid "Execute action"
|
||||
msgstr "Buru ezazu ekintza"
|
||||
msgstr "Ekintza burutu"
|
||||
|
||||
#: static/tmp_js/gui-d-servicespools-actions.js:62
|
||||
msgid "Launch action execution right now?"
|
||||
@@ -669,7 +679,7 @@ msgstr "Indarrean ezeztatu"
|
||||
|
||||
#: static/tmp_js/gui-d-servicespools-publications.js:67
|
||||
msgid "Failed creating publication"
|
||||
msgstr "Argitalpena sortzean hutsa"
|
||||
msgstr "Argitalpena sortzean hutsegitea"
|
||||
|
||||
#: static/tmp_js/gui-d-servicespools-transports.js:29
|
||||
msgid "Add transport"
|
||||
@@ -699,7 +709,7 @@ msgstr "Ez"
|
||||
|
||||
#: static/tmp_js/gui-d-servicespools.js:215
|
||||
msgid "error"
|
||||
msgstr "Errorea"
|
||||
msgstr "errorea"
|
||||
|
||||
#: static/tmp_js/gui-d-servicespools.js:264
|
||||
msgid "Remove Cache element"
|
||||
@@ -720,11 +730,11 @@ msgstr "Autentifikatzailea eta taldea eman behar duzu"
|
||||
|
||||
#: static/tmp_js/gui-d-servicespools.js:329
|
||||
msgid "Remove group"
|
||||
msgstr "Taldea kendu"
|
||||
msgstr "Taldea ezabatu"
|
||||
|
||||
#: static/tmp_js/gui-d-servicespools.js:329
|
||||
msgid "Group removal error"
|
||||
msgstr "Taldea kentzean errorea"
|
||||
msgstr "Taldea ezabatzerakoan errorea"
|
||||
|
||||
#: static/tmp_js/gui-d-servicespools.js:382
|
||||
msgid "Remove Assigned service"
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
#
|
||||
# Translators:
|
||||
# Abdel Baaddi <abaaddi@virtualcable.es>, 2016
|
||||
# Adolfo Gómez <dkmaster at dkmon dot com>, 2012
|
||||
@@ -9,14 +9,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: OpenUDS\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-06-06 13:15+0200\n"
|
||||
"PO-Revision-Date: 2017-09-20 01:16+0000\n"
|
||||
"POT-Creation-Date: 2017-12-21 19:09+0100\n"
|
||||
"PO-Revision-Date: 2017-10-04 11:09+0000\n"
|
||||
"Last-Translator: Adolfo Gómez <dkmaster@dkmon.com>\n"
|
||||
"Language-Team: French (http://www.transifex.com/openuds/openuds/language/fr/)\n"
|
||||
"Language-Team: French (http://www.transifex.com/openuds/openuds/language/"
|
||||
"fr/)\n"
|
||||
"Language: fr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: fr\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
#: static/tmp_js/api-tools.js:322
|
||||
@@ -95,7 +96,7 @@ msgstr "Novembre"
|
||||
msgid "December"
|
||||
msgstr "Décembre"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:10 static/tmp_js/gui-d-services.js:10
|
||||
#: static/tmp_js/gui-d-authenticators.js:10 static/tmp_js/gui-d-services.js:31
|
||||
msgid "Test"
|
||||
msgstr "Test"
|
||||
|
||||
@@ -109,14 +110,14 @@ msgstr "Accepter"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:151
|
||||
#: static/tmp_js/gui-d-connectivity.js:43 static/tmp_js/gui-d-osmanagers.js:39
|
||||
#: static/tmp_js/gui-d-services.js:86 static/tmp_js/gui-d-services.js:128
|
||||
#: static/tmp_js/gui-d-services.js:108 static/tmp_js/gui-d-services.js:150
|
||||
#: static/tmp_js/gui-d-servicespools.js:220
|
||||
msgid "Error accessing data"
|
||||
msgstr "Erreur d'accès aux données"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:168
|
||||
#: static/tmp_js/gui-d-authenticators.js:395
|
||||
#: static/tmp_js/gui-d-services.js:108
|
||||
#: static/tmp_js/gui-d-services.js:130
|
||||
msgid "Information"
|
||||
msgstr "Informations"
|
||||
|
||||
@@ -242,8 +243,8 @@ msgid "days"
|
||||
msgstr "jours"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:23
|
||||
msgid "Dayly"
|
||||
msgstr "quotidien"
|
||||
msgid "Daily"
|
||||
msgstr "Tous les jours"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:24
|
||||
msgid "week"
|
||||
@@ -517,87 +518,95 @@ msgstr "Erreur de création du rapport"
|
||||
msgid "Error obtaining report description"
|
||||
msgstr "Erreur de recuperation de la description du rapport"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:63
|
||||
#: static/tmp_js/gui-d-services.js:85
|
||||
msgid "In Maintenance"
|
||||
msgstr "en maintenance"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:65
|
||||
#: static/tmp_js/gui-d-services.js:87
|
||||
msgid "Active"
|
||||
msgstr "Actif"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:125
|
||||
#: static/tmp_js/gui-d-services.js:147
|
||||
msgid "Service information"
|
||||
msgstr "Service d'information"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:183
|
||||
#: static/tmp_js/gui-d-services.js:205
|
||||
msgid "Edit service"
|
||||
msgstr "Modifier le service"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:183
|
||||
#: static/tmp_js/gui-d-services.js:205
|
||||
msgid "Service creation error"
|
||||
msgstr "Erreur de création du service"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:184
|
||||
#: static/tmp_js/gui-d-services.js:206
|
||||
msgid "New service"
|
||||
msgstr "Nouveau service"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:184
|
||||
#: static/tmp_js/gui-d-services.js:206
|
||||
msgid "Service saving error"
|
||||
msgstr "Erreur de sauvegarde du service"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:185
|
||||
#: static/tmp_js/gui-d-services.js:207
|
||||
msgid "Delete service"
|
||||
msgstr "Supprimer le service"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:185
|
||||
#: static/tmp_js/gui-d-services.js:207
|
||||
msgid "Service deletion error"
|
||||
msgstr "Erreur de suppression du service"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:201 static/tmp_js/gui-d-services.js:224
|
||||
#: static/tmp_js/gui-d-services.js:235
|
||||
msgid "Delete user service"
|
||||
msgstr "Supprimer le service de l’utilisateur"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:235
|
||||
msgid "User service deletion error"
|
||||
msgstr "Erreur de suppression de service utilisateur"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:246 static/tmp_js/gui-d-services.js:269
|
||||
msgid "Maintenance"
|
||||
msgstr "Maintenance"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:210
|
||||
#: static/tmp_js/gui-d-services.js:255
|
||||
msgid "Maintenance Mode"
|
||||
msgstr "Mode maintenance"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:210
|
||||
#: static/tmp_js/gui-d-services.js:255
|
||||
msgid "Enter Maintenance Mode?"
|
||||
msgstr "Entrer en Mode de Maintenance ?"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:210
|
||||
#: static/tmp_js/gui-d-services.js:255
|
||||
msgid "Exit Maintenance Mode?"
|
||||
msgstr "Quitter le Mode de Maintenance ?"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:229
|
||||
#: static/tmp_js/gui-d-services.js:274
|
||||
msgid "Enter maintenance Mode"
|
||||
msgstr "Entrer en Mode de maintenance ?"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:232
|
||||
#: static/tmp_js/gui-d-services.js:277
|
||||
msgid "Exit Maintenance Mode"
|
||||
msgstr "Quitter le Mode de maintenance ?"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:240
|
||||
#: static/tmp_js/gui-d-services.js:285
|
||||
msgid "New services provider"
|
||||
msgstr "Nouveau fournisseur de services"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:240
|
||||
#: static/tmp_js/gui-d-services.js:285
|
||||
msgid "Services provider creation error"
|
||||
msgstr "Erreur de création de fournisseur de services"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:241
|
||||
#: static/tmp_js/gui-d-services.js:286
|
||||
msgid "Edit services provider"
|
||||
msgstr "Modifier le fournisseur de services"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:241
|
||||
#: static/tmp_js/gui-d-services.js:286
|
||||
msgid "Services Provider saving error"
|
||||
msgstr "Erreur de sauvegarde du fournisseur de services "
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:242
|
||||
#: static/tmp_js/gui-d-services.js:287
|
||||
msgid "Delete services provider"
|
||||
msgstr "Supprimer le fournisseur de services"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:242
|
||||
#: static/tmp_js/gui-d-services.js:287
|
||||
msgid "Services Provider deletion error"
|
||||
msgstr "Erreur de suppression du fournisseur de services"
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,20 +1,21 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: OpenUDS\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-06-06 13:15+0200\n"
|
||||
"PO-Revision-Date: 2017-09-20 01:16+0000\n"
|
||||
"POT-Creation-Date: 2017-12-21 19:09+0100\n"
|
||||
"PO-Revision-Date: 2017-10-04 11:09+0000\n"
|
||||
"Last-Translator: Adolfo Gómez <dkmaster@dkmon.com>\n"
|
||||
"Language-Team: Italian (http://www.transifex.com/openuds/openuds/language/it/)\n"
|
||||
"Language-Team: Italian (http://www.transifex.com/openuds/openuds/language/"
|
||||
"it/)\n"
|
||||
"Language: it\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: it\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: static/tmp_js/api-tools.js:322
|
||||
@@ -93,7 +94,7 @@ msgstr "Novembre"
|
||||
msgid "December"
|
||||
msgstr "Dicembre"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:10 static/tmp_js/gui-d-services.js:10
|
||||
#: static/tmp_js/gui-d-authenticators.js:10 static/tmp_js/gui-d-services.js:31
|
||||
msgid "Test"
|
||||
msgstr "Prova"
|
||||
|
||||
@@ -107,14 +108,14 @@ msgstr "Accettare"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:151
|
||||
#: static/tmp_js/gui-d-connectivity.js:43 static/tmp_js/gui-d-osmanagers.js:39
|
||||
#: static/tmp_js/gui-d-services.js:86 static/tmp_js/gui-d-services.js:128
|
||||
#: static/tmp_js/gui-d-services.js:108 static/tmp_js/gui-d-services.js:150
|
||||
#: static/tmp_js/gui-d-servicespools.js:220
|
||||
msgid "Error accessing data"
|
||||
msgstr "Errore di accesso ai dati"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:168
|
||||
#: static/tmp_js/gui-d-authenticators.js:395
|
||||
#: static/tmp_js/gui-d-services.js:108
|
||||
#: static/tmp_js/gui-d-services.js:130
|
||||
msgid "Information"
|
||||
msgstr "Informazioni"
|
||||
|
||||
@@ -240,8 +241,8 @@ msgid "days"
|
||||
msgstr "giorni"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:23
|
||||
msgid "Dayly"
|
||||
msgstr "Riordino giornaliero"
|
||||
msgid "Daily"
|
||||
msgstr "Giornaliero"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:24
|
||||
msgid "week"
|
||||
@@ -515,87 +516,95 @@ msgstr "Errore di creazione di report"
|
||||
msgid "Error obtaining report description"
|
||||
msgstr "Descrizione dell'errore ottenere report"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:63
|
||||
#: static/tmp_js/gui-d-services.js:85
|
||||
msgid "In Maintenance"
|
||||
msgstr "In manutenzione"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:65
|
||||
#: static/tmp_js/gui-d-services.js:87
|
||||
msgid "Active"
|
||||
msgstr "Attivo"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:125
|
||||
#: static/tmp_js/gui-d-services.js:147
|
||||
msgid "Service information"
|
||||
msgstr "Informazioni di servizio"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:183
|
||||
#: static/tmp_js/gui-d-services.js:205
|
||||
msgid "Edit service"
|
||||
msgstr "Modifica servizio"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:183
|
||||
#: static/tmp_js/gui-d-services.js:205
|
||||
msgid "Service creation error"
|
||||
msgstr "Errore di creazione del servizio"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:184
|
||||
#: static/tmp_js/gui-d-services.js:206
|
||||
msgid "New service"
|
||||
msgstr "Nuovo servizio"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:184
|
||||
#: static/tmp_js/gui-d-services.js:206
|
||||
msgid "Service saving error"
|
||||
msgstr "Servizio risparmio errore"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:185
|
||||
#: static/tmp_js/gui-d-services.js:207
|
||||
msgid "Delete service"
|
||||
msgstr "Eliminare il servizio"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:185
|
||||
#: static/tmp_js/gui-d-services.js:207
|
||||
msgid "Service deletion error"
|
||||
msgstr "Errore di omissione del servizio"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:201 static/tmp_js/gui-d-services.js:224
|
||||
#: static/tmp_js/gui-d-services.js:235
|
||||
msgid "Delete user service"
|
||||
msgstr "Elimina utente servizio"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:235
|
||||
msgid "User service deletion error"
|
||||
msgstr "Errore di omissione di servizio dell'utente"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:246 static/tmp_js/gui-d-services.js:269
|
||||
msgid "Maintenance"
|
||||
msgstr "Manutenzione"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:210
|
||||
#: static/tmp_js/gui-d-services.js:255
|
||||
msgid "Maintenance Mode"
|
||||
msgstr "Modalità di manutenzione"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:210
|
||||
#: static/tmp_js/gui-d-services.js:255
|
||||
msgid "Enter Maintenance Mode?"
|
||||
msgstr "Entrare in modalità di manutenzione?"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:210
|
||||
#: static/tmp_js/gui-d-services.js:255
|
||||
msgid "Exit Maintenance Mode?"
|
||||
msgstr "Uscire dalla modalità di manutenzione?"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:229
|
||||
#: static/tmp_js/gui-d-services.js:274
|
||||
msgid "Enter maintenance Mode"
|
||||
msgstr "Entrare in modalità di manutenzione"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:232
|
||||
#: static/tmp_js/gui-d-services.js:277
|
||||
msgid "Exit Maintenance Mode"
|
||||
msgstr "Disattiva modalità di manutenzione"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:240
|
||||
#: static/tmp_js/gui-d-services.js:285
|
||||
msgid "New services provider"
|
||||
msgstr "Nuovo provider di servizi"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:240
|
||||
#: static/tmp_js/gui-d-services.js:285
|
||||
msgid "Services provider creation error"
|
||||
msgstr "Errore di creazione di provider di servizi"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:241
|
||||
#: static/tmp_js/gui-d-services.js:286
|
||||
msgid "Edit services provider"
|
||||
msgstr "Modificare il provider di servizi"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:241
|
||||
#: static/tmp_js/gui-d-services.js:286
|
||||
msgid "Services Provider saving error"
|
||||
msgstr "Provider di servizi risparmiando errore"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:242
|
||||
#: static/tmp_js/gui-d-services.js:287
|
||||
msgid "Delete services provider"
|
||||
msgstr "Eliminare il provider di servizi"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:242
|
||||
#: static/tmp_js/gui-d-services.js:287
|
||||
msgid "Services Provider deletion error"
|
||||
msgstr "Errore di omissione del Provider di servizi"
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,20 +1,21 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: OpenUDS\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-06-06 13:15+0200\n"
|
||||
"PO-Revision-Date: 2017-09-20 01:16+0000\n"
|
||||
"POT-Creation-Date: 2017-12-21 19:09+0100\n"
|
||||
"PO-Revision-Date: 2017-10-04 11:09+0000\n"
|
||||
"Last-Translator: Adolfo Gómez <dkmaster@dkmon.com>\n"
|
||||
"Language-Team: Portuguese (http://www.transifex.com/openuds/openuds/language/pt/)\n"
|
||||
"Language-Team: Portuguese (http://www.transifex.com/openuds/openuds/language/"
|
||||
"pt/)\n"
|
||||
"Language: pt\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: pt\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: static/tmp_js/api-tools.js:322
|
||||
@@ -93,7 +94,7 @@ msgstr "Novembro"
|
||||
msgid "December"
|
||||
msgstr "Dezembro de"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:10 static/tmp_js/gui-d-services.js:10
|
||||
#: static/tmp_js/gui-d-authenticators.js:10 static/tmp_js/gui-d-services.js:31
|
||||
msgid "Test"
|
||||
msgstr "Teste"
|
||||
|
||||
@@ -107,14 +108,14 @@ msgstr "Aceitar"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:151
|
||||
#: static/tmp_js/gui-d-connectivity.js:43 static/tmp_js/gui-d-osmanagers.js:39
|
||||
#: static/tmp_js/gui-d-services.js:86 static/tmp_js/gui-d-services.js:128
|
||||
#: static/tmp_js/gui-d-services.js:108 static/tmp_js/gui-d-services.js:150
|
||||
#: static/tmp_js/gui-d-servicespools.js:220
|
||||
msgid "Error accessing data"
|
||||
msgstr "Erro ao acessar dados"
|
||||
|
||||
#: static/tmp_js/gui-d-authenticators.js:168
|
||||
#: static/tmp_js/gui-d-authenticators.js:395
|
||||
#: static/tmp_js/gui-d-services.js:108
|
||||
#: static/tmp_js/gui-d-services.js:130
|
||||
msgid "Information"
|
||||
msgstr "Informações"
|
||||
|
||||
@@ -240,8 +241,8 @@ msgid "days"
|
||||
msgstr "dias"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:23
|
||||
msgid "Dayly"
|
||||
msgstr "Dayly"
|
||||
msgid "Daily"
|
||||
msgstr "Diário"
|
||||
|
||||
#: static/tmp_js/gui-d-calendar.js:24
|
||||
msgid "week"
|
||||
@@ -515,87 +516,95 @@ msgstr "Erro ao criar o relatório"
|
||||
msgid "Error obtaining report description"
|
||||
msgstr "Descrição de relatório de obtenção do erro"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:63
|
||||
#: static/tmp_js/gui-d-services.js:85
|
||||
msgid "In Maintenance"
|
||||
msgstr "Em manutenção"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:65
|
||||
#: static/tmp_js/gui-d-services.js:87
|
||||
msgid "Active"
|
||||
msgstr "Ativo"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:125
|
||||
#: static/tmp_js/gui-d-services.js:147
|
||||
msgid "Service information"
|
||||
msgstr "Informação de serviço"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:183
|
||||
#: static/tmp_js/gui-d-services.js:205
|
||||
msgid "Edit service"
|
||||
msgstr "Editar serviço"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:183
|
||||
#: static/tmp_js/gui-d-services.js:205
|
||||
msgid "Service creation error"
|
||||
msgstr "Erro de criação do serviço"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:184
|
||||
#: static/tmp_js/gui-d-services.js:206
|
||||
msgid "New service"
|
||||
msgstr "Novo serviço"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:184
|
||||
#: static/tmp_js/gui-d-services.js:206
|
||||
msgid "Service saving error"
|
||||
msgstr "Salvando o erro de serviço"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:185
|
||||
#: static/tmp_js/gui-d-services.js:207
|
||||
msgid "Delete service"
|
||||
msgstr "Excluir o serviço"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:185
|
||||
#: static/tmp_js/gui-d-services.js:207
|
||||
msgid "Service deletion error"
|
||||
msgstr "Erro de exclusão do serviço"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:201 static/tmp_js/gui-d-services.js:224
|
||||
#: static/tmp_js/gui-d-services.js:235
|
||||
msgid "Delete user service"
|
||||
msgstr "Excluir usuário serviço"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:235
|
||||
msgid "User service deletion error"
|
||||
msgstr "Erro de exclusão de serviço do usuário"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:246 static/tmp_js/gui-d-services.js:269
|
||||
msgid "Maintenance"
|
||||
msgstr "Manutenção"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:210
|
||||
#: static/tmp_js/gui-d-services.js:255
|
||||
msgid "Maintenance Mode"
|
||||
msgstr "Modo de manutenção"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:210
|
||||
#: static/tmp_js/gui-d-services.js:255
|
||||
msgid "Enter Maintenance Mode?"
|
||||
msgstr "Entrar no modo de manutenção?"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:210
|
||||
#: static/tmp_js/gui-d-services.js:255
|
||||
msgid "Exit Maintenance Mode?"
|
||||
msgstr "Sair do modo de manutenção?"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:229
|
||||
#: static/tmp_js/gui-d-services.js:274
|
||||
msgid "Enter maintenance Mode"
|
||||
msgstr "Digite o modo de manutenção"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:232
|
||||
#: static/tmp_js/gui-d-services.js:277
|
||||
msgid "Exit Maintenance Mode"
|
||||
msgstr "Sair do modo de manutenção"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:240
|
||||
#: static/tmp_js/gui-d-services.js:285
|
||||
msgid "New services provider"
|
||||
msgstr "Novo provedor de serviços"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:240
|
||||
#: static/tmp_js/gui-d-services.js:285
|
||||
msgid "Services provider creation error"
|
||||
msgstr "Erro de criação do provedor de serviços"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:241
|
||||
#: static/tmp_js/gui-d-services.js:286
|
||||
msgid "Edit services provider"
|
||||
msgstr "Editar Provedor de serviços"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:241
|
||||
#: static/tmp_js/gui-d-services.js:286
|
||||
msgid "Services Provider saving error"
|
||||
msgstr "Salvando o erro do prestador de serviços"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:242
|
||||
#: static/tmp_js/gui-d-services.js:287
|
||||
msgid "Delete services provider"
|
||||
msgstr "Exclua o provedor de serviços"
|
||||
|
||||
#: static/tmp_js/gui-d-services.js:242
|
||||
#: static/tmp_js/gui-d-services.js:287
|
||||
msgid "Services Provider deletion error"
|
||||
msgstr "Erro de exclusão do provedor de serviços"
|
||||
|
||||
|
5410
server/src/uds/locale/zh_CN/LC_MESSAGES/django.po
Normal file
5410
server/src/uds/locale/zh_CN/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
1060
server/src/uds/locale/zh_CN/LC_MESSAGES/djangojs.po
Normal file
1060
server/src/uds/locale/zh_CN/LC_MESSAGES/djangojs.po
Normal file
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,7 @@ from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from uds.core.ui.UserInterface import gui
|
||||
from uds.core.util import encoders
|
||||
from uds.transports.RDP.RDPTransport import RDPTransport
|
||||
from uds.transports.RDP.TRDPTransport import TRDPTransport
|
||||
|
||||
@@ -91,6 +92,7 @@ def unmarshalTRDP(str_):
|
||||
'tunnelCheckServer': tunnelCheckServer
|
||||
}
|
||||
|
||||
|
||||
def transformTransports(apps, schema_editor):
|
||||
'''
|
||||
Move serialization to a better model (it's time, the mode is there since 1.1 :) )
|
||||
@@ -98,13 +100,13 @@ def transformTransports(apps, schema_editor):
|
||||
model = apps.get_model("uds", 'Transport')
|
||||
for t in model.objects.all():
|
||||
if t.data_type == RDPTransport.typeType:
|
||||
values = unmarshalRDP(t.data.decode(RDPTransport.CODEC))
|
||||
values = unmarshalRDP(encoders.decode(t.data, 'base64'))
|
||||
rdp = RDPTransport(Environment.getTempEnv(), values)
|
||||
t.data = rdp.serialize()
|
||||
t.save()
|
||||
|
||||
if t.data_type == TRDPTransport.typeType:
|
||||
values = unmarshalTRDP(t.data.decode(TRDPTransport.CODEC))
|
||||
values = unmarshalTRDP(encoders.decode(t.data, 'base64'))
|
||||
rdp = TRDPTransport(Environment.getTempEnv(), values)
|
||||
t.data = rdp.serialize()
|
||||
t.save()
|
||||
|
25
server/src/uds/migrations/0024_auto_20171025_1405.py
Normal file
25
server/src/uds/migrations/0024_auto_20171025_1405.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.9 on 2017-10-25 14:05
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('uds', '0023_transport_allowed_oss'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='deployedservice',
|
||||
name='allow_users_remove',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='deployedservice',
|
||||
name='short_name',
|
||||
field=models.CharField(default='', max_length=32),
|
||||
),
|
||||
]
|
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.9 on 2017-11-29 09:28
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('uds', '0024_auto_20171025_1405'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='deployedservice',
|
||||
name='ignores_unused',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
@@ -34,7 +34,7 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
__updated__ = '2016-04-05'
|
||||
__updated__ = '2017-12-12'
|
||||
|
||||
from django.db import models
|
||||
from uds.models.UUIDModel import UUIDModel
|
||||
@@ -61,5 +61,18 @@ class Calendar(UUIDModel, TaggingMixin):
|
||||
db_table = 'uds_calendar'
|
||||
app_label = 'uds'
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
logger.debug('Saving calendar')
|
||||
|
||||
res = UUIDModel.save(self, *args, **kwargs)
|
||||
|
||||
# Basically, recalculates all related actions next execution time...
|
||||
try:
|
||||
for v in self.calendaraction_set.all(): v.save()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return res
|
||||
|
||||
def __str__(self):
|
||||
return 'Calendar "{}" modified on {} with {} rules'.format(self.name, self.modified, self.rules.count())
|
||||
|
@@ -33,7 +33,7 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
__updated__ = '2016-10-31'
|
||||
__updated__ = '2017-12-12'
|
||||
|
||||
from django.db import models
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
@@ -116,6 +116,8 @@ class CalendarRule(UUIDModel):
|
||||
if self.interval == 0: # Fix 0 intervals
|
||||
self.interval = 1
|
||||
|
||||
end = datetime.datetime.combine(self.end if self.end is not None else datetime.datetime.max.date(), datetime.datetime.max.time());
|
||||
|
||||
if self.frequency == WEEKDAYS:
|
||||
dw = []
|
||||
l = self.interval
|
||||
@@ -123,11 +125,16 @@ class CalendarRule(UUIDModel):
|
||||
if l & 1 == 1:
|
||||
dw.append(weekdays[i])
|
||||
l >>= 1
|
||||
return rules.rrule(rules.DAILY, byweekday=dw, dtstart=self.start)
|
||||
return rules.rrule(rules.DAILY, byweekday=dw, dtstart=self.start, until=end)
|
||||
else:
|
||||
return rules.rrule(frq_to_rrl[self.frequency], interval=self.interval, dtstart=self.start)
|
||||
return rules.rrule(frq_to_rrl[self.frequency], interval=self.interval, dtstart=self.start, until=end)
|
||||
|
||||
def as_rrule_end(self):
|
||||
if self.interval == 0: # Fix 0 intervals
|
||||
self.interval = 1
|
||||
|
||||
end = datetime.datetime.combine(self.end if self.end is not None else datetime.datetime.max.date(), datetime.datetime.max.time());
|
||||
|
||||
if self.frequency == WEEKDAYS:
|
||||
dw = []
|
||||
l = self.interval
|
||||
@@ -135,9 +142,9 @@ class CalendarRule(UUIDModel):
|
||||
if l & 1 == 1:
|
||||
dw.append(weekdays[i])
|
||||
l >>= 1
|
||||
return rules.rrule(rules.DAILY, byweekday=dw, dtstart=self.start + datetime.timedelta(minutes=self.duration_as_minutes))
|
||||
return rules.rrule(rules.DAILY, byweekday=dw, dtstart=self.start + datetime.timedelta(minutes=self.duration_as_minutes), until=end)
|
||||
else:
|
||||
return rules.rrule(frq_to_rrl[self.frequency], interval=self.interval, dtstart=self.start + datetime.timedelta(minutes=self.duration_as_minutes))
|
||||
return rules.rrule(frq_to_rrl[self.frequency], interval=self.interval, dtstart=self.start + datetime.timedelta(minutes=self.duration_as_minutes), until=end)
|
||||
|
||||
@property
|
||||
def frequency_as_minutes(self):
|
||||
@@ -153,9 +160,10 @@ class CalendarRule(UUIDModel):
|
||||
def save(self, *args, **kwargs):
|
||||
logger.debug('Saving...')
|
||||
self.calendar.modified = getSqlDatetime()
|
||||
self.calendar.save()
|
||||
|
||||
return UUIDModel.save(self, *args, **kwargs)
|
||||
res = UUIDModel.save(self, *args, **kwargs)
|
||||
self.calendar.save()
|
||||
return res
|
||||
|
||||
def __str__(self):
|
||||
return 'Rule {0}: {1}-{2}, {3}, Interval: {4}, duration: {5}'.format(self.name, self.start, self.end, self.frequency, self.interval, self.duration)
|
||||
|
@@ -34,11 +34,12 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
__updated__ = '2016-04-05'
|
||||
__updated__ = '2017-11-15'
|
||||
|
||||
from django.db import models
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
from uds.models.UUIDModel import UUIDModel
|
||||
from uds.core.util import encoders
|
||||
|
||||
import logging
|
||||
import six
|
||||
@@ -57,12 +58,21 @@ class DBFile(UUIDModel):
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
return self.content.decode('base64').decode('zip')
|
||||
try:
|
||||
return encoders.decode(encoders.decode(self.content, 'base64'), 'zip')
|
||||
except Exception:
|
||||
logger.error('DBFile {} has errors and cannot be used'.format(self.name))
|
||||
try:
|
||||
self.delete() # Autodelete, invalid...
|
||||
except:
|
||||
logger.error('Could not even delete {}!!'.format(self.name))
|
||||
|
||||
return ''
|
||||
|
||||
@data.setter
|
||||
def data(self, value):
|
||||
self.size = len(value)
|
||||
self.content = value.encode('zip').encode('base64')
|
||||
self.content = encoders.encode(encoders.encode(value, 'zip'), 'base64')
|
||||
|
||||
def __str__(self):
|
||||
return 'File: {} {} {} {}'.format(self.name, self.size, self.created, self.modified)
|
||||
|
@@ -60,12 +60,13 @@ from uds.core.util.calendar import CalendarChecker
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
import pickle
|
||||
import six
|
||||
|
||||
__updated__ = '2017-01-23'
|
||||
|
||||
__updated__ = '2017-11-29'
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class DeployedService(UUIDModel, TaggingMixin):
|
||||
'''
|
||||
@@ -73,6 +74,7 @@ class DeployedService(UUIDModel, TaggingMixin):
|
||||
'''
|
||||
# pylint: disable=model-missing-unicode
|
||||
name = models.CharField(max_length=128, default='')
|
||||
short_name = models.CharField(max_length=32, default='')
|
||||
comments = models.CharField(max_length=256, default='')
|
||||
service = models.ForeignKey(Service, null=True, blank=True, related_name='deployedServices')
|
||||
osmanager = models.ForeignKey(OSManager, null=True, blank=True, related_name='deployedServices')
|
||||
@@ -81,6 +83,8 @@ class DeployedService(UUIDModel, TaggingMixin):
|
||||
state = models.CharField(max_length=1, default=states.servicePool.ACTIVE, db_index=True)
|
||||
state_date = models.DateTimeField(default=NEVER)
|
||||
show_transports = models.BooleanField(default=True)
|
||||
allow_users_remove = models.BooleanField(default=False)
|
||||
ignores_unused = models.BooleanField(default=False)
|
||||
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)
|
||||
@@ -90,14 +94,12 @@ class DeployedService(UUIDModel, TaggingMixin):
|
||||
fallbackAccess = models.CharField(default=states.action.ALLOW, max_length=8)
|
||||
actionsCalendars = models.ManyToManyField(Calendar, related_name='actionsSP', through='CalendarAction')
|
||||
|
||||
|
||||
initial_srvs = models.PositiveIntegerField(default=0)
|
||||
cache_l1_srvs = models.PositiveIntegerField(default=0)
|
||||
cache_l2_srvs = models.PositiveIntegerField(default=0)
|
||||
max_srvs = models.PositiveIntegerField(default=0)
|
||||
current_pub_revision = models.PositiveIntegerField(default=1)
|
||||
|
||||
|
||||
# Meta service related
|
||||
meta_pools = models.ManyToManyField('self', symmetrical=False)
|
||||
|
||||
@@ -164,6 +166,13 @@ class DeployedService(UUIDModel, TaggingMixin):
|
||||
def is_meta(self):
|
||||
return self.meta_pools.count() == 0
|
||||
|
||||
@property
|
||||
def visual_name(self):
|
||||
logger.debug("SHORT: {} {} {}".format(self.short_name, self.short_name is not None, self.name))
|
||||
if self.short_name is not None and six.text_type(self.short_name).strip() != '':
|
||||
return six.text_type(self.short_name)
|
||||
return six.text_type(self.name)
|
||||
|
||||
def isRestrained(self):
|
||||
'''
|
||||
Maybe this deployed service is having problems, and that may block some task in some
|
||||
@@ -208,7 +217,6 @@ class DeployedService(UUIDModel, TaggingMixin):
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def isAccessAllowed(self, chkDateTime=None):
|
||||
'''
|
||||
Checks if the access for a service pool is allowed or not (based esclusively on associated calendars)
|
||||
@@ -255,7 +263,6 @@ class DeployedService(UUIDModel, TaggingMixin):
|
||||
|
||||
return int((deadLine - chkDateTime).total_seconds())
|
||||
|
||||
|
||||
def storeValue(self, name, value):
|
||||
'''
|
||||
Stores a value inside custom storage
|
||||
|
@@ -55,8 +55,7 @@ from uds.models.Util import getSqlDatetime
|
||||
|
||||
import logging
|
||||
|
||||
__updated__ = '2017-01-26'
|
||||
|
||||
__updated__ = '2017-11-29'
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -394,7 +393,7 @@ class UserService(UUIDModel):
|
||||
|
||||
def release(self):
|
||||
'''
|
||||
A much more convenient method that "remove"
|
||||
A much more convenient method name that "remove" (i think :) )
|
||||
'''
|
||||
self.remove()
|
||||
|
||||
@@ -506,5 +505,6 @@ class UserService(UUIDModel):
|
||||
|
||||
logger.debug('Deleted user service {0}'.format(toDelete))
|
||||
|
||||
|
||||
# Connects a pre deletion signal to Authenticator
|
||||
signals.pre_delete.connect(UserService.beforeDelete, sender=UserService)
|
||||
|
@@ -37,7 +37,7 @@ class WinDomainOsManager(WindowsOsManager):
|
||||
password = gui.PasswordField(length=64, label=_('Password'), order=3, tooltip=_('Password of the account'), required=True)
|
||||
ou = gui.TextField(length=64, label=_('OU'), order=4, tooltip=_('Organizational unit where to add machines in domain (check it before using it). i.e.: ou=My Machines,dc=mydomain,dc=local'))
|
||||
grp = gui.TextField(length=64, label=_('Machine Group'), order=7, tooltip=_('Group to which add machines on creation. If empty, no group will be used. (experimental)'), tab=_('Advanced'))
|
||||
serverHint = gui.TextField(length=64, label=_('Server Hint'), order=8, tooltip=_('In case of several AD servers, which one is prefered for first access'), tab=_('Advanced'))
|
||||
serverHint = gui.TextField(length=64, label=_('Server Hint'), order=8, tooltip=_('In case of several AD servers, which one is preferred'), tab=_('Advanced'))
|
||||
# Inherits base "onLogout"
|
||||
onLogout = WindowsOsManager.onLogout
|
||||
idle = WindowsOsManager.idle
|
||||
|
@@ -41,7 +41,7 @@ from uds.core.ui import gui
|
||||
|
||||
import logging
|
||||
|
||||
__updated__ = '2017-03-17'
|
||||
__updated__ = '2017-11-07'
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -122,6 +122,7 @@ class OVirtLinkedService(Service):
|
||||
length=3,
|
||||
label=_('Reserved Space'),
|
||||
defvalue='32',
|
||||
minValue=0,
|
||||
order=102,
|
||||
tooltip=_('Minimal free space in GB'),
|
||||
required=True
|
||||
@@ -139,6 +140,7 @@ class OVirtLinkedService(Service):
|
||||
label=_("Memory (Mb)"),
|
||||
length=4,
|
||||
defvalue=512,
|
||||
minValue=0,
|
||||
rdonly=False,
|
||||
order=111,
|
||||
tooltip=_('Memory assigned to machines'),
|
||||
@@ -150,6 +152,7 @@ class OVirtLinkedService(Service):
|
||||
label=_("Memory Guaranteed (Mb)"),
|
||||
length=4,
|
||||
defvalue=256,
|
||||
minValue=0,
|
||||
rdonly=False,
|
||||
order=112,
|
||||
tooltip=_('Physical memory guaranteed to machines'),
|
||||
|
354
server/src/uds/services/OpenGnsys/OGDeployment.py
Normal file
354
server/src/uds/services/OpenGnsys/OGDeployment.py
Normal file
@@ -0,0 +1,354 @@
|
||||
# -*- 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 uds.core.services import UserDeployment
|
||||
from uds.core.util.State import State
|
||||
from uds.core.util import log
|
||||
from uds.models.Util import getSqlDatetime
|
||||
|
||||
from . import og
|
||||
import six
|
||||
|
||||
import pickle
|
||||
import logging
|
||||
|
||||
__updated__ = '2017-10-16'
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
opCreate, opError, opFinish, opRemove, opRetry = range(5)
|
||||
|
||||
|
||||
class OGDeployment(UserDeployment):
|
||||
'''
|
||||
This class generates the user consumable elements of the service tree.
|
||||
|
||||
After creating at administration interface an Deployed Service, UDS will
|
||||
create consumable services for users using UserDeployment class as
|
||||
provider of this elements.
|
||||
|
||||
The logic for managing ovirt deployments (user machines in this case) is here.
|
||||
|
||||
'''
|
||||
|
||||
# : Recheck every six seconds by default (for task methods)
|
||||
suggestedTime = 20
|
||||
|
||||
def initialize(self):
|
||||
self._name = 'unknown'
|
||||
self._ip = ''
|
||||
self._mac = ''
|
||||
self._machineId = ''
|
||||
self._stamp = 0
|
||||
self._reason = ''
|
||||
self._queue = []
|
||||
self._uuid = self.dbservice().uuid
|
||||
|
||||
# Serializable needed methods
|
||||
def marshal(self):
|
||||
'''
|
||||
Does nothing right here, we will use envoronment storage in this sample
|
||||
'''
|
||||
return '\1'.join(['v1', self._name, self._ip, self._mac, self._machineId, self._reason, six.text_type(self._stamp), pickle.dumps(self._queue)])
|
||||
|
||||
def unmarshal(self, str_):
|
||||
'''
|
||||
Does nothing here also, all data are keeped at environment storage
|
||||
'''
|
||||
vals = str_.split('\1')
|
||||
if vals[0] == 'v1':
|
||||
self._name, self._ip, self._mac, self._machineId, self._reason, stamp, queue = vals[1:]
|
||||
self._stamp = int(stamp)
|
||||
self._queue = pickle.loads(queue)
|
||||
|
||||
def getName(self):
|
||||
return self._name
|
||||
|
||||
def getUniqueId(self):
|
||||
return self._mac.upper()
|
||||
|
||||
def getIp(self):
|
||||
return self._ip
|
||||
|
||||
def setReady(self):
|
||||
'''
|
||||
Notifies the current "deadline" to the user, before accessing by UDS
|
||||
The machine has been already been started.
|
||||
The problem is that currently there is no way that a machine is in FACT started.
|
||||
OpenGnsys will try it best by sending an WOL
|
||||
'''
|
||||
self.service().notifyDeadline(self._machineId, self.dbservice().deployed_service.getDeadline())
|
||||
|
||||
return State.FINISHED
|
||||
|
||||
def deployForUser(self, user):
|
||||
'''
|
||||
Deploys an service instance for an user.
|
||||
'''
|
||||
logger.debug('Deploying for user')
|
||||
self.__initQueueForDeploy()
|
||||
return self.__executeQueue()
|
||||
|
||||
def deployForCache(self, cacheLevel):
|
||||
'''
|
||||
Deploys an service instance for cache
|
||||
'''
|
||||
self.__initQueueForDeploy() # No Level2 Cache possible
|
||||
return self.__executeQueue()
|
||||
|
||||
def __initQueueForDeploy(self):
|
||||
|
||||
self._queue = [opCreate, opFinish]
|
||||
|
||||
def __checkMachineReady(self):
|
||||
logger.debug('Checking that state of machine {} ({}) is ready'.format(self._machineId, self._name))
|
||||
|
||||
try:
|
||||
status = self.service().status(self._machineId)
|
||||
except Exception as e:
|
||||
logger.exception('Exception at checkMachineReady')
|
||||
return self.__error('Error checking machine: {}'.format(e))
|
||||
|
||||
# possible status are ("off", "oglive", "busy", "linux", "windows", "macos" o "unknown").
|
||||
if status['status'] in ("linux", "windows", "macos"):
|
||||
return State.FINISHED
|
||||
|
||||
return State.RUNNING
|
||||
|
||||
def __getCurrentOp(self):
|
||||
if len(self._queue) == 0:
|
||||
return opFinish
|
||||
|
||||
return self._queue[0]
|
||||
|
||||
def __popCurrentOp(self):
|
||||
if len(self._queue) == 0:
|
||||
return opFinish
|
||||
|
||||
res = self._queue.pop(0)
|
||||
return res
|
||||
|
||||
def __pushFrontOp(self, op):
|
||||
self._queue.insert(0, op)
|
||||
|
||||
def __pushBackOp(self, op):
|
||||
self._queue.append(op)
|
||||
|
||||
def __error(self, reason):
|
||||
'''
|
||||
Internal method to set object as error state
|
||||
|
||||
Returns:
|
||||
State.ERROR, so we can do "return self.__error(reason)"
|
||||
'''
|
||||
logger.debug('Setting error state, reason: {0}'.format(reason))
|
||||
self.doLog(log.ERROR, reason)
|
||||
|
||||
# TODO: Unreserve machine?? Maybe it just better to keep it assigned so UDS don't get it again in a while...
|
||||
|
||||
self._queue = [opError]
|
||||
self._reason = str(reason)
|
||||
return State.ERROR
|
||||
|
||||
def __executeQueue(self):
|
||||
self.__debug('executeQueue')
|
||||
op = self.__getCurrentOp()
|
||||
|
||||
if op == opError:
|
||||
return State.ERROR
|
||||
|
||||
if op == opFinish:
|
||||
return State.FINISHED
|
||||
|
||||
fncs = {
|
||||
opCreate: self.__create,
|
||||
opRetry: self.__retry,
|
||||
opRemove: self.__remove,
|
||||
}
|
||||
|
||||
try:
|
||||
execFnc = fncs.get(op, None)
|
||||
|
||||
if execFnc is None:
|
||||
return self.__error('Unknown operation found at execution queue ({0})'.format(op))
|
||||
|
||||
execFnc()
|
||||
|
||||
return State.RUNNING
|
||||
except Exception as e:
|
||||
logger.exception('Got Exception')
|
||||
return self.__error(e)
|
||||
|
||||
# Queue execution methods
|
||||
def __retry(self):
|
||||
'''
|
||||
Used to retry an operation
|
||||
In fact, this will not be never invoked, unless we push it twice, because
|
||||
checkState method will "pop" first item when a check operation returns State.FINISHED
|
||||
|
||||
At executeQueue this return value will be ignored, and it will only be used at checkState
|
||||
'''
|
||||
return State.FINISHED
|
||||
|
||||
def __create(self):
|
||||
'''
|
||||
Deploys a machine from template for user/cache
|
||||
'''
|
||||
try:
|
||||
r = self.service().reserve()
|
||||
self.service().notifyEvents(r['id'], self._uuid)
|
||||
except Exception as e:
|
||||
# logger.exception('Creating machine')
|
||||
return self.__error('Error creating reservation: {}'.format(e))
|
||||
|
||||
self._machineId = r['id']
|
||||
self._name = r['name']
|
||||
self._mac = r['mac']
|
||||
self._ip = r['ip']
|
||||
self._stamp = getSqlDatetime(unix=True)
|
||||
|
||||
# Store actor version & Known ip
|
||||
self.dbservice().setProperty('actor_version', '1.0-OpenGnsys')
|
||||
self.dbservice().logIP(self._ip)
|
||||
|
||||
def __remove(self):
|
||||
'''
|
||||
Removes a machine from system
|
||||
'''
|
||||
self.service().unreserve(self._machineId)
|
||||
|
||||
# Check methods
|
||||
def __checkCreate(self):
|
||||
'''
|
||||
Checks the state of a deploy for an user or cache
|
||||
'''
|
||||
return self.__checkMachineReady()
|
||||
|
||||
def __checkRemoved(self):
|
||||
'''
|
||||
Checks if a machine has been removed
|
||||
'''
|
||||
return State.FINISHED # No check at all, always true
|
||||
|
||||
def checkState(self):
|
||||
'''
|
||||
Check what operation is going on, and acts acordly to it
|
||||
'''
|
||||
self.__debug('checkState')
|
||||
op = self.__getCurrentOp()
|
||||
|
||||
if op == opError:
|
||||
return State.ERROR
|
||||
|
||||
if op == opFinish:
|
||||
return State.FINISHED
|
||||
|
||||
fncs = {
|
||||
opCreate: self.__checkCreate,
|
||||
opRetry: self.__retry,
|
||||
opRemove: self.__checkRemoved,
|
||||
}
|
||||
|
||||
try:
|
||||
chkFnc = fncs.get(op, None)
|
||||
|
||||
if chkFnc is None:
|
||||
return self.__error('Unknown operation found at check queue ({0})'.format(op))
|
||||
|
||||
state = chkFnc()
|
||||
if state == State.FINISHED:
|
||||
self.__popCurrentOp() # Remove runing op
|
||||
return self.__executeQueue()
|
||||
|
||||
return state
|
||||
except Exception as e:
|
||||
return self.__error(e)
|
||||
|
||||
def finish(self):
|
||||
'''
|
||||
Invoked when the core notices that the deployment of a service has finished.
|
||||
(No matter wether it is for cache or for an user)
|
||||
'''
|
||||
self.__debug('finish')
|
||||
pass
|
||||
|
||||
def reasonOfError(self):
|
||||
'''
|
||||
Returns the reason of the error.
|
||||
|
||||
Remember that the class is responsible of returning this whenever asked
|
||||
for it, and it will be asked everytime it's needed to be shown to the
|
||||
user (when the administation asks for it).
|
||||
'''
|
||||
return self._reason
|
||||
|
||||
def destroy(self):
|
||||
'''
|
||||
Invoked for destroying a deployed service
|
||||
'''
|
||||
self.__debug('destroy')
|
||||
# If executing something, wait until finished to remove it
|
||||
# We simply replace the execution queue
|
||||
self._queue = [opRemove, opFinish]
|
||||
return self.__executeQueue()
|
||||
|
||||
def cancel(self):
|
||||
'''
|
||||
This is a task method. As that, the excepted return values are
|
||||
State values RUNNING, FINISHED or ERROR.
|
||||
|
||||
This can be invoked directly by an administration or by the clean up
|
||||
of the deployed service (indirectly).
|
||||
When administrator requests it, the cancel is "delayed" and not
|
||||
invoked directly.
|
||||
'''
|
||||
return self.destroy()
|
||||
|
||||
@staticmethod
|
||||
def __op2str(op):
|
||||
return {
|
||||
opCreate: 'create',
|
||||
opRemove: 'remove',
|
||||
opError: 'error',
|
||||
opFinish: 'finish',
|
||||
opRetry: 'retry',
|
||||
}.get(op, '????')
|
||||
|
||||
def __debug(self, txt):
|
||||
logger.debug('_name {0}: {1}'.format(txt, self._name))
|
||||
logger.debug('_ip {0}: {1}'.format(txt, self._ip))
|
||||
logger.debug('_mac {0}: {1}'.format(txt, self._mac))
|
||||
logger.debug('_machineId {0}: {1}'.format(txt, self._machineId))
|
||||
logger.debug('Queue at {0}: {1}'.format(txt, [OGDeployment.__op2str(op) for op in self._queue]))
|
128
server/src/uds/services/OpenGnsys/OGPublication.py
Normal file
128
server/src/uds/services/OpenGnsys/OGPublication.py
Normal file
@@ -0,0 +1,128 @@
|
||||
# -*- 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 uds.core.services import Publication
|
||||
from uds.core.util.State import State
|
||||
from uds.models.Util import getSqlDatetime
|
||||
|
||||
import logging
|
||||
|
||||
|
||||
__updated__ = '2017-05-18'
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OGPublication(Publication):
|
||||
'''
|
||||
This class provides the publication of a oVirtLinkedService
|
||||
'''
|
||||
_name = ''
|
||||
|
||||
suggestedTime = 5 # : Suggested recheck time if publication is unfinished in seconds
|
||||
|
||||
def initialize(self):
|
||||
'''
|
||||
This method will be invoked by default __init__ of base class, so it gives
|
||||
us the oportunity to initialize whataver we need here.
|
||||
|
||||
In our case, we setup a few attributes..
|
||||
'''
|
||||
|
||||
# We do not check anything at marshal method, so we ensure that
|
||||
# default values are correctly handled by marshal.
|
||||
self._name = ''
|
||||
|
||||
def marshal(self):
|
||||
'''
|
||||
returns data from an instance of Sample Publication serialized
|
||||
'''
|
||||
return '\t'.join(['v1', self._name])
|
||||
|
||||
def unmarshal(self, data):
|
||||
'''
|
||||
deserializes the data and loads it inside instance.
|
||||
'''
|
||||
logger.debug('Data: {0}'.format(data))
|
||||
vals = data.split('\t')
|
||||
if vals[0] == 'v1':
|
||||
self._name = vals[1]
|
||||
|
||||
def publish(self):
|
||||
'''
|
||||
Realizes the publication of the service
|
||||
'''
|
||||
self._name = 'Publication {}'.format(getSqlDatetime())
|
||||
return State.FINISHED
|
||||
|
||||
def checkState(self):
|
||||
'''
|
||||
Checks state of publication creation
|
||||
'''
|
||||
return State.FINISHED
|
||||
|
||||
def finish(self):
|
||||
'''
|
||||
In our case, finish does nothing
|
||||
'''
|
||||
pass
|
||||
|
||||
def reasonOfError(self):
|
||||
'''
|
||||
If a publication produces an error, here we must notify the reason why
|
||||
it happened. This will be called just after publish or checkState
|
||||
if they return State.ERROR
|
||||
|
||||
Returns an string, in our case, set at checkState
|
||||
'''
|
||||
return 'No error possible :)'
|
||||
|
||||
def destroy(self):
|
||||
'''
|
||||
This is called once a publication is no more needed.
|
||||
|
||||
This method do whatever needed to clean up things, such as
|
||||
removing created "external" data (environment gets cleaned by core),
|
||||
etc..
|
||||
|
||||
The retunred value is the same as when publishing, State.RUNNING,
|
||||
State.FINISHED or State.ERROR.
|
||||
'''
|
||||
return State.FINISHED
|
||||
|
||||
def cancel(self):
|
||||
'''
|
||||
Do same thing as destroy
|
||||
'''
|
||||
return self.destroy()
|
184
server/src/uds/services/OpenGnsys/OGService.py
Normal file
184
server/src/uds/services/OpenGnsys/OGService.py
Normal file
@@ -0,0 +1,184 @@
|
||||
# -*- 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 django.utils.translation import ugettext_noop as _, ugettext
|
||||
from uds.core.transports import protocols
|
||||
from uds.core.services import Service, types as serviceTypes
|
||||
from .OGDeployment import OGDeployment
|
||||
from .OGPublication import OGPublication
|
||||
from . import helpers
|
||||
|
||||
from uds.core.ui import gui
|
||||
|
||||
import logging
|
||||
|
||||
__updated__ = '2017-10-16'
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OGService(Service):
|
||||
'''
|
||||
OpenGnsys Service
|
||||
'''
|
||||
# : Name to show the administrator. This string will be translated BEFORE
|
||||
# : sending it to administration interface, so don't forget to
|
||||
# : mark it as _ (using ugettext_noop)
|
||||
typeName = _('OpenGnsys Machines Service')
|
||||
# : Type used internally to identify this provider
|
||||
typeType = 'openGnsysMachine'
|
||||
# : Description shown at administration interface for this provider
|
||||
typeDescription = _('OpenGnsys physical machines')
|
||||
# : Icon file used as icon for this provider. This string will be translated
|
||||
# : BEFORE sending it to administration interface, so don't forget to
|
||||
# : mark it as _ (using ugettext_noop)
|
||||
iconFile = 'provider.png'
|
||||
|
||||
# Functional related data
|
||||
|
||||
# : If the service provides more than 1 "deployed user" (-1 = no limit,
|
||||
# : 0 = ???? (do not use it!!!), N = max number to deploy
|
||||
maxDeployed = -1
|
||||
# : If we need to generate "cache" for this service, so users can access the
|
||||
# : provided services faster. Is usesCache is True, you will need also
|
||||
# : set publicationType, do take care about that!
|
||||
usesCache = True
|
||||
# : Tooltip shown to user when this item is pointed at admin interface, none
|
||||
# : because we don't use it
|
||||
cacheTooltip = _('Number of desired machines to keep running waiting for an user')
|
||||
|
||||
# : If the service needs a s.o. manager (managers are related to agents
|
||||
# : provided by services itselfs, i.e. virtual machines with actors)
|
||||
needsManager = False
|
||||
# : If true, the system can't do an automatic assignation of a deployed user
|
||||
# : service from this service
|
||||
mustAssignManually = False
|
||||
|
||||
# : Types of publications (preparated data for deploys)
|
||||
# : In our case, we do no need a publication, so this is None
|
||||
publicationType = OGPublication
|
||||
# : Types of deploys (services in cache and/or assigned to users)
|
||||
deployedType = OGDeployment
|
||||
|
||||
allowedProtocols = protocols.GENERIC
|
||||
servicesTypeProvided = (serviceTypes.VDI,)
|
||||
|
||||
# Now the form part
|
||||
ou = gui.ChoiceField(
|
||||
label=_("OU"),
|
||||
order=100,
|
||||
fills={
|
||||
'callbackName' : 'osFillData',
|
||||
'function' : helpers.getResources,
|
||||
'parameters' : ['ov', 'ev', 'ou']
|
||||
},
|
||||
tooltip=_('Organizational Unit'),
|
||||
required=True
|
||||
)
|
||||
|
||||
# Lab is not required, but maybe used as filter
|
||||
lab = gui.ChoiceField(
|
||||
label=_("lab"),
|
||||
order=101,
|
||||
tooltip=_('Laboratory'),
|
||||
required=False
|
||||
)
|
||||
|
||||
# Required, this is the base image
|
||||
image = gui.ChoiceField(
|
||||
label=_("OS Image"),
|
||||
order=102,
|
||||
tooltip=_('OpenGnsys Operating System Image'),
|
||||
required=True
|
||||
)
|
||||
|
||||
maxReservationTime = gui.NumericField(
|
||||
length=3,
|
||||
label=_("Max. reservation time"),
|
||||
order=110,
|
||||
tooltip=_('Security parameter for OpenGnsys to keep reservations at most this hours'),
|
||||
defvalue='24',
|
||||
tab=_('Advanced'),
|
||||
required=False
|
||||
)
|
||||
|
||||
ov = gui.HiddenField(value=None)
|
||||
ev = gui.HiddenField(value=None) # We need to keep the env so we can instantiate the Provider
|
||||
|
||||
def initialize(self, values):
|
||||
'''
|
||||
We check here form values to see if they are valid.
|
||||
|
||||
Note that we check them throught FROM variables, that already has been
|
||||
initialized by __init__ method of base class, before invoking this.
|
||||
'''
|
||||
if values is not None:
|
||||
pass
|
||||
|
||||
def initGui(self):
|
||||
'''
|
||||
Loads required values inside
|
||||
'''
|
||||
ous = [gui.choiceItem(r['id'], r['name']) for r in self.parent().api.getOus()]
|
||||
self.ou.setValues(ous)
|
||||
|
||||
self.ov.setDefValue(self.parent().serialize())
|
||||
self.ev.setDefValue(self.parent().env.key)
|
||||
|
||||
def status(self, machineId):
|
||||
return self.parent().status(machineId)
|
||||
|
||||
def reserve(self):
|
||||
return self.parent().reserve(self.ou.value, self.image.value, self.lab.value, self.maxReservationTime.num())
|
||||
|
||||
def unreserve(self, machineId):
|
||||
return self.parent().unreserve(machineId)
|
||||
|
||||
def notifyEvents(self, machineId, serviceUUID):
|
||||
return self.parent().notifyEvents(machineId, self.getLoginNotifyURL(serviceUUID), self.getLogoutNotifyURL(serviceUUID))
|
||||
|
||||
def notifyDeadline(self, machineId, deadLine):
|
||||
return self.parent().notifyDeadline(machineId, deadLine)
|
||||
|
||||
def _notifyURL(self, uuid, message):
|
||||
# The URL is "GET messages URL".
|
||||
return '{accessURL}rest/actor/PostThoughGet/{uuid}/{message}'.format(
|
||||
accessURL=self.parent().getUDSServerAccessUrl(),
|
||||
uuid=uuid,
|
||||
message=message
|
||||
)
|
||||
|
||||
def getLoginNotifyURL(self, serviceUUID):
|
||||
return self._notifyURL(serviceUUID, 'login')
|
||||
|
||||
def getLogoutNotifyURL(self, serviceUUID):
|
||||
return self._notifyURL(serviceUUID, 'logout')
|
201
server/src/uds/services/OpenGnsys/Provider.py
Normal file
201
server/src/uds/services/OpenGnsys/Provider.py
Normal file
@@ -0,0 +1,201 @@
|
||||
# -*- 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 distributiog.
|
||||
# * 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 permissiog.
|
||||
#
|
||||
# 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.
|
||||
|
||||
'''
|
||||
Created on Jun 22, 2012
|
||||
|
||||
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext_noop as _
|
||||
from uds.core.services import ServiceProvider
|
||||
from uds.core.ui import gui
|
||||
from uds.core.util import validators
|
||||
from defusedxml import minidom
|
||||
|
||||
from .OGService import OGService
|
||||
from . import og
|
||||
|
||||
|
||||
import logging
|
||||
import six
|
||||
|
||||
|
||||
__updated__ = '2017-10-16'
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class OGProvider(ServiceProvider):
|
||||
'''
|
||||
This class represents the sample services provider
|
||||
|
||||
In this class we provide:
|
||||
* The Provider functionality
|
||||
* The basic configuration parameters for the provider
|
||||
* The form fields needed by administrators to configure this provider
|
||||
|
||||
:note: At class level, the translation must be simply marked as so
|
||||
using ugettext_noop. This is so cause we will translate the string when
|
||||
sent to the administration client.
|
||||
|
||||
For this class to get visible at administration client as a provider type,
|
||||
we MUST register it at package __init__.
|
||||
|
||||
'''
|
||||
# : What kind of services we offer, this are classes inherited from Service
|
||||
offers = [OGService]
|
||||
# : Name to show the administrator. This string will be translated BEFORE
|
||||
# : sending it to administration interface, so don't forget to
|
||||
# : mark it as _ (using ugettext_noop)
|
||||
typeName = _('OpenGnsys Platform Provider')
|
||||
# : Type used internally to identify this provider
|
||||
typeType = 'openGnsysPlatform'
|
||||
# : Description shown at administration interface for this provider
|
||||
typeDescription = _('OpenGnsys platform service provider (experimental)')
|
||||
# : Icon file used as icon for this provider. This string will be translated
|
||||
# : BEFORE sending it to administration interface, so don't forget to
|
||||
# : mark it as _ (using ugettext_noop)
|
||||
iconFile = 'provider.png'
|
||||
|
||||
# now comes the form fields
|
||||
# There is always two fields that are requested to the admin, that are:
|
||||
# Service Name, that is a name that the admin uses to name this provider
|
||||
# Description, that is a short description that the admin gives to this provider
|
||||
# Now we are going to add a few fields that we need to use this provider
|
||||
# Remember that these are "dummy" fields, that in fact are not required
|
||||
# but used for sample purposes
|
||||
# If we don't indicate an order, the output order of fields will be
|
||||
# "random"
|
||||
host = gui.TextField(length=64, label=_('Host'), order=1, tooltip=_('OpenGnsys Host'), required=True)
|
||||
port = gui.NumericField(length=5, label=_('Port'), defvalue='443', order=2, tooltip=_('OpenGnsys Port (default is 443, and only ssl connection is allowed)'), required=True)
|
||||
checkCert = gui.CheckBoxField(label=_('Check Cert.'), order=3, tooltip=_('If checked, ssl certificate of OpenGnsys server must be valid (not self signed)'))
|
||||
username = gui.TextField(length=32, label=_('Username'), order=4, tooltip=_('User with valid privileges on OpenGnsys'), required=True)
|
||||
password = gui.PasswordField(lenth=32, label=_('Password'), order=5, tooltip=_('Password of the user of OpenGnsys'), required=True)
|
||||
udsServerAccessUrl = gui.TextField(length=32, label=_('UDS Server URL'), order=6, tooltip=_('URL used by OpenGnsys to access UDS. If empty, UDS will guess it.'), required=False, tab=gui.PARAMETERS_TAB)
|
||||
|
||||
maxPreparingServices = gui.NumericField(length=3, label=_('Creation concurrency'), defvalue='10', minValue=1, maxValue=65536, order=50, tooltip=_('Maximum number of concurrently creating VMs'), required=True, tab=gui.ADVANCED_TAB)
|
||||
maxRemovingServices = gui.NumericField(length=3, label=_('Removal concurrency'), defvalue='5', minValue=1, maxValue=65536, order=51, tooltip=_('Maximum number of concurrently removing VMs'), required=True, tab=gui.ADVANCED_TAB)
|
||||
|
||||
timeout = gui.NumericField(length=3, label=_('Timeout'), defvalue='10', order=90, tooltip=_('Timeout in seconds of connection to OpenGnsys'), required=True, tab=gui.ADVANCED_TAB)
|
||||
|
||||
# Own variables
|
||||
_api = None
|
||||
|
||||
def initialize(self, values=None):
|
||||
'''
|
||||
We will use the "autosave" feature for form fields
|
||||
'''
|
||||
|
||||
# Just reset _api connection variable
|
||||
self._api = None
|
||||
|
||||
if values is not None:
|
||||
self.timeout.value = validators.validateTimeout(self.timeout.value, returnAsInteger=False)
|
||||
logger.debug('Endpoint: {}'.format(self.endpoint))
|
||||
|
||||
try:
|
||||
request = values['_request']
|
||||
|
||||
if self.udsServerAccessUrl.value.strip() == '':
|
||||
self.udsServerAccessUrl.value = request.build_absolute_uri('/')
|
||||
|
||||
if self.udsServerAccessUrl.value[-1] != '/':
|
||||
self.udsServerAccessUrl.value += '/'
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@property
|
||||
def endpoint(self):
|
||||
return 'https://{}:{}/opengnsys/rest'.format(self.host.value, self.port.value)
|
||||
|
||||
@property
|
||||
def api(self):
|
||||
if self._api is None:
|
||||
self._api = og.OpenGnsysClient(self.username.value, self.password.value, self.endpoint, self.cache, self.checkCert.isTrue())
|
||||
|
||||
logger.debug('Api: {}'.format(self._api))
|
||||
return self._api
|
||||
|
||||
def resetApi(self):
|
||||
self._api = None
|
||||
|
||||
def testConnection(self):
|
||||
'''
|
||||
Test that conection to OpenGnsys server is fine
|
||||
|
||||
Returns
|
||||
|
||||
True if all went fine, false if id didn't
|
||||
'''
|
||||
try:
|
||||
if self.api.version[0:5] < '1.1.0':
|
||||
return [False, 'OpenGnsys version is not supported (required version 1.1.0 or newer and found {})'.format(self.api.version)]
|
||||
except Exception as e:
|
||||
logger.exception('Error')
|
||||
return [False, '{}'.format(e)]
|
||||
|
||||
return [True, _('OpenGnsys test connection passed')]
|
||||
|
||||
@staticmethod
|
||||
def test(env, data):
|
||||
'''
|
||||
Test ovirt Connectivity
|
||||
|
||||
Args:
|
||||
env: environment passed for testing (temporal environment passed)
|
||||
|
||||
data: data passed for testing (data obtained from the form
|
||||
definition)
|
||||
|
||||
Returns:
|
||||
Array of two elements, first is True of False, depending on test
|
||||
(True is all right, false is error),
|
||||
second is an String with error, preferably i18n..
|
||||
|
||||
'''
|
||||
return OGProvider(env, data).testConnection()
|
||||
|
||||
def getUDSServerAccessUrl(self):
|
||||
return self.udsServerAccessUrl.value
|
||||
|
||||
def reserve(self, ou, image, lab=0, maxtime=0):
|
||||
return self.api.reserve(ou, image, lab, maxtime)
|
||||
|
||||
def unreserve(self, machineId):
|
||||
return self.api.unreserve(machineId)
|
||||
|
||||
def notifyEvents(self, machineId, loginURL, logoutURL):
|
||||
return self.api.notifyURLs(machineId, loginURL, logoutURL)
|
||||
|
||||
def notifyDeadline(self, machineId, deadLine):
|
||||
return self.api.notifyDeadline(machineId, deadLine)
|
||||
|
||||
def status(self, machineId):
|
||||
return self.api.status(machineId)
|
32
server/src/uds/services/OpenGnsys/__init__.py
Normal file
32
server/src/uds/services/OpenGnsys/__init__.py
Normal file
@@ -0,0 +1,32 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2017 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.
|
||||
|
||||
|
||||
from .Provider import OGProvider
|
||||
|
39
server/src/uds/services/OpenGnsys/helpers.py
Normal file
39
server/src/uds/services/OpenGnsys/helpers.py
Normal file
@@ -0,0 +1,39 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
import logging
|
||||
from uds.core.ui import gui
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def getResources(parameters):
|
||||
'''
|
||||
This helper is designed as a callback for Project Selector
|
||||
'''
|
||||
from .Provider import OGProvider
|
||||
from uds.core.Environment import Environment
|
||||
logger.debug('Parameters received by getResources Helper: {0}'.format(parameters))
|
||||
env = Environment(parameters['ev'])
|
||||
provider = OGProvider(env)
|
||||
provider.unserialize(parameters['ov'])
|
||||
|
||||
api = provider.api
|
||||
|
||||
labs = [gui.choiceItem('0', _('All Labs'))] + [gui.choiceItem(l['id'], l['name']) for l in api.getLabs(ou=parameters['ou'])]
|
||||
images = [gui.choiceItem(z['id'], z['name']) for z in api.getImages(ou=parameters['ou'])]
|
||||
|
||||
data = [
|
||||
{'name': 'lab', 'values': labs },
|
||||
{'name': 'image', 'values': images },
|
||||
]
|
||||
logger.debug('Return data: {}'.format(data))
|
||||
return data
|
251
server/src/uds/services/OpenGnsys/og/__init__.py
Normal file
251
server/src/uds/services/OpenGnsys/og/__init__.py
Normal file
@@ -0,0 +1,251 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2017 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 . import urls
|
||||
from . import fake
|
||||
|
||||
import sys
|
||||
import imp
|
||||
import re
|
||||
|
||||
import logging
|
||||
import six
|
||||
|
||||
import requests
|
||||
import json
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# URLS
|
||||
|
||||
# Fake part
|
||||
FAKE = False
|
||||
CACHE_VALIDITY = 180
|
||||
|
||||
|
||||
# Decorator
|
||||
def ensureConnected(fnc):
|
||||
def inner(*args, **kwargs):
|
||||
args[0].connect()
|
||||
return fnc(*args, **kwargs)
|
||||
return inner
|
||||
|
||||
# Result checker
|
||||
def ensureResponseIsValid(response, errMsg=None):
|
||||
if response.ok is False:
|
||||
if errMsg is None:
|
||||
errMsg = 'Invalid response'
|
||||
|
||||
try:
|
||||
err = response.json()['message'] # Extract any key, in case of error is expected to have only one top key so this will work
|
||||
except Exception:
|
||||
err = response.content
|
||||
errMsg = '{}: {}, ({})'.format(errMsg, err, response.status_code)
|
||||
logger.error('{}: {}'.format(errMsg, response.content))
|
||||
raise Exception(errMsg)
|
||||
|
||||
try:
|
||||
return json.loads(response.content)
|
||||
except Exception:
|
||||
raise Exception('Error communicating with OpenGnsys: {}'.format(response.content[:128]))
|
||||
|
||||
|
||||
class OpenGnsysClient(object):
|
||||
def __init__(self, username, password, endpoint, cache, verifyCert=False):
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.endpoint = endpoint
|
||||
self.auth = None
|
||||
self.cache = cache
|
||||
self.verifyCert = verifyCert
|
||||
self.cachedVersion = None
|
||||
|
||||
@property
|
||||
def headers(self):
|
||||
headers = {'content-type': 'application/json'}
|
||||
if self.auth is not None:
|
||||
headers['Authorization'] = self.auth
|
||||
|
||||
return headers
|
||||
|
||||
def _ogUrl(self, path):
|
||||
return self.endpoint + '/' + path
|
||||
|
||||
def _post(self, path, data, errMsg=None):
|
||||
if not FAKE:
|
||||
return ensureResponseIsValid(
|
||||
requests.post(self._ogUrl(path), data=json.dumps(data), headers=self.headers, verify=self.verifyCert),
|
||||
errMsg=errMsg
|
||||
)
|
||||
# FAKE Connection :)
|
||||
return fake.post(path, data, errMsg)
|
||||
|
||||
def _get(self, path, errMsg=None):
|
||||
if not FAKE:
|
||||
return ensureResponseIsValid(
|
||||
requests.get(self._ogUrl(path), headers=self.headers, verify=self.verifyCert),
|
||||
errMsg=errMsg
|
||||
)
|
||||
# FAKE Connection :)
|
||||
return fake.get(path, errMsg)
|
||||
|
||||
|
||||
def _delete(self, path, errMsg=None):
|
||||
if not FAKE:
|
||||
return ensureResponseIsValid(
|
||||
requests.delete(self._ogUrl(path), headers=self.headers, verify=self.verifyCert),
|
||||
errMsg=errMsg
|
||||
)
|
||||
return fake.delete(path, errMsg)
|
||||
|
||||
def connect(self):
|
||||
if self.auth is not None:
|
||||
return
|
||||
|
||||
cacheKey = 'auth{}{}'.format(self.endpoint, self.username)
|
||||
self.auth = self.cache.get(cacheKey)
|
||||
if self.auth is not None:
|
||||
return
|
||||
|
||||
auth = self._post(urls.LOGIN,
|
||||
data={
|
||||
'username': self.username,
|
||||
'password': self.password
|
||||
},
|
||||
errMsg='Loggin in'
|
||||
)
|
||||
|
||||
self.auth = auth['apikey']
|
||||
self.cache.put(cacheKey, self.auth, CACHE_VALIDITY)
|
||||
|
||||
|
||||
@property
|
||||
def version(self):
|
||||
logger.debug('Getting version')
|
||||
if self.cachedVersion is None:
|
||||
# Retrieve Version & keep it
|
||||
info = self._get(urls.INFO, errMsg="Retrieving info")
|
||||
self.cachedVersion = info['version']
|
||||
|
||||
return self.cachedVersion
|
||||
|
||||
@ensureConnected
|
||||
def getOus(self):
|
||||
# Returns an array of elements with:
|
||||
# 'id': OpenGnsys Id
|
||||
# 'name': OU name
|
||||
# OpenGnsys already returns it in this format :)
|
||||
return self._get(urls.OUS, errMsg='Getting list of ous')
|
||||
|
||||
@ensureConnected
|
||||
def getLabs(self, ou):
|
||||
# Returns a list of available labs on an ou
|
||||
# /ous/{ouid}/labs
|
||||
# Take into accout that we must exclude the ones with "inremotepc" set to false.
|
||||
errMsg = 'Getting list of labs from ou {}'.format(ou)
|
||||
return [{'id': l['id'], 'name': l['name']} for l in self._get(urls.LABS.format(ou=ou), errMsg=errMsg) if l.get('inremotepc', False) is True]
|
||||
|
||||
|
||||
@ensureConnected
|
||||
def getImages(self, ou):
|
||||
# Returns a list of available labs on an ou
|
||||
# /ous/{ouid}/images
|
||||
# Take into accout that we must exclude the ones with "inremotepc" set to false.
|
||||
errMsg = 'Getting list of images from ou {}'.format(ou)
|
||||
return [{'id': l['id'], 'name': l['name']} for l in self._get(urls.IMAGES.format(ou=ou), errMsg=errMsg) if l.get('inremotepc', False) is True]
|
||||
|
||||
@ensureConnected
|
||||
def reserve(self, ou, image, lab=0, maxtime=24):
|
||||
# This method is inteded to "get" a machine from OpenGnsys
|
||||
# The method used is POST
|
||||
# invokes /ous/{ouid}}/images/{imageid}/reserve
|
||||
# also remember to store "labid"
|
||||
# Labid can be "0" that means "all laboratories"
|
||||
errMsg = 'Reserving image {} in ou {}'.format(image, ou)
|
||||
data = {
|
||||
'labid': lab,
|
||||
'maxtime': maxtime
|
||||
}
|
||||
res = self._post(urls.RESERVE.format(ou=ou, image=image), data, errMsg=errMsg)
|
||||
return {
|
||||
'ou': ou,
|
||||
'image': image,
|
||||
'lab': lab,
|
||||
'client': res['id'],
|
||||
'id': '.'.join((six.text_type(ou), six.text_type(res['lab']['id']), six.text_type(res['id']))),
|
||||
'name': res['name'],
|
||||
'ip': res['ip'],
|
||||
'mac': ':'.join(re.findall('..', res['mac']))
|
||||
}
|
||||
|
||||
@ensureConnected
|
||||
def unreserve(self, machineId):
|
||||
# This method releases the previous reservation
|
||||
# Invoked every time we need to release a reservation (i mean, if a reservation is done, this will be called with the obtained id from that reservation)
|
||||
ou, lab, client = machineId.split('.')
|
||||
errMsg = 'Unreserving client {} in lab {} in ou {}'.format(client, lab, ou)
|
||||
return self._delete(urls.UNRESERVE.format(ou=ou, lab=lab, client=client), errMsg=errMsg)
|
||||
|
||||
@ensureConnected
|
||||
def notifyURLs(self, machineId, loginURL, logoutURL):
|
||||
ou, lab, client = machineId.split('.')
|
||||
errMsg = 'Notifying login/logout urls'
|
||||
data = {
|
||||
'urlLogin': loginURL,
|
||||
'urlLogout': logoutURL
|
||||
}
|
||||
|
||||
return self._post(urls.EVENTS.format(ou=ou, lab=lab, client=client), data, errMsg=errMsg)
|
||||
|
||||
@ensureConnected
|
||||
def notifyDeadline(self, machineId, deadLine):
|
||||
ou, lab, client = machineId.split('.')
|
||||
if deadLine is None:
|
||||
deadLine = 0
|
||||
errMsg = 'Notifying deadline'
|
||||
data = {
|
||||
'deadLine': deadLine
|
||||
}
|
||||
|
||||
return self._post(urls.SESSIONS.format(ou=ou, lab=lab, client=client), data, errMsg=errMsg)
|
||||
|
||||
|
||||
@ensureConnected
|
||||
def status(self, id):
|
||||
# This method gets the status of the machine
|
||||
# /ous/{uoid}/labs/{labid}/clients/{clientid}/status
|
||||
# possible status are ("off", "oglive", "busy", "linux", "windows", "macos" o "unknown").
|
||||
# Look at api at informatica.us..
|
||||
ou, lab, client = id.split('.')
|
||||
return self._get(urls.STATUS.format(ou=ou, lab=lab, client=client))
|
227
server/src/uds/services/OpenGnsys/og/fake.py
Normal file
227
server/src/uds/services/OpenGnsys/og/fake.py
Normal file
@@ -0,0 +1,227 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2017 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 . import urls
|
||||
import copy
|
||||
import random
|
||||
import six
|
||||
import logging
|
||||
|
||||
__updated__ = '2017-09-29'
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
AUTH = {
|
||||
"userid": 1001,
|
||||
"apikey": "fakeAPIKeyJustForDeveloping"
|
||||
}
|
||||
|
||||
INFO = {
|
||||
"project": "OpenGnsys",
|
||||
"version": "1.1.0pre",
|
||||
"release": "r5299",
|
||||
"services": [
|
||||
"server",
|
||||
"repository",
|
||||
"tracker"
|
||||
],
|
||||
"oglive": [
|
||||
{
|
||||
"distribution": "xenial",
|
||||
"kernel": "4.8.0-39-generic",
|
||||
"architecture": "amd64",
|
||||
"revision": "r5225",
|
||||
"directory": "ogLive-xenial-4.8.0-amd64-r5225",
|
||||
"iso": "ogLive-xenial-4.8.0-39-generic-amd64-r5225.iso"
|
||||
},
|
||||
{
|
||||
"iso": "ogLive-precise-3.2.0-23-generic-r4820.iso",
|
||||
"directory": "ogLive-precise-3.2.0-i386-r4820",
|
||||
"revision": "r4820",
|
||||
"architecture": "i386",
|
||||
"kernel": "3.2.0-23-generic",
|
||||
"distribution": "precise"
|
||||
} ]
|
||||
}
|
||||
|
||||
OUS = [
|
||||
{
|
||||
"id": "1",
|
||||
"name": "Unidad Organizativa (Default)"
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"name": "Unidad Organizatva VACIA"
|
||||
},
|
||||
]
|
||||
|
||||
LABS = [
|
||||
{
|
||||
"id": "1",
|
||||
"name": "Sala de control",
|
||||
"inremotepc": True,
|
||||
"group": {
|
||||
"id": "0"
|
||||
},
|
||||
"ou": {
|
||||
"id": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"name": "Sala de computación cuántica",
|
||||
"inremotepc": True,
|
||||
"group": {
|
||||
"id": "0"
|
||||
},
|
||||
"ou": {
|
||||
"id": "1"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
IMAGES = [
|
||||
{
|
||||
"id": "1",
|
||||
"name": "Basica1604",
|
||||
"inremotepc": True,
|
||||
"ou": {
|
||||
"id": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"name": "Ubuntu16",
|
||||
"inremotepc": True,
|
||||
"ou": {
|
||||
"id": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
"name": "Ubuntu64 Not in Remote",
|
||||
"inremotepc": False,
|
||||
"ou": {
|
||||
"id": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "4",
|
||||
"name": "Ubuntu96 Not In Remote",
|
||||
"inremotepc": False,
|
||||
"ou": {
|
||||
"id": "1"
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
RESERVE = {
|
||||
"id": 4,
|
||||
"name": "pcpruebas",
|
||||
"mac": "4061860521FE",
|
||||
"ip": "10.1.14.31",
|
||||
"lab": {
|
||||
"id": 1
|
||||
},
|
||||
"ou": {
|
||||
"id": 1
|
||||
}
|
||||
}
|
||||
|
||||
UNRESERVE = ''
|
||||
|
||||
STATUS_OFF = {
|
||||
"id": 4,
|
||||
"ip": "10.1.14.31",
|
||||
"status": "off",
|
||||
"loggedin": False
|
||||
}
|
||||
|
||||
# A couple of status for testing
|
||||
STATUS_READY_LINUX = {
|
||||
"id": 4,
|
||||
"ip": "10.1.14.31",
|
||||
"status": "linux",
|
||||
"loggedin": False
|
||||
}
|
||||
|
||||
STATUS_READY_WINDOWS = {
|
||||
"id": 4,
|
||||
"ip": "10.1.14.31",
|
||||
"status": "windows",
|
||||
"loggedin": False
|
||||
}
|
||||
|
||||
# FAKE post
|
||||
def post(path, data, errMsg):
|
||||
logger.info('FAKE POST request to {} with {} data. ({})'.format(path, data, errMsg))
|
||||
if path == urls.LOGIN:
|
||||
return AUTH
|
||||
elif path == urls.RESERVE.format(ou=1, image=1) or path == urls.RESERVE.format(ou=1, image=2):
|
||||
res = copy.deepcopy(RESERVE)
|
||||
res['name'] += six.text_type(random.randint(5000, 100000))
|
||||
res['mac'] = ''.join(random.choice('0123456789ABCDEF') for __ in range(12))
|
||||
return res
|
||||
|
||||
raise Exception('Unknown FAKE URL on POST: {}'.format(path))
|
||||
|
||||
# FAKE get
|
||||
def get(path, errMsg):
|
||||
logger.info('FAKE GET request to {}. ({})'.format(path, errMsg))
|
||||
if path == urls.INFO:
|
||||
return INFO
|
||||
elif path == urls.OUS:
|
||||
return OUS
|
||||
elif path == urls.LABS.format(ou=1):
|
||||
return LABS
|
||||
elif path == urls.LABS.format(ou=2):
|
||||
return [] # Empty
|
||||
elif path == urls.IMAGES.format(ou=1):
|
||||
return IMAGES
|
||||
elif path == urls.IMAGES.format(ou=2):
|
||||
return []
|
||||
elif path[-6:] == 'status':
|
||||
rnd = random.randint(0, 100)
|
||||
if rnd < 25:
|
||||
return STATUS_READY_LINUX
|
||||
return STATUS_OFF
|
||||
elif path[-6:] == 'events':
|
||||
return ''
|
||||
|
||||
raise Exception('Unknown FAKE URL on GET: {}'.format(path))
|
||||
|
||||
def delete(path, errMsg):
|
||||
logger.info('FAKE DELETE request to {}. ({})'.format(path, errMsg))
|
||||
# Right now, only "unreserve" uses delete, so simply return
|
||||
return UNRESERVE
|
||||
# raise Exception('Unknown FAKE URL on DELETE: {}'.format(path))
|
47
server/src/uds/services/OpenGnsys/og/urls.py
Normal file
47
server/src/uds/services/OpenGnsys/og/urls.py
Normal file
@@ -0,0 +1,47 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2017 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
|
||||
|
||||
# API URL 1: https://www.informatica.us.es/~ramon/opengnsys/?url=opengnsys-api.yml
|
||||
# API URL 2: http://opengnsys.es/wiki/ApiRest
|
||||
|
||||
LOGIN = '/login'
|
||||
INFO = '/info'
|
||||
OUS = '/ous'
|
||||
LABS = '/ous/{ou}/labs'
|
||||
IMAGES = '/ous/{ou}/images'
|
||||
RESERVE = '/ous/{ou}/images/{image}/reserve'
|
||||
UNRESERVE = '/ous/{ou}/labs/{lab}/clients/{client}/unreserve'
|
||||
STATUS = '/ous/{ou}/labs/{lab}/clients/{client}/status'
|
||||
EVENTS = '/ous/{ou}/labs/{lab}/clients/{client}/events'
|
||||
SESSIONS = '/ous/{ou}/labs/{lab}/clients/{client}/session'
|
BIN
server/src/uds/services/OpenGnsys/provider.png
Normal file
BIN
server/src/uds/services/OpenGnsys/provider.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.6 KiB |
@@ -39,8 +39,7 @@ from . import openStack
|
||||
import pickle
|
||||
import logging
|
||||
|
||||
__updated__ = '2017-05-17'
|
||||
|
||||
__updated__ = '2017-11-21'
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -183,8 +182,8 @@ class LiveDeployment(UserDeployment):
|
||||
|
||||
if status == openStack.PAUSED:
|
||||
self.service().resumeMachine(self._vmid)
|
||||
elif status == openStack.STOPPED:
|
||||
self.service().startMachine(self._vmId)
|
||||
elif status in (openStack.STOPPED, openStack.SHUTOFF):
|
||||
self.service().startMachine(self._vmid)
|
||||
|
||||
# Right now, we suppose the machine is ready
|
||||
|
||||
@@ -385,7 +384,6 @@ class LiveDeployment(UserDeployment):
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def __checkStart(self):
|
||||
'''
|
||||
Checks if machine has started
|
||||
|
@@ -43,7 +43,7 @@ import hashlib
|
||||
import six
|
||||
|
||||
|
||||
__updated__ = '2017-03-21'
|
||||
__updated__ = '2017-11-13'
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -595,6 +595,7 @@ class Client(object):
|
||||
# We need api version 3.2 or greater
|
||||
try:
|
||||
r = requests.get(self._authUrl,
|
||||
verify=VERIFY_SSL,
|
||||
headers=self._requestHeaders())
|
||||
except Exception:
|
||||
raise Exception('Connection error')
|
||||
|
@@ -35,7 +35,7 @@ import re
|
||||
|
||||
import logging
|
||||
|
||||
__updated__ = '2016-03-07'
|
||||
__updated__ = '2017-11-21'
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -43,16 +43,18 @@ logger = logging.getLogger(__name__)
|
||||
HARD_REBOOT, MIGRATING, PASSWORD,
|
||||
PAUSED, REBOOT, REBUILD, RESCUED,
|
||||
RESIZED, REVERT_RESIZE, SOFT_DELETED,
|
||||
STOPPED, SUSPENDED, UNKNOWN, VERIFY_RESIZE) = ('ACTIVE', 'BUILDING', 'DELETED', 'ERROR',
|
||||
STOPPED, SUSPENDED, UNKNOWN, VERIFY_RESIZE, SHUTOFF) = ('ACTIVE', 'BUILDING', 'DELETED', 'ERROR',
|
||||
'HARD_REBOOT', 'MIGRATING', 'PASSWORD',
|
||||
'PAUSED', 'REBOOT', 'REBUILD', 'RESCUED',
|
||||
'RESIZED', 'REVERT_RESIZE', 'SOFT_DELETED',
|
||||
'STOPPED', 'SUSPENDED', 'UNKNOWN', 'VERIFY_RESIZE')
|
||||
'STOPPED', 'SUSPENDED', 'UNKNOWN', 'VERIFY_RESIZE', 'SHUTOFF')
|
||||
|
||||
|
||||
# Helpers to check statuses
|
||||
def statusIsLost(status):
|
||||
return status in [DELETED, ERROR, UNKNOWN, SOFT_DELETED]
|
||||
|
||||
|
||||
def sanitizeName(name):
|
||||
'''
|
||||
machine names with [a-zA-Z0-9_-]
|
||||
|
@@ -70,7 +70,7 @@ class IPMachinesService(services.Service):
|
||||
self._ips = []
|
||||
else:
|
||||
self._ips = list('{}~{}'.format(ip, i) for i, ip in enumerate(values['ipList'])) # Allow duplicates right now
|
||||
self._ips.sort()
|
||||
# self._ips.sort()
|
||||
|
||||
def valuesDict(self):
|
||||
ips = (i.split('~')[0] for i in self._ips)
|
||||
|
@@ -1,2 +1,2 @@
|
||||
/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
|
||||
var saveAs=saveAs||"undefined"!=typeof navigator&&navigator.msSaveOrOpenBlob&&navigator.msSaveOrOpenBlob.bind(navigator)||function(e){"use strict";if("undefined"==typeof navigator||!/MSIE [1-9]\./.test(navigator.userAgent)){var t=e.document,n=function(){return e.URL||e.webkitURL||e},o=t.createElementNS("http://www.w3.org/1999/xhtml","a"),r="download"in o,i=function(n){var o=t.createEvent("MouseEvents");o.initMouseEvent("click",!0,!1,e,0,0,0,0,0,!1,!1,!1,!1,0,null),n.dispatchEvent(o)},a=e.webkitRequestFileSystem,c=e.requestFileSystem||a||e.mozRequestFileSystem,s=function(t){(e.setImmediate||e.setTimeout)(function(){throw t},0)},u="application/octet-stream",f=0,d=500,l=function(t){var o=function(){"string"==typeof t?n().revokeObjectURL(t):t.remove()};e.chrome?o():setTimeout(o,d)},v=function(e,t,n){t=[].concat(t);for(var o=t.length;o--;){var r=e["on"+t[o]];if("function"==typeof r)try{r.call(e,n||e)}catch(i){s(i)}}},p=function(t,s){var d,p,w,y=this,m=t.type,S=!1,h=function(){v(y,"writestart progress write writeend".split(" "))},O=function(){if((S||!d)&&(d=n().createObjectURL(t)),p)p.location.href=d;else{var o=e.open(d,"_blank");void 0==o&&"undefined"!=typeof safari&&(e.location.href=d)}y.readyState=y.DONE,h(),l(d)},b=function(e){return function(){return y.readyState!==y.DONE?e.apply(this,arguments):void 0}},g={create:!0,exclusive:!1};return y.readyState=y.INIT,s||(s="download"),r?(d=n().createObjectURL(t),o.href=d,o.download=s,i(o),y.readyState=y.DONE,h(),void l(d)):(/^\s*(?:text\/(?:plain|xml)|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(t.type)&&(t=new Blob(["",t],{type:t.type})),e.chrome&&m&&m!==u&&(w=t.slice||t.webkitSlice,t=w.call(t,0,t.size,u),S=!0),a&&"download"!==s&&(s+=".download"),(m===u||a)&&(p=e),c?(f+=t.size,void c(e.TEMPORARY,f,b(function(e){e.root.getDirectory("saved",g,b(function(e){var n=function(){e.getFile(s,g,b(function(e){e.createWriter(b(function(n){n.onwriteend=function(t){p.location.href=e.toURL(),y.readyState=y.DONE,v(y,"writeend",t),l(e)},n.onerror=function(){var e=n.error;e.code!==e.ABORT_ERR&&O()},"writestart progress write abort".split(" ").forEach(function(e){n["on"+e]=y["on"+e]}),n.write(t),y.abort=function(){n.abort(),y.readyState=y.DONE},y.readyState=y.WRITING}),O)}),O)};e.getFile(s,{create:!1},b(function(e){e.remove(),n()}),b(function(e){e.code===e.NOT_FOUND_ERR?n():O()}))}),O)}),O)):void O())},w=p.prototype,y=function(e,t){return new p(e,t)};return w.abort=function(){var e=this;e.readyState=e.DONE,v(e,"abort")},w.readyState=w.INIT=0,w.WRITING=1,w.DONE=2,w.error=w.onwritestart=w.onprogress=w.onwrite=w.onabort=w.onerror=w.onwriteend=null,y}}("undefined"!=typeof self&&self||"undefined"!=typeof window&&window||this.content);"undefined"!=typeof module&&module.exports?module.exports.saveAs=saveAs:"undefined"!=typeof define&&null!==define&&null!=define.amd&&define([],function(){return saveAs});
|
||||
var saveAs=saveAs||function(e){"use strict";if(typeof e==="undefined"||typeof navigator!=="undefined"&&/MSIE [1-9]\./.test(navigator.userAgent)){return}var t=e.document,n=function(){return e.URL||e.webkitURL||e},r=t.createElementNS("http://www.w3.org/1999/xhtml","a"),o="download"in r,a=function(e){var t=new MouseEvent("click");e.dispatchEvent(t)},i=/constructor/i.test(e.HTMLElement)||e.safari,f=/CriOS\/[\d]+/.test(navigator.userAgent),u=function(t){(e.setImmediate||e.setTimeout)(function(){throw t},0)},s="application/octet-stream",d=1e3*40,c=function(e){var t=function(){if(typeof e==="string"){n().revokeObjectURL(e)}else{e.remove()}};setTimeout(t,d)},l=function(e,t,n){t=[].concat(t);var r=t.length;while(r--){var o=e["on"+t[r]];if(typeof o==="function"){try{o.call(e,n||e)}catch(a){u(a)}}}},p=function(e){if(/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(e.type)){return new Blob([String.fromCharCode(65279),e],{type:e.type})}return e},v=function(t,u,d){if(!d){t=p(t)}var v=this,w=t.type,m=w===s,y,h=function(){l(v,"writestart progress write writeend".split(" "))},S=function(){if((f||m&&i)&&e.FileReader){var r=new FileReader;r.onloadend=function(){var t=f?r.result:r.result.replace(/^data:[^;]*;/,"data:attachment/file;");var n=e.open(t,"_blank");if(!n)e.location.href=t;t=undefined;v.readyState=v.DONE;h()};r.readAsDataURL(t);v.readyState=v.INIT;return}if(!y){y=n().createObjectURL(t)}if(m){e.location.href=y}else{var o=e.open(y,"_blank");if(!o){e.location.href=y}}v.readyState=v.DONE;h();c(y)};v.readyState=v.INIT;if(o){y=n().createObjectURL(t);setTimeout(function(){r.href=y;r.download=u;a(r);h();c(y);v.readyState=v.DONE});return}S()},w=v.prototype,m=function(e,t,n){return new v(e,t||e.name||"download",n)};if(typeof navigator!=="undefined"&&navigator.msSaveOrOpenBlob){return function(e,t,n){t=t||e.name||"download";if(!n){e=p(e)}return navigator.msSaveOrOpenBlob(e,t)}}w.abort=function(){};w.readyState=w.INIT=0;w.WRITING=1;w.DONE=2;w.error=w.onwritestart=w.onprogress=w.onwrite=w.onabort=w.onerror=w.onwriteend=null;return m}(typeof self!=="undefined"&&self||typeof window!=="undefined"&&window||this.content);if(typeof module!=="undefined"&&module.exports){module.exports.saveAs=saveAs}else if(typeof define!=="undefined"&&define!==null&&define.amd!==null){define("FileSaver.js",function(){return saveAs})}
|
||||
|
@@ -1,8 +1,9 @@
|
||||
/*!
|
||||
/**!
|
||||
|
||||
handlebars v4.0.2
|
||||
@license
|
||||
handlebars v4.0.10
|
||||
|
||||
Copyright (C) 2011-2015 by Yehuda Katz
|
||||
Copyright (C) 2011-2016 by Yehuda Katz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -22,7 +23,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
@license
|
||||
*/
|
||||
(function webpackUniversalModuleDefinition(root, factory) {
|
||||
if(typeof exports === 'object' && typeof module === 'object')
|
||||
@@ -78,7 +78,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
/************************************************************************/
|
||||
/******/ ([
|
||||
/* 0 */
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -92,23 +92,23 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
|
||||
// Compiler imports
|
||||
|
||||
var _handlebarsCompilerAst = __webpack_require__(21);
|
||||
var _handlebarsCompilerAst = __webpack_require__(35);
|
||||
|
||||
var _handlebarsCompilerAst2 = _interopRequireDefault(_handlebarsCompilerAst);
|
||||
|
||||
var _handlebarsCompilerBase = __webpack_require__(22);
|
||||
var _handlebarsCompilerBase = __webpack_require__(36);
|
||||
|
||||
var _handlebarsCompilerCompiler = __webpack_require__(27);
|
||||
var _handlebarsCompilerCompiler = __webpack_require__(41);
|
||||
|
||||
var _handlebarsCompilerJavascriptCompiler = __webpack_require__(28);
|
||||
var _handlebarsCompilerJavascriptCompiler = __webpack_require__(42);
|
||||
|
||||
var _handlebarsCompilerJavascriptCompiler2 = _interopRequireDefault(_handlebarsCompilerJavascriptCompiler);
|
||||
|
||||
var _handlebarsCompilerVisitor = __webpack_require__(25);
|
||||
var _handlebarsCompilerVisitor = __webpack_require__(39);
|
||||
|
||||
var _handlebarsCompilerVisitor2 = _interopRequireDefault(_handlebarsCompilerVisitor);
|
||||
|
||||
var _handlebarsNoConflict = __webpack_require__(20);
|
||||
var _handlebarsNoConflict = __webpack_require__(34);
|
||||
|
||||
var _handlebarsNoConflict2 = _interopRequireDefault(_handlebarsNoConflict);
|
||||
|
||||
@@ -144,9 +144,9 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
exports['default'] = inst;
|
||||
module.exports = exports['default'];
|
||||
|
||||
/***/ },
|
||||
/***/ }),
|
||||
/* 1 */
|
||||
/***/ function(module, exports) {
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
"use strict";
|
||||
|
||||
@@ -158,9 +158,9 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
|
||||
exports.__esModule = true;
|
||||
|
||||
/***/ },
|
||||
/***/ }),
|
||||
/* 2 */
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -177,7 +177,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
// Each of these augment the Handlebars object. No need to setup here.
|
||||
// (This is done to easily share code between commonjs and browse envs)
|
||||
|
||||
var _handlebarsSafeString = __webpack_require__(18);
|
||||
var _handlebarsSafeString = __webpack_require__(21);
|
||||
|
||||
var _handlebarsSafeString2 = _interopRequireDefault(_handlebarsSafeString);
|
||||
|
||||
@@ -189,11 +189,11 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
|
||||
var Utils = _interopRequireWildcard(_handlebarsUtils);
|
||||
|
||||
var _handlebarsRuntime = __webpack_require__(19);
|
||||
var _handlebarsRuntime = __webpack_require__(22);
|
||||
|
||||
var runtime = _interopRequireWildcard(_handlebarsRuntime);
|
||||
|
||||
var _handlebarsNoConflict = __webpack_require__(20);
|
||||
var _handlebarsNoConflict = __webpack_require__(34);
|
||||
|
||||
var _handlebarsNoConflict2 = _interopRequireDefault(_handlebarsNoConflict);
|
||||
|
||||
@@ -225,9 +225,9 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
exports['default'] = inst;
|
||||
module.exports = exports['default'];
|
||||
|
||||
/***/ },
|
||||
/***/ }),
|
||||
/* 3 */
|
||||
/***/ function(module, exports) {
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
"use strict";
|
||||
|
||||
@@ -250,9 +250,9 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
|
||||
exports.__esModule = true;
|
||||
|
||||
/***/ },
|
||||
/***/ }),
|
||||
/* 4 */
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -267,15 +267,15 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
|
||||
var _exception2 = _interopRequireDefault(_exception);
|
||||
|
||||
var _helpers = __webpack_require__(7);
|
||||
var _helpers = __webpack_require__(10);
|
||||
|
||||
var _decorators = __webpack_require__(15);
|
||||
var _decorators = __webpack_require__(18);
|
||||
|
||||
var _logger = __webpack_require__(17);
|
||||
var _logger = __webpack_require__(20);
|
||||
|
||||
var _logger2 = _interopRequireDefault(_logger);
|
||||
|
||||
var VERSION = '4.0.2';
|
||||
var VERSION = '4.0.10';
|
||||
exports.VERSION = VERSION;
|
||||
var COMPILER_REVISION = 7;
|
||||
|
||||
@@ -327,7 +327,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
_utils.extend(this.partials, name);
|
||||
} else {
|
||||
if (typeof partial === 'undefined') {
|
||||
throw new _exception2['default']('Attempting to register a partial as undefined');
|
||||
throw new _exception2['default']('Attempting to register a partial called "' + name + '" as undefined');
|
||||
}
|
||||
this.partials[name] = partial;
|
||||
}
|
||||
@@ -357,9 +357,9 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
exports.createFrame = _utils.createFrame;
|
||||
exports.logger = _logger2['default'];
|
||||
|
||||
/***/ },
|
||||
/***/ }),
|
||||
/* 5 */
|
||||
/***/ function(module, exports) {
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -485,12 +485,14 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
return (contextPath ? contextPath + '.' : '') + id;
|
||||
}
|
||||
|
||||
/***/ },
|
||||
/***/ }),
|
||||
/* 6 */
|
||||
/***/ function(module, exports) {
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var _Object$defineProperty = __webpack_require__(7)['default'];
|
||||
|
||||
exports.__esModule = true;
|
||||
|
||||
var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
|
||||
@@ -518,9 +520,23 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
Error.captureStackTrace(this, Exception);
|
||||
}
|
||||
|
||||
if (loc) {
|
||||
this.lineNumber = line;
|
||||
this.column = column;
|
||||
try {
|
||||
if (loc) {
|
||||
this.lineNumber = line;
|
||||
|
||||
// Work around issue under safari where we can't directly set the column value
|
||||
/* istanbul ignore next */
|
||||
if (_Object$defineProperty) {
|
||||
Object.defineProperty(this, 'column', {
|
||||
value: column,
|
||||
enumerable: true
|
||||
});
|
||||
} else {
|
||||
this.column = column;
|
||||
}
|
||||
}
|
||||
} catch (nop) {
|
||||
/* Ignore if the browser is very particular */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -529,9 +545,42 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
exports['default'] = Exception;
|
||||
module.exports = exports['default'];
|
||||
|
||||
/***/ },
|
||||
/***/ }),
|
||||
/* 7 */
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
module.exports = { "default": __webpack_require__(8), __esModule: true };
|
||||
|
||||
/***/ }),
|
||||
/* 8 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
var $ = __webpack_require__(9);
|
||||
module.exports = function defineProperty(it, key, desc){
|
||||
return $.setDesc(it, key, desc);
|
||||
};
|
||||
|
||||
/***/ }),
|
||||
/* 9 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
var $Object = Object;
|
||||
module.exports = {
|
||||
create: $Object.create,
|
||||
getProto: $Object.getPrototypeOf,
|
||||
isEnum: {}.propertyIsEnumerable,
|
||||
getDesc: $Object.getOwnPropertyDescriptor,
|
||||
setDesc: $Object.defineProperty,
|
||||
setDescs: $Object.defineProperties,
|
||||
getKeys: $Object.keys,
|
||||
getNames: $Object.getOwnPropertyNames,
|
||||
getSymbols: $Object.getOwnPropertySymbols,
|
||||
each: [].forEach
|
||||
};
|
||||
|
||||
/***/ }),
|
||||
/* 10 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -540,31 +589,31 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
exports.__esModule = true;
|
||||
exports.registerDefaultHelpers = registerDefaultHelpers;
|
||||
|
||||
var _helpersBlockHelperMissing = __webpack_require__(8);
|
||||
var _helpersBlockHelperMissing = __webpack_require__(11);
|
||||
|
||||
var _helpersBlockHelperMissing2 = _interopRequireDefault(_helpersBlockHelperMissing);
|
||||
|
||||
var _helpersEach = __webpack_require__(9);
|
||||
var _helpersEach = __webpack_require__(12);
|
||||
|
||||
var _helpersEach2 = _interopRequireDefault(_helpersEach);
|
||||
|
||||
var _helpersHelperMissing = __webpack_require__(10);
|
||||
var _helpersHelperMissing = __webpack_require__(13);
|
||||
|
||||
var _helpersHelperMissing2 = _interopRequireDefault(_helpersHelperMissing);
|
||||
|
||||
var _helpersIf = __webpack_require__(11);
|
||||
var _helpersIf = __webpack_require__(14);
|
||||
|
||||
var _helpersIf2 = _interopRequireDefault(_helpersIf);
|
||||
|
||||
var _helpersLog = __webpack_require__(12);
|
||||
var _helpersLog = __webpack_require__(15);
|
||||
|
||||
var _helpersLog2 = _interopRequireDefault(_helpersLog);
|
||||
|
||||
var _helpersLookup = __webpack_require__(13);
|
||||
var _helpersLookup = __webpack_require__(16);
|
||||
|
||||
var _helpersLookup2 = _interopRequireDefault(_helpersLookup);
|
||||
|
||||
var _helpersWith = __webpack_require__(14);
|
||||
var _helpersWith = __webpack_require__(17);
|
||||
|
||||
var _helpersWith2 = _interopRequireDefault(_helpersWith);
|
||||
|
||||
@@ -578,9 +627,9 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
_helpersWith2['default'](instance);
|
||||
}
|
||||
|
||||
/***/ },
|
||||
/* 8 */
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
/***/ }),
|
||||
/* 11 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -621,9 +670,9 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
|
||||
module.exports = exports['default'];
|
||||
|
||||
/***/ },
|
||||
/* 9 */
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
/***/ }),
|
||||
/* 12 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -663,12 +712,6 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
}
|
||||
|
||||
function execIteration(field, index, last) {
|
||||
// Don't iterate over undefined values since we can't execute blocks against them
|
||||
// in non-strict (js) mode.
|
||||
if (context[field] == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data) {
|
||||
data.key = field;
|
||||
data.index = index;
|
||||
@@ -689,7 +732,9 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
if (context && typeof context === 'object') {
|
||||
if (_utils.isArray(context)) {
|
||||
for (var j = context.length; i < j; i++) {
|
||||
execIteration(i, i, i === context.length - 1);
|
||||
if (i in context) {
|
||||
execIteration(i, i, i === context.length - 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var priorKey = undefined;
|
||||
@@ -722,9 +767,9 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
|
||||
module.exports = exports['default'];
|
||||
|
||||
/***/ },
|
||||
/* 10 */
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
/***/ }),
|
||||
/* 13 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -750,9 +795,9 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
|
||||
module.exports = exports['default'];
|
||||
|
||||
/***/ },
|
||||
/* 11 */
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
/***/ }),
|
||||
/* 14 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -783,9 +828,9 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
|
||||
module.exports = exports['default'];
|
||||
|
||||
/***/ },
|
||||
/* 12 */
|
||||
/***/ function(module, exports) {
|
||||
/***/ }),
|
||||
/* 15 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -813,9 +858,9 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
|
||||
module.exports = exports['default'];
|
||||
|
||||
/***/ },
|
||||
/* 13 */
|
||||
/***/ function(module, exports) {
|
||||
/***/ }),
|
||||
/* 16 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -829,9 +874,9 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
|
||||
module.exports = exports['default'];
|
||||
|
||||
/***/ },
|
||||
/* 14 */
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
/***/ }),
|
||||
/* 17 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -866,9 +911,9 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
|
||||
module.exports = exports['default'];
|
||||
|
||||
/***/ },
|
||||
/* 15 */
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
/***/ }),
|
||||
/* 18 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -877,7 +922,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
exports.__esModule = true;
|
||||
exports.registerDefaultDecorators = registerDefaultDecorators;
|
||||
|
||||
var _decoratorsInline = __webpack_require__(16);
|
||||
var _decoratorsInline = __webpack_require__(19);
|
||||
|
||||
var _decoratorsInline2 = _interopRequireDefault(_decoratorsInline);
|
||||
|
||||
@@ -885,9 +930,9 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
_decoratorsInline2['default'](instance);
|
||||
}
|
||||
|
||||
/***/ },
|
||||
/* 16 */
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
/***/ }),
|
||||
/* 19 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -918,13 +963,16 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
|
||||
module.exports = exports['default'];
|
||||
|
||||
/***/ },
|
||||
/* 17 */
|
||||
/***/ function(module, exports) {
|
||||
/***/ }),
|
||||
/* 20 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
'use strict';
|
||||
|
||||
exports.__esModule = true;
|
||||
|
||||
var _utils = __webpack_require__(5);
|
||||
|
||||
var logger = {
|
||||
methodMap: ['debug', 'info', 'warn', 'error'],
|
||||
level: 'info',
|
||||
@@ -932,7 +980,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
// Maps a given level value to the `methodMap` indexes above.
|
||||
lookupLevel: function lookupLevel(level) {
|
||||
if (typeof level === 'string') {
|
||||
var levelMap = logger.methodMap.indexOf(level.toLowerCase());
|
||||
var levelMap = _utils.indexOf(logger.methodMap, level.toLowerCase());
|
||||
if (levelMap >= 0) {
|
||||
level = levelMap;
|
||||
} else {
|
||||
@@ -966,9 +1014,9 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
exports['default'] = logger;
|
||||
module.exports = exports['default'];
|
||||
|
||||
/***/ },
|
||||
/* 18 */
|
||||
/***/ function(module, exports) {
|
||||
/***/ }),
|
||||
/* 21 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
// Build out our basic SafeString type
|
||||
'use strict';
|
||||
@@ -985,12 +1033,14 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
exports['default'] = SafeString;
|
||||
module.exports = exports['default'];
|
||||
|
||||
/***/ },
|
||||
/* 19 */
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
/***/ }),
|
||||
/* 22 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var _Object$seal = __webpack_require__(23)['default'];
|
||||
|
||||
var _interopRequireWildcard = __webpack_require__(3)['default'];
|
||||
|
||||
var _interopRequireDefault = __webpack_require__(1)['default'];
|
||||
@@ -1133,6 +1183,8 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
|
||||
return obj;
|
||||
},
|
||||
// An empty object to use as replacement for null-contexts
|
||||
nullContext: _Object$seal({}),
|
||||
|
||||
noop: env.VM.noop,
|
||||
compilerInfo: templateSpec.compiler
|
||||
@@ -1151,7 +1203,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
blockParams = templateSpec.useBlockParams ? [] : undefined;
|
||||
if (templateSpec.useDepths) {
|
||||
if (options.depths) {
|
||||
depths = context !== options.depths[0] ? [context].concat(options.depths) : options.depths;
|
||||
depths = context != options.depths[0] ? [context].concat(options.depths) : options.depths;
|
||||
} else {
|
||||
depths = [context];
|
||||
}
|
||||
@@ -1200,7 +1252,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
|
||||
|
||||
var currentDepths = depths;
|
||||
if (depths && context !== depths[0]) {
|
||||
if (depths && context != depths[0] && !(context === container.nullContext && depths[0] === null)) {
|
||||
currentDepths = [context].concat(depths);
|
||||
}
|
||||
|
||||
@@ -1231,6 +1283,8 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
}
|
||||
|
||||
function invokePartial(partial, context, options) {
|
||||
// Use the current closure context to save the partial-block if this partial
|
||||
var currentPartialBlock = options.data && options.data['partial-block'];
|
||||
options.partial = true;
|
||||
if (options.ids) {
|
||||
options.data.contextPath = options.ids[0] || options.data.contextPath;
|
||||
@@ -1238,11 +1292,23 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
|
||||
var partialBlock = undefined;
|
||||
if (options.fn && options.fn !== noop) {
|
||||
partialBlock = options.data['partial-block'] = options.fn;
|
||||
(function () {
|
||||
options.data = _base.createFrame(options.data);
|
||||
// Wrapper function to get access to currentPartialBlock from the closure
|
||||
var fn = options.fn;
|
||||
partialBlock = options.data['partial-block'] = function partialBlockWrapper(context) {
|
||||
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
|
||||
|
||||
if (partialBlock.partials) {
|
||||
options.partials = Utils.extend({}, options.partials, partialBlock.partials);
|
||||
}
|
||||
// Restore the partial-block from the closure for the execution of the block
|
||||
// i.e. the part inside the block of the partial call.
|
||||
options.data = _base.createFrame(options.data);
|
||||
options.data['partial-block'] = currentPartialBlock;
|
||||
return fn(context, options);
|
||||
};
|
||||
if (fn.partials) {
|
||||
options.partials = Utils.extend({}, options.partials, fn.partials);
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
if (partial === undefined && partialBlock) {
|
||||
@@ -1277,9 +1343,171 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
return prog;
|
||||
}
|
||||
|
||||
/***/ },
|
||||
/* 20 */
|
||||
/***/ function(module, exports) {
|
||||
/***/ }),
|
||||
/* 23 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
module.exports = { "default": __webpack_require__(24), __esModule: true };
|
||||
|
||||
/***/ }),
|
||||
/* 24 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
__webpack_require__(25);
|
||||
module.exports = __webpack_require__(30).Object.seal;
|
||||
|
||||
/***/ }),
|
||||
/* 25 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
// 19.1.2.17 Object.seal(O)
|
||||
var isObject = __webpack_require__(26);
|
||||
|
||||
__webpack_require__(27)('seal', function($seal){
|
||||
return function seal(it){
|
||||
return $seal && isObject(it) ? $seal(it) : it;
|
||||
};
|
||||
});
|
||||
|
||||
/***/ }),
|
||||
/* 26 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
module.exports = function(it){
|
||||
return typeof it === 'object' ? it !== null : typeof it === 'function';
|
||||
};
|
||||
|
||||
/***/ }),
|
||||
/* 27 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
// most Object methods by ES6 should accept primitives
|
||||
var $export = __webpack_require__(28)
|
||||
, core = __webpack_require__(30)
|
||||
, fails = __webpack_require__(33);
|
||||
module.exports = function(KEY, exec){
|
||||
var fn = (core.Object || {})[KEY] || Object[KEY]
|
||||
, exp = {};
|
||||
exp[KEY] = exec(fn);
|
||||
$export($export.S + $export.F * fails(function(){ fn(1); }), 'Object', exp);
|
||||
};
|
||||
|
||||
/***/ }),
|
||||
/* 28 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
var global = __webpack_require__(29)
|
||||
, core = __webpack_require__(30)
|
||||
, ctx = __webpack_require__(31)
|
||||
, PROTOTYPE = 'prototype';
|
||||
|
||||
var $export = function(type, name, source){
|
||||
var IS_FORCED = type & $export.F
|
||||
, IS_GLOBAL = type & $export.G
|
||||
, IS_STATIC = type & $export.S
|
||||
, IS_PROTO = type & $export.P
|
||||
, IS_BIND = type & $export.B
|
||||
, IS_WRAP = type & $export.W
|
||||
, exports = IS_GLOBAL ? core : core[name] || (core[name] = {})
|
||||
, target = IS_GLOBAL ? global : IS_STATIC ? global[name] : (global[name] || {})[PROTOTYPE]
|
||||
, key, own, out;
|
||||
if(IS_GLOBAL)source = name;
|
||||
for(key in source){
|
||||
// contains in native
|
||||
own = !IS_FORCED && target && key in target;
|
||||
if(own && key in exports)continue;
|
||||
// export native or passed
|
||||
out = own ? target[key] : source[key];
|
||||
// prevent global pollution for namespaces
|
||||
exports[key] = IS_GLOBAL && typeof target[key] != 'function' ? source[key]
|
||||
// bind timers to global for call from export context
|
||||
: IS_BIND && own ? ctx(out, global)
|
||||
// wrap global constructors for prevent change them in library
|
||||
: IS_WRAP && target[key] == out ? (function(C){
|
||||
var F = function(param){
|
||||
return this instanceof C ? new C(param) : C(param);
|
||||
};
|
||||
F[PROTOTYPE] = C[PROTOTYPE];
|
||||
return F;
|
||||
// make static versions for prototype methods
|
||||
})(out) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out;
|
||||
if(IS_PROTO)(exports[PROTOTYPE] || (exports[PROTOTYPE] = {}))[key] = out;
|
||||
}
|
||||
};
|
||||
// type bitmap
|
||||
$export.F = 1; // forced
|
||||
$export.G = 2; // global
|
||||
$export.S = 4; // static
|
||||
$export.P = 8; // proto
|
||||
$export.B = 16; // bind
|
||||
$export.W = 32; // wrap
|
||||
module.exports = $export;
|
||||
|
||||
/***/ }),
|
||||
/* 29 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
// https://github.com/zloirock/core-js/issues/86#issuecomment-115759028
|
||||
var global = module.exports = typeof window != 'undefined' && window.Math == Math
|
||||
? window : typeof self != 'undefined' && self.Math == Math ? self : Function('return this')();
|
||||
if(typeof __g == 'number')__g = global; // eslint-disable-line no-undef
|
||||
|
||||
/***/ }),
|
||||
/* 30 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
var core = module.exports = {version: '1.2.6'};
|
||||
if(typeof __e == 'number')__e = core; // eslint-disable-line no-undef
|
||||
|
||||
/***/ }),
|
||||
/* 31 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
// optional / simple context binding
|
||||
var aFunction = __webpack_require__(32);
|
||||
module.exports = function(fn, that, length){
|
||||
aFunction(fn);
|
||||
if(that === undefined)return fn;
|
||||
switch(length){
|
||||
case 1: return function(a){
|
||||
return fn.call(that, a);
|
||||
};
|
||||
case 2: return function(a, b){
|
||||
return fn.call(that, a, b);
|
||||
};
|
||||
case 3: return function(a, b, c){
|
||||
return fn.call(that, a, b, c);
|
||||
};
|
||||
}
|
||||
return function(/* ...args */){
|
||||
return fn.apply(that, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
/***/ }),
|
||||
/* 32 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
module.exports = function(it){
|
||||
if(typeof it != 'function')throw TypeError(it + ' is not a function!');
|
||||
return it;
|
||||
};
|
||||
|
||||
/***/ }),
|
||||
/* 33 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
module.exports = function(exec){
|
||||
try {
|
||||
return !!exec();
|
||||
} catch(e){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/***/ }),
|
||||
/* 34 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
/* WEBPACK VAR INJECTION */(function(global) {/* global window */
|
||||
'use strict';
|
||||
@@ -1295,15 +1523,16 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
if (root.Handlebars === Handlebars) {
|
||||
root.Handlebars = $Handlebars;
|
||||
}
|
||||
return Handlebars;
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = exports['default'];
|
||||
/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
|
||||
|
||||
/***/ },
|
||||
/* 21 */
|
||||
/***/ function(module, exports) {
|
||||
/***/ }),
|
||||
/* 35 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -1336,9 +1565,9 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
exports['default'] = AST;
|
||||
module.exports = exports['default'];
|
||||
|
||||
/***/ },
|
||||
/* 22 */
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
/***/ }),
|
||||
/* 36 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -1349,15 +1578,15 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
exports.__esModule = true;
|
||||
exports.parse = parse;
|
||||
|
||||
var _parser = __webpack_require__(23);
|
||||
var _parser = __webpack_require__(37);
|
||||
|
||||
var _parser2 = _interopRequireDefault(_parser);
|
||||
|
||||
var _whitespaceControl = __webpack_require__(24);
|
||||
var _whitespaceControl = __webpack_require__(38);
|
||||
|
||||
var _whitespaceControl2 = _interopRequireDefault(_whitespaceControl);
|
||||
|
||||
var _helpers = __webpack_require__(26);
|
||||
var _helpers = __webpack_require__(40);
|
||||
|
||||
var Helpers = _interopRequireWildcard(_helpers);
|
||||
|
||||
@@ -1385,21 +1614,23 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
return strip.accept(_parser2['default'].parse(input));
|
||||
}
|
||||
|
||||
/***/ },
|
||||
/* 23 */
|
||||
/***/ function(module, exports) {
|
||||
/***/ }),
|
||||
/* 37 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
/* istanbul ignore next */
|
||||
// File ignored in coverage tests via setting in .istanbul.yml
|
||||
/* Jison generated parser */
|
||||
"use strict";
|
||||
|
||||
exports.__esModule = true;
|
||||
var handlebars = (function () {
|
||||
var parser = { trace: function trace() {},
|
||||
yy: {},
|
||||
symbols_: { "error": 2, "root": 3, "program": 4, "EOF": 5, "program_repetition0": 6, "statement": 7, "mustache": 8, "block": 9, "rawBlock": 10, "partial": 11, "partialBlock": 12, "content": 13, "COMMENT": 14, "CONTENT": 15, "openRawBlock": 16, "rawBlock_repetition_plus0": 17, "END_RAW_BLOCK": 18, "OPEN_RAW_BLOCK": 19, "helperName": 20, "openRawBlock_repetition0": 21, "openRawBlock_option0": 22, "CLOSE_RAW_BLOCK": 23, "openBlock": 24, "block_option0": 25, "closeBlock": 26, "openInverse": 27, "block_option1": 28, "OPEN_BLOCK": 29, "openBlock_repetition0": 30, "openBlock_option0": 31, "openBlock_option1": 32, "CLOSE": 33, "OPEN_INVERSE": 34, "openInverse_repetition0": 35, "openInverse_option0": 36, "openInverse_option1": 37, "openInverseChain": 38, "OPEN_INVERSE_CHAIN": 39, "openInverseChain_repetition0": 40, "openInverseChain_option0": 41, "openInverseChain_option1": 42, "inverseAndProgram": 43, "INVERSE": 44, "inverseChain": 45, "inverseChain_option0": 46, "OPEN_ENDBLOCK": 47, "OPEN": 48, "mustache_repetition0": 49, "mustache_option0": 50, "OPEN_UNESCAPED": 51, "mustache_repetition1": 52, "mustache_option1": 53, "CLOSE_UNESCAPED": 54, "OPEN_PARTIAL": 55, "partialName": 56, "partial_repetition0": 57, "partial_option0": 58, "openPartialBlock": 59, "OPEN_PARTIAL_BLOCK": 60, "openPartialBlock_repetition0": 61, "openPartialBlock_option0": 62, "param": 63, "sexpr": 64, "OPEN_SEXPR": 65, "sexpr_repetition0": 66, "sexpr_option0": 67, "CLOSE_SEXPR": 68, "hash": 69, "hash_repetition_plus0": 70, "hashSegment": 71, "ID": 72, "EQUALS": 73, "blockParams": 74, "OPEN_BLOCK_PARAMS": 75, "blockParams_repetition_plus0": 76, "CLOSE_BLOCK_PARAMS": 77, "path": 78, "dataName": 79, "STRING": 80, "NUMBER": 81, "BOOLEAN": 82, "UNDEFINED": 83, "NULL": 84, "DATA": 85, "pathSegments": 86, "SEP": 87, "$accept": 0, "$end": 1 },
|
||||
terminals_: { 2: "error", 5: "EOF", 14: "COMMENT", 15: "CONTENT", 18: "END_RAW_BLOCK", 19: "OPEN_RAW_BLOCK", 23: "CLOSE_RAW_BLOCK", 29: "OPEN_BLOCK", 33: "CLOSE", 34: "OPEN_INVERSE", 39: "OPEN_INVERSE_CHAIN", 44: "INVERSE", 47: "OPEN_ENDBLOCK", 48: "OPEN", 51: "OPEN_UNESCAPED", 54: "CLOSE_UNESCAPED", 55: "OPEN_PARTIAL", 60: "OPEN_PARTIAL_BLOCK", 65: "OPEN_SEXPR", 68: "CLOSE_SEXPR", 72: "ID", 73: "EQUALS", 75: "OPEN_BLOCK_PARAMS", 77: "CLOSE_BLOCK_PARAMS", 80: "STRING", 81: "NUMBER", 82: "BOOLEAN", 83: "UNDEFINED", 84: "NULL", 85: "DATA", 87: "SEP" },
|
||||
productions_: [0, [3, 2], [4, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [13, 1], [10, 3], [16, 5], [9, 4], [9, 4], [24, 6], [27, 6], [38, 6], [43, 2], [45, 3], [45, 1], [26, 3], [8, 5], [8, 5], [11, 5], [12, 3], [59, 5], [63, 1], [63, 1], [64, 5], [69, 1], [71, 3], [74, 3], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [56, 1], [56, 1], [79, 2], [78, 1], [86, 3], [86, 1], [6, 0], [6, 2], [17, 1], [17, 2], [21, 0], [21, 2], [22, 0], [22, 1], [25, 0], [25, 1], [28, 0], [28, 1], [30, 0], [30, 2], [31, 0], [31, 1], [32, 0], [32, 1], [35, 0], [35, 2], [36, 0], [36, 1], [37, 0], [37, 1], [40, 0], [40, 2], [41, 0], [41, 1], [42, 0], [42, 1], [46, 0], [46, 1], [49, 0], [49, 2], [50, 0], [50, 1], [52, 0], [52, 2], [53, 0], [53, 1], [57, 0], [57, 2], [58, 0], [58, 1], [61, 0], [61, 2], [62, 0], [62, 1], [66, 0], [66, 2], [67, 0], [67, 1], [70, 1], [70, 2], [76, 1], [76, 2]],
|
||||
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$) {
|
||||
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$
|
||||
/**/) {
|
||||
|
||||
var $0 = $$.length - 1;
|
||||
switch (yystate) {
|
||||
@@ -1936,7 +2167,8 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
this.begin(condition);
|
||||
} };
|
||||
lexer.options = {};
|
||||
lexer.performAction = function anonymous(yy, yy_, $avoiding_name_collisions, YY_START) {
|
||||
lexer.performAction = function anonymous(yy, yy_, $avoiding_name_collisions, YY_START
|
||||
/**/) {
|
||||
|
||||
function strip(start, end) {
|
||||
return yy_.yytext = yy_.yytext.substr(start, yy_.yyleng - end);
|
||||
@@ -2103,7 +2335,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
return 72;
|
||||
break;
|
||||
case 42:
|
||||
return 72;
|
||||
yy_.yytext = yy_.yytext.replace(/\\([\\\]])/g, '$1');return 72;
|
||||
break;
|
||||
case 43:
|
||||
return 'INVALID';
|
||||
@@ -2113,7 +2345,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
break;
|
||||
}
|
||||
};
|
||||
lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/, /^(?:[^\x00]+)/, /^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/, /^(?:\{\{\{\{(?=[^/]))/, /^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/, /^(?:[^\x00]*?(?=(\{\{\{\{)))/, /^(?:[\s\S]*?--(~)?\}\})/, /^(?:\()/, /^(?:\))/, /^(?:\{\{\{\{)/, /^(?:\}\}\}\})/, /^(?:\{\{(~)?>)/, /^(?:\{\{(~)?#>)/, /^(?:\{\{(~)?#\*?)/, /^(?:\{\{(~)?\/)/, /^(?:\{\{(~)?\^\s*(~)?\}\})/, /^(?:\{\{(~)?\s*else\s*(~)?\}\})/, /^(?:\{\{(~)?\^)/, /^(?:\{\{(~)?\s*else\b)/, /^(?:\{\{(~)?\{)/, /^(?:\{\{(~)?&)/, /^(?:\{\{(~)?!--)/, /^(?:\{\{(~)?![\s\S]*?\}\})/, /^(?:\{\{(~)?\*?)/, /^(?:=)/, /^(?:\.\.)/, /^(?:\.(?=([=~}\s\/.)|])))/, /^(?:[\/.])/, /^(?:\s+)/, /^(?:\}(~)?\}\})/, /^(?:(~)?\}\})/, /^(?:"(\\["]|[^"])*")/, /^(?:'(\\[']|[^'])*')/, /^(?:@)/, /^(?:true(?=([~}\s)])))/, /^(?:false(?=([~}\s)])))/, /^(?:undefined(?=([~}\s)])))/, /^(?:null(?=([~}\s)])))/, /^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/, /^(?:as\s+\|)/, /^(?:\|)/, /^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)|]))))/, /^(?:\[[^\]]*\])/, /^(?:.)/, /^(?:$)/];
|
||||
lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/, /^(?:[^\x00]+)/, /^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/, /^(?:\{\{\{\{(?=[^\/]))/, /^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/, /^(?:[^\x00]*?(?=(\{\{\{\{)))/, /^(?:[\s\S]*?--(~)?\}\})/, /^(?:\()/, /^(?:\))/, /^(?:\{\{\{\{)/, /^(?:\}\}\}\})/, /^(?:\{\{(~)?>)/, /^(?:\{\{(~)?#>)/, /^(?:\{\{(~)?#\*?)/, /^(?:\{\{(~)?\/)/, /^(?:\{\{(~)?\^\s*(~)?\}\})/, /^(?:\{\{(~)?\s*else\s*(~)?\}\})/, /^(?:\{\{(~)?\^)/, /^(?:\{\{(~)?\s*else\b)/, /^(?:\{\{(~)?\{)/, /^(?:\{\{(~)?&)/, /^(?:\{\{(~)?!--)/, /^(?:\{\{(~)?![\s\S]*?\}\})/, /^(?:\{\{(~)?\*?)/, /^(?:=)/, /^(?:\.\.)/, /^(?:\.(?=([=~}\s\/.)|])))/, /^(?:[\/.])/, /^(?:\s+)/, /^(?:\}(~)?\}\})/, /^(?:(~)?\}\})/, /^(?:"(\\["]|[^"])*")/, /^(?:'(\\[']|[^'])*')/, /^(?:@)/, /^(?:true(?=([~}\s)])))/, /^(?:false(?=([~}\s)])))/, /^(?:undefined(?=([~}\s)])))/, /^(?:null(?=([~}\s)])))/, /^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/, /^(?:as\s+\|)/, /^(?:\|)/, /^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)|]))))/, /^(?:\[(\\\]|[^\]])*\])/, /^(?:.)/, /^(?:$)/];
|
||||
lexer.conditions = { "mu": { "rules": [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44], "inclusive": false }, "emu": { "rules": [2], "inclusive": false }, "com": { "rules": [6], "inclusive": false }, "raw": { "rules": [3, 4, 5], "inclusive": false }, "INITIAL": { "rules": [0, 1, 44], "inclusive": true } };
|
||||
return lexer;
|
||||
})();
|
||||
@@ -2122,12 +2354,12 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
this.yy = {};
|
||||
}Parser.prototype = parser;parser.Parser = Parser;
|
||||
return new Parser();
|
||||
})();exports.__esModule = true;
|
||||
exports['default'] = handlebars;
|
||||
})();exports["default"] = handlebars;
|
||||
module.exports = exports["default"];
|
||||
|
||||
/***/ },
|
||||
/* 24 */
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
/***/ }),
|
||||
/* 38 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -2135,7 +2367,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
|
||||
exports.__esModule = true;
|
||||
|
||||
var _visitor = __webpack_require__(25);
|
||||
var _visitor = __webpack_require__(39);
|
||||
|
||||
var _visitor2 = _interopRequireDefault(_visitor);
|
||||
|
||||
@@ -2349,9 +2581,9 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
exports['default'] = WhitespaceControl;
|
||||
module.exports = exports['default'];
|
||||
|
||||
/***/ },
|
||||
/* 25 */
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
/***/ }),
|
||||
/* 39 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -2492,9 +2724,9 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
exports['default'] = Visitor;
|
||||
module.exports = exports['default'];
|
||||
|
||||
/***/ },
|
||||
/* 26 */
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
/***/ }),
|
||||
/* 40 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -2725,9 +2957,9 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
};
|
||||
}
|
||||
|
||||
/***/ },
|
||||
/* 27 */
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
/***/ }),
|
||||
/* 41 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
/* eslint-disable new-cap */
|
||||
|
||||
@@ -2746,7 +2978,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
|
||||
var _utils = __webpack_require__(5);
|
||||
|
||||
var _ast = __webpack_require__(21);
|
||||
var _ast = __webpack_require__(35);
|
||||
|
||||
var _ast2 = _interopRequireDefault(_ast);
|
||||
|
||||
@@ -2816,7 +3048,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
for (var _name in knownHelpers) {
|
||||
/* istanbul ignore else */
|
||||
if (_name in knownHelpers) {
|
||||
options.knownHelpers[_name] = knownHelpers[_name];
|
||||
this.options.knownHelpers[_name] = knownHelpers[_name];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3231,6 +3463,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
throw new _exception2['default']('You must pass a string or Handlebars AST to Handlebars.compile. You passed ' + input);
|
||||
}
|
||||
|
||||
options = _utils.extend({}, options);
|
||||
if (!('data' in options)) {
|
||||
options.data = true;
|
||||
}
|
||||
@@ -3300,9 +3533,9 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
}
|
||||
}
|
||||
|
||||
/***/ },
|
||||
/* 28 */
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
/***/ }),
|
||||
/* 42 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -3318,7 +3551,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
|
||||
var _utils = __webpack_require__(5);
|
||||
|
||||
var _codeGen = __webpack_require__(29);
|
||||
var _codeGen = __webpack_require__(43);
|
||||
|
||||
var _codeGen2 = _interopRequireDefault(_codeGen);
|
||||
|
||||
@@ -4089,11 +4322,11 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
child = children[i];
|
||||
compiler = new this.compiler(); // eslint-disable-line new-cap
|
||||
|
||||
var index = this.matchExistingProgram(child);
|
||||
var existing = this.matchExistingProgram(child);
|
||||
|
||||
if (index == null) {
|
||||
if (existing == null) {
|
||||
this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children
|
||||
index = this.context.programs.length;
|
||||
var index = this.context.programs.length;
|
||||
child.index = index;
|
||||
child.name = 'program' + index;
|
||||
this.context.programs[index] = compiler.compile(child, options, this.context, !this.precompile);
|
||||
@@ -4102,12 +4335,14 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
|
||||
this.useDepths = this.useDepths || compiler.useDepths;
|
||||
this.useBlockParams = this.useBlockParams || compiler.useBlockParams;
|
||||
child.useDepths = this.useDepths;
|
||||
child.useBlockParams = this.useBlockParams;
|
||||
} else {
|
||||
child.index = index;
|
||||
child.name = 'program' + index;
|
||||
child.index = existing.index;
|
||||
child.name = 'program' + existing.index;
|
||||
|
||||
this.useDepths = this.useDepths || child.useDepths;
|
||||
this.useBlockParams = this.useBlockParams || child.useBlockParams;
|
||||
this.useDepths = this.useDepths || existing.useDepths;
|
||||
this.useBlockParams = this.useBlockParams || existing.useBlockParams;
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -4115,7 +4350,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
for (var i = 0, len = this.context.environments.length; i < len; i++) {
|
||||
var environment = this.context.environments[i];
|
||||
if (environment && environment.equals(child)) {
|
||||
return i;
|
||||
return environment;
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -4296,13 +4531,14 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
setupHelper: function setupHelper(paramSize, name, blockHelper) {
|
||||
var params = [],
|
||||
paramsInit = this.setupHelperArgs(name, paramSize, params, blockHelper);
|
||||
var foundHelper = this.nameLookup('helpers', name, 'helper');
|
||||
var foundHelper = this.nameLookup('helpers', name, 'helper'),
|
||||
callContext = this.aliasable(this.contextName(0) + ' != null ? ' + this.contextName(0) + ' : (container.nullContext || {})');
|
||||
|
||||
return {
|
||||
params: params,
|
||||
paramsInit: paramsInit,
|
||||
name: foundHelper,
|
||||
callParams: [this.contextName(0)].concat(params)
|
||||
callParams: [callContext].concat(params)
|
||||
};
|
||||
},
|
||||
|
||||
@@ -4428,9 +4664,9 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
exports['default'] = JavaScriptCompiler;
|
||||
module.exports = exports['default'];
|
||||
|
||||
/***/ },
|
||||
/* 29 */
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
/***/ }),
|
||||
/* 43 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
/* global define */
|
||||
'use strict';
|
||||
@@ -4598,7 +4834,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
exports['default'] = CodeGen;
|
||||
module.exports = exports['default'];
|
||||
|
||||
/***/ }
|
||||
/***/ })
|
||||
/******/ ])
|
||||
});
|
||||
;
|
329
server/src/uds/static/css/font-awesome.css
vendored
329
server/src/uds/static/css/font-awesome.css
vendored
@@ -1,13 +1,13 @@
|
||||
/*!
|
||||
* Font Awesome 4.4.0 by @davegandy - http://fontawesome.io - @fontawesome
|
||||
* Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
|
||||
* License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
|
||||
*/
|
||||
/* FONT PATH
|
||||
* -------------------------- */
|
||||
@font-face {
|
||||
font-family: 'FontAwesome';
|
||||
src: url('../fonts/fontawesome-webfont.eot?v=4.4.0');
|
||||
src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.4.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.4.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.4.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.4.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.4.0#fontawesomeregular') format('svg');
|
||||
src: url('../fonts/fontawesome-webfont.eot?v=4.7.0');
|
||||
src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@@ -118,31 +118,31 @@
|
||||
}
|
||||
}
|
||||
.fa-rotate-90 {
|
||||
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1);
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";
|
||||
-webkit-transform: rotate(90deg);
|
||||
-ms-transform: rotate(90deg);
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
.fa-rotate-180 {
|
||||
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2);
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";
|
||||
-webkit-transform: rotate(180deg);
|
||||
-ms-transform: rotate(180deg);
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
.fa-rotate-270 {
|
||||
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";
|
||||
-webkit-transform: rotate(270deg);
|
||||
-ms-transform: rotate(270deg);
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
.fa-flip-horizontal {
|
||||
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";
|
||||
-webkit-transform: scale(-1, 1);
|
||||
-ms-transform: scale(-1, 1);
|
||||
transform: scale(-1, 1);
|
||||
}
|
||||
.fa-flip-vertical {
|
||||
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
|
||||
-webkit-transform: scale(1, -1);
|
||||
-ms-transform: scale(1, -1);
|
||||
transform: scale(1, -1);
|
||||
@@ -1383,7 +1383,7 @@
|
||||
.fa-digg:before {
|
||||
content: "\f1a6";
|
||||
}
|
||||
.fa-pied-piper:before {
|
||||
.fa-pied-piper-pp:before {
|
||||
content: "\f1a7";
|
||||
}
|
||||
.fa-pied-piper-alt:before {
|
||||
@@ -1509,6 +1509,7 @@
|
||||
content: "\f1ce";
|
||||
}
|
||||
.fa-ra:before,
|
||||
.fa-resistance:before,
|
||||
.fa-rebel:before {
|
||||
content: "\f1d0";
|
||||
}
|
||||
@@ -1831,6 +1832,7 @@
|
||||
content: "\f23e";
|
||||
}
|
||||
.fa-battery-4:before,
|
||||
.fa-battery:before,
|
||||
.fa-battery-full:before {
|
||||
content: "\f240";
|
||||
}
|
||||
@@ -2024,3 +2026,312 @@
|
||||
.fa-fonticons:before {
|
||||
content: "\f280";
|
||||
}
|
||||
.fa-reddit-alien:before {
|
||||
content: "\f281";
|
||||
}
|
||||
.fa-edge:before {
|
||||
content: "\f282";
|
||||
}
|
||||
.fa-credit-card-alt:before {
|
||||
content: "\f283";
|
||||
}
|
||||
.fa-codiepie:before {
|
||||
content: "\f284";
|
||||
}
|
||||
.fa-modx:before {
|
||||
content: "\f285";
|
||||
}
|
||||
.fa-fort-awesome:before {
|
||||
content: "\f286";
|
||||
}
|
||||
.fa-usb:before {
|
||||
content: "\f287";
|
||||
}
|
||||
.fa-product-hunt:before {
|
||||
content: "\f288";
|
||||
}
|
||||
.fa-mixcloud:before {
|
||||
content: "\f289";
|
||||
}
|
||||
.fa-scribd:before {
|
||||
content: "\f28a";
|
||||
}
|
||||
.fa-pause-circle:before {
|
||||
content: "\f28b";
|
||||
}
|
||||
.fa-pause-circle-o:before {
|
||||
content: "\f28c";
|
||||
}
|
||||
.fa-stop-circle:before {
|
||||
content: "\f28d";
|
||||
}
|
||||
.fa-stop-circle-o:before {
|
||||
content: "\f28e";
|
||||
}
|
||||
.fa-shopping-bag:before {
|
||||
content: "\f290";
|
||||
}
|
||||
.fa-shopping-basket:before {
|
||||
content: "\f291";
|
||||
}
|
||||
.fa-hashtag:before {
|
||||
content: "\f292";
|
||||
}
|
||||
.fa-bluetooth:before {
|
||||
content: "\f293";
|
||||
}
|
||||
.fa-bluetooth-b:before {
|
||||
content: "\f294";
|
||||
}
|
||||
.fa-percent:before {
|
||||
content: "\f295";
|
||||
}
|
||||
.fa-gitlab:before {
|
||||
content: "\f296";
|
||||
}
|
||||
.fa-wpbeginner:before {
|
||||
content: "\f297";
|
||||
}
|
||||
.fa-wpforms:before {
|
||||
content: "\f298";
|
||||
}
|
||||
.fa-envira:before {
|
||||
content: "\f299";
|
||||
}
|
||||
.fa-universal-access:before {
|
||||
content: "\f29a";
|
||||
}
|
||||
.fa-wheelchair-alt:before {
|
||||
content: "\f29b";
|
||||
}
|
||||
.fa-question-circle-o:before {
|
||||
content: "\f29c";
|
||||
}
|
||||
.fa-blind:before {
|
||||
content: "\f29d";
|
||||
}
|
||||
.fa-audio-description:before {
|
||||
content: "\f29e";
|
||||
}
|
||||
.fa-volume-control-phone:before {
|
||||
content: "\f2a0";
|
||||
}
|
||||
.fa-braille:before {
|
||||
content: "\f2a1";
|
||||
}
|
||||
.fa-assistive-listening-systems:before {
|
||||
content: "\f2a2";
|
||||
}
|
||||
.fa-asl-interpreting:before,
|
||||
.fa-american-sign-language-interpreting:before {
|
||||
content: "\f2a3";
|
||||
}
|
||||
.fa-deafness:before,
|
||||
.fa-hard-of-hearing:before,
|
||||
.fa-deaf:before {
|
||||
content: "\f2a4";
|
||||
}
|
||||
.fa-glide:before {
|
||||
content: "\f2a5";
|
||||
}
|
||||
.fa-glide-g:before {
|
||||
content: "\f2a6";
|
||||
}
|
||||
.fa-signing:before,
|
||||
.fa-sign-language:before {
|
||||
content: "\f2a7";
|
||||
}
|
||||
.fa-low-vision:before {
|
||||
content: "\f2a8";
|
||||
}
|
||||
.fa-viadeo:before {
|
||||
content: "\f2a9";
|
||||
}
|
||||
.fa-viadeo-square:before {
|
||||
content: "\f2aa";
|
||||
}
|
||||
.fa-snapchat:before {
|
||||
content: "\f2ab";
|
||||
}
|
||||
.fa-snapchat-ghost:before {
|
||||
content: "\f2ac";
|
||||
}
|
||||
.fa-snapchat-square:before {
|
||||
content: "\f2ad";
|
||||
}
|
||||
.fa-pied-piper:before {
|
||||
content: "\f2ae";
|
||||
}
|
||||
.fa-first-order:before {
|
||||
content: "\f2b0";
|
||||
}
|
||||
.fa-yoast:before {
|
||||
content: "\f2b1";
|
||||
}
|
||||
.fa-themeisle:before {
|
||||
content: "\f2b2";
|
||||
}
|
||||
.fa-google-plus-circle:before,
|
||||
.fa-google-plus-official:before {
|
||||
content: "\f2b3";
|
||||
}
|
||||
.fa-fa:before,
|
||||
.fa-font-awesome:before {
|
||||
content: "\f2b4";
|
||||
}
|
||||
.fa-handshake-o:before {
|
||||
content: "\f2b5";
|
||||
}
|
||||
.fa-envelope-open:before {
|
||||
content: "\f2b6";
|
||||
}
|
||||
.fa-envelope-open-o:before {
|
||||
content: "\f2b7";
|
||||
}
|
||||
.fa-linode:before {
|
||||
content: "\f2b8";
|
||||
}
|
||||
.fa-address-book:before {
|
||||
content: "\f2b9";
|
||||
}
|
||||
.fa-address-book-o:before {
|
||||
content: "\f2ba";
|
||||
}
|
||||
.fa-vcard:before,
|
||||
.fa-address-card:before {
|
||||
content: "\f2bb";
|
||||
}
|
||||
.fa-vcard-o:before,
|
||||
.fa-address-card-o:before {
|
||||
content: "\f2bc";
|
||||
}
|
||||
.fa-user-circle:before {
|
||||
content: "\f2bd";
|
||||
}
|
||||
.fa-user-circle-o:before {
|
||||
content: "\f2be";
|
||||
}
|
||||
.fa-user-o:before {
|
||||
content: "\f2c0";
|
||||
}
|
||||
.fa-id-badge:before {
|
||||
content: "\f2c1";
|
||||
}
|
||||
.fa-drivers-license:before,
|
||||
.fa-id-card:before {
|
||||
content: "\f2c2";
|
||||
}
|
||||
.fa-drivers-license-o:before,
|
||||
.fa-id-card-o:before {
|
||||
content: "\f2c3";
|
||||
}
|
||||
.fa-quora:before {
|
||||
content: "\f2c4";
|
||||
}
|
||||
.fa-free-code-camp:before {
|
||||
content: "\f2c5";
|
||||
}
|
||||
.fa-telegram:before {
|
||||
content: "\f2c6";
|
||||
}
|
||||
.fa-thermometer-4:before,
|
||||
.fa-thermometer:before,
|
||||
.fa-thermometer-full:before {
|
||||
content: "\f2c7";
|
||||
}
|
||||
.fa-thermometer-3:before,
|
||||
.fa-thermometer-three-quarters:before {
|
||||
content: "\f2c8";
|
||||
}
|
||||
.fa-thermometer-2:before,
|
||||
.fa-thermometer-half:before {
|
||||
content: "\f2c9";
|
||||
}
|
||||
.fa-thermometer-1:before,
|
||||
.fa-thermometer-quarter:before {
|
||||
content: "\f2ca";
|
||||
}
|
||||
.fa-thermometer-0:before,
|
||||
.fa-thermometer-empty:before {
|
||||
content: "\f2cb";
|
||||
}
|
||||
.fa-shower:before {
|
||||
content: "\f2cc";
|
||||
}
|
||||
.fa-bathtub:before,
|
||||
.fa-s15:before,
|
||||
.fa-bath:before {
|
||||
content: "\f2cd";
|
||||
}
|
||||
.fa-podcast:before {
|
||||
content: "\f2ce";
|
||||
}
|
||||
.fa-window-maximize:before {
|
||||
content: "\f2d0";
|
||||
}
|
||||
.fa-window-minimize:before {
|
||||
content: "\f2d1";
|
||||
}
|
||||
.fa-window-restore:before {
|
||||
content: "\f2d2";
|
||||
}
|
||||
.fa-times-rectangle:before,
|
||||
.fa-window-close:before {
|
||||
content: "\f2d3";
|
||||
}
|
||||
.fa-times-rectangle-o:before,
|
||||
.fa-window-close-o:before {
|
||||
content: "\f2d4";
|
||||
}
|
||||
.fa-bandcamp:before {
|
||||
content: "\f2d5";
|
||||
}
|
||||
.fa-grav:before {
|
||||
content: "\f2d6";
|
||||
}
|
||||
.fa-etsy:before {
|
||||
content: "\f2d7";
|
||||
}
|
||||
.fa-imdb:before {
|
||||
content: "\f2d8";
|
||||
}
|
||||
.fa-ravelry:before {
|
||||
content: "\f2d9";
|
||||
}
|
||||
.fa-eercast:before {
|
||||
content: "\f2da";
|
||||
}
|
||||
.fa-microchip:before {
|
||||
content: "\f2db";
|
||||
}
|
||||
.fa-snowflake-o:before {
|
||||
content: "\f2dc";
|
||||
}
|
||||
.fa-superpowers:before {
|
||||
content: "\f2dd";
|
||||
}
|
||||
.fa-wpexplorer:before {
|
||||
content: "\f2de";
|
||||
}
|
||||
.fa-meetup:before {
|
||||
content: "\f2e0";
|
||||
}
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
border: 0;
|
||||
}
|
||||
.sr-only-focusable:active,
|
||||
.sr-only-focusable:focus {
|
||||
position: static;
|
||||
width: auto;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
overflow: visible;
|
||||
clip: auto;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user