Commit a2dbbde10e44f298c4aade36612d01105ea81c1a
1 parent
d42ec18b
Exists in
dev
Adding new configpyros class, renaming previous configpyros class to obsconfig_class
Showing
14 changed files
with
1293 additions
and
1088 deletions
Show diff stats
... | ... | @@ -0,0 +1,75 @@ |
1 | +import yaml,os,time | |
2 | +import pykwalify.core | |
3 | +from pykwalify.errors import SchemaError | |
4 | + | |
5 | +class configpyros: | |
6 | + def check_and_return_config(self,yaml_file:str,schema_file:str)->dict: | |
7 | + """ | |
8 | + Check if yaml_file is valid for the schema_file and return an dictionary of the config file | |
9 | + | |
10 | + Args: | |
11 | + yaml_file (str): Path to the config_file to be validated | |
12 | + schema_file (str): Path to the schema file | |
13 | + | |
14 | + Returns: | |
15 | + dict: dictionary of the config file (with values) | |
16 | + """ | |
17 | + # disable pykwalify error to clean the output | |
18 | + #####logging.disable(logging.ERROR) | |
19 | + try: | |
20 | + can_yaml_file_be_read = False | |
21 | + while can_yaml_file_be_read != True: | |
22 | + if os.access(yaml_file, os.R_OK): | |
23 | + can_yaml_file_be_read = True | |
24 | + else: | |
25 | + print(f"{yaml_file} can't be accessed, waiting for availability") | |
26 | + time.sleep(0.5) | |
27 | + | |
28 | + c = pykwalify.core.Core(source_file=yaml_file, schema_files=[self.config_path+"/"+schema_file]) | |
29 | + return c.validate(raise_exception=True) | |
30 | + except SchemaError: | |
31 | + for error in c.errors: | |
32 | + print("Error :",str(error).split(". Path")[0]) | |
33 | + print("Path to error :",error.path) | |
34 | + return None | |
35 | + except IOError: | |
36 | + print("Error when reading the observatory config file") | |
37 | + | |
38 | + | |
39 | + def read_and_check_config_file(self,yaml_file:str)->dict: | |
40 | + """ | |
41 | + Read the schema key of the config file to retrieve schema name and proceed to the checking of that config file | |
42 | + Call check_and_return_config function and print its return. | |
43 | + | |
44 | + Args: | |
45 | + yaml_file (str): path to the config file | |
46 | + Returns: | |
47 | + dict: Dictionary of the config file (with values) | |
48 | + """ | |
49 | + try: | |
50 | + can_config_file_be_read = False | |
51 | + while can_config_file_be_read != True: | |
52 | + | |
53 | + if os.access(yaml_file, os.R_OK): | |
54 | + can_config_file_be_read = True | |
55 | + else: | |
56 | + print(f"{yaml_file} can't be accessed, waiting for availability") | |
57 | + time.sleep(0.5) | |
58 | + with open(yaml_file, 'r') as stream: | |
59 | + print(f"Reading {yaml_file}") | |
60 | + config_file = yaml.safe_load(stream) | |
61 | + result = self.check_and_return_config(yaml_file,config_file["schema"]) | |
62 | + if result == None: | |
63 | + print("Error when reading and validating config file, please check the errors right above") | |
64 | + exit(1) | |
65 | + return result | |
66 | + | |
67 | + except yaml.YAMLError as exc: | |
68 | + print(exc) | |
69 | + except Exception as e: | |
70 | + print(e) | |
71 | + return None | |
72 | + | |
73 | + def __init__(self,pyros_config_file): | |
74 | + self.config_path = os.path.abspath(os.path.join(os.environ.get("DJANGO_PATH"),"../../../config/pyros//")) | |
75 | + self.pyros_config = self.read_and_check_config_file(pyros_config_file) | ... | ... |
... | ... | @@ -0,0 +1,24 @@ |
1 | +type: map | |
2 | +mapping: | |
3 | + schema: | |
4 | + type: str | |
5 | + required: True | |
6 | + general: | |
7 | + type: map | |
8 | + required: True | |
9 | + mapping: | |
10 | + logo: | |
11 | + type: str | |
12 | + required: True | |
13 | + color_theme: | |
14 | + type: str | |
15 | + required: True | |
16 | + nb_element_per_page: | |
17 | + type: int | |
18 | + required: True | |
19 | + USR: | |
20 | + type: any | |
21 | + SCP: | |
22 | + type: any | |
23 | + OCF: | |
24 | + type: any | |
0 | 25 | \ No newline at end of file | ... | ... |
pyros.py
... | ... | @@ -21,6 +21,7 @@ import sys |
21 | 21 | import time |
22 | 22 | import re |
23 | 23 | import glob |
24 | +from config.pyros.config_pyros import configpyros | |
24 | 25 | from git import Repo |
25 | 26 | #import logging |
26 | 27 | |
... | ... | @@ -569,6 +570,8 @@ def test(app): |
569 | 570 | print("Running tests") |
570 | 571 | os.environ["PATH_TO_OBSCONF_FOLDER"] = os.path.join(os.path.abspath(PYROS_DJANGO_BASE_DIR),"obsconfig/fixtures/") |
571 | 572 | os.environ["unit_name"] = "" |
573 | + configfile = 'config_pyros.yml' | |
574 | + os.environ["pyros_config_file"] = os.path.join(os.path.abspath(PYROS_DJANGO_BASE_DIR),"../../../config/pyros/", configfile) | |
572 | 575 | #start_dir = os.getcwd() |
573 | 576 | if app == None: |
574 | 577 | #apps = ['obsconfig','scientific_program','common', 'scheduler', 'routine_manager', 'user_manager', 'alert_manager.tests.TestStrategyChange'] |
... | ... | @@ -663,7 +666,6 @@ def initdb(): |
663 | 666 | return True |
664 | 667 | |
665 | 668 | |
666 | - | |
667 | 669 | @pyros_launcher.command(help="Launch an agent") |
668 | 670 | #@global_test_options |
669 | 671 | @click.argument('agent') |
... | ... | @@ -677,8 +679,15 @@ def start(agent:str, configfile:str, observatory:str, unit:str): |
677 | 679 | log.debug("Running start command") |
678 | 680 | if configfile: |
679 | 681 | log.debug("With config file"+ configfile) |
682 | + os.environ["pyros_config_file"] = os.path.join(os.path.abspath(PYROS_DJANGO_BASE_DIR),"../../../config/pyros/", configfile) | |
680 | 683 | else: |
681 | - configfile = '' | |
684 | + configfile = 'config_pyros.yml' | |
685 | + os.environ["pyros_config_file"] = os.path.join(os.path.abspath(PYROS_DJANGO_BASE_DIR),"../../../config/pyros/", configfile) | |
686 | + | |
687 | + logo_name = configpyros(os.environ["pyros_config_file"]).pyros_config["general"]["logo"] | |
688 | + logo_path = os.path.join(os.path.abspath(PYROS_DJANGO_BASE_DIR),"../../../config/pyros/", logo_name) | |
689 | + media_path = os.path.join(os.path.abspath(PYROS_DJANGO_BASE_DIR),"misc/static/media",logo_name) | |
690 | + shutil.copy(logo_path,media_path) | |
682 | 691 | #if test_mode(): print("in test mode") |
683 | 692 | #if verbose_mode(): print("in verbose mode") |
684 | 693 | if observatory == None or len(observatory) == 0: |
... | ... | @@ -709,8 +718,8 @@ def start(agent:str, configfile:str, observatory:str, unit:str): |
709 | 718 | ''' |
710 | 719 | |
711 | 720 | # add path to pyros_django folder as the config class is supposed to work within this folder |
712 | - #cmd_test_obs_config = f"-c \"from src.core.pyros_django.obsconfig.configpyros import ConfigPyros\nConfigPyros('{os.path.join(PYROS_DJANGO_BASE_DIR,os.environ.get('PATH_TO_OBSCONF_FILE'))}')\"" | |
713 | - cmd_test_obs_config = f"-c \"from src.core.pyros_django.obsconfig.configpyros import ConfigPyros\nConfigPyros('{obs_config_file_path}')\"" | |
721 | + #cmd_test_obs_config = f"-c \"from src.core.pyros_django.obsconfig.obsconfig_class import OBSConfig\nOBSConfig('{os.path.join(PYROS_DJANGO_BASE_DIR,os.environ.get('PATH_TO_OBSCONF_FILE'))}')\"" | |
722 | + cmd_test_obs_config = f"-c \"from src.core.pyros_django.obsconfig.obsconfig_class import OBSConfig\nOBSConfig('{obs_config_file_path}')\"" | |
714 | 723 | if not execProcessFromVenv(cmd_test_obs_config): |
715 | 724 | # Observatory configuration has an issue |
716 | 725 | exit(1) | ... | ... |
src/core/pyros_django/agent/Agent.py
... | ... | @@ -128,7 +128,7 @@ from common.models import AgentSurvey, AgentCmd, AgentLogs |
128 | 128 | |
129 | 129 | #from config.configpyros import ConfigPyros |
130 | 130 | from config.old_config.configpyros import ConfigPyros as ConfigPyrosOld |
131 | -from src.core.pyros_django.obsconfig.configpyros import ConfigPyros | |
131 | +from src.core.pyros_django.obsconfig.obsconfig_class import OBSConfig | |
132 | 132 | |
133 | 133 | #from dashboard.views import get_sunelev |
134 | 134 | #from devices.TelescopeRemoteControlDefault import TelescopeRemoteControlDefault |
... | ... | @@ -460,7 +460,7 @@ class Agent: |
460 | 460 | obs_config_file_path = os.environ["PATH_TO_OBSCONF_FILE"] |
461 | 461 | path_to_obs_config_folder = os.environ["PATH_TO_OBSCONF_FOLDER"] |
462 | 462 | unit = os.environ["unit_name"] |
463 | - oc = ConfigPyros(obs_config_file_path) | |
463 | + oc = OBSConfig(obs_config_file_path) | |
464 | 464 | self.set_config(oc, obs_config_file_path, path_to_obs_config_folder, unit) |
465 | 465 | |
466 | 466 | #self.name = name |
... | ... | @@ -545,7 +545,7 @@ class Agent: |
545 | 545 | |
546 | 546 | |
547 | 547 | |
548 | - def set_config(self, oc: ConfigPyros, obs_config_file_path: str, path_to_obs_config_folder: str, unit: str): | |
548 | + def set_config(self, oc: OBSConfig, obs_config_file_path: str, path_to_obs_config_folder: str, unit: str): | |
549 | 549 | self._oc = { |
550 | 550 | 'config' : oc, |
551 | 551 | 'env' : [ | ... | ... |
src/core/pyros_django/dashboard/views.py
... | ... | @@ -25,10 +25,10 @@ from devices.CameraVISRemoteControlDefault import CameraVISRemoteControlDefault |
25 | 25 | from devices.CameraNIRRemoteControlDefault import CameraNIRRemoteControlDefault |
26 | 26 | from devices import PLC |
27 | 27 | import time,os |
28 | -from obsconfig.configpyros import ConfigPyros | |
28 | +from src.core.pyros_django.obsconfig.obsconfig_class import OBSConfig | |
29 | 29 | from django.conf import settings as pyros_settings |
30 | - | |
31 | 30 | sys.path.append("../../..") |
31 | +from config.pyros.config_pyros import configpyros | |
32 | 32 | #import utils.celme as celme |
33 | 33 | import vendor.guitastro as guitastro |
34 | 34 | |
... | ... | @@ -41,11 +41,13 @@ MAX_LOGS_LINES = 100 |
41 | 41 | log = l.setupLogger("dashboard", "dashboard") |
42 | 42 | |
43 | 43 | def index(request): |
44 | - config = ConfigPyros(os.environ["PATH_TO_OBSCONF_FILE"],os.environ["unit_name"]) | |
44 | + config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"],os.environ["unit_name"]) | |
45 | 45 | observatory_name = config.get_obs_name() |
46 | 46 | unit_name = config.unit_name |
47 | 47 | request.session["obsname"] = observatory_name+" "+unit_name |
48 | - | |
48 | + request.session["pyros_config"] = pyros_settings.CONFIG_PYROS | |
49 | + logo = pyros_settings.CONFIG_PYROS.get("general").get("logo") | |
50 | + request.session["logo"] = "media/"+logo | |
49 | 51 | message = "" |
50 | 52 | if request.user.is_authenticated: |
51 | 53 | if SP_Period_Guest.objects.filter(email=request.user.email): | ... | ... |
59.3 KB
src/core/pyros_django/misc/templates/base.html
... | ... | @@ -40,6 +40,13 @@ |
40 | 40 | |
41 | 41 | <body> |
42 | 42 | <style> |
43 | +:root{ | |
44 | + --main-bg-color: {{ request.session.pyros_config.general.color_theme }} | |
45 | +} | |
46 | +.bg-gradient-primary{ | |
47 | + background-color: var(--main-bg-color); | |
48 | + background-image: None; | |
49 | +} | |
43 | 50 | /* |
44 | 51 | #page-wrapper { |
45 | 52 | width: 80vw; |
... | ... | @@ -125,6 +132,12 @@ a { |
125 | 132 | margin-right:1vw; |
126 | 133 | } |
127 | 134 | |
135 | +#logo_icon{ | |
136 | + height: 4.5vh; | |
137 | + width: 4.5vw; | |
138 | + float:left; | |
139 | + margin-right:1vw; | |
140 | +} | |
128 | 141 | .dropdown_user_actions { |
129 | 142 | overflow: hidden; |
130 | 143 | min-width:8.5vw; |
... | ... | @@ -241,7 +254,11 @@ footer{ |
241 | 254 | <div class="sidebar-brand d-flex align-items-center justify-content-center"> |
242 | 255 | <a class="navbar-brand" href={% url "index" %} style="color: black;"> |
243 | 256 | <img class="sidebar-brand-icon" src="{% static 'media/home.png' %}" id="home_icon"></a> |
244 | - <div class="sidebar-brand-text mx-3">PyROS</div> | |
257 | + {% if request.session.logo %} | |
258 | + <img class="sidebar-brand-icon" src="{% static request.session.logo %}" id="logo_icon"></a> | |
259 | + {% else %} | |
260 | + <div class="sidebar-brand-text mx-3">PyROS</div> | |
261 | + {% endif %} | |
245 | 262 | |
246 | 263 | </div> |
247 | 264 | {% comment %} OLD SIDE MENU |
... | ... | @@ -253,7 +270,7 @@ footer{ |
253 | 270 | <li class="nav-item"><a class="nav-link" href="{% url "current_schedule" %}">Schedule</a></li> |
254 | 271 | {# TODO: TBD #} |
255 | 272 | {% if USER_LEVEL|ifinlist:"Admin,Operator,Unit-PI,Management board member,Observer,TAC" %} |
256 | - <li class="nav-item"><a class="nav-link" href="{% url "requests_list" %}">Request</a></li> | |
273 | + <li class="nav-item"><a class="nav-link" href="{% url "sequences_list" %}">Request</a></li> | |
257 | 274 | <li class="nav-item"><a class="nav-link" href="#">Images</a></li> |
258 | 275 | {% endif %} |
259 | 276 | ... | ... |
src/core/pyros_django/obsconfig/configpyros.py renamed to src/core/pyros_django/obsconfig/obsconfig_class.py
1 | -#!/usr/bin/env python3 | |
2 | -import pykwalify.core | |
3 | -import sys | |
4 | -import yaml, logging, os, pickle, time | |
5 | -from datetime import datetime | |
6 | -from pykwalify.errors import PyKwalifyException,SchemaError | |
7 | -from pathlib import Path | |
8 | - | |
9 | -class ConfigPyros: | |
10 | - # (AKo) : Config file path is checked on the settings file, if the file isn't valid (i.e not found) the error will be launched by the settings file when starting the application | |
11 | - devices_links = {} | |
12 | - current_file = None | |
13 | - #COMPONENT_PATH = os.path.join(os.environ["DJANGO_PATH"],"../../../config/components/") | |
14 | - #GENERIC_DEVICES_PATH = os.path.join(os.environ["DJANGO_PATH"],"../../../config/devices/") | |
15 | - pickle_file = "obsconfig.p" | |
16 | - obs_config = None | |
17 | - devices = None | |
18 | - computers = None | |
19 | - agents = None | |
20 | - obs_config_file_content = None | |
21 | - #obs_config_path = os.environ.get("PATH_TO_OBSCONF_FOLDER",os.path.join(os.environ["DJANGO_PATH"],"../../../privatedev/config/default/")) | |
22 | - errors = None | |
23 | - | |
24 | - def verify_if_pickle_needs_to_be_updated(self, observatory_config_file)->bool: | |
25 | - """ | |
26 | - | |
27 | - Args: | |
28 | - observatory_config_file ([type]): [description] | |
29 | - | |
30 | - Returns: | |
31 | - bool: [description] | |
32 | - """ | |
33 | - self.CONFIG_PATH = os.path.dirname(observatory_config_file)+"/" | |
34 | - self.obs_config_path = self.CONFIG_PATH | |
35 | - #self.CONFIG_PATH = self.obs_config_path | |
36 | - if os.path.isfile(self.CONFIG_PATH+self.pickle_file) == False: | |
37 | - return True | |
38 | - else: | |
39 | - pickle_file_mtime = os.path.getmtime(self.CONFIG_PATH+self.pickle_file) | |
40 | - obs_config_mtime = os.path.getmtime(observatory_config_file) | |
41 | - | |
42 | - obs_config = self.read_and_check_config_file(observatory_config_file) | |
43 | - if obs_config_mtime > pickle_file_mtime: | |
44 | - # create obs file (yaml) from pickle["obsconfig"] with date of pickle within history folder-> nom ficher + année + mois + jour + datetime (avec secondes) -> YYYY/MM/DD H:m:s | |
45 | - pickle_datetime = datetime.utcfromtimestamp(pickle_file_mtime).strftime("%Y%m%d_%H%M%S") | |
46 | - # Create history folder if doesn't exist | |
47 | - Path(self.obs_config_path+"/history/").mkdir(parents=True, exist_ok=True) | |
48 | - file_name = f"{self.obs_config_path}/history/observatory_{pickle_datetime}.yml" | |
49 | - config_file = open(observatory_config_file,"r") | |
50 | - | |
51 | - with open(file_name, 'w') as f: | |
52 | - f.write(config_file.read()) | |
53 | - return True | |
54 | - if obs_config == None: | |
55 | - print(f"Error when trying to read config file (path of config file : {observatory_config_file}") | |
56 | - return -1 | |
57 | - self.obs_config = obs_config | |
58 | - # check last date of modification for devices files | |
59 | - for device in self.obs_config["OBSERVATORY"]["DEVICES"]: | |
60 | - device_file = self.CONFIG_PATH+device["DEVICE"]["file"] | |
61 | - device_file_mtime = os.path.getmtime(device_file) | |
62 | - if device_file_mtime > pickle_file_mtime: | |
63 | - return True | |
64 | - | |
65 | - for computer in self.obs_config["OBSERVATORY"]["COMPUTERS"]: | |
66 | - computer_file = self.CONFIG_PATH+computer["COMPUTER"]["file"] | |
67 | - computer_file_mtime = os.path.getmtime(computer_file) | |
68 | - if computer_file_mtime > pickle_file_mtime: | |
69 | - return True | |
70 | - return False | |
71 | - | |
72 | - def load(self, observatory_config_file): | |
73 | - pickle_needs_to_be_updated = self.verify_if_pickle_needs_to_be_updated(observatory_config_file) | |
74 | - # check if we already read and load devices configuration and if pickle needs to be updated | |
75 | - if pickle_needs_to_be_updated == False and self.devices != None: | |
76 | - return None | |
77 | - else: | |
78 | - if os.path.isfile(self.CONFIG_PATH+self.pickle_file) and pickle_needs_to_be_updated == False: | |
79 | - print("Reading pickle file") | |
80 | - try: | |
81 | - can_pickle_file_be_read = False | |
82 | - while can_pickle_file_be_read != True: | |
83 | - if os.access(self.CONFIG_PATH+self.pickle_file, os.R_OK): | |
84 | - pickle_dict = pickle.load(open(self.CONFIG_PATH+self.pickle_file,"rb")) | |
85 | - can_pickle_file_be_read = True | |
86 | - else: | |
87 | - time.sleep(0.5) | |
88 | - except IOError: | |
89 | - print("Error when reading the pickle file") | |
90 | - try: | |
91 | - self.obs_config = pickle_dict["obs_config"] | |
92 | - self.computers = pickle_dict["computers"] | |
93 | - self.devices = pickle_dict["devices"] | |
94 | - self.devices_links = pickle_dict["devices_links"] | |
95 | - self.obs_config_file_content = pickle_dict["obs_config_file_content"] | |
96 | - self.raw_config = pickle_dict["raw_config"] | |
97 | - except: | |
98 | - # we rewrite the pickle file, the content will be the same otherwise we would be in the else case | |
99 | - print("Rewritting the pickle file (an error occured while reading it, the content will be the same as it was") | |
100 | - pickle_dict = {} | |
101 | - | |
102 | - self.obs_config = self.read_and_check_config_file(observatory_config_file) | |
103 | - obs_file = open(observatory_config_file,"r") | |
104 | - pickle_dict["raw_config"] = obs_file.read() | |
105 | - obs_file.close() | |
106 | - self.raw_config = pickle_dict["raw_config"] | |
107 | - pickle_dict["obs_config"] = self.obs_config | |
108 | - pickle_dict["devices"] = self.get_devices() | |
109 | - pickle_dict["computers"] = self.get_computers() | |
110 | - pickle_dict["devices_links"] = self.devices_links | |
111 | - pickle_dict["obs_config_file_content"] = self.read_and_check_config_file(observatory_config_file) | |
112 | - print("Writing pickle file") | |
113 | - pickle.dump(pickle_dict,open(self.CONFIG_PATH+self.pickle_file,"wb")) | |
114 | - else: | |
115 | - print("Pickle file needs to be created or updated") | |
116 | - pickle_dict = {} | |
117 | - | |
118 | - self.obs_config = self.read_and_check_config_file(observatory_config_file) | |
119 | - pickle_dict["obs_config"] = self.obs_config | |
120 | - pickle_dict["devices"] = self.get_devices() | |
121 | - pickle_dict["computers"] = self.get_computers() | |
122 | - pickle_dict["devices_links"] = self.devices_links | |
123 | - pickle_dict["obs_config_file_content"] = self.read_and_check_config_file(observatory_config_file) | |
124 | - print("Writing pickle file") | |
125 | - pickle.dump(pickle_dict,open(self.CONFIG_PATH+self.pickle_file,"wb")) | |
126 | - | |
127 | - def check_and_return_config(self,yaml_file:str,schema_file:str)->dict: | |
128 | - """ | |
129 | - Check if yaml_file is valid for the schema_file and return an dictionary of the config file | |
130 | - | |
131 | - Args: | |
132 | - yaml_file (str): Path to the config_file to be validated | |
133 | - schema_file (str): Path to the schema file | |
134 | - | |
135 | - Returns: | |
136 | - dict: dictionary of the config file (with values) | |
137 | - """ | |
138 | - # disable pykwalify error to clean the output | |
139 | - #####logging.disable(logging.ERROR) | |
140 | - try: | |
141 | - can_yaml_file_be_read = False | |
142 | - while can_yaml_file_be_read != True: | |
143 | - if os.access(yaml_file, os.R_OK): | |
144 | - can_yaml_file_be_read = True | |
145 | - else: | |
146 | - print(f"{yaml_file} can't be accessed, waiting for availability") | |
147 | - time.sleep(0.5) | |
148 | - | |
149 | - c = pykwalify.core.Core(source_file=yaml_file, schema_files=[self.SCHEMA_PATH+schema_file]) | |
150 | - return c.validate(raise_exception=True) | |
151 | - except SchemaError: | |
152 | - for error in c.errors: | |
153 | - print("Error :",str(error).split(". Path")[0]) | |
154 | - print("Path to error :",error.path) | |
155 | - self.errors = c.errors | |
156 | - return None | |
157 | - except IOError: | |
158 | - print("Error when reading the observatory config file") | |
159 | - | |
160 | - @staticmethod | |
161 | - def check_config(yaml_file:str,schema_file:str)->any: | |
162 | - """ | |
163 | - Check if yaml_file is valid for the schema_file and return a boolean or list of errors according the schema | |
164 | - | |
165 | - Args: | |
166 | - yaml_file (str): Path to the config_file to be validated | |
167 | - schema_file (str): Path to the schema file | |
168 | - | |
169 | - Returns: | |
170 | - any: boolean (True) if the configuration is valid according the schema or a list of error otherwise | |
171 | - """ | |
172 | - # disable pykwalify error to clean the output | |
173 | - ####logging.disable(logging.ERROR) | |
174 | - try: | |
175 | - can_yaml_file_be_read = False | |
176 | - while can_yaml_file_be_read != True: | |
177 | - if os.access(yaml_file, os.R_OK): | |
178 | - can_yaml_file_be_read = True | |
179 | - else: | |
180 | - print(f"{yaml_file} can't be accessed, waiting for availability") | |
181 | - time.sleep(0.5) | |
182 | - | |
183 | - c = pykwalify.core.Core(source_file=yaml_file, schema_files=[schema_file]) | |
184 | - c.validate(raise_exception=True) | |
185 | - return True | |
186 | - except SchemaError: | |
187 | - for error in c.errors: | |
188 | - print("Error :",str(error).split(". Path")[0]) | |
189 | - print("Path to error :",error.path) | |
190 | - | |
191 | - return c.errors | |
192 | - except IOError: | |
193 | - print("Error when reading the observatory config file") | |
194 | - | |
195 | - def read_and_check_config_file(self,yaml_file:str)->dict: | |
196 | - """ | |
197 | - Read the schema key of the config file to retrieve schema name and proceed to the checking of that config file | |
198 | - Call check_and_return_config function and print its return. | |
199 | - | |
200 | - Args: | |
201 | - yaml_file (str): path to the config file | |
202 | - Returns: | |
203 | - dict: Dictionary of the config file (with values) | |
204 | - """ | |
205 | - self.current_file = yaml_file | |
206 | - try: | |
207 | - can_config_file_be_read = False | |
208 | - while can_config_file_be_read != True: | |
209 | - | |
210 | - if os.access(yaml_file, os.R_OK): | |
211 | - can_config_file_be_read = True | |
212 | - else: | |
213 | - print(f"{yaml_file} can't be accessed, waiting for availability") | |
214 | - time.sleep(0.5) | |
215 | - with open(yaml_file, 'r') as stream: | |
216 | - print(f"Reading {yaml_file}") | |
217 | - config_file = yaml.safe_load(stream) | |
218 | - | |
219 | - self.DJANGO_PATH = os.environ.get("DJANGO_PATH",os.path.abspath(os.path.dirname(yaml_file))) | |
220 | - self.SCHEMA_PATH = os.path.join(self.DJANGO_PATH,"../../../config/schemas/") | |
221 | - self.CONFIG_PATH = self.obs_config_path | |
222 | - self.COMPONENT_PATH = os.path.join(self.DJANGO_PATH,"../../../config/components/") | |
223 | - self.GENERIC_DEVICES_PATH = os.path.join(self.DJANGO_PATH,"../../../config/devices/") | |
224 | - result = self.check_and_return_config(yaml_file,config_file["schema"]) | |
225 | - if result == None: | |
226 | - print("Error when reading and validating config file, please check the errors right above") | |
227 | - exit(1) | |
228 | - return result | |
229 | - | |
230 | - except yaml.YAMLError as exc: | |
231 | - print(exc) | |
232 | - except Exception as e: | |
233 | - print(e) | |
234 | - return None | |
235 | - | |
236 | - def read_generic_component_and_return_attributes(self,component_name:str)->dict: | |
237 | - file_path = self.COMPONENT_PATH + component_name + ".yml" | |
238 | - try: | |
239 | - with open(file_path, 'r') as stream: | |
240 | - config_file = yaml.safe_load(stream) | |
241 | - | |
242 | - attributes = {} | |
243 | - for attribute in config_file: | |
244 | - | |
245 | - attribute = attribute["attribute"] | |
246 | - attributes[attribute.pop("key")] = attribute | |
247 | - return attributes | |
248 | - except yaml.YAMLError as exc: | |
249 | - print(exc) | |
250 | - except Exception as e: | |
251 | - print(e) | |
252 | - return None | |
253 | - | |
254 | - def read_capability_of_device(self,capability:dict)->dict: | |
255 | - """ | |
256 | - Read capability of device and inherit attributes from generic component then overwrite attributes defined in device config | |
257 | - | |
258 | - Args: | |
259 | - capability (dict): dictionary containing a capabilitiy (keys : component and attributes) | |
260 | - | |
261 | - Returns: | |
262 | - dict: dictionary of capability inherited by generic component and overwritten his attributes by current attributes of capability | |
263 | - """ | |
264 | - | |
265 | - component_attributes = self.read_generic_component_and_return_attributes(capability["component"]) | |
266 | - | |
267 | - attributes = {} | |
268 | - # get all attributes of device's capability | |
269 | - for attribute in capability["attributes"]: | |
270 | - attribute = attribute["attribute"] | |
271 | - | |
272 | - attributes[attribute.pop("key")] = attribute | |
273 | - | |
274 | - # for each attributes of generic component attributes | |
275 | - for attribute_name in attributes.keys(): | |
276 | - # merge attributes of general component with specified component in device config file | |
277 | - new_attributes = {**component_attributes[attribute_name],**attributes[attribute_name]} | |
278 | - if "is_enum" in component_attributes[attribute_name].keys(): | |
279 | - # make an intersection of both list of values | |
280 | - new_attributes["value"] = list(set(attributes[attribute_name]["value"]) & set(component_attributes[attribute_name]["value"])) | |
281 | - if len(new_attributes["value"]) == 0: | |
282 | - print(f"Value of lastly read device's attribute '{attribute_name}' isn't one of the values of component configuration for this device (component configuration value(s): {component_attributes[attribute_name]['value']}) (actual value : {attributes[attribute_name]['value']})") | |
283 | - exit(1) | |
284 | - component_attributes[attribute_name] = new_attributes | |
285 | - | |
286 | - # return inherited and overwritten attributes of capability | |
287 | - capability["attributes"] = component_attributes | |
288 | - return capability | |
289 | - | |
290 | - def get_devices_names_and_file(self) -> dict: | |
291 | - """ | |
292 | - Return a dictionary giving the device file name by the device name | |
293 | - Returns: | |
294 | - dict: key is device name, value is file name | |
295 | - """ | |
296 | - devices_names_and_files = {} | |
297 | - for device in self.obs_config["OBSERVATORY"]["DEVICES"]: | |
298 | - device = device["DEVICE"] | |
299 | - | |
300 | - devices_names_and_files[device["name"]] = device["file"] | |
301 | - return devices_names_and_files | |
302 | - | |
303 | - def read_device_config_file(self,config_file_name:str,is_generic=False)->dict: | |
304 | - """ | |
305 | - Read the device config file, inherit generic device config if "generic" key is present in "DEVICE". | |
306 | - Associate capabilities of attached_devices if this device has attached_devices. | |
307 | - Inherit capabilities from generic component and overwritte attributes defined in the device config | |
308 | - Args: | |
309 | - config_file_name (str): file name to be read | |
310 | - is_generic (bool, optional): tells if we're reading a generic configuration (is_generic =True) or not (is_generic = False). Defaults to False. | |
311 | - | |
312 | - Returns: | |
313 | - dict: formatted device configuration (attributes, capabilities...) | |
314 | - """ | |
315 | - self.current_file = config_file_name | |
316 | - devices_name_and_file = self.get_devices_names_and_file() | |
317 | - if not is_generic: | |
318 | - # check if device config file is listed in observatory configuration | |
319 | - current_device_name = [device for device,file_name in devices_name_and_file.items() if file_name in config_file_name[len(self.CONFIG_PATH):] ] | |
320 | - if len(current_device_name) <= 0: | |
321 | - print(f"Current file '{config_file_name[len(self.CONFIG_PATH):]}' isn't listed in observatory configuration") | |
322 | - print("The devices names and files are: ") | |
323 | - for device_name,file_name in devices_name_and_file.items(): | |
324 | - print(f"device name: '{device_name}', device filename: '{file_name}'") | |
325 | - exit(1) | |
326 | - print(f"Reading {config_file_name}") | |
327 | - try: | |
328 | - with open(config_file_name, 'r') as stream: | |
329 | - config_file = yaml.safe_load(stream) | |
330 | - # if we're reading a generic device configuration, the path to get the schema is different than usual | |
331 | - self.SCHEMA_PATH =os.path.join(self.DJANGO_PATH,"../../../config/schemas/") | |
332 | - | |
333 | - # read and verify that the device configuration match the schema | |
334 | - result = self.check_and_return_config(config_file_name,config_file["schema"]) | |
335 | - # if the configuration didn't match the schema or had an error when reading the file | |
336 | - if result == None: | |
337 | - print("Error when reading and validating config file, please check the errors right above") | |
338 | - exit(1) | |
339 | - else: | |
340 | - # the configuration is valid | |
341 | - # storing DEVICE key in device (DEVICE can contains : attributes of device, capabilities, attached devices) | |
342 | - device = result["DEVICE"] | |
343 | - generic_device_config = None | |
344 | - # if the device is associated to an generic configuration, we'll read that generic configuration to inherit his attributes | |
345 | - if "generic" in device: | |
346 | - # storing the whole current config | |
347 | - current_config = result | |
348 | - # read and get the generic device config | |
349 | - generic_device_config = self.read_device_config_file(self.GENERIC_DEVICES_PATH+device["generic"],is_generic=True) | |
350 | - # merge whole device config but we need to merge capabilities differently after | |
351 | - new_config = {**generic_device_config["DEVICE"],**current_config["DEVICE"]} | |
352 | - result["DEVICE"] = new_config | |
353 | - | |
354 | - # device has capabilities | |
355 | - if "CAPABILITIES" in device: | |
356 | - capabilities = [] | |
357 | - # if the device is associated to a generic device we need to associate his capabilities with the generic capabilities and overwrite them | |
358 | - if generic_device_config != None: | |
359 | - # we're making a copy of generic device config so we can remove items during loop | |
360 | - copy_generic_device_config = generic_device_config.copy() | |
361 | - # We have to extend capabilities of generic device configuration | |
362 | - for capability in current_config["DEVICE"]["CAPABILITIES"]: | |
363 | - is_capability_in_generic_config = False | |
364 | - current_config_capability = capability["CAPABILITY"] | |
365 | - current_config_component = current_config_capability["component"] | |
366 | - # find if this component was defined in generic_device_config | |
367 | - for index,generic_config_capability in enumerate(generic_device_config["DEVICE"]["CAPABILITIES"]): | |
368 | - # if the current capability is the capability of the component we're looking for | |
369 | - if current_config_component == generic_config_capability["component"]: | |
370 | - is_capability_in_generic_config = True | |
371 | - # we're merging their attributes | |
372 | - new_attributes = generic_config_capability["attributes"].copy() | |
373 | - attributes = {} | |
374 | - current_config_attributes = current_config_capability["attributes"] | |
375 | - generic_config_attributes = generic_config_capability["attributes"] | |
376 | - for attribute in current_config_attributes: | |
377 | - attribute = attribute["attribute"] | |
378 | - attributes[attribute.pop("key")] = attribute | |
379 | - # for each attributes of device component attributes | |
380 | - for attribute_name in attributes.keys(): | |
381 | - # merge attributes of general component with specified component in device config file | |
382 | - new_attributes[attribute_name] = {**generic_config_attributes[attribute_name],**attributes[attribute_name]} | |
383 | - if "is_enum" in generic_config_attributes[attribute_name].keys(): | |
384 | - # make an intersection of both list of values | |
385 | - new_attributes[attribute_name]["value"] = list(set(attributes[attribute_name]["value"]) & set(generic_config_attributes[attribute_name]["value"])) | |
386 | - if len(new_attributes[attribute_name]["value"]) == 0: | |
387 | - print(f"Value of device '{config_file_name}' for attribute '{attribute_name}' isn't one of the values of generic configuration for this device (generic value(s): {generic_config_attributes[attribute_name]['value']}) (actual value : {attributes[attribute_name]['value']})") | |
388 | - exit(1) | |
389 | - # removing this capability from generic device configuration | |
390 | - generic_device_config["DEVICE"]["CAPABILITIES"].pop(index) | |
391 | - capabilities.append({"component": current_config_component,"attributes":new_attributes}) | |
392 | - break | |
393 | - if is_capability_in_generic_config == False: | |
394 | - current_config_capability = self.read_capability_of_device(current_config_capability) | |
395 | - # the component defined in the current_config isn't defined in generic config (should not happen but we'll deal with that case anyway) : we're simply adding this capability | |
396 | - capabilities.append(current_config_capability) | |
397 | - # looping through generic device config's capabilities in order to add them to current device configuration | |
398 | - for generic_config_capability in generic_device_config["DEVICE"]["CAPABILITIES"]: | |
399 | - capabilities.append(generic_config_capability) | |
400 | - else: | |
401 | - # device not associated to a generic device configuration | |
402 | - for capability in device["CAPABILITIES"]: | |
403 | - capability = capability["CAPABILITY"] | |
404 | - capabilities.append(self.read_capability_of_device(capability)) | |
405 | - device["CAPABILITIES"] = capabilities | |
406 | - # associate capabilities to final device configuration (stored in result variable) | |
407 | - result["DEVICE"]["CAPABILITIES"] = device["CAPABILITIES"] | |
408 | - if "ATTACHED_DEVICES" in device.keys(): | |
409 | - # device has attached devices, we need to read their configuration in order to get their capabilities and add them to the current device | |
410 | - devices_name_and_file = self.get_devices_names_and_file() | |
411 | - active_devices = self.get_active_devices() | |
412 | - for attached_device in device["ATTACHED_DEVICES"]: | |
413 | - is_attached_device_link_to_agent = False | |
414 | - # we're looking for if the attached device is associated to an agent (i.e. the device is considered as 'active'). However an attached_device shoudn't be active | |
415 | - for active_device in active_devices: | |
416 | - if devices_name_and_file[active_device] == attached_device["file"]: | |
417 | - # the attached device is an active device (so it's linked to an agent) | |
418 | - is_attached_device_link_to_agent = True | |
419 | - break | |
420 | - if self.CONFIG_PATH+attached_device["file"] != config_file_name and not is_attached_device_link_to_agent: | |
421 | - # if the attached device isn't the device itself and not active | |
422 | - | |
423 | - # get configuration of attached device | |
424 | - config_of_attached_device = self.read_device_config_file(self.CONFIG_PATH+attached_device["file"]) | |
425 | - capabilities_of_attached_device = None | |
426 | - if "CAPABILITIES" in config_of_attached_device["DEVICE"].keys(): | |
427 | - capabilities_of_attached_device = config_of_attached_device["DEVICE"]["CAPABILITIES"] | |
428 | - if capabilities_of_attached_device != None: | |
429 | - # get name of device corresponding to the config file name | |
430 | - parent_device_name = [device for device,file_name in devices_name_and_file.items() if file_name == config_file_name[len(self.CONFIG_PATH):]] | |
431 | - attached_device_name = [device for device,file_name in devices_name_and_file.items() if file_name == attached_device["file"]] | |
432 | - if len(parent_device_name) > 0 : | |
433 | - parent_device_name = parent_device_name[0] | |
434 | - else: | |
435 | - print(f"Attached device filename '{config_file_name[len(self.CONFIG_PATH):]}' is not listed in observatory devices files names") | |
436 | - print("The devices names and files are: ") | |
437 | - for device_name,file_name in devices_name_and_file.items(): | |
438 | - print(f"device name: '{device_name}', device filename: '{file_name}'") | |
439 | - exit(1) | |
440 | - if len(attached_device_name) > 0 : | |
441 | - attached_device_name = attached_device_name[0] | |
442 | - else: | |
443 | - | |
444 | - print(f"Attached device filename '{attached_device['file']}' is not listed in observatory devices files names") | |
445 | - print("The devices names and files are: ") | |
446 | - for device_name,file_name in devices_name_and_file.items(): | |
447 | - print(f"device name: '{device_name}', device filename: '{file_name}'") | |
448 | - exit(1) | |
449 | - # associate attached device to his 'parent' device (parent device is the currently read device) | |
450 | - self.devices_links[attached_device_name] = parent_device_name | |
451 | - for capability in capabilities_of_attached_device: | |
452 | - # add capabilities of attached device to current device | |
453 | - result["DEVICE"]["CAPABILITIES"].append(capability) | |
454 | - return result | |
455 | - except yaml.YAMLError as exc: | |
456 | - print(exc) | |
457 | - except Exception as e: | |
458 | - print(e) | |
459 | - exit(1) | |
460 | - #return None | |
461 | - | |
462 | - def __init__(self, observatory_config_file:str, unit_name:str="") -> None: | |
463 | - """ | |
464 | - Initiate class with the config file | |
465 | - set content attribute to a dictionary containing all values from the config file | |
466 | - | |
467 | - Args: | |
468 | - config_file_name (str): path to the config file | |
469 | - """ | |
470 | - self.load(observatory_config_file) | |
471 | - if unit_name == "": | |
472 | - # By default we will use the first unit | |
473 | - self.unit_name = self.get_units_name()[0] | |
474 | - else: | |
475 | - self.unit_name = unit_name | |
476 | - | |
477 | - def get_obs_name(self) -> str: | |
478 | - """ | |
479 | - Return name of the observatory | |
480 | - | |
481 | - Returns: | |
482 | - str: Name of the observatory | |
483 | - """ | |
484 | - return self.obs_config["OBSERVATORY"]["name"] | |
485 | - | |
486 | - def get_channels(self, unit_name: str) -> dict: | |
487 | - | |
488 | - """ | |
489 | - return dictionary of channels | |
490 | - | |
491 | - Args: | |
492 | - unit_name (str): Name of the unit | |
493 | - Returns: | |
494 | - dict: [description] | |
495 | - """ | |
496 | - unit = self.get_unit_by_name(unit_name) | |
497 | - channels = {} | |
498 | - | |
499 | - for channel_id in range(len(unit["TOPOLOGY"]["CHANNELS"])): | |
500 | - channel = unit["TOPOLOGY"]["CHANNELS"][channel_id]["CHANNEL"] | |
501 | - channels[channel["name"]] = channel | |
502 | - return channels | |
503 | - | |
504 | - def get_computers(self) -> dict: | |
505 | - """ | |
506 | - return dictionary of computers | |
507 | - | |
508 | - Returns: | |
509 | - dict: [description] | |
510 | - """ | |
511 | - if self.computers != None: | |
512 | - return self.computers | |
513 | - else: | |
514 | - computers = {} | |
515 | - for computer_id in range(len(self.obs_config["OBSERVATORY"]["COMPUTERS"])): | |
516 | - computer = self.obs_config["OBSERVATORY"]["COMPUTERS"][computer_id]["COMPUTER"] | |
517 | - if( "file" in computer.keys() ): | |
518 | - computer["computer_config"]= self.read_and_check_config_file(self.CONFIG_PATH+computer["file"])["COMPUTER"] | |
519 | - computers[computer["name"]] = computer | |
520 | - return computers | |
521 | - | |
522 | - def get_devices(self) -> dict: | |
523 | - """ | |
524 | - return dictionary of devices | |
525 | - | |
526 | - Returns: | |
527 | - dict: [description] | |
528 | - """ | |
529 | - if self.devices != None: | |
530 | - return self.devices | |
531 | - | |
532 | - else: | |
533 | - devices = {} | |
534 | - for device_id in range(len(self.obs_config["OBSERVATORY"]["DEVICES"])): | |
535 | - device = self.obs_config["OBSERVATORY"]["DEVICES"][device_id]["DEVICE"] | |
536 | - if( "file" in device.keys() ): | |
537 | - device["device_config"] = self.read_device_config_file(self.CONFIG_PATH+device["file"])["DEVICE"] | |
538 | - devices[device["name"]] = device | |
539 | - return devices | |
540 | - | |
541 | - def get_devices_names(self)->list: | |
542 | - """Return the list of devices names of an observatory | |
543 | - | |
544 | - Returns: | |
545 | - list: list of names of devices | |
546 | - """ | |
547 | - devices_names = [] | |
548 | - for device in self.obs_config["OBSERVATORY"]["DEVICES"]: | |
549 | - devices_names.append(device["DEVICE"]["name"]) | |
550 | - return devices_names | |
551 | - | |
552 | - def get_agents(self,unit_name)->dict: | |
553 | - """ | |
554 | - return dictionary of agents | |
555 | - | |
556 | - Args: | |
557 | - unit_name (str): name of the unit | |
558 | - | |
559 | - Returns: | |
560 | - dict: dictionary of agents. For each agents tell the name, computer, device, protocole, etc... | |
561 | - """ | |
562 | - unit = self.get_unit_by_name(unit_name) | |
563 | - if self.agents != None: | |
564 | - return self.agents | |
565 | - else: | |
566 | - agents = {} | |
567 | - | |
568 | - | |
569 | - for agent_id in range(len(unit["AGENTS"])): | |
570 | - # Agents is a list containing dictionary that have only one key | |
571 | - key = list(unit["AGENTS"][agent_id].keys())[0] | |
572 | - agent = unit["AGENTS"][agent_id][key] | |
573 | - agents[agent["name"]] = agent | |
574 | - return agents | |
575 | - | |
576 | - def get_layouts(self, unit_name: str) -> dict: | |
577 | - """ | |
578 | - Return dictionary of layouts | |
579 | - | |
580 | - Args: | |
581 | - unit_name (str): name of the unit | |
582 | - Returns: | |
583 | - dict: dictionary of layouts | |
584 | - """ | |
585 | - unit = self.get_unit_by_name(unit_name) | |
586 | - info = {} | |
587 | - info["layouts"] = {} | |
588 | - for layout_id in range(len(unit["TOPOLOGY"]["LAYOUTS"])): | |
589 | - layout = unit["TOPOLOGY"]["LAYOUTS"][layout_id]["LAYOUT"] | |
590 | - info["layouts"][layout["name"]] = layout | |
591 | - return info | |
592 | - | |
593 | - def get_albums(self,unit_name : str) -> dict: | |
594 | - """ | |
595 | - Return dictionary of layouts | |
596 | - | |
597 | - Args: | |
598 | - unit_name (str): name of the unit | |
599 | - Returns: | |
600 | - dict: dictionary of layouts | |
601 | - """ | |
602 | - unit = self.get_unit_by_name(unit_name) | |
603 | - info = {} | |
604 | - info["albums"] = {} | |
605 | - for album_id in range(len(unit["TOPOLOGY"]["ALBUMS"])): | |
606 | - album = unit["TOPOLOGY"]["ALBUMS"][album_id]["ALBUM"] | |
607 | - info["albums"][album["name"]] = album | |
608 | - return info | |
609 | - | |
610 | - def get_channel_information(self, unit_name: str, channel_name: str) -> dict: | |
611 | - """ | |
612 | - Return information of the given channel name of a unit | |
613 | - | |
614 | - Args: | |
615 | - unit_name (str): Name of the unit | |
616 | - channel_name (str): name of the channel | |
617 | - | |
618 | - Returns: | |
619 | - dict: dictionary containing all values that define this channel | |
620 | - """ | |
621 | - channels = self.get_channels(unit_name) | |
622 | - return channels[channel_name] | |
623 | - | |
624 | - def get_topology(self, unit_name:str)->dict: | |
625 | - """ | |
626 | - Return dictionary of the topology of the observatory | |
627 | - | |
628 | - Args: | |
629 | - unit_name (str): Name of the unit | |
630 | - Returns: | |
631 | - dict: dictionary representing the topology of an unit (security, mount, channels, layouts, albums) | |
632 | - """ | |
633 | - unit = self.get_unit_by_name(unit_name) | |
634 | - topology = {} | |
635 | - for key in unit["TOPOLOGY"].keys(): | |
636 | - branch = unit["TOPOLOGY"][key] | |
637 | - if key == "CHANNELS": | |
638 | - topology[key] = self.get_channels(unit_name) | |
639 | - elif key == "LAYOUTS": | |
640 | - topology[key] = self.get_layouts(unit_name) | |
641 | - elif key == "ALBUMS": | |
642 | - topology[key] = self.get_albums(unit_name) | |
643 | - else: | |
644 | - topology[key] = branch | |
645 | - return topology | |
646 | - | |
647 | - def get_active_agents(self, unit_name: str) -> list: | |
648 | - """ | |
649 | - Return the list of active agents (i.e. agents that have an association with a device) | |
650 | - | |
651 | - Args: | |
652 | - unit_name (str): Name of the unit | |
653 | - | |
654 | - Returns: | |
655 | - list: kist of the name of active agents | |
656 | - """ | |
657 | - return list(self.get_agents(unit_name).keys()) | |
658 | - | |
659 | - def get_units(self)->dict: | |
660 | - """ | |
661 | - Return all units sort by name defined in the config file | |
662 | - | |
663 | - Returns: | |
664 | - dict: dictionary giving for a unit_name, his content (name,database,topology,agents,...) | |
665 | - """ | |
666 | - result = {} | |
667 | - units = self.obs_config["OBSERVATORY"]["UNITS"] | |
668 | - for unit in units: | |
669 | - unit = unit["UNIT"] | |
670 | - result[unit["name"]] = unit | |
671 | - return result | |
672 | - | |
673 | - def get_components_agents(self, unit_name: str) -> dict: | |
674 | - """ | |
675 | - Return dictionary of component_agents of the given unit | |
676 | - | |
677 | - Args: | |
678 | - unit_name (str): Name of the unit | |
679 | - | |
680 | - Returns: | |
681 | - dict: dictionary sort by component name giving the associated agent (agent name) | |
682 | - """ | |
683 | - components_agents = {} | |
684 | - topology = self.get_topology(unit_name) | |
685 | - for element in topology: | |
686 | - if element in ("SECURITY","MOUNT","CHANNELS"): | |
687 | - if(element != "CHANNELS"): | |
688 | - for component_agent in topology[element]["COMPONENT_AGENTS"]: | |
689 | - component_name = list(component_agent.keys())[0] | |
690 | - components_agents[component_name] = component_agent[component_name] | |
691 | - else: | |
692 | - for channel in topology[element]: | |
693 | - for component_agent in topology[element][channel]["COMPONENT_AGENTS"]: | |
694 | - component_name = list(component_agent.keys())[0] | |
695 | - components_agents[component_name] = component_agent[component_name] | |
696 | - return components_agents | |
697 | - | |
698 | - def get_units_name(self)->list: | |
699 | - """ | |
700 | - Return list of units names | |
701 | - | |
702 | - Returns: | |
703 | - [list]: names of units | |
704 | - """ | |
705 | - return list(self.get_units().keys()) | |
706 | - | |
707 | - def get_unit_by_name(self, name: str) -> dict: | |
708 | - """ | |
709 | - Return dictionary containing definition of the unit that matches the given name | |
710 | - | |
711 | - Args: | |
712 | - name (str): name of the unit | |
713 | - | |
714 | - Returns: | |
715 | - dict: dictonary representing the unit | |
716 | - """ | |
717 | - return self.get_units()[name] | |
718 | - | |
719 | - def get_agents_per_computer(self, unit_name: str) -> dict: | |
720 | - """ | |
721 | - Return dictionary that give for each computer, what are the associated agents to it as a list | |
722 | - | |
723 | - Args: | |
724 | - unit_name (str): Name of the unit | |
725 | - | |
726 | - Returns: | |
727 | - dict: dictionary that give for each computer, what are the associated agents to it as a list | |
728 | - | |
729 | - """ | |
730 | - agents_per_computer = {} | |
731 | - agents = self.get_agents(unit_name) | |
732 | - for agent in agents: | |
733 | - computer_name = agents[agent]["computer"] | |
734 | - if(agents[agent]["computer"] not in agents_per_computer.keys()): | |
735 | - agents_per_computer[computer_name] = [agent] | |
736 | - else: | |
737 | - agents_per_computer[computer_name].append(agent) | |
738 | - return agents_per_computer | |
739 | - | |
740 | - | |
741 | - def get_agents_per_device(self,unit_name:str)->dict: | |
742 | - """ | |
743 | - Return dictionary that give for each device, what are the associated agents to it as a list | |
744 | - | |
745 | - Args: | |
746 | - unit_name (str): Name of the unit | |
747 | - | |
748 | - Returns: | |
749 | - dict: dictionary that give for each device, what are the associated agents to it as a list | |
750 | - | |
751 | - """ | |
752 | - agents_per_device = {} | |
753 | - agents = self.get_agents(unit_name) | |
754 | - for agent in agents: | |
755 | - if("device" in agents[agent].keys()): | |
756 | - device_name = agents[agent]["device"] | |
757 | - if device_name in self.get_devices_names(): | |
758 | - if(agents[agent]["device"] not in agents_per_device.keys()): | |
759 | - agents_per_device[device_name] = [agent] | |
760 | - else: | |
761 | - agents_per_device[device_name].append(agent) | |
762 | - else: | |
763 | - print(f"Error: device name '{device_name}' for agent '{agent}' is not known in the configuration file. The device name must match one of the names defined in the DEVICES section") | |
764 | - exit(1) | |
765 | - return agents_per_device | |
766 | - | |
767 | - def get_active_devices(self)->list: | |
768 | - """ | |
769 | - Return a list of active device names | |
770 | - | |
771 | - Returns: | |
772 | - list: list of active device names | |
773 | - """ | |
774 | - active_devices = [] | |
775 | - for unit_name in self.get_units(): | |
776 | - for device in self.get_agents_per_device(unit_name): | |
777 | - active_devices.append(device) | |
778 | - return active_devices | |
779 | - | |
780 | - def get_active_computers(self)->list: | |
781 | - """ | |
782 | - Return a list of active computer names | |
783 | - | |
784 | - Returns: | |
785 | - list: list of active computer names | |
786 | - """ | |
787 | - active_computers = [] | |
788 | - for unit_name in self.get_units(): | |
789 | - unit = self.get_unit_by_name(unit_name) | |
790 | - for computer in self.get_agents_per_computer(unit_name): | |
791 | - active_computers.append(computer) | |
792 | - return active_computers | |
793 | - | |
794 | - def get_agent_information(self,unit_name:str,agent_name:str)->dict: | |
795 | - """ | |
796 | - Give the dictionary of attributes of the agent for an unit. | |
797 | - | |
798 | - Args: | |
799 | - unit (dict): dictonary representing the unit | |
800 | - agent_name (str): agent name | |
801 | - | |
802 | - Returns: | |
803 | - dict: dictionary containing attributes of the agent | |
804 | - """ | |
805 | - return self.get_agents(unit_name)[agent_name] | |
806 | - | |
807 | - def get_device_information(self,device_name:str)->dict: | |
808 | - """ | |
809 | - Give the dictionary of the attributes of the device | |
810 | - | |
811 | - Args: | |
812 | - device_name (str): device name | |
813 | - | |
814 | - Returns: | |
815 | - dict: dictionary containing attributes of the device | |
816 | - """ | |
817 | - return self.get_devices()[device_name] | |
818 | - | |
819 | - def get_database_for_unit(self, unit_name: str) -> dict: | |
820 | - """ | |
821 | - Return dictionary of attributes of the database for an unit | |
822 | - | |
823 | - Args: | |
824 | - unit_name (str): unit name | |
825 | - | |
826 | - Returns: | |
827 | - dict: dictionary of attributes of the database for an unit | |
828 | - """ | |
829 | - return self.get_unit_by_name(unit_name)["DATABASE"] | |
830 | - | |
831 | - def get_device_for_agent(self, unit_name: str, agent_name: str) -> str: | |
832 | - """ | |
833 | - Return device name associated to the agent | |
834 | - | |
835 | - Args: | |
836 | - unit (dict): dictonary representing the unit | |
837 | - agent_name (str): agent name | |
838 | - | |
839 | - Returns: | |
840 | - str: device name associated to this agent | |
841 | - """ | |
842 | - agents_per_device = self.get_agents_per_device(unit_name) | |
843 | - for device in agents_per_device: | |
844 | - if agent_name in agents_per_device[device]: | |
845 | - return self.get_device_information(device) | |
846 | - | |
847 | - def get_unit_of_computer(self, computer_name: str) -> str: | |
848 | - """ | |
849 | - Return the name of the unit where the computer is used | |
850 | - | |
851 | - Args: | |
852 | - computer_name (str): computer name | |
853 | - | |
854 | - Returns: | |
855 | - str: unit name | |
856 | - """ | |
857 | - for unit_name in self.get_units(): | |
858 | - if(computer_name in self.get_agents_per_computer(unit_name)): | |
859 | - return unit_name | |
860 | - | |
861 | - def get_unit_of_device(self, device_name:str)->str: | |
862 | - """ | |
863 | - Return the name of the unit where the device is used | |
864 | - | |
865 | - Args: | |
866 | - device_name (str): device name | |
867 | - | |
868 | - Returns: | |
869 | - str: unit name | |
870 | - """ | |
871 | - for unit_name in self.get_units(): | |
872 | - if(device_name in self.get_agents_per_device(unit_name)): | |
873 | - return unit_name | |
874 | - | |
875 | - def get_device_power(self,device_name:str)->dict: | |
876 | - """ | |
877 | - Return dictionary that contains informations about power if this information is present in the device config file | |
878 | - | |
879 | - Return None if this information isn't stored in device's config file | |
880 | - Args: | |
881 | - device_name (str): name of the device | |
882 | - | |
883 | - Returns: | |
884 | - dict: informations about power of device | |
885 | - """ | |
886 | - return self.get_devices()[device_name]["device_config"].get("power") | |
887 | - | |
888 | - | |
889 | - def get_device_capabilities(self, device_name:str)->list: | |
890 | - """ | |
891 | - Return dictionary that contains informations about capabilities if this information is present in the device config file | |
892 | - | |
893 | - Return empty list if this information isn't stored in device's config file | |
894 | - Args: | |
895 | - device_name (str): name of the device | |
896 | - | |
897 | - Returns: | |
898 | - list: list of capabilities of device | |
899 | - """ | |
900 | - list_of_capabilities = [] | |
901 | - capabilities = self.get_devices()[device_name]["device_config"].get("CAPABILITIES") | |
902 | - return capabilities | |
903 | - | |
904 | - def get_device_connector(self,device_name:str)->dict: | |
905 | - """ | |
906 | - Return dictionary that contains informations about connector if this information is present in the device config file | |
907 | - | |
908 | - Return None if this information isn't stored in device's config file | |
909 | - Args: | |
910 | - device_name (str): name of the device | |
911 | - | |
912 | - Returns: | |
913 | - dict: informations about connector of device | |
914 | - """ | |
915 | - return self.get_devices()[device_name]["device_config"].get("connector") | |
916 | - | |
917 | - def get_computer_power(self, computer_name: str) -> dict: | |
918 | - """ | |
919 | - Return dictionary that contains informations about power if this information is present in the device config file | |
920 | - | |
921 | - Return None if this information isn't stored in device's config file | |
922 | - Args: | |
923 | - device_name (str): name of the device | |
924 | - | |
925 | - Returns: | |
926 | - dict: informations about connector of device | |
927 | - """ | |
928 | - return self.get_computers()[computer_name]["computer_config"].get("power") | |
929 | - | |
930 | - def getDeviceControllerNameForAgent(self,unit_name:str,agent_name:str)->tuple: | |
931 | - agent = self.get_agent_information(unit_name,agent_name) | |
932 | - return (agent["device"],"DeviceController"+agent["device"]) | |
933 | - | |
934 | - def getDeviceConfigForDeviceController(self,device_name:str)->dict: | |
935 | - return self.get_devices()[device_name]["device_config"] | |
936 | - | |
937 | - def getCommParamsForAgentDevice(self,unit_name:str,agent_name:str)->tuple: | |
938 | - agent = self.get_agent_information(unit_name,agent_name) | |
939 | - device_config = self.getDeviceConfigForDeviceController(agent["device"]) | |
940 | - comm_access = agent.get("comm_access",None) | |
941 | - comm = device_config["comm"] | |
942 | - return (comm_access,comm) | |
943 | - | |
944 | - def getChannelCapabilities(self,unit_name:str,channel_name:str)->list: | |
945 | - channel = self.get_channel_information(unit_name,channel_name) | |
946 | - result = [] | |
947 | - for component_agent in channel["COMPONENT_AGENTS"]: | |
948 | - component = list(component_agent.keys())[0] | |
949 | - agent = component_agent[component] | |
950 | - device = self.getDeviceControllerNameForAgent(unit_name,agent)[0] | |
951 | - device_capabilities = self.get_device_capabilities(device) | |
952 | - for capability in device_capabilities: | |
953 | - if capability["component"] == component: | |
954 | - result.append(capability) | |
955 | - return result | |
956 | - | |
957 | - def getEditableAttributesOfCapability(self,capability:dict)->dict: | |
958 | - editable_fields = {} | |
959 | - attributes = capability.get("attributes") | |
960 | - for attribute in attributes: | |
961 | - if attributes[attribute]["is_editable"]: | |
962 | - editable_fields[attribute] = attributes[attribute] | |
963 | - return editable_fields | |
964 | - | |
965 | - def getUneditableAttributesOfCapability(self,capability:dict)->dict: | |
966 | - uneditable_fields = {} | |
967 | - attributes = capability.get("attributes") | |
968 | - for attribute in attributes: | |
969 | - if attributes[attribute]["is_editable"] == False: | |
970 | - uneditable_fields[attribute] = attributes[attribute] | |
971 | - return uneditable_fields | |
972 | - | |
973 | - def getEditableAttributesOfChannel(self,unit_name:str,channel_name:str)->list: | |
974 | - capabilities = self.getChannelCapabilities(unit_name,channel_name) | |
975 | - # merged_result = {} | |
976 | - # for capability in capabilities: | |
977 | - # merged_result[capability["component"]] = self.getEditableAttributesOfCapability(capability) | |
978 | - # return merged_result | |
979 | - merged_result = [] | |
980 | - for capability in capabilities: | |
981 | - attributes = self.getEditableAttributesOfCapability(capability) | |
982 | - if len(attributes.keys()) > 0: | |
983 | - merged_result.append(attributes) | |
984 | - return merged_result | |
985 | - | |
986 | - def getUneditableAttributesOfChannel(self,unit_name:str,channel_name:str)->list: | |
987 | - capabilities = self.getChannelCapabilities(unit_name,channel_name) | |
988 | - # merged_result = {} | |
989 | - # for capability in capabilities: | |
990 | - # merged_result[capability["component"]] = self.getEditableAttributesOfCapability(capability) | |
991 | - # return merged_result | |
992 | - merged_result = [] | |
993 | - for capability in capabilities: | |
994 | - attributes = self.getUneditableAttributesOfCapability(capability) | |
995 | - if len(attributes.keys()) > 0: | |
996 | - merged_result.append(attributes) | |
997 | - return merged_result | |
998 | - | |
999 | - | |
1000 | - # def getLogicOfChannelGroups(self,unit_name): | |
1001 | - # return self.get_layouts(unit_name)["global_group_logic"] | |
1002 | - | |
1003 | - | |
1004 | - def getLayoutByName(self, unit_name:str, name_of_layout): | |
1005 | - return self.get_layouts(unit_name)["layouts"][name_of_layout] | |
1006 | - | |
1007 | - def getAlbumByName(self,unit_name : str, name_of_album): | |
1008 | - return self.get_albums(unit_name)["albums"][name_of_album] | |
1009 | - | |
1010 | - def getEditableAttributesOfMount(self,unit_name): | |
1011 | - capabilities = self.get_device_capabilities(self.get_device_for_agent(unit_name,"mount")["name"]) | |
1012 | - merged_result = [] | |
1013 | - for capability in capabilities: | |
1014 | - attributes = self.getEditableAttributesOfCapability(capability) | |
1015 | - if len(attributes.keys()) > 0: | |
1016 | - merged_result.append(attributes) | |
1017 | - return merged_result | |
1018 | - | |
1019 | - | |
1020 | - def getHorizonLine(self, unit_name): | |
1021 | - horizon = self.get_unit_by_name(unit_name).get("horizon") | |
1022 | - return horizon.get("line") | |
1023 | - | |
1024 | -def main(): | |
1025 | - # config = ConfigPyros("../../../../privatedev/config/guitalens/observatory_guitalens.yml") | |
1026 | - # unit_name = config.get_units_name()[0] | |
1027 | - # dc = config.getDeviceControllerNameForAgent(unit_name,"mount")[0] | |
1028 | - #print(config.getDeviceConfigForDeviceController(dc)) | |
1029 | - #print(config.getCommParamsForAgentDevice(unit_name,"mount")) | |
1030 | - # print(config.getChannelCapabilities(unit_name,"OpticalChannel_up")) | |
1031 | - # print(config.get_channel_groups(unit_name)) | |
1032 | - # print(config.getEditableAttributesOfCapability(config.getChannelCapabilities(unit_name,"OpticalChannel_up")[0])) | |
1033 | - # print(config.getEditableAttributesOfChannel(unit_name,"OpticalChannel_up")) | |
1034 | - config = ConfigPyros("../../../../privatedev/config/tnc/observatory_tnc.yml") | |
1035 | - unit_name = config.get_units_name()[0] | |
1036 | - #dc = config.getDeviceControllerNameForAgent(unit_name,"mount")[0] | |
1037 | - #print(config.getDeviceConfigForDeviceController(dc)) | |
1038 | - #print(config.getCommParamsForAgentDevice(unit_name,"mount")) | |
1039 | - # print(config.getChannelCapabilities(unit_name,"OpticalChannel_down2")) | |
1040 | - # print(config.get_channel_groups(unit_name)) | |
1041 | - # print(config.getEditableAttributesOfCapability(config.getChannelCapabilities(unit_name,"OpticalChannel_down2")[0])) | |
1042 | - # print(config.getEditableAttributesOfChannel(unit_name,"OpticalChannel_down2")) | |
1043 | - print(config.getEditableAttributesOfMount(unit_name)) | |
1044 | - #print(config.get_devices()["FLI-Kepler4040"]["device_config"]) | |
1045 | - #print(config.get_devices()["FLI-Kepler4040"]["device_config"]["CAPABILITIES"][1]["attributes"]["manufacturer"]) | |
1046 | - #print(config.get_devices()["FLI-Kepler4040"]["device_config"]["CAPABILITIES"]) | |
1047 | - #print(config.get_devices()["AstroMecCA-TM350"]["device_config"]["CAPABILITIES"]) | |
1048 | -if __name__ == "__main__": | |
1049 | - | |
1 | +#!/usr/bin/env python3 | |
2 | +import pykwalify.core | |
3 | +import sys | |
4 | +import yaml, logging, os, pickle, time | |
5 | +from datetime import datetime | |
6 | +from pykwalify.errors import PyKwalifyException,SchemaError | |
7 | +from pathlib import Path | |
8 | + | |
9 | +class OBSConfig: | |
10 | + # (AKo) : Config file path is checked on the settings file, if the file isn't valid (i.e not found) the error will be launched by the settings file when starting the application | |
11 | + devices_links = {} | |
12 | + current_file = None | |
13 | + #COMPONENT_PATH = os.path.join(os.environ["DJANGO_PATH"],"../../../config/components/") | |
14 | + #GENERIC_DEVICES_PATH = os.path.join(os.environ["DJANGO_PATH"],"../../../config/devices/") | |
15 | + pickle_file = "obsconfig.p" | |
16 | + obs_config = None | |
17 | + devices = None | |
18 | + computers = None | |
19 | + agents = None | |
20 | + obs_config_file_content = None | |
21 | + #obs_config_path = os.environ.get("PATH_TO_OBSCONF_FOLDER",os.path.join(os.environ["DJANGO_PATH"],"../../../privatedev/config/default/")) | |
22 | + errors = None | |
23 | + | |
24 | + def verify_if_pickle_needs_to_be_updated(self, observatory_config_file)->bool: | |
25 | + """ | |
26 | + | |
27 | + Args: | |
28 | + observatory_config_file ([type]): [description] | |
29 | + | |
30 | + Returns: | |
31 | + bool: [description] | |
32 | + """ | |
33 | + self.CONFIG_PATH = os.path.dirname(observatory_config_file)+"/" | |
34 | + self.obs_config_path = self.CONFIG_PATH | |
35 | + #self.CONFIG_PATH = self.obs_config_path | |
36 | + if os.path.isfile(self.CONFIG_PATH+self.pickle_file) == False: | |
37 | + return True | |
38 | + else: | |
39 | + pickle_file_mtime = os.path.getmtime(self.CONFIG_PATH+self.pickle_file) | |
40 | + obs_config_mtime = os.path.getmtime(observatory_config_file) | |
41 | + | |
42 | + obs_config = self.read_and_check_config_file(observatory_config_file) | |
43 | + if obs_config_mtime > pickle_file_mtime: | |
44 | + # create obs file (yaml) from pickle["obsconfig"] with date of pickle within history folder-> nom ficher + année + mois + jour + datetime (avec secondes) -> YYYY/MM/DD H:m:s | |
45 | + pickle_datetime = datetime.utcfromtimestamp(pickle_file_mtime).strftime("%Y%m%d_%H%M%S") | |
46 | + # Create history folder if doesn't exist | |
47 | + Path(self.obs_config_path+"/history/").mkdir(parents=True, exist_ok=True) | |
48 | + file_name = f"{self.obs_config_path}/history/observatory_{pickle_datetime}.yml" | |
49 | + config_file = open(observatory_config_file,"r") | |
50 | + | |
51 | + with open(file_name, 'w') as f: | |
52 | + f.write(config_file.read()) | |
53 | + return True | |
54 | + if obs_config == None: | |
55 | + print(f"Error when trying to read config file (path of config file : {observatory_config_file}") | |
56 | + return -1 | |
57 | + self.obs_config = obs_config | |
58 | + # check last date of modification for devices files | |
59 | + for device in self.obs_config["OBSERVATORY"]["DEVICES"]: | |
60 | + device_file = self.CONFIG_PATH+device["DEVICE"]["file"] | |
61 | + device_file_mtime = os.path.getmtime(device_file) | |
62 | + if device_file_mtime > pickle_file_mtime: | |
63 | + return True | |
64 | + | |
65 | + for computer in self.obs_config["OBSERVATORY"]["COMPUTERS"]: | |
66 | + computer_file = self.CONFIG_PATH+computer["COMPUTER"]["file"] | |
67 | + computer_file_mtime = os.path.getmtime(computer_file) | |
68 | + if computer_file_mtime > pickle_file_mtime: | |
69 | + return True | |
70 | + return False | |
71 | + | |
72 | + def load(self, observatory_config_file): | |
73 | + pickle_needs_to_be_updated = self.verify_if_pickle_needs_to_be_updated(observatory_config_file) | |
74 | + # check if we already read and load devices configuration and if pickle needs to be updated | |
75 | + if pickle_needs_to_be_updated == False and self.devices != None: | |
76 | + return None | |
77 | + else: | |
78 | + if os.path.isfile(self.CONFIG_PATH+self.pickle_file) and pickle_needs_to_be_updated == False: | |
79 | + print("Reading pickle file") | |
80 | + try: | |
81 | + can_pickle_file_be_read = False | |
82 | + while can_pickle_file_be_read != True: | |
83 | + if os.access(self.CONFIG_PATH+self.pickle_file, os.R_OK): | |
84 | + pickle_dict = pickle.load(open(self.CONFIG_PATH+self.pickle_file,"rb")) | |
85 | + can_pickle_file_be_read = True | |
86 | + else: | |
87 | + time.sleep(0.5) | |
88 | + except IOError: | |
89 | + print("Error when reading the pickle file") | |
90 | + try: | |
91 | + self.obs_config = pickle_dict["obs_config"] | |
92 | + self.computers = pickle_dict["computers"] | |
93 | + self.devices = pickle_dict["devices"] | |
94 | + self.devices_links = pickle_dict["devices_links"] | |
95 | + self.obs_config_file_content = pickle_dict["obs_config_file_content"] | |
96 | + self.raw_config = pickle_dict["raw_config"] | |
97 | + except: | |
98 | + # we rewrite the pickle file, the content will be the same otherwise we would be in the else case | |
99 | + print("Rewritting the pickle file (an error occured while reading it, the content will be the same as it was") | |
100 | + pickle_dict = {} | |
101 | + | |
102 | + self.obs_config = self.read_and_check_config_file(observatory_config_file) | |
103 | + obs_file = open(observatory_config_file,"r") | |
104 | + pickle_dict["raw_config"] = obs_file.read() | |
105 | + obs_file.close() | |
106 | + self.raw_config = pickle_dict["raw_config"] | |
107 | + pickle_dict["obs_config"] = self.obs_config | |
108 | + pickle_dict["devices"] = self.get_devices() | |
109 | + pickle_dict["computers"] = self.get_computers() | |
110 | + pickle_dict["devices_links"] = self.devices_links | |
111 | + pickle_dict["obs_config_file_content"] = self.read_and_check_config_file(observatory_config_file) | |
112 | + print("Writing pickle file") | |
113 | + pickle.dump(pickle_dict,open(self.CONFIG_PATH+self.pickle_file,"wb")) | |
114 | + else: | |
115 | + print("Pickle file needs to be created or updated") | |
116 | + pickle_dict = {} | |
117 | + | |
118 | + self.obs_config = self.read_and_check_config_file(observatory_config_file) | |
119 | + pickle_dict["obs_config"] = self.obs_config | |
120 | + pickle_dict["devices"] = self.get_devices() | |
121 | + pickle_dict["computers"] = self.get_computers() | |
122 | + pickle_dict["devices_links"] = self.devices_links | |
123 | + pickle_dict["obs_config_file_content"] = self.read_and_check_config_file(observatory_config_file) | |
124 | + print("Writing pickle file") | |
125 | + pickle.dump(pickle_dict,open(self.CONFIG_PATH+self.pickle_file,"wb")) | |
126 | + | |
127 | + def check_and_return_config(self,yaml_file:str,schema_file:str)->dict: | |
128 | + """ | |
129 | + Check if yaml_file is valid for the schema_file and return an dictionary of the config file | |
130 | + | |
131 | + Args: | |
132 | + yaml_file (str): Path to the config_file to be validated | |
133 | + schema_file (str): Path to the schema file | |
134 | + | |
135 | + Returns: | |
136 | + dict: dictionary of the config file (with values) | |
137 | + """ | |
138 | + # disable pykwalify error to clean the output | |
139 | + #####logging.disable(logging.ERROR) | |
140 | + try: | |
141 | + can_yaml_file_be_read = False | |
142 | + while can_yaml_file_be_read != True: | |
143 | + if os.access(yaml_file, os.R_OK): | |
144 | + can_yaml_file_be_read = True | |
145 | + else: | |
146 | + print(f"{yaml_file} can't be accessed, waiting for availability") | |
147 | + time.sleep(0.5) | |
148 | + | |
149 | + c = pykwalify.core.Core(source_file=yaml_file, schema_files=[self.SCHEMA_PATH+schema_file]) | |
150 | + return c.validate(raise_exception=True) | |
151 | + except SchemaError: | |
152 | + for error in c.errors: | |
153 | + print("Error :",str(error).split(". Path")[0]) | |
154 | + print("Path to error :",error.path) | |
155 | + self.errors = c.errors | |
156 | + return None | |
157 | + except IOError: | |
158 | + print("Error when reading the observatory config file") | |
159 | + | |
160 | + @staticmethod | |
161 | + def check_config(yaml_file:str,schema_file:str)->any: | |
162 | + """ | |
163 | + Check if yaml_file is valid for the schema_file and return a boolean or list of errors according the schema | |
164 | + | |
165 | + Args: | |
166 | + yaml_file (str): Path to the config_file to be validated | |
167 | + schema_file (str): Path to the schema file | |
168 | + | |
169 | + Returns: | |
170 | + any: boolean (True) if the configuration is valid according the schema or a list of error otherwise | |
171 | + """ | |
172 | + # disable pykwalify error to clean the output | |
173 | + ####logging.disable(logging.ERROR) | |
174 | + try: | |
175 | + can_yaml_file_be_read = False | |
176 | + while can_yaml_file_be_read != True: | |
177 | + if os.access(yaml_file, os.R_OK): | |
178 | + can_yaml_file_be_read = True | |
179 | + else: | |
180 | + print(f"{yaml_file} can't be accessed, waiting for availability") | |
181 | + time.sleep(0.5) | |
182 | + | |
183 | + c = pykwalify.core.Core(source_file=yaml_file, schema_files=[schema_file]) | |
184 | + c.validate(raise_exception=True) | |
185 | + return True | |
186 | + except SchemaError: | |
187 | + for error in c.errors: | |
188 | + print("Error :",str(error).split(". Path")[0]) | |
189 | + print("Path to error :",error.path) | |
190 | + | |
191 | + return c.errors | |
192 | + except IOError: | |
193 | + print("Error when reading the observatory config file") | |
194 | + | |
195 | + def read_and_check_config_file(self,yaml_file:str)->dict: | |
196 | + """ | |
197 | + Read the schema key of the config file to retrieve schema name and proceed to the checking of that config file | |
198 | + Call check_and_return_config function and print its return. | |
199 | + | |
200 | + Args: | |
201 | + yaml_file (str): path to the config file | |
202 | + Returns: | |
203 | + dict: Dictionary of the config file (with values) | |
204 | + """ | |
205 | + self.current_file = yaml_file | |
206 | + try: | |
207 | + can_config_file_be_read = False | |
208 | + while can_config_file_be_read != True: | |
209 | + | |
210 | + if os.access(yaml_file, os.R_OK): | |
211 | + can_config_file_be_read = True | |
212 | + else: | |
213 | + print(f"{yaml_file} can't be accessed, waiting for availability") | |
214 | + time.sleep(0.5) | |
215 | + with open(yaml_file, 'r') as stream: | |
216 | + print(f"Reading {yaml_file}") | |
217 | + config_file = yaml.safe_load(stream) | |
218 | + | |
219 | + self.DJANGO_PATH = os.environ.get("DJANGO_PATH",os.path.abspath(os.path.dirname(yaml_file))) | |
220 | + self.SCHEMA_PATH = os.path.join(self.DJANGO_PATH,"../../../config/schemas/") | |
221 | + self.CONFIG_PATH = self.obs_config_path | |
222 | + self.COMPONENT_PATH = os.path.join(self.DJANGO_PATH,"../../../config/components/") | |
223 | + self.GENERIC_DEVICES_PATH = os.path.join(self.DJANGO_PATH,"../../../config/devices/") | |
224 | + result = self.check_and_return_config(yaml_file,config_file["schema"]) | |
225 | + if result == None: | |
226 | + print("Error when reading and validating config file, please check the errors right above") | |
227 | + exit(1) | |
228 | + return result | |
229 | + | |
230 | + except yaml.YAMLError as exc: | |
231 | + print(exc) | |
232 | + except Exception as e: | |
233 | + print(e) | |
234 | + return None | |
235 | + | |
236 | + def read_generic_component_and_return_attributes(self,component_name:str)->dict: | |
237 | + file_path = self.COMPONENT_PATH + component_name + ".yml" | |
238 | + try: | |
239 | + with open(file_path, 'r') as stream: | |
240 | + config_file = yaml.safe_load(stream) | |
241 | + | |
242 | + attributes = {} | |
243 | + for attribute in config_file: | |
244 | + | |
245 | + attribute = attribute["attribute"] | |
246 | + attributes[attribute.pop("key")] = attribute | |
247 | + return attributes | |
248 | + except yaml.YAMLError as exc: | |
249 | + print(exc) | |
250 | + except Exception as e: | |
251 | + print(e) | |
252 | + return None | |
253 | + | |
254 | + def read_capability_of_device(self,capability:dict)->dict: | |
255 | + """ | |
256 | + Read capability of device and inherit attributes from generic component then overwrite attributes defined in device config | |
257 | + | |
258 | + Args: | |
259 | + capability (dict): dictionary containing a capabilitiy (keys : component and attributes) | |
260 | + | |
261 | + Returns: | |
262 | + dict: dictionary of capability inherited by generic component and overwritten his attributes by current attributes of capability | |
263 | + """ | |
264 | + | |
265 | + component_attributes = self.read_generic_component_and_return_attributes(capability["component"]) | |
266 | + | |
267 | + attributes = {} | |
268 | + # get all attributes of device's capability | |
269 | + for attribute in capability["attributes"]: | |
270 | + attribute = attribute["attribute"] | |
271 | + | |
272 | + attributes[attribute.pop("key")] = attribute | |
273 | + | |
274 | + # for each attributes of generic component attributes | |
275 | + for attribute_name in attributes.keys(): | |
276 | + # merge attributes of general component with specified component in device config file | |
277 | + new_attributes = {**component_attributes[attribute_name],**attributes[attribute_name]} | |
278 | + if "is_enum" in component_attributes[attribute_name].keys(): | |
279 | + # make an intersection of both list of values | |
280 | + new_attributes["value"] = list(set(attributes[attribute_name]["value"]) & set(component_attributes[attribute_name]["value"])) | |
281 | + if len(new_attributes["value"]) == 0: | |
282 | + print(f"Value of lastly read device's attribute '{attribute_name}' isn't one of the values of component configuration for this device (component configuration value(s): {component_attributes[attribute_name]['value']}) (actual value : {attributes[attribute_name]['value']})") | |
283 | + exit(1) | |
284 | + component_attributes[attribute_name] = new_attributes | |
285 | + | |
286 | + # return inherited and overwritten attributes of capability | |
287 | + capability["attributes"] = component_attributes | |
288 | + return capability | |
289 | + | |
290 | + def get_devices_names_and_file(self) -> dict: | |
291 | + """ | |
292 | + Return a dictionary giving the device file name by the device name | |
293 | + Returns: | |
294 | + dict: key is device name, value is file name | |
295 | + """ | |
296 | + devices_names_and_files = {} | |
297 | + for device in self.obs_config["OBSERVATORY"]["DEVICES"]: | |
298 | + device = device["DEVICE"] | |
299 | + | |
300 | + devices_names_and_files[device["name"]] = device["file"] | |
301 | + return devices_names_and_files | |
302 | + | |
303 | + def read_device_config_file(self,config_file_name:str,is_generic=False)->dict: | |
304 | + """ | |
305 | + Read the device config file, inherit generic device config if "generic" key is present in "DEVICE". | |
306 | + Associate capabilities of attached_devices if this device has attached_devices. | |
307 | + Inherit capabilities from generic component and overwritte attributes defined in the device config | |
308 | + Args: | |
309 | + config_file_name (str): file name to be read | |
310 | + is_generic (bool, optional): tells if we're reading a generic configuration (is_generic =True) or not (is_generic = False). Defaults to False. | |
311 | + | |
312 | + Returns: | |
313 | + dict: formatted device configuration (attributes, capabilities...) | |
314 | + """ | |
315 | + self.current_file = config_file_name | |
316 | + devices_name_and_file = self.get_devices_names_and_file() | |
317 | + if not is_generic: | |
318 | + # check if device config file is listed in observatory configuration | |
319 | + current_device_name = [device for device,file_name in devices_name_and_file.items() if file_name in config_file_name[len(self.CONFIG_PATH):] ] | |
320 | + if len(current_device_name) <= 0: | |
321 | + print(f"Current file '{config_file_name[len(self.CONFIG_PATH):]}' isn't listed in observatory configuration") | |
322 | + print("The devices names and files are: ") | |
323 | + for device_name,file_name in devices_name_and_file.items(): | |
324 | + print(f"device name: '{device_name}', device filename: '{file_name}'") | |
325 | + exit(1) | |
326 | + print(f"Reading {config_file_name}") | |
327 | + try: | |
328 | + with open(config_file_name, 'r') as stream: | |
329 | + config_file = yaml.safe_load(stream) | |
330 | + # if we're reading a generic device configuration, the path to get the schema is different than usual | |
331 | + self.SCHEMA_PATH =os.path.join(self.DJANGO_PATH,"../../../config/schemas/") | |
332 | + | |
333 | + # read and verify that the device configuration match the schema | |
334 | + result = self.check_and_return_config(config_file_name,config_file["schema"]) | |
335 | + # if the configuration didn't match the schema or had an error when reading the file | |
336 | + if result == None: | |
337 | + print("Error when reading and validating config file, please check the errors right above") | |
338 | + exit(1) | |
339 | + else: | |
340 | + # the configuration is valid | |
341 | + # storing DEVICE key in device (DEVICE can contains : attributes of device, capabilities, attached devices) | |
342 | + device = result["DEVICE"] | |
343 | + generic_device_config = None | |
344 | + # if the device is associated to an generic configuration, we'll read that generic configuration to inherit his attributes | |
345 | + if "generic" in device: | |
346 | + # storing the whole current config | |
347 | + current_config = result | |
348 | + # read and get the generic device config | |
349 | + generic_device_config = self.read_device_config_file(self.GENERIC_DEVICES_PATH+device["generic"],is_generic=True) | |
350 | + # merge whole device config but we need to merge capabilities differently after | |
351 | + new_config = {**generic_device_config["DEVICE"],**current_config["DEVICE"]} | |
352 | + result["DEVICE"] = new_config | |
353 | + | |
354 | + # device has capabilities | |
355 | + if "CAPABILITIES" in device: | |
356 | + capabilities = [] | |
357 | + # if the device is associated to a generic device we need to associate his capabilities with the generic capabilities and overwrite them | |
358 | + if generic_device_config != None: | |
359 | + # we're making a copy of generic device config so we can remove items during loop | |
360 | + copy_generic_device_config = generic_device_config.copy() | |
361 | + # We have to extend capabilities of generic device configuration | |
362 | + for capability in current_config["DEVICE"]["CAPABILITIES"]: | |
363 | + is_capability_in_generic_config = False | |
364 | + current_config_capability = capability["CAPABILITY"] | |
365 | + current_config_component = current_config_capability["component"] | |
366 | + # find if this component was defined in generic_device_config | |
367 | + for index,generic_config_capability in enumerate(generic_device_config["DEVICE"]["CAPABILITIES"]): | |
368 | + # if the current capability is the capability of the component we're looking for | |
369 | + if current_config_component == generic_config_capability["component"]: | |
370 | + is_capability_in_generic_config = True | |
371 | + # we're merging their attributes | |
372 | + new_attributes = generic_config_capability["attributes"].copy() | |
373 | + attributes = {} | |
374 | + current_config_attributes = current_config_capability["attributes"] | |
375 | + generic_config_attributes = generic_config_capability["attributes"] | |
376 | + for attribute in current_config_attributes: | |
377 | + attribute = attribute["attribute"] | |
378 | + attributes[attribute.pop("key")] = attribute | |
379 | + # for each attributes of device component attributes | |
380 | + for attribute_name in attributes.keys(): | |
381 | + # merge attributes of general component with specified component in device config file | |
382 | + new_attributes[attribute_name] = {**generic_config_attributes[attribute_name],**attributes[attribute_name]} | |
383 | + if "is_enum" in generic_config_attributes[attribute_name].keys(): | |
384 | + # make an intersection of both list of values | |
385 | + new_attributes[attribute_name]["value"] = list(set(attributes[attribute_name]["value"]) & set(generic_config_attributes[attribute_name]["value"])) | |
386 | + if len(new_attributes[attribute_name]["value"]) == 0: | |
387 | + print(f"Value of device '{config_file_name}' for attribute '{attribute_name}' isn't one of the values of generic configuration for this device (generic value(s): {generic_config_attributes[attribute_name]['value']}) (actual value : {attributes[attribute_name]['value']})") | |
388 | + exit(1) | |
389 | + # removing this capability from generic device configuration | |
390 | + generic_device_config["DEVICE"]["CAPABILITIES"].pop(index) | |
391 | + capabilities.append({"component": current_config_component,"attributes":new_attributes}) | |
392 | + break | |
393 | + if is_capability_in_generic_config == False: | |
394 | + current_config_capability = self.read_capability_of_device(current_config_capability) | |
395 | + # the component defined in the current_config isn't defined in generic config (should not happen but we'll deal with that case anyway) : we're simply adding this capability | |
396 | + capabilities.append(current_config_capability) | |
397 | + # looping through generic device config's capabilities in order to add them to current device configuration | |
398 | + for generic_config_capability in generic_device_config["DEVICE"]["CAPABILITIES"]: | |
399 | + capabilities.append(generic_config_capability) | |
400 | + else: | |
401 | + # device not associated to a generic device configuration | |
402 | + for capability in device["CAPABILITIES"]: | |
403 | + capability = capability["CAPABILITY"] | |
404 | + capabilities.append(self.read_capability_of_device(capability)) | |
405 | + device["CAPABILITIES"] = capabilities | |
406 | + # associate capabilities to final device configuration (stored in result variable) | |
407 | + result["DEVICE"]["CAPABILITIES"] = device["CAPABILITIES"] | |
408 | + if "ATTACHED_DEVICES" in device.keys(): | |
409 | + # device has attached devices, we need to read their configuration in order to get their capabilities and add them to the current device | |
410 | + devices_name_and_file = self.get_devices_names_and_file() | |
411 | + active_devices = self.get_active_devices() | |
412 | + for attached_device in device["ATTACHED_DEVICES"]: | |
413 | + is_attached_device_link_to_agent = False | |
414 | + # we're looking for if the attached device is associated to an agent (i.e. the device is considered as 'active'). However an attached_device shoudn't be active | |
415 | + for active_device in active_devices: | |
416 | + if devices_name_and_file[active_device] == attached_device["file"]: | |
417 | + # the attached device is an active device (so it's linked to an agent) | |
418 | + is_attached_device_link_to_agent = True | |
419 | + break | |
420 | + if self.CONFIG_PATH+attached_device["file"] != config_file_name and not is_attached_device_link_to_agent: | |
421 | + # if the attached device isn't the device itself and not active | |
422 | + | |
423 | + # get configuration of attached device | |
424 | + config_of_attached_device = self.read_device_config_file(self.CONFIG_PATH+attached_device["file"]) | |
425 | + capabilities_of_attached_device = None | |
426 | + if "CAPABILITIES" in config_of_attached_device["DEVICE"].keys(): | |
427 | + capabilities_of_attached_device = config_of_attached_device["DEVICE"]["CAPABILITIES"] | |
428 | + if capabilities_of_attached_device != None: | |
429 | + # get name of device corresponding to the config file name | |
430 | + parent_device_name = [device for device,file_name in devices_name_and_file.items() if file_name == config_file_name[len(self.CONFIG_PATH):]] | |
431 | + attached_device_name = [device for device,file_name in devices_name_and_file.items() if file_name == attached_device["file"]] | |
432 | + if len(parent_device_name) > 0 : | |
433 | + parent_device_name = parent_device_name[0] | |
434 | + else: | |
435 | + print(f"Attached device filename '{config_file_name[len(self.CONFIG_PATH):]}' is not listed in observatory devices files names") | |
436 | + print("The devices names and files are: ") | |
437 | + for device_name,file_name in devices_name_and_file.items(): | |
438 | + print(f"device name: '{device_name}', device filename: '{file_name}'") | |
439 | + exit(1) | |
440 | + if len(attached_device_name) > 0 : | |
441 | + attached_device_name = attached_device_name[0] | |
442 | + else: | |
443 | + | |
444 | + print(f"Attached device filename '{attached_device['file']}' is not listed in observatory devices files names") | |
445 | + print("The devices names and files are: ") | |
446 | + for device_name,file_name in devices_name_and_file.items(): | |
447 | + print(f"device name: '{device_name}', device filename: '{file_name}'") | |
448 | + exit(1) | |
449 | + # associate attached device to his 'parent' device (parent device is the currently read device) | |
450 | + self.devices_links[attached_device_name] = parent_device_name | |
451 | + for capability in capabilities_of_attached_device: | |
452 | + # add capabilities of attached device to current device | |
453 | + result["DEVICE"]["CAPABILITIES"].append(capability) | |
454 | + return result | |
455 | + except yaml.YAMLError as exc: | |
456 | + print(exc) | |
457 | + except Exception as e: | |
458 | + print(e) | |
459 | + exit(1) | |
460 | + #return None | |
461 | + | |
462 | + def __init__(self, observatory_config_file:str, unit_name:str="") -> None: | |
463 | + """ | |
464 | + Initiate class with the config file | |
465 | + set content attribute to a dictionary containing all values from the config file | |
466 | + | |
467 | + Args: | |
468 | + config_file_name (str): path to the config file | |
469 | + """ | |
470 | + self.load(observatory_config_file) | |
471 | + if unit_name == "": | |
472 | + # By default we will use the first unit | |
473 | + self.unit_name = self.get_units_name()[0] | |
474 | + else: | |
475 | + self.unit_name = unit_name | |
476 | + | |
477 | + def get_obs_name(self) -> str: | |
478 | + """ | |
479 | + Return name of the observatory | |
480 | + | |
481 | + Returns: | |
482 | + str: Name of the observatory | |
483 | + """ | |
484 | + return self.obs_config["OBSERVATORY"]["name"] | |
485 | + | |
486 | + def get_channels(self, unit_name: str) -> dict: | |
487 | + | |
488 | + """ | |
489 | + return dictionary of channels | |
490 | + | |
491 | + Args: | |
492 | + unit_name (str): Name of the unit | |
493 | + Returns: | |
494 | + dict: [description] | |
495 | + """ | |
496 | + unit = self.get_unit_by_name(unit_name) | |
497 | + channels = {} | |
498 | + | |
499 | + for channel_id in range(len(unit["TOPOLOGY"]["CHANNELS"])): | |
500 | + channel = unit["TOPOLOGY"]["CHANNELS"][channel_id]["CHANNEL"] | |
501 | + channels[channel["name"]] = channel | |
502 | + return channels | |
503 | + | |
504 | + def get_computers(self) -> dict: | |
505 | + """ | |
506 | + return dictionary of computers | |
507 | + | |
508 | + Returns: | |
509 | + dict: [description] | |
510 | + """ | |
511 | + if self.computers != None: | |
512 | + return self.computers | |
513 | + else: | |
514 | + computers = {} | |
515 | + for computer_id in range(len(self.obs_config["OBSERVATORY"]["COMPUTERS"])): | |
516 | + computer = self.obs_config["OBSERVATORY"]["COMPUTERS"][computer_id]["COMPUTER"] | |
517 | + if( "file" in computer.keys() ): | |
518 | + computer["computer_config"]= self.read_and_check_config_file(self.CONFIG_PATH+computer["file"])["COMPUTER"] | |
519 | + computers[computer["name"]] = computer | |
520 | + return computers | |
521 | + | |
522 | + def get_devices(self) -> dict: | |
523 | + """ | |
524 | + return dictionary of devices | |
525 | + | |
526 | + Returns: | |
527 | + dict: [description] | |
528 | + """ | |
529 | + if self.devices != None: | |
530 | + return self.devices | |
531 | + | |
532 | + else: | |
533 | + devices = {} | |
534 | + for device_id in range(len(self.obs_config["OBSERVATORY"]["DEVICES"])): | |
535 | + device = self.obs_config["OBSERVATORY"]["DEVICES"][device_id]["DEVICE"] | |
536 | + if( "file" in device.keys() ): | |
537 | + device["device_config"] = self.read_device_config_file(self.CONFIG_PATH+device["file"])["DEVICE"] | |
538 | + devices[device["name"]] = device | |
539 | + return devices | |
540 | + | |
541 | + def get_devices_names(self)->list: | |
542 | + """Return the list of devices names of an observatory | |
543 | + | |
544 | + Returns: | |
545 | + list: list of names of devices | |
546 | + """ | |
547 | + devices_names = [] | |
548 | + for device in self.obs_config["OBSERVATORY"]["DEVICES"]: | |
549 | + devices_names.append(device["DEVICE"]["name"]) | |
550 | + return devices_names | |
551 | + | |
552 | + def get_agents(self,unit_name)->dict: | |
553 | + """ | |
554 | + return dictionary of agents | |
555 | + | |
556 | + Args: | |
557 | + unit_name (str): name of the unit | |
558 | + | |
559 | + Returns: | |
560 | + dict: dictionary of agents. For each agents tell the name, computer, device, protocole, etc... | |
561 | + """ | |
562 | + unit = self.get_unit_by_name(unit_name) | |
563 | + if self.agents != None: | |
564 | + return self.agents | |
565 | + else: | |
566 | + agents = {} | |
567 | + | |
568 | + | |
569 | + for agent_id in range(len(unit["AGENTS"])): | |
570 | + # Agents is a list containing dictionary that have only one key | |
571 | + key = list(unit["AGENTS"][agent_id].keys())[0] | |
572 | + agent = unit["AGENTS"][agent_id][key] | |
573 | + agents[agent["name"]] = agent | |
574 | + return agents | |
575 | + | |
576 | + def get_layouts(self, unit_name: str) -> dict: | |
577 | + """ | |
578 | + Return dictionary of layouts | |
579 | + | |
580 | + Args: | |
581 | + unit_name (str): name of the unit | |
582 | + Returns: | |
583 | + dict: dictionary of layouts | |
584 | + """ | |
585 | + unit = self.get_unit_by_name(unit_name) | |
586 | + info = {} | |
587 | + info["layouts"] = {} | |
588 | + for layout_id in range(len(unit["TOPOLOGY"]["LAYOUTS"])): | |
589 | + layout = unit["TOPOLOGY"]["LAYOUTS"][layout_id]["LAYOUT"] | |
590 | + info["layouts"][layout["name"]] = layout | |
591 | + return info | |
592 | + | |
593 | + def get_albums(self,unit_name : str) -> dict: | |
594 | + """ | |
595 | + Return dictionary of layouts | |
596 | + | |
597 | + Args: | |
598 | + unit_name (str): name of the unit | |
599 | + Returns: | |
600 | + dict: dictionary of layouts | |
601 | + """ | |
602 | + unit = self.get_unit_by_name(unit_name) | |
603 | + info = {} | |
604 | + info["albums"] = {} | |
605 | + for album_id in range(len(unit["TOPOLOGY"]["ALBUMS"])): | |
606 | + album = unit["TOPOLOGY"]["ALBUMS"][album_id]["ALBUM"] | |
607 | + info["albums"][album["name"]] = album | |
608 | + return info | |
609 | + | |
610 | + def get_channel_information(self, unit_name: str, channel_name: str) -> dict: | |
611 | + """ | |
612 | + Return information of the given channel name of a unit | |
613 | + | |
614 | + Args: | |
615 | + unit_name (str): Name of the unit | |
616 | + channel_name (str): name of the channel | |
617 | + | |
618 | + Returns: | |
619 | + dict: dictionary containing all values that define this channel | |
620 | + """ | |
621 | + channels = self.get_channels(unit_name) | |
622 | + return channels[channel_name] | |
623 | + | |
624 | + def get_topology(self, unit_name:str)->dict: | |
625 | + """ | |
626 | + Return dictionary of the topology of the observatory | |
627 | + | |
628 | + Args: | |
629 | + unit_name (str): Name of the unit | |
630 | + Returns: | |
631 | + dict: dictionary representing the topology of an unit (security, mount, channels, layouts, albums) | |
632 | + """ | |
633 | + unit = self.get_unit_by_name(unit_name) | |
634 | + topology = {} | |
635 | + for key in unit["TOPOLOGY"].keys(): | |
636 | + branch = unit["TOPOLOGY"][key] | |
637 | + if key == "CHANNELS": | |
638 | + topology[key] = self.get_channels(unit_name) | |
639 | + elif key == "LAYOUTS": | |
640 | + topology[key] = self.get_layouts(unit_name) | |
641 | + elif key == "ALBUMS": | |
642 | + topology[key] = self.get_albums(unit_name) | |
643 | + else: | |
644 | + topology[key] = branch | |
645 | + return topology | |
646 | + | |
647 | + def get_active_agents(self, unit_name: str) -> list: | |
648 | + """ | |
649 | + Return the list of active agents (i.e. agents that have an association with a device) | |
650 | + | |
651 | + Args: | |
652 | + unit_name (str): Name of the unit | |
653 | + | |
654 | + Returns: | |
655 | + list: kist of the name of active agents | |
656 | + """ | |
657 | + return list(self.get_agents(unit_name).keys()) | |
658 | + | |
659 | + def get_units(self)->dict: | |
660 | + """ | |
661 | + Return all units sort by name defined in the config file | |
662 | + | |
663 | + Returns: | |
664 | + dict: dictionary giving for a unit_name, his content (name,database,topology,agents,...) | |
665 | + """ | |
666 | + result = {} | |
667 | + units = self.obs_config["OBSERVATORY"]["UNITS"] | |
668 | + for unit in units: | |
669 | + unit = unit["UNIT"] | |
670 | + result[unit["name"]] = unit | |
671 | + return result | |
672 | + | |
673 | + def get_components_agents(self, unit_name: str) -> dict: | |
674 | + """ | |
675 | + Return dictionary of component_agents of the given unit | |
676 | + | |
677 | + Args: | |
678 | + unit_name (str): Name of the unit | |
679 | + | |
680 | + Returns: | |
681 | + dict: dictionary sort by component name giving the associated agent (agent name) | |
682 | + """ | |
683 | + components_agents = {} | |
684 | + topology = self.get_topology(unit_name) | |
685 | + for element in topology: | |
686 | + if element in ("SECURITY","MOUNT","CHANNELS"): | |
687 | + if(element != "CHANNELS"): | |
688 | + for component_agent in topology[element]["COMPONENT_AGENTS"]: | |
689 | + component_name = list(component_agent.keys())[0] | |
690 | + components_agents[component_name] = component_agent[component_name] | |
691 | + else: | |
692 | + for channel in topology[element]: | |
693 | + for component_agent in topology[element][channel]["COMPONENT_AGENTS"]: | |
694 | + component_name = list(component_agent.keys())[0] | |
695 | + components_agents[component_name] = component_agent[component_name] | |
696 | + return components_agents | |
697 | + | |
698 | + def get_units_name(self)->list: | |
699 | + """ | |
700 | + Return list of units names | |
701 | + | |
702 | + Returns: | |
703 | + [list]: names of units | |
704 | + """ | |
705 | + return list(self.get_units().keys()) | |
706 | + | |
707 | + def get_unit_by_name(self, name: str) -> dict: | |
708 | + """ | |
709 | + Return dictionary containing definition of the unit that matches the given name | |
710 | + | |
711 | + Args: | |
712 | + name (str): name of the unit | |
713 | + | |
714 | + Returns: | |
715 | + dict: dictonary representing the unit | |
716 | + """ | |
717 | + return self.get_units()[name] | |
718 | + | |
719 | + def get_agents_per_computer(self, unit_name: str) -> dict: | |
720 | + """ | |
721 | + Return dictionary that give for each computer, what are the associated agents to it as a list | |
722 | + | |
723 | + Args: | |
724 | + unit_name (str): Name of the unit | |
725 | + | |
726 | + Returns: | |
727 | + dict: dictionary that give for each computer, what are the associated agents to it as a list | |
728 | + | |
729 | + """ | |
730 | + agents_per_computer = {} | |
731 | + agents = self.get_agents(unit_name) | |
732 | + for agent in agents: | |
733 | + computer_name = agents[agent]["computer"] | |
734 | + if(agents[agent]["computer"] not in agents_per_computer.keys()): | |
735 | + agents_per_computer[computer_name] = [agent] | |
736 | + else: | |
737 | + agents_per_computer[computer_name].append(agent) | |
738 | + return agents_per_computer | |
739 | + | |
740 | + | |
741 | + def get_agents_per_device(self,unit_name:str)->dict: | |
742 | + """ | |
743 | + Return dictionary that give for each device, what are the associated agents to it as a list | |
744 | + | |
745 | + Args: | |
746 | + unit_name (str): Name of the unit | |
747 | + | |
748 | + Returns: | |
749 | + dict: dictionary that give for each device, what are the associated agents to it as a list | |
750 | + | |
751 | + """ | |
752 | + agents_per_device = {} | |
753 | + agents = self.get_agents(unit_name) | |
754 | + for agent in agents: | |
755 | + if("device" in agents[agent].keys()): | |
756 | + device_name = agents[agent]["device"] | |
757 | + if device_name in self.get_devices_names(): | |
758 | + if(agents[agent]["device"] not in agents_per_device.keys()): | |
759 | + agents_per_device[device_name] = [agent] | |
760 | + else: | |
761 | + agents_per_device[device_name].append(agent) | |
762 | + else: | |
763 | + print(f"Error: device name '{device_name}' for agent '{agent}' is not known in the configuration file. The device name must match one of the names defined in the DEVICES section") | |
764 | + exit(1) | |
765 | + return agents_per_device | |
766 | + | |
767 | + def get_active_devices(self)->list: | |
768 | + """ | |
769 | + Return a list of active device names | |
770 | + | |
771 | + Returns: | |
772 | + list: list of active device names | |
773 | + """ | |
774 | + active_devices = [] | |
775 | + for unit_name in self.get_units(): | |
776 | + for device in self.get_agents_per_device(unit_name): | |
777 | + active_devices.append(device) | |
778 | + return active_devices | |
779 | + | |
780 | + def get_active_computers(self)->list: | |
781 | + """ | |
782 | + Return a list of active computer names | |
783 | + | |
784 | + Returns: | |
785 | + list: list of active computer names | |
786 | + """ | |
787 | + active_computers = [] | |
788 | + for unit_name in self.get_units(): | |
789 | + unit = self.get_unit_by_name(unit_name) | |
790 | + for computer in self.get_agents_per_computer(unit_name): | |
791 | + active_computers.append(computer) | |
792 | + return active_computers | |
793 | + | |
794 | + def get_agent_information(self,unit_name:str,agent_name:str)->dict: | |
795 | + """ | |
796 | + Give the dictionary of attributes of the agent for an unit. | |
797 | + | |
798 | + Args: | |
799 | + unit (dict): dictonary representing the unit | |
800 | + agent_name (str): agent name | |
801 | + | |
802 | + Returns: | |
803 | + dict: dictionary containing attributes of the agent | |
804 | + """ | |
805 | + return self.get_agents(unit_name)[agent_name] | |
806 | + | |
807 | + def get_device_information(self,device_name:str)->dict: | |
808 | + """ | |
809 | + Give the dictionary of the attributes of the device | |
810 | + | |
811 | + Args: | |
812 | + device_name (str): device name | |
813 | + | |
814 | + Returns: | |
815 | + dict: dictionary containing attributes of the device | |
816 | + """ | |
817 | + return self.get_devices()[device_name] | |
818 | + | |
819 | + def get_database_for_unit(self, unit_name: str) -> dict: | |
820 | + """ | |
821 | + Return dictionary of attributes of the database for an unit | |
822 | + | |
823 | + Args: | |
824 | + unit_name (str): unit name | |
825 | + | |
826 | + Returns: | |
827 | + dict: dictionary of attributes of the database for an unit | |
828 | + """ | |
829 | + return self.get_unit_by_name(unit_name)["DATABASE"] | |
830 | + | |
831 | + def get_device_for_agent(self, unit_name: str, agent_name: str) -> str: | |
832 | + """ | |
833 | + Return device name associated to the agent | |
834 | + | |
835 | + Args: | |
836 | + unit (dict): dictonary representing the unit | |
837 | + agent_name (str): agent name | |
838 | + | |
839 | + Returns: | |
840 | + str: device name associated to this agent | |
841 | + """ | |
842 | + agents_per_device = self.get_agents_per_device(unit_name) | |
843 | + for device in agents_per_device: | |
844 | + if agent_name in agents_per_device[device]: | |
845 | + return self.get_device_information(device) | |
846 | + | |
847 | + def get_unit_of_computer(self, computer_name: str) -> str: | |
848 | + """ | |
849 | + Return the name of the unit where the computer is used | |
850 | + | |
851 | + Args: | |
852 | + computer_name (str): computer name | |
853 | + | |
854 | + Returns: | |
855 | + str: unit name | |
856 | + """ | |
857 | + for unit_name in self.get_units(): | |
858 | + if(computer_name in self.get_agents_per_computer(unit_name)): | |
859 | + return unit_name | |
860 | + | |
861 | + def get_unit_of_device(self, device_name:str)->str: | |
862 | + """ | |
863 | + Return the name of the unit where the device is used | |
864 | + | |
865 | + Args: | |
866 | + device_name (str): device name | |
867 | + | |
868 | + Returns: | |
869 | + str: unit name | |
870 | + """ | |
871 | + for unit_name in self.get_units(): | |
872 | + if(device_name in self.get_agents_per_device(unit_name)): | |
873 | + return unit_name | |
874 | + | |
875 | + def get_device_power(self,device_name:str)->dict: | |
876 | + """ | |
877 | + Return dictionary that contains informations about power if this information is present in the device config file | |
878 | + | |
879 | + Return None if this information isn't stored in device's config file | |
880 | + Args: | |
881 | + device_name (str): name of the device | |
882 | + | |
883 | + Returns: | |
884 | + dict: informations about power of device | |
885 | + """ | |
886 | + return self.get_devices()[device_name]["device_config"].get("power") | |
887 | + | |
888 | + | |
889 | + def get_device_capabilities(self, device_name:str)->list: | |
890 | + """ | |
891 | + Return dictionary that contains informations about capabilities if this information is present in the device config file | |
892 | + | |
893 | + Return empty list if this information isn't stored in device's config file | |
894 | + Args: | |
895 | + device_name (str): name of the device | |
896 | + | |
897 | + Returns: | |
898 | + list: list of capabilities of device | |
899 | + """ | |
900 | + list_of_capabilities = [] | |
901 | + capabilities = self.get_devices()[device_name]["device_config"].get("CAPABILITIES") | |
902 | + return capabilities | |
903 | + | |
904 | + def get_device_connector(self,device_name:str)->dict: | |
905 | + """ | |
906 | + Return dictionary that contains informations about connector if this information is present in the device config file | |
907 | + | |
908 | + Return None if this information isn't stored in device's config file | |
909 | + Args: | |
910 | + device_name (str): name of the device | |
911 | + | |
912 | + Returns: | |
913 | + dict: informations about connector of device | |
914 | + """ | |
915 | + return self.get_devices()[device_name]["device_config"].get("connector") | |
916 | + | |
917 | + def get_computer_power(self, computer_name: str) -> dict: | |
918 | + """ | |
919 | + Return dictionary that contains informations about power if this information is present in the device config file | |
920 | + | |
921 | + Return None if this information isn't stored in device's config file | |
922 | + Args: | |
923 | + device_name (str): name of the device | |
924 | + | |
925 | + Returns: | |
926 | + dict: informations about connector of device | |
927 | + """ | |
928 | + return self.get_computers()[computer_name]["computer_config"].get("power") | |
929 | + | |
930 | + def getDeviceControllerNameForAgent(self,unit_name:str,agent_name:str)->tuple: | |
931 | + agent = self.get_agent_information(unit_name,agent_name) | |
932 | + return (agent["device"],"DeviceController"+agent["device"]) | |
933 | + | |
934 | + def getDeviceConfigForDeviceController(self,device_name:str)->dict: | |
935 | + return self.get_devices()[device_name]["device_config"] | |
936 | + | |
937 | + def getCommParamsForAgentDevice(self,unit_name:str,agent_name:str)->tuple: | |
938 | + agent = self.get_agent_information(unit_name,agent_name) | |
939 | + device_config = self.getDeviceConfigForDeviceController(agent["device"]) | |
940 | + comm_access = agent.get("comm_access",None) | |
941 | + comm = device_config["comm"] | |
942 | + return (comm_access,comm) | |
943 | + | |
944 | + def getChannelCapabilities(self,unit_name:str,channel_name:str)->list: | |
945 | + channel = self.get_channel_information(unit_name,channel_name) | |
946 | + result = [] | |
947 | + for component_agent in channel["COMPONENT_AGENTS"]: | |
948 | + component = list(component_agent.keys())[0] | |
949 | + agent = component_agent[component] | |
950 | + device = self.getDeviceControllerNameForAgent(unit_name,agent)[0] | |
951 | + device_capabilities = self.get_device_capabilities(device) | |
952 | + for capability in device_capabilities: | |
953 | + if capability["component"] == component: | |
954 | + result.append(capability) | |
955 | + return result | |
956 | + | |
957 | + def getEditableAttributesOfCapability(self,capability:dict)->dict: | |
958 | + editable_fields = {} | |
959 | + attributes = capability.get("attributes") | |
960 | + for attribute in attributes: | |
961 | + if attributes[attribute]["is_editable"]: | |
962 | + editable_fields[attribute] = attributes[attribute] | |
963 | + return editable_fields | |
964 | + | |
965 | + def getUneditableAttributesOfCapability(self,capability:dict)->dict: | |
966 | + uneditable_fields = {} | |
967 | + attributes = capability.get("attributes") | |
968 | + for attribute in attributes: | |
969 | + if attributes[attribute]["is_editable"] == False: | |
970 | + uneditable_fields[attribute] = attributes[attribute] | |
971 | + return uneditable_fields | |
972 | + | |
973 | + def getEditableAttributesOfChannel(self,unit_name:str,channel_name:str)->list: | |
974 | + capabilities = self.getChannelCapabilities(unit_name,channel_name) | |
975 | + # merged_result = {} | |
976 | + # for capability in capabilities: | |
977 | + # merged_result[capability["component"]] = self.getEditableAttributesOfCapability(capability) | |
978 | + # return merged_result | |
979 | + merged_result = [] | |
980 | + for capability in capabilities: | |
981 | + attributes = self.getEditableAttributesOfCapability(capability) | |
982 | + if len(attributes.keys()) > 0: | |
983 | + merged_result.append(attributes) | |
984 | + return merged_result | |
985 | + | |
986 | + def getUneditableAttributesOfChannel(self,unit_name:str,channel_name:str)->list: | |
987 | + capabilities = self.getChannelCapabilities(unit_name,channel_name) | |
988 | + # merged_result = {} | |
989 | + # for capability in capabilities: | |
990 | + # merged_result[capability["component"]] = self.getEditableAttributesOfCapability(capability) | |
991 | + # return merged_result | |
992 | + merged_result = [] | |
993 | + for capability in capabilities: | |
994 | + attributes = self.getUneditableAttributesOfCapability(capability) | |
995 | + if len(attributes.keys()) > 0: | |
996 | + merged_result.append(attributes) | |
997 | + return merged_result | |
998 | + | |
999 | + | |
1000 | + # def getLogicOfChannelGroups(self,unit_name): | |
1001 | + # return self.get_layouts(unit_name)["global_group_logic"] | |
1002 | + | |
1003 | + | |
1004 | + def getLayoutByName(self, unit_name:str, name_of_layout): | |
1005 | + return self.get_layouts(unit_name)["layouts"][name_of_layout] | |
1006 | + | |
1007 | + def getAlbumByName(self,unit_name : str, name_of_album): | |
1008 | + return self.get_albums(unit_name)["albums"][name_of_album] | |
1009 | + | |
1010 | + def getEditableAttributesOfMount(self,unit_name): | |
1011 | + capabilities = self.get_device_capabilities(self.get_device_for_agent(unit_name,"mount")["name"]) | |
1012 | + merged_result = [] | |
1013 | + for capability in capabilities: | |
1014 | + attributes = self.getEditableAttributesOfCapability(capability) | |
1015 | + if len(attributes.keys()) > 0: | |
1016 | + merged_result.append(attributes) | |
1017 | + return merged_result | |
1018 | + | |
1019 | + | |
1020 | + def getHorizonLine(self, unit_name): | |
1021 | + horizon = self.get_unit_by_name(unit_name).get("horizon") | |
1022 | + return horizon.get("line") | |
1023 | + | |
1024 | +def main(): | |
1025 | + # config = ConfigPyros("../../../../privatedev/config/guitalens/observatory_guitalens.yml") | |
1026 | + # unit_name = config.get_units_name()[0] | |
1027 | + # dc = config.getDeviceControllerNameForAgent(unit_name,"mount")[0] | |
1028 | + #print(config.getDeviceConfigForDeviceController(dc)) | |
1029 | + #print(config.getCommParamsForAgentDevice(unit_name,"mount")) | |
1030 | + # print(config.getChannelCapabilities(unit_name,"OpticalChannel_up")) | |
1031 | + # print(config.get_channel_groups(unit_name)) | |
1032 | + # print(config.getEditableAttributesOfCapability(config.getChannelCapabilities(unit_name,"OpticalChannel_up")[0])) | |
1033 | + # print(config.getEditableAttributesOfChannel(unit_name,"OpticalChannel_up")) | |
1034 | + config = OBSConfig("../../../../privatedev/config/tnc/observatory_tnc.yml") | |
1035 | + unit_name = config.get_units_name()[0] | |
1036 | + #dc = config.getDeviceControllerNameForAgent(unit_name,"mount")[0] | |
1037 | + #print(config.getDeviceConfigForDeviceController(dc)) | |
1038 | + #print(config.getCommParamsForAgentDevice(unit_name,"mount")) | |
1039 | + # print(config.getChannelCapabilities(unit_name,"OpticalChannel_down2")) | |
1040 | + # print(config.get_channel_groups(unit_name)) | |
1041 | + # print(config.getEditableAttributesOfCapability(config.getChannelCapabilities(unit_name,"OpticalChannel_down2")[0])) | |
1042 | + # print(config.getEditableAttributesOfChannel(unit_name,"OpticalChannel_down2")) | |
1043 | + print(config.getEditableAttributesOfMount(unit_name)) | |
1044 | + #print(config.get_devices()["FLI-Kepler4040"]["device_config"]) | |
1045 | + #print(config.get_devices()["FLI-Kepler4040"]["device_config"]["CAPABILITIES"][1]["attributes"]["manufacturer"]) | |
1046 | + #print(config.get_devices()["FLI-Kepler4040"]["device_config"]["CAPABILITIES"]) | |
1047 | + #print(config.get_devices()["AstroMecCA-TM350"]["device_config"]["CAPABILITIES"]) | |
1048 | +if __name__ == "__main__": | |
1049 | + | |
1050 | 1050 | main() |
1051 | 1051 | \ No newline at end of file | ... | ... |
src/core/pyros_django/obsconfig/templates/obsconfig/obs_astronomer_config.html
... | ... | @@ -49,7 +49,53 @@ |
49 | 49 | </div> |
50 | 50 | {% endfor %} |
51 | 51 | </div> |
52 | - | |
52 | + {% elif category == "ALBUMS" %} | |
53 | + <li class="collapsible collapsible_category"> ALBUMS </li> | |
54 | + | |
55 | + <div class="content"> | |
56 | + {% for album in category_content.values %} | |
57 | + <h2 class="collapsible collapsible_name">Name : {{ album|get_item:"name" }}</h2> | |
58 | + <div class="content"> | |
59 | + <table class="table table-bordered table-hover table-striped"> | |
60 | + <thead> | |
61 | + <tr> | |
62 | + <th> Channels </th> | |
63 | + </tr> | |
64 | + </thead> | |
65 | + <tbody> | |
66 | + {% for channel in album|get_item:"CHANNELS" %} | |
67 | + | |
68 | + <tr> | |
69 | + <td> {{ channel }} </td> | |
70 | + </tr> | |
71 | + {% endfor %} | |
72 | + </table> | |
73 | + </div> | |
74 | + {% endfor %} | |
75 | + </div> | |
76 | + {% elif category == "LAYOUTS" %} | |
77 | + <li class="collapsible collapsible_category"> LAYOUTS </li> | |
78 | + <div class="content"> | |
79 | + {% for layout in category_content.values %} | |
80 | + <h2 class="collapsible collapsible_name">Name : {{ layout|get_item:"name" }}</h2> | |
81 | + <div class="content"> | |
82 | + <table class="table table-bordered table-hover table-striped"> | |
83 | + <thead> | |
84 | + <tr> | |
85 | + <th> Albums </th> | |
86 | + </tr> | |
87 | + </thead> | |
88 | + <tbody> | |
89 | + {% for album in layout|get_item:"ALBUMS" %} | |
90 | + | |
91 | + <tr> | |
92 | + <td> {{ album }} </td> | |
93 | + </tr> | |
94 | + {% endfor %} | |
95 | + </table> | |
96 | + </div> | |
97 | + {% endfor %} | |
98 | + </div> | |
53 | 99 | {% else %} |
54 | 100 | |
55 | 101 | <li class="collapsible collapsible_category">{{ category }} </li> |
... | ... | @@ -87,7 +133,9 @@ |
87 | 133 | </div> |
88 | 134 | {% endfor %} |
89 | 135 | </ul> |
90 | -</div> | |
136 | + | |
137 | + {{ albums }} | |
138 | + | |
91 | 139 | <script> |
92 | 140 | var coll = document.getElementsByClassName("collapsible"); |
93 | 141 | var i; | ... | ... |
src/core/pyros_django/obsconfig/tests.py
1 | 1 | from django.test import TestCase |
2 | 2 | from common.models import PyrosUser |
3 | -from .configpyros import ConfigPyros | |
3 | +from .obsconfig_class import OBSConfig | |
4 | 4 | from django.urls import reverse |
5 | 5 | import unittest,os |
6 | 6 | class ObservatoryConfigurationTests(TestCase): |
... | ... | @@ -11,13 +11,13 @@ class ObservatoryConfigurationTests(TestCase): |
11 | 11 | os.remove("obsconfig/fixtures/obsconfig.p") |
12 | 12 | |
13 | 13 | u1 = PyrosUser.objects.get(username="haribo") |
14 | - config_ko = ConfigPyros("obsconfig/fixtures/observatory_configuration_ko.yml") | |
14 | + config_ko = OBSConfig("obsconfig/fixtures/observatory_configuration_ko.yml") | |
15 | 15 | if os.path.exists("obsconfig/fixtures/obsconfig.p"): |
16 | 16 | os.remove("obsconfig/fixtures/obsconfig.p") |
17 | 17 | |
18 | 18 | def test_OCF_read_config_get_content(self): |
19 | 19 | os.environ["PATH_TO_OBSCONF_FILE"] = "obsconfig/fixtures/observatory_configuration_ok_simple.yml" |
20 | - config = ConfigPyros("obsconfig/fixtures/observatory_configuration_ok_simple.yml") | |
20 | + config = OBSConfig("obsconfig/fixtures/observatory_configuration_ok_simple.yml") | |
21 | 21 | # get_devices, and get_computers are already tested in load |
22 | 22 | self.assertEqual(config.get_obs_name(),"observatory_test") |
23 | 23 | self.assertEqual(len(config.get_units()),1) |
... | ... | @@ -35,7 +35,7 @@ class ObservatoryConfigurationTests(TestCase): |
35 | 35 | |
36 | 36 | def test_OCF_view_config_simple(self): |
37 | 37 | os.environ["PATH_TO_OBSCONF_FILE"] = "obsconfig/fixtures/observatory_configuration_ok_simple.yml" |
38 | - config = ConfigPyros("obsconfig/fixtures/observatory_configuration_ok_simple.yml") | |
38 | + config = OBSConfig("obsconfig/fixtures/observatory_configuration_ok_simple.yml") | |
39 | 39 | u1 = PyrosUser.objects.get(username="haribo") |
40 | 40 | u1.set_password("password123") |
41 | 41 | u1.save() |
... | ... | @@ -53,7 +53,7 @@ class ObservatoryConfigurationTests(TestCase): |
53 | 53 | |
54 | 54 | def test_OCF_view_config_complex(self): |
55 | 55 | os.environ["PATH_TO_OBSCONF_FILE"] = "obsconfig/fixtures/observatory_configuration_ok_complex.yml" |
56 | - config = ConfigPyros("obsconfig/fixtures/observatory_configuration_ok_complex.yml") | |
56 | + config = OBSConfig("obsconfig/fixtures/observatory_configuration_ok_complex.yml") | |
57 | 57 | u1 = PyrosUser.objects.get(username="haribo") |
58 | 58 | u1.set_password("password123") |
59 | 59 | u1.save() | ... | ... |
src/core/pyros_django/obsconfig/views.py
1 | 1 | from django import conf |
2 | 2 | from django.shortcuts import render |
3 | -from .configpyros import ConfigPyros | |
3 | +from .obsconfig_class import OBSConfig | |
4 | 4 | from django.conf import settings |
5 | 5 | from src.core.pyros_django.dashboard.decorator import level_required |
6 | 6 | from django.contrib.auth.decorators import login_required |
... | ... | @@ -30,7 +30,7 @@ def get_nested_dictionaries_as_list(dic,result=[]): |
30 | 30 | @login_required |
31 | 31 | @level_required("Admin","Observer","Management","Operator","Unit-PI") |
32 | 32 | def obs_global_config(request): |
33 | - config = ConfigPyros(os.environ["PATH_TO_OBSCONF_FILE"],os.environ["unit_name"]) | |
33 | + config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"],os.environ["unit_name"]) | |
34 | 34 | units_names = list(config.get_units().keys()) |
35 | 35 | pickle_file_mtime = os.path.getmtime(config.CONFIG_PATH+config.pickle_file) |
36 | 36 | pickle_datetime = datetime.utcfromtimestamp(pickle_file_mtime).strftime("%Y/%m/%d %H:%M:%S") |
... | ... | @@ -52,7 +52,7 @@ def obs_global_config(request): |
52 | 52 | @login_required |
53 | 53 | @level_required("Admin","Unit-PI","Operator") |
54 | 54 | def obs_hardware_config(request): |
55 | - config = ConfigPyros(os.environ["PATH_TO_OBSCONF_FILE"],os.environ["unit_name"]) | |
55 | + config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"],os.environ["unit_name"]) | |
56 | 56 | devices = config.get_devices() |
57 | 57 | active_devices = config.get_active_devices() |
58 | 58 | computers = config.get_computers() |
... | ... | @@ -71,7 +71,7 @@ def obs_hardware_config(request): |
71 | 71 | @login_required |
72 | 72 | @level_required("Admin","Unit-PI","Operator") |
73 | 73 | def unit_hardware_configuration(request,unit_name): |
74 | - config = ConfigPyros(os.environ["PATH_TO_OBSCONF_FILE"],os.environ["unit_name"]) | |
74 | + config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"],os.environ["unit_name"]) | |
75 | 75 | devices = config.get_devices() |
76 | 76 | |
77 | 77 | return render(request, 'obsconfig/unit_hardware_configuration.html', {'config':config}) |
... | ... | @@ -79,7 +79,7 @@ def unit_hardware_configuration(request,unit_name): |
79 | 79 | @login_required |
80 | 80 | @level_required("Admin","Unit-PI","Operator") |
81 | 81 | def computer_details(request,computer_name): |
82 | - config = ConfigPyros(os.environ["PATH_TO_OBSCONF_FILE"],os.environ["unit_name"]) | |
82 | + config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"],os.environ["unit_name"]) | |
83 | 83 | computer_detail = yaml.dump(config.get_computers()[computer_name]) |
84 | 84 | """ |
85 | 85 | computer_detail = { re.sub("^_","",key):value for key,value in config.get_computers()[computer_name].items() } |
... | ... | @@ -101,7 +101,7 @@ def computer_details(request,computer_name): |
101 | 101 | @login_required |
102 | 102 | @level_required("Admin","Unit-PI","Unit-board","Operator","Observer") |
103 | 103 | def device_details(request,device_name): |
104 | - config = ConfigPyros(os.environ["PATH_TO_OBSCONF_FILE"],os.environ["unit_name"]) | |
104 | + config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"],os.environ["unit_name"]) | |
105 | 105 | # We're removing "_" at the beginning of each key : |
106 | 106 | device = config.get_devices()[device_name] |
107 | 107 | #device_detail = yaml.dump(device) |
... | ... | @@ -156,16 +156,15 @@ def device_details(request,device_name): |
156 | 156 | @login_required |
157 | 157 | @level_required("Admin","Observer","Management","Operator","Unit-PI","TAC") |
158 | 158 | def obs_astronomer_config(request): |
159 | - config = ConfigPyros(os.environ["PATH_TO_OBSCONF_FILE"],os.environ["unit_name"]) | |
159 | + config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"],os.environ["unit_name"]) | |
160 | 160 | units = config.get_units() |
161 | 161 | units_topologies = {} |
162 | 162 | # for each unit |
163 | 163 | for unit_name in units: |
164 | 164 | |
165 | 165 | units_topologies[unit_name] = config.get_topology(unit_name) |
166 | - # removing channel_groups, not useful in this view | |
167 | - units_topologies[unit_name].pop("LAYOUTS") | |
168 | - units_topologies[unit_name].pop("ALBUMS") | |
166 | + layouts = units_topologies[unit_name].pop("LAYOUTS")["layouts"] | |
167 | + albums = units_topologies[unit_name].pop("ALBUMS")["albums"] | |
169 | 168 | # for each category (security, mount, channels) |
170 | 169 | for category in units_topologies[unit_name]: |
171 | 170 | if category != "CHANNELS": |
... | ... | @@ -187,14 +186,17 @@ def obs_astronomer_config(request): |
187 | 186 | device_of_agent = config.get_device_for_agent(unit_name,agent) |
188 | 187 | index = units_topologies[unit_name]["CHANNELS"][channel_name]["COMPONENT_AGENTS"].index(component_agent) |
189 | 188 | units_topologies[unit_name]["CHANNELS"][channel_name]["COMPONENT_AGENTS"][index][component_name] = device_of_agent |
190 | - | |
191 | - return render(request,"obsconfig/obs_astronomer_config.html", { "units_topologies" : units_topologies }) | |
189 | + units_topologies[unit_name]["ALBUMS"] = albums | |
190 | + units_topologies[unit_name]["LAYOUTS"] = layouts | |
191 | + return render(request,"obsconfig/obs_astronomer_config.html", { | |
192 | + "units_topologies" : units_topologies, | |
193 | + }) | |
192 | 194 | |
193 | 195 | |
194 | 196 | @login_required |
195 | 197 | @level_required("Admin") |
196 | 198 | def obs_agents_config(request): |
197 | - config = ConfigPyros(os.environ["PATH_TO_OBSCONF_FILE"],os.environ["unit_name"]) | |
199 | + config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"],os.environ["unit_name"]) | |
198 | 200 | units = config.get_units() |
199 | 201 | units_topologies = {} |
200 | 202 | active_agents_by_unit = {} |
... | ... | @@ -205,8 +207,9 @@ def obs_agents_config(request): |
205 | 207 | active_agents_by_unit[unit_name] = config.get_active_agents(unit_name) |
206 | 208 | # topology of the current unit |
207 | 209 | units_topologies[unit_name] = config.get_topology(unit_name) |
208 | - # removing channel_groups, not useful in this view | |
209 | - units_topologies[unit_name].pop("CHANNEL_GROUPS") | |
210 | + # removing albums and layouts, not useful in this view | |
211 | + units_topologies[unit_name].pop("LAYOUTS") | |
212 | + units_topologies[unit_name].pop("ALBUMS") | |
210 | 213 | for category in units_topologies[unit_name]: |
211 | 214 | if category != "CHANNELS": |
212 | 215 | # Security and Mount are directly a dictionary containing the attributes of those categories |
... | ... | @@ -233,7 +236,7 @@ def obs_agents_config(request): |
233 | 236 | @login_required |
234 | 237 | @level_required("Admin","Operator","Unit-PI","Unit-board") |
235 | 238 | def edit_config(request): |
236 | - config = ConfigPyros(os.environ["PATH_TO_OBSCONF_FILE"],os.environ["unit_name"]) | |
239 | + config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"],os.environ["unit_name"]) | |
237 | 240 | return render(request,"obsconfig/edit_config.html",{"config_file":config.raw_config}) |
238 | 241 | |
239 | 242 | @login_required |
... | ... | @@ -263,7 +266,7 @@ def verify_config(request): |
263 | 266 | response_data["is_valid"] = False |
264 | 267 | response_data["message"] = "Missing schema" |
265 | 268 | schema_path = os.path.join(os.environ["DJANGO_PATH"],"../../../config/schemas/") |
266 | - config = ConfigPyros.check_config(temp_config_file.name,schema_path+schema) | |
269 | + config = OBSConfig.check_config(temp_config_file.name,schema_path+schema) | |
267 | 270 | if type(config) == bool and config: |
268 | 271 | response_data["is_valid"] = True |
269 | 272 | else: | ... | ... |
src/core/pyros_django/pyros/settings.py
... | ... | @@ -44,10 +44,23 @@ MODULES_VERSIONS = { |
44 | 44 | |
45 | 45 | |
46 | 46 | |
47 | -import os,re,platform, subprocess | |
47 | +import os,re,platform, subprocess, sys | |
48 | 48 | from datetime import date, datetime |
49 | 49 | |
50 | 50 | import django |
51 | +# append previous parent folders to import configpyros | |
52 | +sys.path.append("../../..") | |
53 | +from config.pyros.config_pyros import configpyros | |
54 | + | |
55 | +PYROS_CONFIG_FILE = os.environ.get("pyros_config_file") | |
56 | +if PYROS_CONFIG_FILE: | |
57 | + CONFIG_PYROS = configpyros(os.environ["pyros_config_file"]).pyros_config | |
58 | + NB_ELEMENT_PER_PAGE = CONFIG_PYROS.get("general").get("nb_element_per_page") | |
59 | + | |
60 | +def read_version_number_from_file(version_file:str) -> str: | |
61 | + version = open(version_file,"r").readline() | |
62 | + return version | |
63 | + | |
51 | 64 | # duplicate from the same function in pyros.py ... |
52 | 65 | def set_environment_variables_if_not_configured(env_path: str,env_sample_path: str)->None: |
53 | 66 | """ |
... | ... | @@ -404,6 +417,8 @@ python_version = subprocess.run( "python --version | cut -d ' ' -f 2 | cut -d '. |
404 | 417 | python_version = python_version.stdout |
405 | 418 | day = "2022-01-06" |
406 | 419 | django_version_major,django_version_minor = django.VERSION[:2][0],django.VERSION[:2][1] |
407 | -pyros_version = "0.3.3.0" | |
420 | +pyros_version = read_version_number_from_file("../../../VERSION") | |
421 | +#TODO: create function to read VERSION file | |
422 | + | |
408 | 423 | #pyros_version = "0.2.12.0" |
409 | 424 | VERSION_NUMBER = f"{pyros_version}_{django_version_major}.{django_version_minor}_{python_version}_{day}" | ... | ... |