added tree command to allow an full overview of uds data

This commit is contained in:
Adolfo Gómez García 2022-09-16 18:45:37 +02:00
parent 8c6390733c
commit 4ec8841a57
No known key found for this signature in database
GPG Key ID: DD1ABF20724CDA23
4 changed files with 314 additions and 8 deletions

View File

@ -30,6 +30,7 @@
"""
@author: Adolfo Gómez, dkmaster at dkmon dot com
"""
import typing
import logging
from django.core.management.base import BaseCommand
@ -42,10 +43,10 @@ class Command(BaseCommand):
args = "<mod.name=value mod.name=value mod.name=value...>"
help = "Updates configuration values. If mod is omitted, UDS will be used. Omit whitespaces betwen name, =, and value (they must be a single param)"
def add_arguments(self, parser):
def add_arguments(self, parser) -> None:
parser.add_argument('name_value', nargs='+', type=str)
def handle(self, *args, **options):
def handle(self, *args, **options) -> None:
logger.debug("Handling settings")
GlobalConfig.initialize()
try:
@ -62,5 +63,5 @@ class Command(BaseCommand):
): # If not exists, try to store value without any special parameters
Config.section(mod).value(name, value).get()
except Exception as e:
print('The command could not be processed: {}'.format(e))
self.stderr.write('The command could not be processed: {}'.format(e))
logger.exception('Exception processing %s', args)

View File

@ -41,8 +41,7 @@ logger = logging.getLogger(__name__)
class Command(BaseCommand):
args = "<mod.name=value mod.name=value mod.name=value...>"
help = "Updates configuration values. If mod is omitted, UDS will be used. Omit whitespaces betwen name, =, and value (they must be a single param)"
help = "Show current PUBLIC configuration of UDS broker (passwords are not shown)"
def add_arguments(self, parser):
parser.add_argument(
@ -71,7 +70,7 @@ class Command(BaseCommand):
writer.writerow([section, key, value['value']])
else:
v = value['value'].replace('\n', '\\n')
print(f'{section}.{key}="{v}"')
self.stdout.write(f'{section}.{key}="{v}"')
except Exception as e:
print('The command could not be processed: {}'.format(e))

View File

@ -161,8 +161,8 @@ class Command(BaseCommand):
if not start and not stop:
if pid:
sys.stdout.write(
self.stdout.write(
"Task manager found running (pid file exists: {0})\n".format(pid)
)
else:
sys.stdout.write("Task manager not foud (pid file do not exits)\n")
self.stdout.write("Task manager not foud (pid file do not exits)\n")

View File

@ -0,0 +1,306 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2019 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
"""
import logging
import typing
import yaml
import collections
from django.core.management.base import BaseCommand
from uds.core.util import config
from uds import models
logger = logging.getLogger(__name__)
if typing.TYPE_CHECKING:
from uds.core.module import Module
from django.db import models as dbmodels
def getSerializedFromManagedObject(
mod: 'models.ManagedObjectModel',
removableFields: typing.Optional[typing.List[str]] = None,
) -> typing.Mapping[str, typing.Any]:
try:
obj = mod.getInstance()
gui = {i['name']: i['gui']['type'] for i in obj.guiDescription()}
values = obj.valuesDict()
# Remove password fields
for k, v in gui.items():
if v == 'password':
values[k] = '********'
# Some names are know "secret data"
for i in ('serverCertificate', 'privateKey'):
if i in values:
values[i] = '********'
# remove removable fields
for i in removableFields or []:
if i in values:
del values[i]
# Append typeName to list
values['typeName'] = str(obj.typeName)
values['comments'] = mod.comments
return values
except Exception:
return {}
def getSerializedFromModel(
mod: 'dbmodels.Model', removableFields: typing.Optional[typing.List[str]] = None, passwordFields: typing.Optional[typing.List[str]] = None
) -> typing.Mapping[str, typing.Any]:
removableFields = removableFields or []
passwordFields = passwordFields or []
try:
values = mod._meta.managers[0].filter(pk=mod.pk).values()[0]
for i in ['uuid', 'id'] + removableFields:
if i in values:
del values[i]
for i in passwordFields:
if i in values:
values[i] = '********'
return values
except Exception:
return {}
class Command(BaseCommand):
help = "Outputs all UDS Trees of elements in YAML format"
def add_arguments(self, parser):
pass
def handle(self, *args, **options):
logger.debug("Show Tree")
# firt, genertate Provider-service-servicepool tree
cntr = 0
def counter(s: str) -> str:
nonlocal cntr
cntr += 1
return f'{cntr:02d}.-{s}'
tree = {}
try:
providers = {}
for provider in models.Provider.objects.all():
services = {}
for service in provider.services.all():
servicePools = {}
for servicePool in service.deployedServices.all():
# get publications
publications = {}
for publication in servicePool.publications.all():
# Get all changelogs for this publication
try:
changelogs = models.ServicePoolPublicationChangelog.objects.filter(
publication=publication
).values(
'stamp', 'revision', 'log'
)
changelogs = list(changelogs)
except Exception:
changelogs = []
publications[publication.revision] = getSerializedFromModel(
publication, ['data']
)
publications[publication.revision][
'changelogs'
] = changelogs
# get assigned groups
groups = []
for group in servicePool.assignedGroups.all():
groups.append(group.pretty_name)
# get calendar actions
calendarActions = {}
for calendarAction in models.CalendarAction.objects.filter(
service_pool=servicePool
):
calendarActions[calendarAction.calendar.name] = {
'action': calendarAction.action,
'params': calendarAction.prettyParams,
'at_start': calendarAction.at_start,
'events_offset': calendarAction.events_offset,
'last_execution': calendarAction.last_execution,
'next_execution': calendarAction.next_execution,
}
# get calendar access
calendarAccess = {}
for ca in models.CalendarAccess.objects.filter(
service_pool=servicePool
):
calendarAccess[ca.calendar.name] = ca.access
servicePools[servicePool.name] = {
'_': getSerializedFromModel(servicePool),
'calendarAccess': calendarAccess,
'calendarActions': calendarActions,
'groups': groups,
'publications': publications,
}
services[service.name] = {
'_': getSerializedFromManagedObject(service),
'servicePools': servicePools,
}
providers[provider.name] = {
'_': getSerializedFromManagedObject(provider),
'services': services,
}
tree[counter('PROVIDERS')] = providers
# authenticators
authenticators = {}
for authenticator in models.Authenticator.objects.all():
# Groups
# groups = {}
# for group in authenticator.groups.all():
# groups[group.name] = getSerializedFromModel(group)
authenticators[authenticator.name] = {
'_': getSerializedFromManagedObject(authenticator),
# 'groups': groups,
}
tree[counter('AUTHENTICATORS')] = authenticators
# transports
transports = {}
for transport in models.Transport.objects.all():
transports[transport.name] = getSerializedFromManagedObject(transport)
tree[counter('TRANSPORTS')] = transports
# Networks
networks = {}
for network in models.Network.objects.all():
networks[network.name] = {
'networks': network.net_string,
'transports': [t.name for t in network.transports.all()],
}
tree[counter('NETWORKS')] = networks
# os managers
osManagers = {}
for osManager in models.OSManager.objects.all():
osManagers[osManager.name] = getSerializedFromManagedObject(osManager)
tree[counter('OSMANAGERS')] = osManagers
# calendars
calendars = {}
for calendar in models.Calendar.objects.all():
# calendar rules
rules = {}
for rule in models.CalendarRule.objects.filter(calendar=calendar):
rules[rule.name] = getSerializedFromModel(
rule, ['calendar_id', 'name']
)
calendars[calendar.name] = {
'_': getSerializedFromModel(calendar),
'rules': rules,
}
tree[counter('CALENDARS')] = calendars
# Metapools
metapools = {}
for metapool in models.MetaPool.objects.all():
metapools[metapool.name] = getSerializedFromModel(metapool)
tree[counter('METAPOOLS')] = metapools
# accounts
accounts = {}
for account in models.Account.objects.all():
accounts[account.name] = {
'_': getSerializedFromModel(account),
'usages': list(
account.usages.all().values(
'user_name', 'pool_name', 'start', 'end'
)
),
}
tree[counter('ACCOUNTS')] = accounts
# Service pool groups
servicePoolGroups = {}
for servicePoolGroup in models.ServicePoolGroup.objects.all():
servicePoolGroups[servicePoolGroup.name] = {
'comments': servicePoolGroup.comments,
'servicePools': [sp.name for sp in servicePoolGroup.servicesPools.all()], # type: ignore
}
tree[counter('SERVICEPOOLGROUPS')] = servicePoolGroups
# Gallery
gallery = {}
for galleryItem in models.Image.objects.all():
gallery[galleryItem.name] = {
'size': galleryItem.size,
'stamp': galleryItem.stamp,
'length': len(galleryItem.data),
}
tree[counter('GALLERY')] = gallery
# Actor tokens
actorTokens = {}
for actorToken in models.ActorToken.objects.all():
actorTokens[actorToken.hostname] = getSerializedFromModel(actorToken, passwordFields=['token'])
tree[counter('ACTORTOKENS')] = actorTokens
# Tunnel tokens
tunnelTokens = {}
for tunnelToken in models.TunnelToken.objects.all():
tunnelTokens[tunnelToken.hostname] = getSerializedFromModel(tunnelToken, passwordFields=['token'])
tree[counter('TUNNELTOKENS')] = tunnelTokens
self.stdout.write(yaml.dump(tree, default_flow_style=False))
except Exception as e:
print('The command could not be processed: {}'.format(e))
logger.exception('Exception processing %s', args)