Blame view

src/core/pyros_django/obsconfig/views.py 16.3 KB
cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
1
2
from django import conf
from django.shortcuts import render
a2f47fb6   Alexis Koralewski   updating display ...
3
from .configpyros import ConfigPyros
cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
4
5
6
from django.conf import settings
from src.core.pyros_django.dashboard.decorator import level_required
from django.contrib.auth.decorators import login_required
d0501f5e   Alexis Koralewski   use environment v...
7
8
from django.http import HttpResponse
from datetime import datetime
c27a895d   Alexis Koralewski   add display of ra...
9
from django.views.decorators.csrf import csrf_exempt
d0501f5e   Alexis Koralewski   use environment v...
10
import re, yaml,tempfile,json,os
cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
11

6686d675   Alexis Koralewski   new version of ob...
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def get_nested_dictionaries_as_list(dic,result=[]):
    for key,value in dic.items():
        print(value,type(value))
        print(result)
        if isinstance(value, dict):
            print("recursive call")
            get_nested_dictionaries_as_list(value,result)
        elif isinstance(value,list) and key == "CAPABILITIES":
            print("VALUE OF LIST =======          ",value)
            for element in value:
                #print(element)
                if isinstance(element,dict):
                    get_nested_dictionaries_as_list(element,result)
                
        else:
            result.append((key,value))
    return result
cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
29

cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
30
@login_required
6686d675   Alexis Koralewski   new version of ob...
31
@level_required("Admin","Observer","Management","Operator","Unit-PI")
cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
32
def obs_global_config(request):
d0501f5e   Alexis Koralewski   use environment v...
33
    config = ConfigPyros(os.environ["PATH_TO_OBSCONF_FILE"])
cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
34
    units_names = list(config.get_units().keys())
d0501f5e   Alexis Koralewski   use environment v...
35
36
    pickle_file_mtime = os.path.getmtime(config.CONFIG_PATH+config.pickle_file)
    pickle_datetime = datetime.utcfromtimestamp(pickle_file_mtime).strftime("%Y/%m/%d %H:%M:%S")
b755ff73   Alexis Koralewski   adding variables ...
37
38
39
40
41
42
43
44
45
46
47
48
49
    CAN_EDIT_CONFIG = request.session.get("role") in ("Admin","Operator","Unit-PI","Unit-board")
    CAN_VIEW_HARDWARE_CONFIG = request.session.get("role") in ("Admin","Operator","Unit-PI","Unit-board")
    CAN_VIEW_ASTRONOMER_CONFIG = request.session.get("role") in ("Admin","Operator","Unit-PI","Observer","Management board manager","Unit-board")
    CAN_VIEW_AGENTS_CONFIG =  request.session.get("role") == "Admin"
    return render(request,"obsconfig/global_obs_configuration.html",{
        "units_names" : units_names,
        "obs_name": config.get_obs_name(),
        "last_modification_time":pickle_datetime,
        "CAN_EDIT_CONFIG":CAN_EDIT_CONFIG,
        "CAN_VIEW_HARDWARE_CONFIG": CAN_VIEW_HARDWARE_CONFIG,
        "CAN_VIEW_ASTRONOMER_CONFIG":CAN_VIEW_ASTRONOMER_CONFIG,
        "CAN_VIEW_AGENTS_CONFIG":CAN_VIEW_AGENTS_CONFIG
    })
cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
50
51


cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
52
@login_required
6686d675   Alexis Koralewski   new version of ob...
53
@level_required("Admin","Unit-PI","Operator")
cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
54
def obs_hardware_config(request):
d0501f5e   Alexis Koralewski   use environment v...
55
    config = ConfigPyros(os.environ["PATH_TO_OBSCONF_FILE"])
cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
56
57
58
59
60
61
62
63
64
65
66
67
    devices = config.get_devices()
    active_devices = config.get_active_devices()
    computers = config.get_computers()
    active_computers = config.get_active_computers()
    # give for a device name (key) the unit name where it is used (value)
    device_to_unit = {}
    # give for a computer name (key) the unit name where it is used (value)
    computer_to_unit = {}
    for device in active_devices:
        device_to_unit[device] = config.get_unit_of_device(device)
    for computer in active_computers:
        computer_to_unit[computer] = config.get_unit_of_computer(computer)
a2f47fb6   Alexis Koralewski   updating display ...
68
69
    devices_links = config.devices_links
    return render(request, 'obsconfig/obs_hardware_configuration.html', {'devices':devices,"active_devices":active_devices,"computers":computers,"active_computers":active_computers, "device_to_unit" : device_to_unit, "computer_to_unit" : computer_to_unit,"devices_links":devices_links})                              
cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
70
    
cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
71
@login_required
6686d675   Alexis Koralewski   new version of ob...
72
@level_required("Admin","Unit-PI","Operator")
cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
73
def unit_hardware_configuration(request,unit_name):
d0501f5e   Alexis Koralewski   use environment v...
74
    config = ConfigPyros(os.environ["PATH_TO_OBSCONF_FILE"])
cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
75
76
77
78
    devices = config.get_devices()
   
    return render(request, 'obsconfig/unit_hardware_configuration.html', {'config':config})                              
    
cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
79
@login_required
6686d675   Alexis Koralewski   new version of ob...
80
@level_required("Admin","Unit-PI","Operator")
cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
81
def computer_details(request,computer_name):
d0501f5e   Alexis Koralewski   use environment v...
82
    config = ConfigPyros(os.environ["PATH_TO_OBSCONF_FILE"])
6686d675   Alexis Koralewski   new version of ob...
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
    computer_detail = yaml.dump(config.get_computers()[computer_name])
    """
    computer_detail = { re.sub("^_","",key):value for key,value in config.get_computers()[computer_name].items() }

     # We're removing "_" at the beginning of each key :
    power = { re.sub("^_","",key):value for key,value in config.get_computer_power(computer_name).items() }
    
    computer_config = computer_detail["computer_config"]
    # We're removing "_" at the beginning of each key :
    computer_detail["computer_config"] = { re.sub("^_","",key):value for key,value in computer_config.items() }
    # Remove power key as we have this information in the same 
    if power is not None:
        computer_detail["computer_config"].pop("power")
    return render(request,"obsconfig/computer_details.html", {"computer_detail" : computer_detail, "power" : power})
    """
    return render(request,"obsconfig/computer_details.html", {"computer_detail" : computer_detail})
cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
99

cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
100

cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
101
@login_required
c27a895d   Alexis Koralewski   add display of ra...
102
@level_required("Admin","Unit-PI","Unit-board","Operator","Observer")
cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
103
def device_details(request,device_name):
d0501f5e   Alexis Koralewski   use environment v...
104
    config = ConfigPyros(os.environ["PATH_TO_OBSCONF_FILE"])
6686d675   Alexis Koralewski   new version of ob...
105
106
    # We're removing "_" at the beginning of each key :
    device = config.get_devices()[device_name]
ee2a5e47   Alexis Koralewski   New version of ob...
107
108
    #device_detail  = yaml.dump(device)
    #config_device = open(config.CONFIG_PATH+device["file"],"r").read()
a2f47fb6   Alexis Koralewski   updating display ...
109
110
    devices_links = config.devices_links
    file_name_to_device_name = {v:k for k,v in config.get_devices_names_and_file().items()}
a2f47fb6   Alexis Koralewski   updating display ...
111
    active_devices = config.get_active_devices()
6686d675   Alexis Koralewski   new version of ob...
112
113
114
115
116
    """
    Alternative solutions:
        - test_device -> we're using a recursive function to get all nested dicitonaries but we loose a level of 
        "precision" (no separations of capabilities for example)
        - device_detail -> we use multiple function to get specific value (not global enougth, if we had another key we have to change the code)
ee2a5e47   Alexis Koralewski   New version of ob...
117
    """
6686d675   Alexis Koralewski   new version of ob...
118
119
120


    device_detail = { re.sub("^_","",key):value for key,value in config.get_devices()[device_name].items()}
ee2a5e47   Alexis Koralewski   New version of ob...
121
    #test_device=  get_nested_dictionaries_as_list(device_detail,[])
6686d675   Alexis Koralewski   new version of ob...
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
    
    if config.get_device_power(device_name) is not None:
        # We're removing "_" at the beginning of each key :
        power = { re.sub("^_","",key):value for key,value in config.get_device_power(device_name).items() }
    else :
        power = None
    if config.get_device_connector(device_name) is not None:
        # We're removing "_" at the beginning of each key :
        connector = { re.sub("^_","",key):value for key,value in config.get_device_connector(device_name).items() }
    else:
        connector = None
    
    capabilities = []
    if config.get_device_capabilities(device_name) is not None:
        copy_capabilities = config.get_device_capabilities(device_name)
        for capability in copy_capabilities:
            # We're removing "_" at the beginning of each key :
            capabilities.append( { re.sub("^_","",key):value for key,value in capability.items() })
    
        
    device_config = device_detail["device_config"]
    # We're removing "_" at the beginning of each key :
    device_detail["device_config"] = { re.sub("^_","",key):value for key,value in device_config.items() }
    # Remove power key as we have this information in the same 
    if power is not None:
        device_detail["device_config"].pop("power")
    if connector is not None:
        device_detail["device_config"].pop("connector")
    if len(capabilities)!= 0:
        device_detail["device_config"].pop("CAPABILITIES")
a2f47fb6   Alexis Koralewski   updating display ...
152
    return render(request,"obsconfig/device_details.html", { "device_detail" : device_detail, "power" : power, "connector" : connector, "capabilities" : capabilities,"devices_links":devices_links,"file_name_to_device_name": file_name_to_device_name,"active_devices":active_devices} )
ee2a5e47   Alexis Koralewski   New version of ob...
153
154
    
    #return render(request,"obsconfig/device_details.html", { "device_detail" : device_detail, "config" : config_device })
cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
155

cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
156
@login_required
6686d675   Alexis Koralewski   new version of ob...
157
@level_required("Admin","Observer","Management","Operator","Unit-PI","TAC")
cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
158
def obs_astronomer_config(request):
d0501f5e   Alexis Koralewski   use environment v...
159
    config = ConfigPyros(os.environ["PATH_TO_OBSCONF_FILE"])
cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
    units = config.get_units()
    units_topologies = {}
    # for each unit
    for unit_name in units:
       
        units_topologies[unit_name] = config.get_topology(units[unit_name])
        # removing channel_groups, not useful in this view
        units_topologies[unit_name].pop("CHANNEL_GROUPS")
        # for each category (security, mount, channels)
        for category in units_topologies[unit_name]:
            if category != "CHANNELS":
                # Security and Mount are directly a dictionary containing the attributes of those categories
                # However, component_agents is a list so we need to iterate through this list
                for component_agent in units_topologies[unit_name][category]["COMPONENT_AGENTS"]:
                    component_name = list(component_agent.keys())[0]
                    agent = component_agent[component_name]
                    device_of_agent = config.get_device_for_agent(units[unit_name],agent)
                    # get the index of the current component, agent couple
                    index = units_topologies[unit_name][category]["COMPONENT_AGENTS"].index(component_agent)
                    units_topologies[unit_name][category]["COMPONENT_AGENTS"][index][component_name] = device_of_agent
            else:
                # Channels is composed of a list of channel, we're looping through it
                for channel_name in units_topologies[unit_name]["CHANNELS"] :
                    for component_agent in units_topologies[unit_name]["CHANNELS"][channel_name]["COMPONENT_AGENTS"] :
                        component_name = list(component_agent.keys())[0]
                        agent = component_agent[component_name]
                        device_of_agent = config.get_device_for_agent(units[unit_name],agent)
                        index = units_topologies[unit_name]["CHANNELS"][channel_name]["COMPONENT_AGENTS"].index(component_agent)
                        units_topologies[unit_name]["CHANNELS"][channel_name]["COMPONENT_AGENTS"][index][component_name] = device_of_agent

    return render(request,"obsconfig/obs_astronomer_config.html", { "units_topologies" : units_topologies })


cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
193
@login_required
6686d675   Alexis Koralewski   new version of ob...
194
@level_required("Admin")
cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
195
def obs_agents_config(request):
d0501f5e   Alexis Koralewski   use environment v...
196
    config = ConfigPyros(os.environ["PATH_TO_OBSCONF_FILE"])
cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
197
198
199
200
201
    units = config.get_units()
    units_topologies = {}
    active_agents_by_unit = {}
    # for each unit
    for unit_name in units:
b7becde4   Alexis Koralewski   Updating UI (foot...
202
        agents = config.get_agents(units[unit_name])
cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
203
204
205
206
207
208
209
210
211
212
213
214
        # list of active agents of the current unit
        active_agents_by_unit[unit_name] = config.get_active_agents(config.get_unit_by_name(unit_name))
        # topology of the current unit
        units_topologies[unit_name] = config.get_topology(units[unit_name])
        # removing channel_groups, not useful in this view
        units_topologies[unit_name].pop("CHANNEL_GROUPS")
        for category in units_topologies[unit_name]:
            if category != "CHANNELS":
                # Security and Mount are directly a dictionary containing the attributes of those categories
                # However, component_agents is a list so we need to iterate through this list
                for component_agent in units_topologies[unit_name][category]["COMPONENT_AGENTS"]:
                    component_name = list(component_agent.keys())[0]
b7becde4   Alexis Koralewski   Updating UI (foot...
215
                    agent = agents[component_agent[component_name]]
cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
216
217
218
219
220
221
222
223
                     # get the index of the current component, agent couple
                    index = units_topologies[unit_name][category]["COMPONENT_AGENTS"].index(component_agent)
                    units_topologies[unit_name][category]["COMPONENT_AGENTS"][index][component_name] = agent
            else:
                # Channels is composed of a list of channel, we're looping through it
                for channel_name in units_topologies[unit_name]["CHANNELS"] :
                    for component_agent in units_topologies[unit_name]["CHANNELS"][channel_name]["COMPONENT_AGENTS"] :
                        component_name = list(component_agent.keys())[0]
b7becde4   Alexis Koralewski   Updating UI (foot...
224
                        agent = agents[component_agent[component_name]]
cc4e5a62   Alexis Koralewski   Add ConfigPyrosv2...
225
226
227
228
                         # get the index of the current component, agent couple
                        index = units_topologies[unit_name]["CHANNELS"][channel_name]["COMPONENT_AGENTS"].index(component_agent)
                        units_topologies[unit_name]["CHANNELS"][channel_name]["COMPONENT_AGENTS"][index][component_name] = agent

d0501f5e   Alexis Koralewski   use environment v...
229
230
231
232
    return render(request,"obsconfig/obs_agents_config.html", { "units_topologies" : units_topologies, "active_agents_by_unit" : active_agents_by_unit})


@login_required
aaf35888   Alexis Koralewski   add unit-board to...
233
@level_required("Admin","Operator","Unit-PI","Unit-board")
d0501f5e   Alexis Koralewski   use environment v...
234
235
def edit_config(request):
    config = ConfigPyros(os.environ["PATH_TO_OBSCONF_FILE"])
b755ff73   Alexis Koralewski   adding variables ...
236
    return render(request,"obsconfig/edit_config.html",{"config_file":config.raw_config})
d0501f5e   Alexis Koralewski   use environment v...
237
238

@login_required
aaf35888   Alexis Koralewski   add unit-board to...
239
@level_required("Admin","Operator","Unit-PI","Unit-board")
d0501f5e   Alexis Koralewski   use environment v...
240
241
242
243
244
245
def verify_config(request):
    if request.POST.get("config"):
        temp_config_file = tempfile.NamedTemporaryFile(mode = 'w+',suffix=".yml")
        temp_config_file.write(request.POST.get("config"))
        temp_config_file.seek(0)
        response_data = {}
81f42637   Alexis Koralewski   F14 : When editin...
246
247
248
249
250
251
252
253
254
255
256
257
        try:
            config_file = yaml.safe_load(temp_config_file.read())
        except yaml.YAMLError as exc:
            if hasattr(exc, 'problem_mark'):
                yaml_error_message = ""
                if exc.context != None:
                    yaml_error_message += str(exc.problem_mark) + '\n  ' + str(exc.problem) + ' ' + str(exc.context) 
                else:
                    yaml_error_message = str(exc.problem_mark.name) + '\n  ' +str(exc.problem)
                response_data["is_valid"] = False
                response_data["yaml_error_message"] = yaml_error_message
            return HttpResponse(json.dumps(response_data), content_type="application/json")
d0501f5e   Alexis Koralewski   use environment v...
258
259
260
261
        temp_config_file.seek(0)
        schema = config_file["schema"]
        errors = []
        if schema == None:
b755ff73   Alexis Koralewski   adding variables ...
262
            response_data["is_valid"] = False
d0501f5e   Alexis Koralewski   use environment v...
263
264
265
266
267
268
269
270
271
272
273
274
275
276
            response_data["message"] = "Missing schema"
        schema_path = os.path.join(os.environ["DJANGO_PATH"],"../../../config/schemas/")
        config = ConfigPyros.check_config(temp_config_file.name,schema_path+schema)
        if type(config) == bool and config:
            response_data["is_valid"] = True
        else:    
            response_data["is_valid"] = False
            for error in config:
                errors.append((f"Error : {str(error).split('. Path')[0]}",f"Path to error : '{error.path}'"))
            response_data["message"] = errors
        temp_config_file.close()
        return HttpResponse(json.dumps(response_data), content_type="application/json")

@login_required
aaf35888   Alexis Koralewski   add unit-board to...
277
@level_required("Admin","Operator","Unit-PI","Unit-board")
d0501f5e   Alexis Koralewski   use environment v...
278
279
280
def save_config(request):
    if request.POST:
        if request.POST["config"]:
15355c5b   Alexis Koralewski   use environment v...
281
            with open(os.environ["PATH_TO_OBSCONF_FILE"],"w") as obs_config_file:
d0501f5e   Alexis Koralewski   use environment v...
282
283
284
                obs_config_file.write(request.POST.get("config"))


c27a895d   Alexis Koralewski   add display of ra...
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
            return HttpResponse("Ok !")

@login_required
@level_required("Admin","Unit-PI","Unit-board","Operator","Observer")
@csrf_exempt
def view_raw_component_config_file(request):
    COMPONENT_PATH = os.path.join(os.environ["DJANGO_PATH"],"../../../config/components/")

    if request.POST:
        try:
            yaml_file = open(COMPONENT_PATH+request.POST["yaml_file"])
            content = yaml_file.readlines()
        except:
            content = "Component defined within the device configuration file"
        return HttpResponse(content)

@login_required
@level_required("Admin","Unit-PI","Unit-board","Operator","Observer")
@csrf_exempt
def view_raw_generic_device_config_file(request):
    GENERIC_DEVICES_PATH = os.path.join(os.environ["DJANGO_PATH"],"../../../config/devices/")
    if request.POST:
        yaml_file = open(GENERIC_DEVICES_PATH+request.POST["yaml_file"])
        content = yaml_file.readlines()
        return HttpResponse(content)

@login_required
@level_required("Admin","Unit-PI","Unit-board","Operator","Observer")
@csrf_exempt
def view_raw_device_config_file(request):
    obs_folder = os.environ["PATH_TO_OBSCONF_FOLDER"]
    if request.POST:
        yaml_file = open(obs_folder+request.POST["yaml_file"])
        content = yaml_file.readlines()
53ce3372   Alexis Koralewski   add diagrams that...
319
320
321
322
323
324
        return HttpResponse(content)

@login_required
@level_required("Admin","Unit-PI","Unit-board","Operator","Observer")
def obs_config_help(request):
    return render(request,"obsconfig/obs_config_help.html")