source: ogClient-Git/src/live/ogOperations.py @ 81ee4b0

Last change on this file since 81ee4b0 was 81ee4b0, checked in by Jose M. Guisado <jguisado@…>, 3 years ago

live: generate cache.txt file in refresh

Generates a cache.txt file if a cache partition is detected.

OpenGnsys stores information about stored images in its 'cache'
partition via a text file.

The file is stored in a samba shared directory, mounted at
'/opt/opengnsys/log/' in a live client. The file name is '{ip}.cache.txt'.

Previously, the generation of this file was delegated to external bash
scripts.

  • Property mode set to 100644
File size: 12.8 KB
Line 
1#
2# Copyright (C) 2020-2021 Soleta Networks <info@soleta.eu>
3#
4# This program is free software: you can redistribute it and/or modify it under
5# the terms of the GNU Affero General Public License as published by the
6# Free Software Foundation; either version 3 of the License, or
7# (at your option) any later version.
8
9import os
10import subprocess
11
12import fdisk
13
14from src.ogClient import ogClient
15from src.ogRest import ThreadState
16from src.live.partcodes import GUID_MAP
17
18from src.utils.net import ethtool
19from src.utils.menu import generate_menu
20from src.utils.fs import mount_mkdir, umount, get_usedperc
21from src.utils.probe import os_probe, cache_probe
22from src.utils.disk import get_disks
23from src.utils.cache import generate_cache_txt
24
25
26OG_SHELL = '/bin/bash'
27
28class OgLiveOperations:
29    def __init__(self, config):
30        self._url = config['opengnsys']['url']
31        self._url_log = config['opengnsys']['url_log']
32
33    def _restartBrowser(self, url):
34        try:
35            proc = subprocess.call(["pkill", "-9", "browser"])
36            proc = subprocess.Popen(["browser", "-qws", url])
37        except:
38            raise ValueError('Error: cannot restart browser')
39
40    def _refresh_payload_disk(self, cxt, part_setup, num_disk):
41        part_setup['disk'] = str(num_disk)
42        part_setup['disk_type'] = 'DISK'
43        part_setup['code'] = '2' if cxt.label.name == 'gpt' else '1'
44        part_setup['partition'] = '0'
45        part_setup['filesystem'] = ''
46        part_setup['os'] = ''
47        part_setup['size'] = str(cxt.nsectors * cxt.sector_size // 1024)
48        part_setup['used_size'] = '0'
49
50    def _refresh_payload_partition(self, cxt, pa, part_setup, disk):
51        parttype = cxt.partition_to_string(pa, fdisk.FDISK_FIELD_TYPEID)
52        fstype = cxt.partition_to_string(pa, fdisk.FDISK_FIELD_FSTYPE)
53        padev = cxt.partition_to_string(pa, fdisk.FDISK_FIELD_DEVICE)
54        size = cxt.partition_to_string(pa, fdisk.FDISK_FIELD_SIZE)
55        partnum = pa.partno + 1
56        source = padev
57        target = padev.replace('dev', 'mnt')
58
59        if cxt.label.name == 'gpt':
60            code = GUID_MAP.get(parttype, 0x0)
61        else:
62            code = int(parttype, base=16)
63
64        if mount_mkdir(source, target):
65            probe_result = os_probe(target)
66            part_setup['os'] = probe_result
67            part_setup['used_size'] = get_usedperc(target)
68            umount(target)
69        else:
70            part_setup['os'] = ''
71            part_setup['used_size'] = '0'
72
73
74        part_setup['disk_type'] = ''
75        part_setup['partition'] = str(partnum)
76        part_setup['filesystem'] = fstype.upper() if fstype else 'EMPTY'
77        # part_setup['code'] = hex(code).removeprefix('0x')
78        part_setup['code'] = hex(code)[2:]
79        part_setup['size'] = str(int(size) // 1024)
80
81    def _refresh_part_setup_cache(self, cxt, pa, part_setup, cache):
82        padev = cxt.partition_to_string(pa, fdisk.FDISK_FIELD_DEVICE)
83        if padev == cache:
84            part_setup['filesystem'] = 'CACHE'
85            part_setup['code'] = 'ca'
86
87
88    def parseGetConf(self, out):
89        parsed = {'serial_number': '',
90              'disk_setup': list(),
91              'partition_setup': list()}
92        configs = out.split('\n')
93        for line in configs[:-1]:
94            if 'ser=' in line:
95                parsed['serial_number'] = line.partition('ser=')[2]
96            else:
97                part_setup = {}
98                params = dict(param.split('=') for param in line.split('\t'))
99                # Parse partition configuration.
100                part_setup['disk'] = params['disk']
101                part_setup['disk_type'] = params.get('dtype', '')
102                part_setup['partition'] = params['par']
103                part_setup['code'] = params['cpt']
104                part_setup['filesystem'] = params['fsi']
105                part_setup['os'] = params['soi']
106                part_setup['size'] = params['tam']
107                part_setup['used_size'] = params['uso']
108                if part_setup['partition'] == '0':
109                    parsed['disk_setup'].append(part_setup)
110                else:
111                    parsed['partition_setup'].append(part_setup)
112        return parsed
113
114    def poweroff(self):
115        if os.path.exists('/scripts/oginit'):
116            cmd = f'source {ogClient.OG_PATH}etc/preinit/loadenviron.sh; ' \
117                  f'{ogClient.OG_PATH}scripts/poweroff'
118            subprocess.call([cmd], shell=True, executable=OG_SHELL)
119        else:
120            subprocess.call(['/sbin/poweroff'])
121
122    def reboot(self):
123        if os.path.exists('/scripts/oginit'):
124            cmd = f'source {ogClient.OG_PATH}etc/preinit/loadenviron.sh; ' \
125                  f'{ogClient.OG_PATH}scripts/reboot'
126            subprocess.call([cmd], shell=True, executable=OG_SHELL)
127        else:
128            subprocess.call(['/sbin/reboot'])
129
130    def shellrun(self, request, ogRest):
131        cmd = request.getrun()
132        cmds = cmd.split(";|\n\r")
133
134        self._restartBrowser(self._url_log)
135
136        try:
137            ogRest.proc = subprocess.Popen(cmds,
138                               stdout=subprocess.PIPE,
139                               shell=True,
140                               executable=OG_SHELL)
141            (output, error) = ogRest.proc.communicate()
142        except:
143            raise ValueError('Error: Incorrect command value')
144
145        cmd_get_conf = f'{ogClient.OG_PATH}interfaceAdm/getConfiguration'
146        result = subprocess.check_output([cmd_get_conf], shell=True)
147        self._restartBrowser(self._url)
148
149        return output.decode('utf-8')
150
151    def session(self, request, ogRest):
152        disk = request.getDisk()
153        partition = request.getPartition()
154        cmd = f'{ogClient.OG_PATH}interfaceAdm/IniciarSesion {disk} {partition}'
155
156        try:
157            ogRest.proc = subprocess.Popen([cmd],
158                               stdout=subprocess.PIPE,
159                               shell=True,
160                               executable=OG_SHELL)
161            (output, error) = ogRest.proc.communicate()
162        except:
163            raise ValueError('Error: Incorrect command value')
164
165        return output.decode('utf-8')
166
167    def software(self, request, path, ogRest):
168        disk = request.getDisk()
169        partition = request.getPartition()
170
171        self._restartBrowser(self._url_log)
172
173        try:
174            cmd = f'{ogClient.OG_PATH}interfaceAdm/InventarioSoftware {disk} ' \
175                  f'{partition} {path}'
176
177            ogRest.proc = subprocess.Popen([cmd],
178                               stdout=subprocess.PIPE,
179                               shell=True,
180                               executable=OG_SHELL)
181            (output, error) = ogRest.proc.communicate()
182        except:
183            raise ValueError('Error: Incorrect command value')
184
185        self._restartBrowser(self._url)
186
187        software = ''
188        with open(path, 'r') as f:
189            software = f.read()
190        return software
191
192    def hardware(self, path, ogRest):
193        self._restartBrowser(self._url_log)
194
195        try:
196            cmd = f'{ogClient.OG_PATH}interfaceAdm/InventarioHardware {path}'
197            ogRest.proc = subprocess.Popen([cmd],
198                               stdout=subprocess.PIPE,
199                               shell=True,
200                               executable=OG_SHELL)
201            (output, error) = ogRest.proc.communicate()
202        except:
203            raise ValueError('Error: Incorrect command value')
204
205        self._restartBrowser(self._url)
206
207        return output.decode('utf-8')
208
209    def setup(self, request, ogRest):
210        table_type = request.getType()
211        disk = request.getDisk()
212        cache = request.getCache()
213        cache_size = request.getCacheSize()
214        partlist = request.getPartitionSetup()
215        cfg = f'dis={disk}*che={cache}*tch={cache_size}!'
216
217        for part in partlist:
218            cfg += f'par={part["partition"]}*cpt={part["code"]}*' \
219                   f'sfi={part["filesystem"]}*tam={part["size"]}*' \
220                   f'ope={part["format"]}%'
221
222            if ogRest.terminated:
223                break
224
225        cmd = f'{ogClient.OG_PATH}interfaceAdm/Configurar {table_type} {cfg}'
226        try:
227            ogRest.proc = subprocess.Popen([cmd],
228                               stdout=subprocess.PIPE,
229                               shell=True,
230                               executable=OG_SHELL)
231            (output, error) = ogRest.proc.communicate()
232        except:
233            raise ValueError('Error: Incorrect command value')
234
235        cmd_get_conf = f'{ogClient.OG_PATH}interfaceAdm/getConfiguration'
236        result = subprocess.check_output([cmd_get_conf], shell=True)
237        self._restartBrowser(self._url)
238
239        return self.parseGetConf(result.decode('utf-8'))
240
241    def image_restore(self, request, ogRest):
242        disk = request.getDisk()
243        partition = request.getPartition()
244        name = request.getName()
245        repo = request.getRepo()
246        ctype = request.getType()
247        profile = request.getProfile()
248        cid = request.getId()
249        cmd = f'{ogClient.OG_PATH}interfaceAdm/RestaurarImagen {disk} {partition} ' \
250              f'{name} {repo} {ctype}'
251
252        self._restartBrowser(self._url_log)
253
254        try:
255            ogRest.proc = subprocess.Popen([cmd],
256                               stdout=subprocess.PIPE,
257                               shell=True,
258                               executable=OG_SHELL)
259            (output, error) = ogRest.proc.communicate()
260            if (ogRest.proc.returncode):
261                raise Exception
262        except:
263            raise ValueError('Error: Incorrect command value')
264
265        cmd_get_conf = f'{ogClient.OG_PATH}interfaceAdm/getConfiguration'
266        result = subprocess.check_output([cmd_get_conf], shell=True)
267        self._restartBrowser(self._url)
268
269        return output.decode('utf-8')
270
271    def image_create(self, path, request, ogRest):
272        disk = request.getDisk()
273        partition = request.getPartition()
274        name = request.getName()
275        repo = request.getRepo()
276        cmd_software = f'{ogClient.OG_PATH}interfaceAdm/InventarioSoftware {disk} ' \
277                   f'{partition} {path}'
278        cmd_create_image = f'{ogClient.OG_PATH}interfaceAdm/CrearImagen {disk} ' \
279                   f'{partition} {name} {repo}'
280
281        self._restartBrowser(self._url_log)
282
283        try:
284            ogRest.proc = subprocess.Popen([cmd_software],
285                               stdout=subprocess.PIPE,
286                               shell=True,
287                               executable=OG_SHELL)
288            (output, error) = ogRest.proc.communicate()
289        except:
290            raise ValueError('Error: Incorrect command value')
291
292        if ogRest.terminated:
293            return
294
295        try:
296            ogRest.proc = subprocess.Popen([cmd_create_image],
297                               stdout=subprocess.PIPE,
298                               shell=True,
299                               executable=OG_SHELL)
300            ogRest.proc.communicate()
301        except:
302            raise ValueError('Error: Incorrect command value')
303
304        if ogRest.proc.returncode != 0:
305            raise ValueError('Error: Image creation failed')
306
307        with open('/tmp/image.info') as file_info:
308            line = file_info.readline().rstrip()
309
310        image_info = {}
311
312        (image_info['clonator'],
313         image_info['compressor'],
314         image_info['filesystem'],
315         image_info['datasize'],
316         image_info['clientname']) = line.split(':', 5)
317
318        os.remove('/tmp/image.info')
319
320        self._restartBrowser(self._url)
321
322        return image_info
323
324    def refresh(self, ogRest):
325        self._restartBrowser(self._url_log)
326
327        cache = cache_probe()
328        disks = get_disks()
329        interface = os.getenv('DEVICE')
330        link = ethtool(interface)
331        parsed = { 'serial_number': '',
332                'disk_setup': [],
333                'partition_setup': [],
334                'link': link
335        }
336
337        for num_disk, disk in enumerate(get_disks(), start=1):
338            print(disk)
339            part_setup = {}
340            try:
341                cxt = fdisk.Context(device=f'/dev/{disk}')
342            except:
343                continue
344
345            self._refresh_payload_disk(cxt, part_setup, num_disk)
346            parsed['disk_setup'].append(part_setup)
347
348            for pa in cxt.partitions:
349                part_setup = part_setup.copy()
350                self._refresh_payload_partition(cxt, pa, part_setup, disk)
351                self._refresh_part_setup_cache(cxt, pa, part_setup, cache)
352                parsed['partition_setup'].append(part_setup)
353
354        generate_menu(parsed['partition_setup'])
355        generate_cache_txt()
356        self._restartBrowser(self._url)
357
358        return parsed
359
360    def probe(self, ogRest):
361
362        interface = os.getenv('DEVICE')
363        speed = ethtool(interface)
364
365        return {'status': 'OPG' if ogRest.state != ThreadState.BUSY else 'BSY',
366                'speed': speed}
Note: See TracBrowser for help on using the repository browser.