annuaires_pg.py 11.4 KB
Newer Older
1
# -*- mode: python; coding: utf-8 -*-
2

Daniel Stan's avatar
Daniel Stan committed
3
import os
4
import psycopg2
5

6
from functools import wraps
7

8
import time
9
import socket
10

11
conn = None
12 13
# : échec définitif, on raise une exception direct
def_failed = False
14 15
def _need_conn(f):
    """Décorateur à appliquer aux fonctions nécessitant une connexion pgsql"""
16 17
    retries = 1
    delay = 5
18 19
    @wraps(f)
    def first_connect(*args, **kwargs):
20 21
        global conn, def_failed
        if def_failed:
22
            raise NameError("La connexion à la pase postgresql ne peut être établie.")
23 24
        attempts = 0
        while not conn or not attempts:
25 26 27 28
            host = os.getenv('DBG_ANNUAIRE', 'pgsql.v4.adm.crans.org')

            # Test habituel sur vo:
            if host == '1' or __name__.endswith('annuaires_pg_test'):
29
                host='localhost'
30

31
            # "connecting …"
32
            try:
33
                if not conn:
34 35 36
                    if attempts:
                        # Attend un peu avant de reessayer
                        time.sleep(delay)
Daniel Stan's avatar
Daniel Stan committed
37
                    conn = psycopg2.connect(user='crans_ro', database='django',
38
                                            host=host)
39 40
                    if conn.status != psycopg2.extensions.STATUS_IN_TRANSACTION:
                        conn.set_session(autocommit=True)
41
                return f(*args, **kwargs)
42
            except psycopg2.OperationalError:
43
                attempts += 1
44 45 46
                conn = None   # Connexion morte, on recommence à zéro
                if attempts > retries:
                    def_failed = True
47
                    raise NameError("Trop d'erreur de connection %i tentatives sur %i" % (attempts, retries))
48 49
            # Les scripts appelant annuaires_pg n'ont pas à connaître le
            # backend pgsql. On utilise donc une exception plus standard
50
    return first_connect
51

52
# Le v est virtuel.
Gabriel Detraz's avatar
Batk  
Gabriel Detraz committed
53
bat_switchs = ["a", "b", "c", "g", "h", "i", "j", "k", "m", "o", "p", "r", "v"]
54

55 56 57 58
class ChbreNotFound(ValueError):
    """Lorsqu'une chambre n'existe pas"""
    pass

59
@_need_conn
60 61 62 63 64 65
def chbre_prises(batiment, chambre = None):
    """Correspondance chambre -> prise"""
    batiment = batiment.lower()
    if chambre:
        chambre = chambre.lower()
        cur = conn.cursor()
Daniel Stan's avatar
Daniel Stan committed
66
        cur.execute("SELECT prise_crans FROM prises_prise WHERE (batiment, chambre) = (%s, %s)", (batiment, chambre))
67 68 69
        try:
            return "%03d" % cur.fetchone()[0]
        except TypeError:
70
            raise ChbreNotFound("Chambre inexistante bat %r, chbre %r" % (batiment, chambre))
71 72
    else:
        cur = conn.cursor()
Daniel Stan's avatar
Daniel Stan committed
73
        cur.execute("SELECT chambre, prise_crans FROM prises_prise WHERE batiment = %s", batiment)
74 75 76 77
        ret = {}
        for chambre, prise_crans in cur.fetchall():
            ret[chambre] = "%03d" % prise_crans
        if not ret:
78
            raise ValueError("Batiment %s inexistant" % batiment)
79 80
        return ret

81
@_need_conn
82 83 84 85
def chbre_commentaire(batiment, chambre):
    """ Renvoie le commentaire associé à la chambre """
    global conn
    batiment = batiment.lower()
86
    chambre = chambre.lower()
87
    cur = conn.cursor()
Daniel Stan's avatar
Daniel Stan committed
88
    cur.execute("SELECT commentaire FROM prises_prise WHERE (batiment, chambre) = (%s,%s)", (batiment, chambre))
89 90 91
    try:
        return cur.fetchone()[0]
    except TypeError:
92
        raise ValueError("Chambre inexistante bat %r, chbre %r" % (batiment, chambre))
93

94 95 96 97 98
@_need_conn
def lieux_public():
    cur = conn.cursor()
    cur.execute("SELECT batiment,chambre FROM prises_prise WHERE public=True")
    results = cur.fetchall()
Gabriel Detraz's avatar
Fix  
Gabriel Detraz committed
99
    return [batiment.upper()+chambre for batiment, chambre in results]
100

101 102 103 104 105 106 107
@_need_conn
def disabled_radius():
    cur = conn.cursor()
    cur.execute("SELECT batiment,chambre FROM prises_prise WHERE has_radius=False")
    results = cur.fetchall()
    return [batiment.upper()+chambre for batiment, chambre in results]

108 109 110 111 112 113
@_need_conn
def poe_enabled():
    cur = conn.cursor()
    cur.execute("SELECT UPPER(batiment)||TO_CHAR(prise_crans, 'FM099'), poe_status FROM prises_prise;")
    return cur.fetchall()

114
@_need_conn
115 116 117 118 119
def reverse(batiment, prise = None):
    """Correspondance prise -> chambre"""
    batiment = batiment.lower()
    if prise:
        cur = conn.cursor()
Daniel Stan's avatar
Daniel Stan committed
120
        cur.execute("SELECT chambre FROM prises_prise WHERE (batiment, prise_crans) = (%s, %s)", (batiment, int(prise)))
121 122 123
        try:
            return [chbre for (chbre,) in cur.fetchall()]
        except TypeError:
124
            raise ValueError("Prise %s inexistante" % prise)
125 126
    else:
        cur = conn.cursor()
Daniel Stan's avatar
Daniel Stan committed
127
        cur.execute("SELECT chambre, prise_crans FROM prises_prise WHERE batiment = %s", batiment)
128 129 130 131 132 133 134 135
        ret = {}
        for chambre, prise_crans in cur.fetchall():
            try:
                ret["%03d" % prise_crans].append(chambre)
            except KeyError:
                ret["%03d" % prise_crans] = [chambre]

        if not ret:
136
            raise ValueError("Batiment %s inexistant" % batiment)
137
        return ret
138
@_need_conn
139 140 141 142 143
def is_connected(batiment, chambre):
    """Cablage physique effectue ?"""
    batiment = batiment.lower()
    chambre = chambre.lower()
    cur = conn.cursor()
Daniel Stan's avatar
Daniel Stan committed
144
    cur.execute("SELECT cablage_effectue FROM prises_prise WHERE (batiment, chambre) = (%s, %s)", (batiment, chambre))
145 146 147 148
    return cur.fetchone()[0]

# Prises d'uplink, de machines du crans / Prises d'utilité CRANS
uplink_prises={ 'a' :
149 150 151 152 153 154 155 156
{ 49 : 'uplink->bata-2',  50 : 'libre-service',
 149 : 'uplink->bata-2', 150 : 'libre-service',
 424 : 'uplink->bata-2', 423 : 'libre-service',
 325 : 'uplink->bata-2', 326 : 'libre-service',
 201 : 'uplink->bata-0', 202 :  'uplink->bata-1',
 203 : 'uplink->bata-4', 204 :  'uplink->bata-3',
 224 : 'uplink->backbone',
 },
157
'b' :
158
{ 49 : 'libre-service',  50 : 'uplink->batb-4',
Gabriel Detraz's avatar
Gabriel Detraz committed
159
 135 : 'libre-service',
160 161 162
 149 : 'libre-service', 150 : 'uplink->batb-4',
 249 : 'libre-service', 250 : 'uplink->batb-4', # 249 morte ?! (olasd 21/01/2010)
 349 : 'libre-service', 350 : 'uplink->batb-4',
163 164
 401 : 'uplink->batb-0', 402 :  'uplink->batb-1',
 403 : 'uplink->batb-2', 404 :  'uplink->batb-3',
Hamza Dely's avatar
Hamza Dely committed
165
 405 : 'uplink->backbone',
166
 626 : 'uplink->backbone',
Gabriel Detraz's avatar
Gabriel Detraz committed
167
 },
168 169
'c' :
{ 49 : 'uplink->batc-3',  50 : 'libre-service',
170
 147 : 'uplink->batc-3',
171
 224 : 'uplink->batc-3',
172
 301 : 'uplink->batc-0', 302 : 'uplink->batc-1',
Daniel Stan's avatar
Daniel Stan committed
173 174 175 176
 304 : 'uplink->batc-4',
 303 : 'uplink->batc-2', 324 : 'uplink->backbone',
 426 : 'uplink->batc-3',
},
177
'g' :
178
{
179
  22 : 'uplink->backbone',
180
  23 : 'libre-service', 24 : 'uplink->batg-8',
181

182
  149 : 'uplink->batg-8',  150 : 'libre-service',
183

184
  247 : 'uplink->batg-8',
185

186
 449 : 'uplink->batg-9',  450 : 'libre-service',
187

188
 549 : 'uplink->batg-9',  550 : 'libre-service',
189

190
 649 : 'uplink->batg-9',  650 : 'libre-service',
191

192
 801 : 'uplink->batg-1',  802 : 'uplink->batg-2',
193

194
 825 : 'uplink->batg-0',
195 196 197
 823 : 'uplink->batg-9',

 901 : 'uplink->batg-4',  902 : 'uplink->batg-5',
198
 903 : 'uplink->batg-6',  904 : 'uplink->batg-8',
199 200
 },
'h' :
Gabriel Detraz's avatar
Gabriel Detraz committed
201 202 203
{ 1 : 'uplink->bath-2',  50 : 'libre-service',
 119 : 'uplink->bath-2', 150 : 'libre-service',
 221 : 'uplink->bath-0', 219 : 'uplink->bath-1',
204 205 206
 222 : 'uplink->bath-3', 223 : 'uplink->backbone',
 224 : 'libre-service',
 303 : 'uplink->bath-2' },
207 208 209 210
'i' :
{ 49 : 'uplink->bati-3',  50 : 'libre-service',
 149 : 'uplink->bati-3', 150 : 'libre-service',
 301 : 'uplink->bati-0', 302 : 'uplink->bati-1',
Gabriel Detraz's avatar
Gabriel Detraz committed
211
 328 : 'uplink->backbone' },
212 213
'j' :
{ 49 : 'uplink->batj-3', 50 : 'libre-service',
Maxime Bombar's avatar
Maxime Bombar committed
214
 147 : 'uplink->batj-3',
215
 223 : 'uplink->batj-3',  224 : 'libre-service',
Gabriel Detraz's avatar
Gabriel Detraz committed
216
 328 : 'uplink->backbone',
217
 301 : 'uplink->batj-0', 303 : 'uplink->batj-1',
Daniel Stan's avatar
Daniel Stan committed
218 219
 305 : 'uplink->batj-2', 307 : 'uplink->batj-4',
 421 : 'uplink->batj-3', 422 : 'libre-service',
220
},
Daniel Stan's avatar
Daniel Stan committed
221
'k' : {
222
 23 : 'uplink->backbone',
Daniel Stan's avatar
Daniel Stan committed
223
},
224 225 226 227 228 229 230 231
'm' :
{
 49 : 'libre-service', 50 : 'uplink->batm-7',
 149 : 'libre-service', 150 : 'uplink->batm-7',
 249 : 'libre-service',  250 : 'uplink->batm-7',
 349 : 'libre-service',  350 : 'uplink->batm-7',
 449 : 'libre-service',  450 : 'uplink->batm-7',
 549 : 'libre-service',  550 : 'uplink->batm-7',
232
 650 : 'uplink->batm-7',
233

234 235
 747 : 'libre-service', 750 : 'libre-service',
 751 : 'libre-service', 752 : 'libre-service',
236

237
 749 : 'uplink->backbone', 720 : 'uplink->batm-0',
238 239 240 241 242
 719 : 'uplink->batm-1', 718 : 'uplink->batm-2',
 717 : 'uplink->batm-3', 716 : 'uplink->batm-4',
 715 : 'uplink->batm-5', 714 : 'uplink->batm-6',
 },
 'p' :
243
{
244 245 246
 49 : 'uplink->batp-4 (R4.1)',
 149: 'uplink->batp-4 (R3.1)',
 249: 'uplink->batp-4 (R2.1)',
247
 347: 'uplink->batp-4 (R1.2)',
248 249
 401: 'uplink->batp-3', 402: 'uplink->batp-2',
 403: 'uplink->batp-1', 404: 'uplink->batp-0',
250
 405: 'libre-service', 409: 'uplink->bato-1',
251
},
252
 'o' :
253
 {
Daniel Stan's avatar
Daniel Stan committed
254 255 256
 25 : 'uplink->bato-1', 26 : 'libre-service',
 101 : 'uplink->bato-0', 121: 'uplink->NRD',
 122: 'uplink->backbone', 123: 'uplink->backbone (unused)',
257
 124: 'uplink->batp-4',
Daniel Stan's avatar
Daniel Stan committed
258
 } ,
Gabriel Detraz's avatar
Gabriel Detraz committed
259 260 261 262
 'r' :
 {
 27 : 'uplink->backbone', 28 : 'libre-service',
 } ,
Daniel Stan's avatar
Daniel Stan committed
263 264
 'v' :
 {
Daniel Stan's avatar
Daniel Stan committed
265
  23: 'uplink', 24 : 'uplink',
Daniel Stan's avatar
Daniel Stan committed
266
 } ,
Daniel Stan's avatar
Daniel Stan committed
267 268 269
'backbone' : #For your consideration
 {
 #A: 12eth+12fibre,  B: 24 eth
Daniel Stan's avatar
Daniel Stan committed
270
 'A1': 'odlyd',     'B1': 'bata',
271 272
 'A2': 'zamokv5',   'B2': 'libre-service',
 'A3': 'sable',     'B3': 'sable-zrt',
Daniel Stan's avatar
Daniel Stan committed
273
 'A4': 'komaz',     'B4': 'fy',
274 275 276 277 278 279 280
 'A5': 'zbee',      'B5': 'switch-ilo',
 'A6': 'thot',      'B6': 'vigile 0B',
 'A7': 'odlyd',      'B7': 'kdell',
 'A8': 'odlyd-zrt', 'B8': 'batb',
 'A9': 'osm3',      'B9': '2b',
 'A10': 'osm3-idrac','B10': 'fz',
 'A11': 'osm4-idrac','B11': 'ft',
281
                    'B12': 'nols2',
282
 'A13': 'batm',
283
 'A14': 'batp',     'B14': 'zamok',
284
 'A15': 'batc',     'B15': 'charybde',
285
 'A16': 'bato',
Gabriel Detraz's avatar
Gabriel Detraz committed
286 287
 'A17': 'bati',
 'A18': 'bath',     'B18': 'nols',
Daniel Stan's avatar
Daniel Stan committed
288 289
 'A19': 'batj',
 'A20': 'batg',
Gabriel Detraz's avatar
Gabriel Detraz committed
290 291
 'A21': 'batk',     'B21': 'osm1',
 'A22': 'batr',     'B22': 'osm1-ilo',
292 293
                    'B23': 'osm2',
                    'B24': 'osm2-ilo',
Daniel Stan's avatar
Daniel Stan committed
294
 },
295 296
}

297 298 299
_SPECIAL_SWITCHES = [
    'backbone.switches.crans.org',
    'minigiga.switches.crans.org',
300 301
]
_HIDDEN_SWITCHES = [
302
    'batv-0.switches.crans.org',
303
]
304

305 306 307
def guess_switch_fqdn(switch_name):
    """Retourne le FQDN d'un switch à partir de son nom"""

308 309 310 311 312
    try:
        return socket.gethostbyname_ex(switch_name + ".switches.crans.org")[0]
    except socket.gaierror:
        pass

313 314 315 316 317 318 319
    try:
        return socket.gethostbyname_ex(switch_name)[0]
    except socket.gaierror:
        pass

    raise socket.gaierror

320
def all_switchs(bat=None, hide=_SPECIAL_SWITCHES + _HIDDEN_SWITCHES):
321 322 323 324 325 326 327 328
    """Retourne la liste des switchs pour un batiment.

    Si bat est donné, seulement pour le bâtiment demandé, sinon pour
    tous les bâtiments. bat peut être une liste aussi. Le backbone n'est
    pas pris en compte. La convention est batx-y sauf si y=0 et on a donc
    simplement batx"""

    if bat == None:
329
        bat = list(bat_switchs)
330 331 332
    if type(bat) not in [ tuple, list ] :
        bat = [bat]
    switchs = []
333 334 335
    for b in bat:
        indexes = set(n/100 for n in uplink_prises[b])
        for i in indexes:
336 337 338 339 340 341
            switch_name = "bat%s-%s" % (b, i)
            try:
                hostname = guess_switch_fqdn(switch_name)
            except socket.gaierror:
                print "Le switch %s ne semble pas exister." % (switch_name,)
                continue
342 343
            if hostname not in hide:
                switchs.append(hostname)
344
    switchs = set(switchs)
345
    # on ajoute quand-même le backbone et/ou multiprise-v6 si demandé
346 347
    switchs |= (set(_SPECIAL_SWITCHES) - set(hide))
    switchs = list(switchs)
348 349

    switchs.sort()
350 351 352 353
    return switchs

# Locaux clubs : lecture dans chbre_prises et ajout des locaux dans les bats non
# manageables
Gabriel Detraz's avatar
Gabriel Detraz committed
354
def locaux_clubs():
355
    """ Retourne le dictionaire des locaux club : {bat: [locaux]} """
Gabriel Detraz's avatar
Gabriel Detraz committed
356
    locaux_clubs = {key: key for key in lieux_public()}
357
    return locaux_clubs