mirror of
https://github.com/LibreELEC/LibreELEC.tv.git
synced 2025-07-27 04:36:40 +00:00
docker: add kodi notifications
This commit is contained in:
parent
189c3afeff
commit
6b55e44eba
@ -1,3 +1,6 @@
|
|||||||
|
8.0.103
|
||||||
|
- Allow using kodi notifications based on Docker events API
|
||||||
|
|
||||||
8.0.102
|
8.0.102
|
||||||
- Update to docker 1.11.1
|
- Update to docker 1.11.1
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
PKG_NAME="docker"
|
PKG_NAME="docker"
|
||||||
PKG_VERSION="1.11.1"
|
PKG_VERSION="1.11.1"
|
||||||
PKG_REV="102"
|
PKG_REV="103"
|
||||||
PKG_ARCH="any"
|
PKG_ARCH="any"
|
||||||
PKG_ADDON_PROJECTS="Generic RPi RPi2"
|
PKG_ADDON_PROJECTS="Generic RPi RPi2"
|
||||||
PKG_LICENSE="ASL"
|
PKG_LICENSE="ASL"
|
||||||
|
@ -19,13 +19,13 @@
|
|||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import threading
|
||||||
import time
|
import time
|
||||||
import xbmc
|
import xbmc
|
||||||
import xbmcaddon
|
import xbmcaddon
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
|
|
||||||
sys.path.append('/usr/share/kodi/addons/service.libreelec.settings')
|
sys.path.append('/usr/share/kodi/addons/service.libreelec.settings')
|
||||||
|
|
||||||
import oe
|
import oe
|
||||||
|
|
||||||
__author__ = 'lrusak'
|
__author__ = 'lrusak'
|
||||||
@ -36,20 +36,257 @@ __servicename__ = __addon__.getAddonInfo('id') + '.service'
|
|||||||
__socket__ = __path__ + '/systemd/' + __addon__.getAddonInfo('id') + '.socket'
|
__socket__ = __path__ + '/systemd/' + __addon__.getAddonInfo('id') + '.socket'
|
||||||
__socketname__ = __addon__.getAddonInfo('id') + '.socket'
|
__socketname__ = __addon__.getAddonInfo('id') + '.socket'
|
||||||
|
|
||||||
|
sys.path.append(__path__ + '/lib')
|
||||||
|
import dockermon
|
||||||
|
|
||||||
|
# docker events for api 1.23 (docker version 1.11.x)
|
||||||
|
# https://docs.docker.com/engine/reference/api/docker_remote_api_v1.23/#monitor-docker-s-events
|
||||||
|
|
||||||
|
docker_events = {
|
||||||
|
'container': {
|
||||||
|
'string': 30030,
|
||||||
|
'event': {
|
||||||
|
'attach': {
|
||||||
|
'string': 30031,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
'commit': {
|
||||||
|
'string': 30032,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
'copy': {
|
||||||
|
'string': 30033,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
'create': {
|
||||||
|
'string': 30034,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
'destroy': {
|
||||||
|
'string': 30035,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
'die': {
|
||||||
|
'string': 30036,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
'exec_create': {
|
||||||
|
'string': 30037,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
'exec_start': {
|
||||||
|
'string': 30038,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
'export': {
|
||||||
|
'string': 30039,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
'kill': {
|
||||||
|
'string': 30040,
|
||||||
|
'enabled': True,
|
||||||
|
},
|
||||||
|
'oom': {
|
||||||
|
'string': 30041,
|
||||||
|
'enabled': True,
|
||||||
|
},
|
||||||
|
'pause': {
|
||||||
|
'string': 30042,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
'rename': {
|
||||||
|
'string': 30043,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
'resize': {
|
||||||
|
'string': 30044,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
'restart': {
|
||||||
|
'string': 30045,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
'start': {
|
||||||
|
'string': 30046,
|
||||||
|
'enabled': True,
|
||||||
|
},
|
||||||
|
'stop': {
|
||||||
|
'string': 30047,
|
||||||
|
'enabled': True,
|
||||||
|
},
|
||||||
|
'top': {
|
||||||
|
'string': 30048,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
'unpause': {
|
||||||
|
'string': 30049,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
'update': {
|
||||||
|
'string': 30050,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'image': {
|
||||||
|
'string': 30060,
|
||||||
|
'event': {
|
||||||
|
'delete': {
|
||||||
|
'string': 30061,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
'import': {
|
||||||
|
'string': 30062,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
'pull': {
|
||||||
|
'string': 30063,
|
||||||
|
'enabled': True,
|
||||||
|
},
|
||||||
|
'push': {
|
||||||
|
'string': 30064,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
'tag': {
|
||||||
|
'string': 30065,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
'untag': {
|
||||||
|
'string': 30066,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'volume': {
|
||||||
|
'string': 30070,
|
||||||
|
'event': {
|
||||||
|
'create': {
|
||||||
|
'string': 30071,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
'mount': {
|
||||||
|
'string': 30072,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
'unmount': {
|
||||||
|
'string': 30073,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
'destroy': {
|
||||||
|
'string': 30074,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'network': {
|
||||||
|
'string': 30080,
|
||||||
|
'event': {
|
||||||
|
'create': {
|
||||||
|
'string': 30081,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
'connect': {
|
||||||
|
'string': 30082,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
'disconnect': {
|
||||||
|
'string': 30083,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
'destroy': {
|
||||||
|
'string': 30084,
|
||||||
|
'enabled': '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def print_notification(json_data):
|
||||||
|
event_string = docker_events[json_data['Type']]['event'][json_data['Action']]['string']
|
||||||
|
if __addon__.getSetting('notifications') is '0': # default
|
||||||
|
if docker_events[json_data['Type']]['event'][json_data['Action']]['enabled']:
|
||||||
|
try:
|
||||||
|
message = unicode(' '.join([__addon__.getLocalizedString(30010),
|
||||||
|
json_data['Actor']['Attributes']['name'],
|
||||||
|
'|',
|
||||||
|
__addon__.getLocalizedString(30012),
|
||||||
|
__addon__.getLocalizedString(event_string)]))
|
||||||
|
except KeyError as e:
|
||||||
|
message = unicode(' '.join([__addon__.getLocalizedString(30011),
|
||||||
|
json_data['Type'],
|
||||||
|
'|',
|
||||||
|
__addon__.getLocalizedString(30012),
|
||||||
|
__addon__.getLocalizedString(event_string)]))
|
||||||
|
|
||||||
|
elif __addon__.getSetting('notifications') is '1': # all
|
||||||
|
try:
|
||||||
|
message = unicode(' '.join([__addon__.getLocalizedString(30010),
|
||||||
|
json_data['Actor']['Attributes']['name'],
|
||||||
|
'|',
|
||||||
|
__addon__.getLocalizedString(30012),
|
||||||
|
__addon__.getLocalizedString(event_string)]))
|
||||||
|
except KeyError as e:
|
||||||
|
message = unicode(' '.join([__addon__.getLocalizedString(30011),
|
||||||
|
json_data['Type'],
|
||||||
|
'|',
|
||||||
|
__addon__.getLocalizedString(30012),
|
||||||
|
__addon__.getLocalizedString(event_string)]))
|
||||||
|
|
||||||
|
elif __addon__.getSetting('notifications') is '2': # none
|
||||||
|
pass
|
||||||
|
|
||||||
|
elif __addon__.getSetting('notifications') is '3': # custom
|
||||||
|
if __addon__.getSetting(json_data['Action']) == 'true':
|
||||||
|
try:
|
||||||
|
message = unicode(' '.join([__addon__.getLocalizedString(30010),
|
||||||
|
json_data['Actor']['Attributes']['name'],
|
||||||
|
'|',
|
||||||
|
__addon__.getLocalizedString(30012),
|
||||||
|
__addon__.getLocalizedString(event_string)]))
|
||||||
|
except KeyError as e:
|
||||||
|
message = unicode(' '.join([__addon__.getLocalizedString(30011),
|
||||||
|
json_data['Type'],
|
||||||
|
'|',
|
||||||
|
__addon__.getLocalizedString(30012),
|
||||||
|
__addon__.getLocalizedString(event_string)]))
|
||||||
|
|
||||||
|
dialog = xbmcgui.Dialog()
|
||||||
|
try:
|
||||||
|
if message is not '':
|
||||||
|
length = int(__addon__.getSetting('notification_length')) * 1000
|
||||||
|
dialog.notification('Docker', message, '/storage/.kodi/addons/service.system.docker/icon.png', length)
|
||||||
|
xbmc.log('## service.system.docker ## ' + unicode(message))
|
||||||
|
except NameError as e:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class dockermonThread(threading.Thread):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
threading.Thread.__init__(self)
|
||||||
|
self._is_running = True
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
while self._is_running:
|
||||||
|
dockermon.watch(print_notification)
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self._is_running = False
|
||||||
|
|
||||||
class Main(object):
|
class Main(object):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
|
||||||
monitor = DockerMonitor(self)
|
monitor = DockerMonitor(self)
|
||||||
|
|
||||||
|
if not Docker().is_active():
|
||||||
if not Docker().is_enabled():
|
if not Docker().is_enabled():
|
||||||
Docker().enable()
|
Docker().enable()
|
||||||
Docker().start()
|
Docker().start()
|
||||||
|
|
||||||
while not monitor.abortRequested():
|
while not monitor.abortRequested():
|
||||||
if monitor.waitForAbort():
|
if monitor.waitForAbort():
|
||||||
Docker().stop()
|
# we don't want to stop or disable docker while it's installed
|
||||||
Docker().disable()
|
pass
|
||||||
|
|
||||||
class Docker(object):
|
class Docker(object):
|
||||||
|
|
||||||
@ -95,9 +332,11 @@ class DockerMonitor(xbmc.Monitor):
|
|||||||
xbmc.Monitor.__init__(self)
|
xbmc.Monitor.__init__(self)
|
||||||
|
|
||||||
def onSettingsChanged(self):
|
def onSettingsChanged(self):
|
||||||
Docker().restart()
|
pass
|
||||||
|
|
||||||
if ( __name__ == "__main__" ):
|
if ( __name__ == "__main__" ):
|
||||||
|
dockermonThread().start()
|
||||||
Main()
|
Main()
|
||||||
|
|
||||||
del DockerMonitor
|
del DockerMonitor
|
||||||
|
dockermonThread().stop()
|
||||||
|
153
packages/addons/service/docker/source/lib/dockermon.py
Executable file
153
packages/addons/service/docker/source/lib/dockermon.py
Executable file
@ -0,0 +1,153 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
"""docker monitor using docker /events HTTP streaming API"""
|
||||||
|
"""https://github.com/CyberInt/dockermon"""
|
||||||
|
|
||||||
|
from contextlib import closing
|
||||||
|
from functools import partial
|
||||||
|
from socket import socket, AF_UNIX
|
||||||
|
from subprocess import Popen, PIPE
|
||||||
|
from sys import stdout, version_info
|
||||||
|
import json
|
||||||
|
import shlex
|
||||||
|
|
||||||
|
if version_info[:2] < (3, 0):
|
||||||
|
from httplib import OK as HTTP_OK
|
||||||
|
from urlparse import urlparse
|
||||||
|
else:
|
||||||
|
from http.client import OK as HTTP_OK
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
__version__ = '0.2.2'
|
||||||
|
# buffer size must be 256 or lower otherwise events won't show in realtime
|
||||||
|
bufsize = 256
|
||||||
|
default_sock_url = 'ipc:///var/run/docker.sock'
|
||||||
|
|
||||||
|
|
||||||
|
class DockermonError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def read_http_header(sock):
|
||||||
|
"""Read HTTP header from socket, return header and rest of data."""
|
||||||
|
buf = []
|
||||||
|
hdr_end = '\r\n\r\n'
|
||||||
|
|
||||||
|
while True:
|
||||||
|
buf.append(sock.recv(bufsize).decode('utf-8'))
|
||||||
|
data = ''.join(buf)
|
||||||
|
i = data.find(hdr_end)
|
||||||
|
if i == -1:
|
||||||
|
continue
|
||||||
|
return data[:i], data[i + len(hdr_end):]
|
||||||
|
|
||||||
|
|
||||||
|
def header_status(header):
|
||||||
|
"""Parse HTTP status line, return status (int) and reason."""
|
||||||
|
status_line = header[:header.find('\r')]
|
||||||
|
# 'HTTP/1.1 200 OK' -> (200, 'OK')
|
||||||
|
fields = status_line.split(None, 2)
|
||||||
|
return int(fields[1]), fields[2]
|
||||||
|
|
||||||
|
|
||||||
|
def connect(url):
|
||||||
|
"""Connect to UNIX or TCP socket.
|
||||||
|
|
||||||
|
url can be either tcp://<host>:port or ipc://<path>
|
||||||
|
"""
|
||||||
|
url = urlparse(url)
|
||||||
|
if url.scheme == 'tcp':
|
||||||
|
sock = socket()
|
||||||
|
netloc = tuple(url.netloc.rsplit(':', 1))
|
||||||
|
hostname = socket.gethostname()
|
||||||
|
elif url.scheme == 'ipc':
|
||||||
|
sock = socket(AF_UNIX)
|
||||||
|
netloc = url.path
|
||||||
|
hostname = 'localhost'
|
||||||
|
else:
|
||||||
|
raise ValueError('unknown socket type: %s' % url.scheme)
|
||||||
|
|
||||||
|
sock.connect(netloc)
|
||||||
|
return sock, hostname
|
||||||
|
|
||||||
|
|
||||||
|
def watch(callback, url=default_sock_url):
|
||||||
|
"""Watch docker events. Will call callback with each new event (dict).
|
||||||
|
|
||||||
|
url can be either tcp://<host>:port or ipc://<path>
|
||||||
|
"""
|
||||||
|
sock, hostname = connect(url)
|
||||||
|
request = 'GET /events HTTP/1.1\nHost: %s\n\n' % hostname
|
||||||
|
request = request.encode('utf-8')
|
||||||
|
|
||||||
|
with closing(sock):
|
||||||
|
sock.sendall(request)
|
||||||
|
header, payload = read_http_header(sock)
|
||||||
|
status, reason = header_status(header)
|
||||||
|
if status != HTTP_OK:
|
||||||
|
raise DockermonError('bad HTTP status: %s %s' % (status, reason))
|
||||||
|
|
||||||
|
# Messages are \r\n<size in hex><JSON payload>\r\n
|
||||||
|
buf = [payload]
|
||||||
|
while True:
|
||||||
|
chunk = sock.recv(bufsize)
|
||||||
|
if not chunk:
|
||||||
|
raise EOFError('socket closed')
|
||||||
|
buf.append(chunk.decode('utf-8'))
|
||||||
|
data = ''.join(buf)
|
||||||
|
i = data.find('\r\n')
|
||||||
|
if i == -1:
|
||||||
|
continue
|
||||||
|
|
||||||
|
size = int(data[:i], 16)
|
||||||
|
start = i + 2 # Skip initial \r\n
|
||||||
|
|
||||||
|
if len(data) < start + size + 2:
|
||||||
|
continue
|
||||||
|
payload = data[start:start+size]
|
||||||
|
callback(json.loads(payload))
|
||||||
|
buf = [data[start+size+2:]] # Skip \r\n suffix
|
||||||
|
|
||||||
|
|
||||||
|
def print_callback(msg):
|
||||||
|
"""Print callback, prints message to stdout as JSON in one line."""
|
||||||
|
json.dump(msg, stdout)
|
||||||
|
stdout.write('\n')
|
||||||
|
stdout.flush()
|
||||||
|
|
||||||
|
|
||||||
|
def prog_callback(prog, msg):
|
||||||
|
"""Program callback, calls prog with message in stdin"""
|
||||||
|
pipe = Popen(prog, stdin=PIPE)
|
||||||
|
data = json.dumps(msg)
|
||||||
|
pipe.stdin.write(data.encode('utf-8'))
|
||||||
|
pipe.stdin.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
|
||||||
|
parser = ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument('--prog', default=None,
|
||||||
|
help='program to call (e.g. "jq --unbuffered .")')
|
||||||
|
parser.add_argument(
|
||||||
|
'--socket-url', default=default_sock_url,
|
||||||
|
help='socket url (ipc:///path/to/sock or tcp:///host:port)')
|
||||||
|
parser.add_argument(
|
||||||
|
'--version', help='print version and exit',
|
||||||
|
action='store_true', default=False)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.version:
|
||||||
|
print('dockermon %s' % __version__)
|
||||||
|
raise SystemExit
|
||||||
|
|
||||||
|
if args.prog:
|
||||||
|
prog = shlex.split(args.prog)
|
||||||
|
callback = partial(prog_callback, prog)
|
||||||
|
else:
|
||||||
|
callback = print_callback
|
||||||
|
|
||||||
|
try:
|
||||||
|
watch(callback, args.socket_url)
|
||||||
|
except (KeyboardInterrupt, EOFError):
|
||||||
|
pass
|
@ -0,0 +1,197 @@
|
|||||||
|
# Kodi Media Center language file
|
||||||
|
# Addon Name: docker
|
||||||
|
# Addon id: service.system.docker
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30000"
|
||||||
|
msgid "Settings"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30001"
|
||||||
|
msgid "Notifications"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30002"
|
||||||
|
msgid "Default"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30003"
|
||||||
|
msgid "All"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30004"
|
||||||
|
msgid "Off"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30005"
|
||||||
|
msgid "Custom"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30006"
|
||||||
|
msgid "Notification Length (Seconds)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30010"
|
||||||
|
msgid "Name:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30011"
|
||||||
|
msgid "Type:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30012"
|
||||||
|
msgid "Action:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30030"
|
||||||
|
msgid "Container"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30031"
|
||||||
|
msgid "attach"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30032"
|
||||||
|
msgid "commit"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30033"
|
||||||
|
msgid "copy"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30034"
|
||||||
|
msgid "create"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30035"
|
||||||
|
msgid "destroy"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30036"
|
||||||
|
msgid "die"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30037"
|
||||||
|
msgid "exec_create"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30038"
|
||||||
|
msgid "exec_start"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30039"
|
||||||
|
msgid "export"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30040"
|
||||||
|
msgid "kill"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30041"
|
||||||
|
msgid "out of memory"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30042"
|
||||||
|
msgid "pause"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30043"
|
||||||
|
msgid "rename"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30044"
|
||||||
|
msgid "resize"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30045"
|
||||||
|
msgid "restart"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30046"
|
||||||
|
msgid "start"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30047"
|
||||||
|
msgid "stop"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30048"
|
||||||
|
msgid "top"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30049"
|
||||||
|
msgid "unpause"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30050"
|
||||||
|
msgid "update"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30060"
|
||||||
|
msgid "Image"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30061"
|
||||||
|
msgid "delete"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30062"
|
||||||
|
msgid "import"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30063"
|
||||||
|
msgid "pull"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30064"
|
||||||
|
msgid "push"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30065"
|
||||||
|
msgid "tag"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30066"
|
||||||
|
msgid "untag"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30070"
|
||||||
|
msgid "Volume"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30071"
|
||||||
|
msgid "create"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30072"
|
||||||
|
msgid "mount"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30073"
|
||||||
|
msgid "unmount"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30074"
|
||||||
|
msgid "destroy"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30080"
|
||||||
|
msgid "Network"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30081"
|
||||||
|
msgid "create"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30082"
|
||||||
|
msgid "connect"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30083"
|
||||||
|
msgid "disconnect"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30084"
|
||||||
|
msgid "destroy"
|
||||||
|
msgstr ""
|
15
packages/addons/service/docker/source/resources/settings.xml
Normal file
15
packages/addons/service/docker/source/resources/settings.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||||
|
<settings>
|
||||||
|
<category label="30000">
|
||||||
|
<setting label="30001" type="enum" id="notifications" default="0" lvalues="30002|30003|30004|30005"/>
|
||||||
|
<setting label="30034" type="bool" id="create" default="false" visible="eq(-1,3)" subsetting="true"/>
|
||||||
|
<setting label="30035" type="bool" id="destroy" default="false" visible="eq(-2,3)" subsetting="true"/>
|
||||||
|
<setting label="30036" type="bool" id="die" default="false" visible="eq(-3,3)" subsetting="true"/>
|
||||||
|
<setting label="30040" type="bool" id="kill" default="false" visible="eq(-4,3)" subsetting="true"/>
|
||||||
|
<setting label="30041" type="bool" id="oom" default="false" visible="eq(-5,3)" subsetting="true"/>
|
||||||
|
<setting label="30046" type="bool" id="start" default="false" visible="eq(-6,3)" subsetting="true"/>
|
||||||
|
<setting label="30047" type="bool" id="stop" default="false" visible="eq(-7,3)" subsetting="true"/>
|
||||||
|
<setting label="30006" type="slider" id="notification_length" default="5" visible="lt(-8,2) | gt(-8,2)" range="2,10" option="int" />
|
||||||
|
</category>
|
||||||
|
</settings>
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user