Commit 963ed803 authored by dobli's avatar dobli
Browse files

added ui frame to file generation

parent 9f9d8bf0
Showing with 184 additions and 14 deletions
+184 -14
#!/usr/bin/env python3 #!/usr/bin/env python3
""" Python module to assist creating and maintaining docker openHab stacks.""" """ Python module to assist creating and maintaining docker openHab stacks."""
import crypt import crypt
from enum import Enum
import logging import logging
import os import os
from hashlib import md5 from hashlib import md5
...@@ -42,10 +43,11 @@ COMPOSE_NAME = 'docker-stack.yml' ...@@ -42,10 +43,11 @@ COMPOSE_NAME = 'docker-stack.yml'
SKELETON_NAME = 'docker-skeleton.yml' SKELETON_NAME = 'docker-skeleton.yml'
TEMPLATES_NAME = 'docker-templates.yml' TEMPLATES_NAME = 'docker-templates.yml'
CONFIG_DIRS = ['mosquitto', 'nodered', 'ssh', CONFIG_DIRS = ['mosquitto', 'nodered', 'ssh',
'traefik', 'volumerize', 'postgres'] 'traefik', 'volumerize', 'postgres', 'pb-framr']
TEMPLATE_FILES = [ TEMPLATE_FILES = [
'mosquitto/mosquitto.conf', 'nodered/nodered_package.json', 'mosquitto/mosquitto.conf', 'nodered/nodered_package.json',
'nodered/nodered_settings.js', 'ssh/sshd_config', 'traefik/traefik.toml' 'pb-framr/logo.svg', 'nodered/nodered_settings.js',
'ssh/sshd_config', 'traefik/traefik.toml'
] ]
EDIT_FILES = { EDIT_FILES = {
"mosquitto_passwords": "mosquitto/mosquitto_passwords", "mosquitto_passwords": "mosquitto/mosquitto_passwords",
...@@ -56,7 +58,8 @@ EDIT_FILES = { ...@@ -56,7 +58,8 @@ EDIT_FILES = {
"known_hosts": "ssh/known_hosts", "known_hosts": "ssh/known_hosts",
"backup_config": "volumerize/backup_config.json", "backup_config": "volumerize/backup_config.json",
"postgres_user": "postgres/user", "postgres_user": "postgres/user",
"postgres_passwd": "postgres/passwd" "postgres_passwd": "postgres/passwd",
"pb_framr_pages": "pb-framr/pages.json"
} }
CONSTRAINTS = {"building": "node.labels.building"} CONSTRAINTS = {"building": "node.labels.building"}
SERVICES = { SERVICES = {
...@@ -66,6 +69,10 @@ SERVICES = { ...@@ -66,6 +69,10 @@ SERVICES = {
"postgres": "postgres_X", "postgres": "postgres_X",
"mqtt": "mqtt_X" "mqtt": "mqtt_X"
} }
FRONTEND_SERVICES = {
"openhab": "OpenHAB",
"nodered": "Node-RED"
}
# Default Swarm port # Default Swarm port
SWARM_PORT = 2377 SWARM_PORT = 2377
...@@ -73,6 +80,22 @@ SWARM_PORT = 2377 ...@@ -73,6 +80,22 @@ SWARM_PORT = 2377
UID = 9001 UID = 9001
# Username for admin # Username for admin
ADMIN_USER = 'ohadmin' ADMIN_USER = 'ohadmin'
class Service(Enum):
SFTP = ("SFTP", "sftp_X", False)
OPENHAB = ("OpenHAB", "openhab_X", True, '/', 'dashboard')
NODERED = ("Node-RED", "nodered_X", True, 'nodered', 'ballot')
POSTGRES = ("Postgre SQL", "postgres_X", False)
MQTT = ("Mosquitto MQTT Broker", "mqtt_X", False)
def __init__(self, fullname, compose_entry, frontend,
prefix=None, icon=None):
self.fullname = fullname
self.compose_entry = compose_entry
self.frontend = frontend
self.prefix = prefix
self.icon = icon
# >>> # >>>
...@@ -399,6 +422,25 @@ def generate_traefik_user_line(username, password): ...@@ -399,6 +422,25 @@ def generate_traefik_user_line(username, password):
return line return line
def generate_pb_framr_entry(host, service):
"""Generates a single entry of the framr file
:host: host this entry is intended for
:service: entry from service enum
:returns: a dict fitting the asked entry
"""
entry = {}
entry['title'] = service.fullname
if service.prefix == "/":
entry['url'] = f'http://{host}/'
pass
else:
entry['url'] = f'/{service.prefix}_{host}'
entry['icon'] = service.icon
return entry
def generate_mosquitto_file(base_dir, username, password): def generate_mosquitto_file(base_dir, username, password):
"""Generates a mosquitto password file using mosquitto_passwd system tool """Generates a mosquitto password file using mosquitto_passwd system tool
...@@ -531,6 +573,26 @@ def generate_volumerize_file(base_dir, hosts): ...@@ -531,6 +573,26 @@ def generate_volumerize_file(base_dir, hosts):
base_dir, EDIT_FILES['backup_config'], configs, json=True) base_dir, EDIT_FILES['backup_config'], configs, json=True)
def generate_pb_framr_file(base_dir, frames):
"""Generates config for pb framr landing page
:base_dir: path that contains custom config folder
:frames: a dict that contains hosts with matching name and services
"""
configs = []
for f in frames:
building = {
'instance': f['building'],
'entries': [generate_pb_framr_entry(f['host'], s)
for s in f['services'] if s.frontend]
}
configs.append(building)
create_or_replace_config_file(
base_dir, EDIT_FILES['pb_framr_pages'], configs, json=True)
def create_or_replace_config_file(base_dir, config_path, content, json=False): def create_or_replace_config_file(base_dir, config_path, content, json=False):
"""Creates or replaces a config file with new content """Creates or replaces a config file with new content
...@@ -545,8 +607,6 @@ def create_or_replace_config_file(base_dir, config_path, content, json=False): ...@@ -545,8 +607,6 @@ def create_or_replace_config_file(base_dir, config_path, content, json=False):
json.dump(content, file, indent=2) json.dump(content, file, indent=2)
else: else:
file.write(content) file.write(content)
# >>> # >>>
...@@ -929,8 +989,15 @@ def init_menu(args): ...@@ -929,8 +989,15 @@ def init_menu(args):
generate_id_rsa_files(base_dir) generate_id_rsa_files(base_dir)
generate_host_key_files(base_dir, hosts) generate_host_key_files(base_dir, hosts)
frames = []
for i, host in enumerate(hosts): for i, host in enumerate(hosts):
init_machine_menu(base_dir, host, i) building, services = init_machine_menu(base_dir, host, i)
frames.append({'host': host,
'building': building, 'services': services})
# When frames is not empty generate frame config
if frames:
generate_pb_framr_file(base_dir, frames)
# print(answers) # print(answers)
print(f"Configuration files for {stack_name} generated in {base_dir}") print(f"Configuration files for {stack_name} generated in {base_dir}")
...@@ -949,25 +1016,25 @@ def init_machine_menu(base_dir, host, increment): ...@@ -949,25 +1016,25 @@ def init_machine_menu(base_dir, host, increment):
:base_dir: Directory of config files :base_dir: Directory of config files
:host: docker-machine host :host: docker-machine host
:increment: incrementing number to ensure ports are unique :increment: incrementing number to ensure ports are unique
:return: choosen building name and services
""" """
# Prompt for services # Prompt for services
building = qust.text(f'Choose a name for building on server {host}', building = qust.text(f'Choose a name for building on server {host}',
default=f'{host}', style=st).ask() default=f'{host}', style=st).ask()
services = qust.checkbox(f'What services shall {host} provide?', services = qust.checkbox(f'What services shall {host} provide?',
choices=generate_cb_choices(SERVICES.keys(), choices=generate_cb_service_choices(checked=True),
checked=True),
style=st).ask() style=st).ask()
if 'sftp' in services: if Service.SFTP in services:
add_sftp_service(base_dir, host, increment) add_sftp_service(base_dir, host, increment)
if 'openhab' in services: if Service.OPENHAB in services:
add_openhab_service(base_dir, host) add_openhab_service(base_dir, host)
if 'nodered' in services: if Service.NODERED in services:
add_nodered_service(base_dir, host) add_nodered_service(base_dir, host)
if 'mqtt' in services: if Service.MQTT in services:
add_mqtt_service(base_dir, host, increment) add_mqtt_service(base_dir, host, increment)
if 'postgres' in services: if Service.POSTGRES in services:
add_postgres_service(base_dir, host) add_postgres_service(base_dir, host)
print(building) return building, services
# *** Exec Menu Entries *** # *** Exec Menu Entries ***
...@@ -1008,6 +1075,17 @@ def generate_cb_choices(list, checked=False): ...@@ -1008,6 +1075,17 @@ def generate_cb_choices(list, checked=False):
return [{'name': m, 'checked': checked} for m in list] return [{'name': m, 'checked': checked} for m in list]
def generate_cb_service_choices(checked=False):
"""Generates checkbox entries for the sevice enum
:checked: if true, selections will be checked by default
:returns: A list of dicts with name keys
"""
return [
{'name': s.fullname, 'value': s, 'checked': checked} for s in Service
]
def docker_client_prompt(message_details=''): def docker_client_prompt(message_details=''):
"""Show list of docker machines and return selection """Show list of docker machines and return selection
......
...@@ -37,6 +37,10 @@ configs: ...@@ -37,6 +37,10 @@ configs:
file: ./postgres/user file: ./postgres/user
postgres_passwd: postgres_passwd:
file: ./postgres/passwd file: ./postgres/passwd
landing_logo:
file: ./pb-framr/logo.svg
landing_pages:
file: ./pb-framr/pages.json
volumes: volumes:
openhab_addons: openhab_addons:
...@@ -71,3 +75,25 @@ services: ...@@ -71,3 +75,25 @@ services:
placement: placement:
constraints: constraints:
- node.role == manager - node.role == manager
landing:
image: doblix/pb-framr
networks:
- habnet
configs:
- source: landing_logo
target: /usr/share/nginx/html/logo.svg
- source: landing_pages
target: /usr/share/nginx/html/pages.json
deploy:
labels:
- traefik.docker.network=habnet
- traefik.port=80
- traefik.backend=landing
- traefik.main.frontend.priority=2
- traefik.main.frontend.redirect.regex=^(.*)/landing$$
- traefik.main.frontend.redirect.replacement=$$1/landing/
- traefik.main.frontend.rule=PathPrefix:/landing;ReplacePathRegex:^/landing/(.*)
/$$1
placement:
constraints:
- node.role == manager
...@@ -37,6 +37,10 @@ configs: ...@@ -37,6 +37,10 @@ configs:
file: ./postgres/user file: ./postgres/user
postgres_passwd: postgres_passwd:
file: ./postgres/passwd file: ./postgres/passwd
landing_logo:
file: ./pb-framr/logo.svg
landing_pages:
file: ./pb-framr/pages.json
volumes: volumes:
openhab_addons: openhab_addons:
......
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="105.83334mm"
height="105.83334mm"
viewBox="0 0 105.83334 105.83334"
version="1.1"
id="svg8">
<defs
id="defs2" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
transform="translate(-28.600533,-86.069031)">
<circle
style="opacity:1;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:0.12197291;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path881"
cx="81.517204"
cy="138.9857"
r="52.916672" />
<g
transform="matrix(1.4629556,0,0,1.4629556,-15.525955,-77.21686)"
id="g891"
style="fill:#ffffff">
<text
id="text859"
y="143.47997"
x="52.784073"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:1.25;font-family:OpenSans;-inkscape-font-specification:OpenSans;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.69999981px;font-family:OpenSans;-inkscape-font-specification:OpenSans;fill:#ffffff;stroke-width:0.26458332"
y="143.47997"
x="52.784073"
id="tspan857">Your</tspan></text>
<text
id="text859-8"
y="158.60701"
x="48.7006"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:1.25;font-family:Z003;-inkscape-font-specification:Z003;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.69999981px;font-family:OpenSans;-inkscape-font-specification:'OpenSans Bold';fill:#ffffff;stroke-width:0.26458332"
y="158.60701"
x="48.7006"
id="tspan857-9">Logo</tspan></text>
</g>
</g>
</svg>
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment