1
0
mirror of https://github.com/dkmstr/openuds.git synced 2025-10-15 19:35:45 +03:00

93 Commits
v2.1 ... 2.2

Author SHA1 Message Date
Adolfo Gómez García
a0797ac07f Updated translations 2017-12-21 19:10:36 +01:00
Adolfo Gómez García
bbcc06f503 Fixes on RDP for Linux.
On linux client freerdp 1.1, seems that /home-drive with
/drive:media,/media and /printer makes the client fail. (Works fine with
2.0).
We have replaced so:
1.- Now by default, on linux, redirect drives redirects /media
2.- You can check "redirect home" so /home is also redirected (take
care, /home, not home folder)
3.- If you want /home-drives, you can include it on "custom parameters"
2017-12-21 19:04:06 +01:00
Adolfo Gómez García
6fbf419064 Updated translations 2017-12-20 12:28:52 +01:00
Adolfo Gómez García
cabc906758 Fixed a lof of transports string literals 2017-12-20 12:17:17 +01:00
Adolfo Gómez García
0b7535e76e Fixed string on x2go transport 2017-12-20 00:10:24 +01:00
Adolfo Gómez García
f536112312 Updated translations 2017-12-19 22:42:50 +01:00
Adolfo Gómez García
ab2ca6d527 Fixed text & descriptions for transports 2017-12-19 22:33:05 +01:00
Adolfo Gómez García
442b78ef2e Fixed smartcard parameter when empty 2017-12-19 13:52:20 +01:00
Adolfo Gómez García
9cfc348ee6 RDP aditions. 2017-12-18 12:48:15 +01:00
Adolfo Gómez García
0f1d1af736 RDP Transport fixes
* Now preferences are not for user, that is not useful. Now screen size,
color deppth, etc.. is selected on the transports.
* Preferences for RDP has been removed from "user preferences"
2017-12-15 12:33:15 +01:00
Adolfo Gómez García
6830d8db4e Fixed Client plugin version 2017-12-15 11:16:22 +01:00
Adolfo Gómez García
d8db218c6d * Updated linux agents to include "udsvapp"
* Fixed reloading of master key, to do it "less sensitive" to database connection errros
* Removed nonsense info log from storage
* Added "customCmd" for UDS vApp on linux
2017-12-14 15:17:46 +01:00
Adolfo Gómez García
81dd4e3b8c a little optimization 2017-12-12 19:42:09 +01:00
Adolfo Gómez García
9519c7c95b More fixes to scheduled actions. Now all should work as expected with start/end dates 2017-12-12 18:42:44 +01:00
Adolfo Gómez García
afa7cb8f39 Fixed another calendar issue 2017-12-12 18:04:47 +01:00
Adolfo Gómez García
dc640fd400 Added support so a deployed service can "ignore unused but assigned"
state. That is, UDS now can enforce the check to be ignored.
2017-11-29 12:50:54 +01:00
Adolfo Gómez García
76c822b015 Fixed RDPFile to split linux command lines correctly 2017-11-29 12:49:49 +01:00
Adolfo Gómez García
c7513328eb Smaill fix for authenticators list (so we can determine type) 2017-11-23 06:38:12 +01:00
Adolfo Gómez García
e5f0fcce69 Fixed SHUTOFF on OpenStack 2017-11-21 12:48:44 +01:00
Adolfo Gómez García
004acbab9a Fixing up thin thin plugin 2017-11-20 14:56:19 +01:00
Adolfo Gómez García
c566ec47a2 Fixes for thin thin plugin 2017-11-20 13:56:28 +01:00
Adolfo Gómez García
3e1a31954a * Added "requestLogoff", to be invoked before "user removal" of a
service
2017-11-20 01:01:52 +01:00
Adolfo Gómez García
ec5473d99f Fixed autoatrributes && rdp 2017-11-15 15:36:36 +01:00
Adolfo Gómez García
4cf62de3fd Fixing up encoders 2017-11-15 14:21:45 +01:00
Adolfo Gómez García
a43d6af237 Fixing up encoders/decoders 2017-11-15 13:11:34 +01:00
Adolfo Gómez García
f41f431f38 Added "hex" encoder if needed 2017-11-15 13:02:13 +01:00
Adolfo Gómez García
c5af877fce Fixed DBfile new "encoding-decoding' 2017-11-15 13:01:47 +01:00
Adolfo Gómez García
88dd6e7494 Fixed encoding to bzip2 2017-11-15 12:41:40 +01:00
Adolfo Gómez García
e69800ccd2 Fixing up removing ".encode(bzip2, base64, zip)" from strings for Python 3 compatibility 2017-11-14 14:51:26 +01:00
Adolfo Gómez García
b1ba02c1f3 Added support or "ignore ssl" cert to openstack (to allow self signed) 2017-11-13 09:23:02 +01:00
Adolfo Gómez García
cb13ac1617 Fixing up REST api for client access 2017-11-10 14:10:26 +01:00
Adolfo Gómez García
de051d99ac Small fix 2017-11-09 08:20:03 +01:00
Adolfo Gómez García
fa1e6a69e8 Added "minValue" (cosmetic) to memory & storage sizes on oVirt Service 2017-11-07 11:09:14 +01:00
Adolfo Gómez García
417ddc6d8d * Fixed calendar, so next execution updates correctly on calendar rule
edition
2017-11-06 10:56:35 +01:00
Adolfo Gómez García
238f08b7dd Fixed id on auth list on REST api 2017-11-03 12:13:32 +01:00
Adolfo Gómez García
739ee728d3 upgraded to 2.2 2017-10-30 10:24:45 +01:00
Adolfo Gómez García
c5b9233d4a Lots of new things
* Now we can have a short name for correct visualization of UDS Services
* We now can allow release services
2017-10-26 13:35:53 +02:00
Adolfo Gómez García
22a8933e82 Added "release" icon (trash) 2017-10-26 10:47:55 +02:00
Adolfo Gómez García
a185baccdd fixed so /rest is allowed 2017-10-25 13:45:22 +02:00
Adolfo Gómez García
2521f41e76 fixed so now Redirect To Https will redirect ALWAYS :) 2017-10-25 13:26:08 +02:00
Adolfo Gómez García
815de57b86 fixed executing 2017-10-19 14:27:17 +02:00
Adolfo Gómez García
428ddd493c * Fixed actor version
* Fixed run
2017-10-19 10:38:41 +02:00
Adolfo Gómez García
4de93ddf1f adapted "run" windows registry key value, so it contains also arguments 2017-10-19 08:07:45 +02:00
Adolfo Gómez García
e6b75e3807 Now, run allows the use of a full command line with parameters 2017-10-18 15:27:56 +02:00
Adolfo Gómez García
e835c018b4 Logs for debugging 2017-10-18 13:18:37 +02:00
Adolfo Gómez García
098620cd05 Fix 2017-10-17 13:20:20 +02:00
Adolfo Gómez García
214f9c397b Improved "ensureResponseIsOk" to detect non json responses... 2017-10-17 13:14:56 +02:00
Adolfo Gómez García
47ae741952 Fixed exception on "notifyReadyFromOsManager' 2017-10-16 19:06:41 +02:00
Adolfo Gómez García
907fad4a55 On 2.2 and later, we force the python executable to be "python2.7" on
linux platforms (except for thin udsclient, that will execute python)
2017-10-16 13:08:47 +02:00
Adolfo Gómez García
7c3e289a6b * Deadline now on its own method
* Removed deadline from notifyUrls. It was a nonsense :).
2017-10-16 12:15:46 +02:00
Adolfo Gómez García
1f5a647ff3 Deadline & Fake set to false
* Added deadline to notifyURLS
* Now Fake = False
2017-10-16 12:02:01 +02:00
Adolfo Gómez García
0eebe6a0a5 Fixed Removal of service without os manager on "logout" 2017-10-16 11:10:39 +02:00
Adolfo Gómez García
8aa1607fee Fixed auth log 2017-10-13 13:05:45 +02:00
Adolfo Gómez García
0aa1311836 Fixed small bug on "useEmptyCreds". In fact, not used but "bug" exists 2017-10-13 12:16:32 +02:00
Adolfo Gómez García
6d49ae6667 Fixed IP Machines service && "execute" method of action calendars 2017-10-12 01:23:02 +02:00
Adolfo Gómez García
e60a4bc8fa Added "udsvapp" possibility 2017-10-09 15:18:59 +02:00
Adolfo Gómez García
6e5b73c3b7 Catalan up to date 2017-10-09 09:21:23 +02:00
Adolfo Gómez García
ae4330fc9d Fixed custom spice error message on dashboard 2017-10-05 11:08:43 +02:00
Adolfo Gómez García
b7147661e7 * Fixed several translations && translations upgrade
* Also removed nonsense check for AD
2017-10-05 10:58:25 +02:00
Adolfo Gómez García
3c07d2f667 Several improvements to messages for transports, and minor client
scripts fixed

* Added custom error message for SPICE
* Remover "import QT" from NX client scripts
2017-10-04 11:30:29 +02:00
Adolfo Gómez García
f54d87a295 Fixed NX legacy password generator. The $ must not be scaped to "\$" :) 2017-10-04 11:26:16 +02:00
Adolfo Gómez García
13f97248f6 Fixed OSManager to allow:
1.- Posting "POST"-only messages through "GET" method. (Only basic
messages)
 2.- Allow notify login/logout for user services without os manager
2017-10-02 10:36:45 +02:00
Adolfo Gómez García
786945fcbf Fixiung up og 2017-09-29 12:33:03 +02:00
Adolfo Gómez García
1070b716a2 Fixiung up og 2017-09-29 12:10:21 +02:00
Adolfo Gómez García
ad1bf0fe0b Fixiung up og 2017-09-29 11:49:37 +02:00
Adolfo Gómez García
df70dd4fd8 Fixiung up og 2017-09-29 11:37:37 +02:00
Adolfo Gómez García
3038c545ce Fixiung up og 2017-09-29 11:09:35 +02:00
Adolfo Gómez García
8d3b28e3cb Fixiung up og 2017-09-29 11:05:29 +02:00
Adolfo Gómez García
c431e39d4a Fixiung up og 2017-09-29 11:04:13 +02:00
Adolfo Gómez García
770f2eef09 Fixing up og 2017-09-29 11:01:28 +02:00
Adolfo Gómez García
e702ff6bca Fixing up OpenGnsys 2017-09-29 10:02:44 +02:00
Adolfo Gómez García
c6cc1f2b43 x2go transport fix 2017-09-27 08:07:43 +02:00
Adolfo Gómez García
06f5184ddd Fixed OpenGnsys 2017-09-25 14:18:19 +02:00
Adolfo Gómez García
d5688116e3 Merge remote-tracking branch 'origin/v2.1' into v2.2 2017-09-21 15:49:39 +02:00
Adolfo Gómez García
1ec1104356 Fixed advanced "alt class" setting to be optional 2017-09-21 09:26:34 +02:00
Adolfo Gómez García
234eb98f0c Included support for retrieving secondary LDAP records so the
information can be completed
2017-09-20 10:14:05 +02:00
Adolfo Gómez García
fa7ab534a3 Updated openGnsys 2017-09-19 08:33:24 +02:00
Adolfo Gómez García
ee661461a5 Fixed windows domain "release", so if domain is not found can be easyly
identified
2017-09-19 08:32:51 +02:00
Adolfo Gómez García
aecf6854a4 Fixed Domain Os Manager for 2017-09-18 14:13:49 +02:00
Adolfo Gómez García
34cd90f9a1 Fixed macosx CoRD transports && backported OpenGnsys 2017-09-18 08:33:17 +02:00
Adolfo Gómez García
201fb8ff9b A couple of cosmetic fixes 2017-09-13 14:31:41 +02:00
Adolfo Gómez García
d071f86c31 Updated filesaver version 2017-09-13 08:26:51 +02:00
Adolfo Gómez García
1c133eacd0 Fix for Windomain 2017-09-13 08:22:10 +02:00
Adolfo Gómez García
89ed019788 Fixed WinDomainOsManager to, in case of several servers actin as DC, look for the machine on all of them 2017-09-07 15:05:44 +02:00
Adolfo Gómez García
9930f4323c Small fix for windomain os manager, to better inform on errors adding to
group.
2017-09-04 18:12:03 +02:00
Adolfo Gómez García
115beefa8a Fixed RDP TRansport to leave enabled by default clipboard redirection, for backward consistence 2017-09-01 13:05:27 +02:00
Adolfo Gómez García
611f3590e6 Added clipboard enabling/disabling on transport 2017-09-01 12:37:13 +02:00
Adolfo Gómez García
59635839cf Added check so we cannot use "no credentials" with an sec that is not rdp and set default sec level to "rdp" for html5 transport 2017-09-01 12:36:39 +02:00
Adolfo Gómez García
4853729d7c Merge remote-tracking branch 'origin/v2.1' into v2.2 2017-08-04 15:19:58 +02:00
Adolfo Gómez García
a8b3b80f75 Merge remote-tracking branch 'origin/v2.1' into v2.2 2017-08-02 16:21:14 +02:00
Adolfo Gómez García
e5a6916109 Fixing up for 2.2 future release 2017-07-27 14:12:45 +02:00
Adolfo Gómez García
6a3093c49d Fix for miniamal server version to use "old method" 2017-07-27 14:04:36 +02:00
Adolfo Gómez García
498154ffb1 Updated version & Initial 2.2 uds client fix so it will support signed scripts 2017-07-27 14:01:04 +02:00
136 changed files with 24484 additions and 9371 deletions

View File

@@ -1 +1 @@
2.1.0
2.2.0

6
actors/.gitignore vendored
View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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
View File

@@ -0,0 +1,5 @@
#!/bin/sh
/usr/bin/udsactor login "$USER"
$@
/usr/bin/udsactor logout "$USER"

View File

@@ -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

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
#
# Copyright (c) 2014 Virtual Cable S.L.

View File

@@ -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()

View File

@@ -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'):

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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."

View File

@@ -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

View File

@@ -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]))

View File

@@ -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

View File

@@ -65,6 +65,7 @@ def login():
return 0
def logout():
global headers
h = Http()

View File

@@ -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',
)

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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')

View File

@@ -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):

View File

@@ -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:

View File

@@ -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
}

View File

@@ -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):

View File

@@ -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)

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View File

@@ -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'))

View File

@@ -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'

View File

@@ -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

View File

@@ -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))

View File

@@ -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())

View File

@@ -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:

View File

@@ -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)

View File

@@ -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

View File

@@ -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):

View File

@@ -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

View File

@@ -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 = {}

View File

@@ -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))

View File

@@ -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

View File

@@ -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'))

View File

@@ -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:

View File

@@ -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)

View 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

View File

@@ -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)

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 lutilisateur"
#: 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

View File

@@ -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

View File

@@ -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"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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()

View 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),
),
]

View File

@@ -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),
),
]

View File

@@ -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())

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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'),

View 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]))

View 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()

View 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')

View 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)

View 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

View 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

View 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))

View 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))

View 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'

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@@ -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

View File

@@ -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')

View File

@@ -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_-]

View File

@@ -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)

View File

@@ -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})}

View File

@@ -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'];
/***/ }
/***/ })
/******/ ])
});
;

View File

@@ -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