from typing import List, IO from cocluto.inventory import Inventory from cocluto.SimpaDbUtil import SqlDatabaseReader, SqlFile from urllib.parse import urlparse from abc import ABC, abstractmethod from sys import stdout import re class Table(): columns_data: List[List[str]] column_labels: List[str] def __init__(self): self.columns_data = [] self.columns_labels = [] class TableRenderer(ABC): @abstractmethod def render_table(self, table: Table, file: IO): pass class TsvTableRenderer(TableRenderer): def render_table(self, table: Table, file: IO): assert len(table.column_labels) == len(table.columns_data) num_cols = len(table.columns_data) num_rows = len(table.columns_data[0]) file.write('#%s\n' % '\t'.join(table.column_labels)) for irow in range(num_rows): row = [] for icol in range(num_cols): row.append(table.columns_data[icol][irow]) file.write('%s\n' % '\t'.join(row)) class MarkdownTableRenderer(TableRenderer): @staticmethod def get_md_table_separator_line(columns_width): sep_line = '|' for col_width in columns_width: sep_line += '-' * col_width sep_line += '|' return sep_line def render_table(self, table: Table, file: IO): assert len(table.column_labels) == len(table.columns_data) num_cols = len(table.columns_data) num_rows = len(table.columns_data[0]) cols_width = [0 for col in range(num_cols)] for icol in range(num_cols): cols_width[icol] = max(cols_width[icol], len(table.column_labels[icol])) for irow in range(num_rows): cols_width[icol] = max(cols_width[icol], len(table.columns_data[icol][irow])) sep_line = MarkdownTableRenderer.get_md_table_separator_line([col_width + 2 for col_width in cols_width]) line_format = "|" + ''.join([" %%%ds |" % col_width for col_width in cols_width]) file.write('%s\n' % sep_line) file.write('%s\n' % (line_format % tuple(table.column_labels))) file.write('%s\n' % sep_line) for irow in range(num_rows): row = [] for icol in range(num_cols): row.append(table.columns_data[icol][irow]) file.write('%s\n' % (line_format % tuple(row))) file.write('%s\n' % sep_line) class WikiTableRenderer(TableRenderer): def render_table(self, table: Table, file: IO): assert len(table.column_labels) == len(table.columns_data) num_cols = len(table.columns_data) num_rows = len(table.columns_data[0]) cols_width = [0 for col in range(num_cols)] for icol in range(num_cols): cols_width[icol] = max(cols_width[icol], len(table.column_labels[icol])) for irow in range(num_rows): cols_width[icol] = max(cols_width[icol], len(table.columns_data[icol][irow])) cols_width = [col_width + 1 for col_width in cols_width] header_line_format = "^" + ''.join([" %%%ds ^" % col_width for col_width in cols_width]) line_format = "|" + ''.join([" %%%ds |" % col_width for col_width in cols_width]) file.write('%s\n' % (header_line_format % tuple(table.column_labels))) for irow in range(num_rows): row = [] for icol in range(num_cols): row.append(table.columns_data[icol][irow]) file.write('%s\n' % (line_format % tuple(row))) def print_machine_table(machines_fqdn: List[str], inventory: Inventory, out_file: IO, table_renderer: Table): table = Table() machines_name = [] machines_spec_id = [] machines_serial_number = [] for machine_fqdn in machines_fqdn: machine_name = machine_fqdn.split('.')[0] machine_spec_id = inventory.machine_name_to_machine_spec_id(machine_name) serial_number = inventory.get_machine_serial_number(machine_name) machines_name.append(machine_name) machines_spec_id.append(machine_spec_id) machines_serial_number.append(serial_number) table.column_labels = [ 'Nom', 'Modèle', 'Numéro de Série' ] table.columns_data = [machines_name, machines_spec_id, machines_serial_number] table_renderer.render_table(table, out_file) # get the list of machines hosted at the dsi def get_hosted_machines_fqdn(inventory: Inventory) -> List[str]: machines_fqdn = [] domain_name = 'ipr.univ-rennes1.fr' dsi_rack_ids = [ 'rack_dsi_h7', 'rack_dsi_i7' ] conditions = [('rack_id == "%s"' % rack_id) for rack_id in dsi_rack_ids] sql_query = 'select machine_id from rackable_machine_to_location where %s;' % ' or '.join(conditions) rows = inventory.query(sql_query) for (machine_id, ) in rows: match = re.match(r'(?P[a-z]+)(?P[0-9]+)_(?P[0-9]+)', machine_id) if match: # handle containers such as physix72_75 from_index = int(match['from']) to_index = int(match['to']) cluster_name = match['cluster_name'] # print(from_index, to_index) for machine_index in range(from_index, to_index + 1): machine_id = '%s%d' % (cluster_name, machine_index) if machine_id == 'physix75': machine_id = 'alambix75' # hack because the inventory expects all machines of a container such as physix72_75 to belong to the same cluster, and it's not the case here machines_fqdn.append('%s.%s' % (machine_id, domain_name)) else: if re.match('^switch', machine_id) is None: # ignore switches, as they belong to the dsi machines_fqdn.append('%s.%s' % (machine_id, domain_name)) return sorted(machines_fqdn) def main(): itinv_db_url = "file:///home/graffy/work/simpaweb/itinv.git/itinv.sql" url_parts = urlparse(itinv_db_url) sql_source = SqlFile(url_parts.path) sql_reader = SqlDatabaseReader(sql_source) inventory = Inventory(sql_reader) machines_fqdn = get_hosted_machines_fqdn(inventory) print('ipr machines hosted at the dsi (%d):' % len(machines_fqdn)) table_renderer = MarkdownTableRenderer() print_machine_table(machines_fqdn, inventory, stdout, table_renderer) if __name__ == '__main__': main()