umnetdb-utils 0.1.4__py3-none-any.whl → 0.1.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
umnetdb_utils/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  from .umnetequip import UMnetequip
2
2
  from .umnetinfo import UMnetinfo
3
3
  from .umnetdisco import Umnetdisco
4
- from .umnetdb import UMnetdb
4
+ from .umnetdb import UMnetdb
umnetdb_utils/base.py CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  from typing import Union
3
2
  from os import getenv
4
3
  import re
@@ -10,14 +9,16 @@ from sqlalchemy.orm import Session
10
9
 
11
10
  logger = logging.getLogger(__name__)
12
11
 
12
+
13
13
  class UMnetdbBase:
14
14
  """
15
15
  Base helper class
16
16
  """
17
+
17
18
  # set in child classes - you can use environment variables within curly braces here
18
19
  URL = None
19
20
 
20
- def __init__(self, env_file:str=".env"):
21
+ def __init__(self, env_file: str = ".env"):
21
22
  """
22
23
  Initiate a umnetdb object. Optionally provide a path to a file with environment variables
23
24
  containing the credentials for the database. If no file is provided and there's no ".env",
@@ -73,14 +74,24 @@ class UMnetdbBase:
73
74
  def __exit__(self, fexc_type, exc_val, exc_tb):
74
75
  self.close()
75
76
 
76
- def __getattr__(self, val:str):
77
+ def __getattr__(self, val: str):
77
78
  if self.session:
78
79
  return getattr(self.session, val)
79
80
 
80
81
  raise AttributeError(self)
81
82
 
82
- def _build_select(self, select, table, joins=None, where=None, order_by=None, limit=None, group_by=None, distinct=False) -> str:
83
- '''
83
+ def _build_select(
84
+ self,
85
+ select,
86
+ table,
87
+ joins=None,
88
+ where=None,
89
+ order_by=None,
90
+ limit=None,
91
+ group_by=None,
92
+ distinct=False,
93
+ ) -> str:
94
+ """
84
95
  Generic 'select' query string builder built from standard query components as input.
85
96
  The user is required to generate substrings for the more complex inputs
86
97
  (eg joins, where statements), this function just puts all the components
@@ -93,7 +104,7 @@ class UMnetdbBase:
93
104
  ex: "node_ip nip"
94
105
  :joins: a list of strings representing join statements. Include the actual 'join' part!
95
106
  ex: ["join node n on nip.mac = n.mac", "join device d on d.ip = n.switch"]
96
- :where: For a single where statement, provide a string. For multiple provide a list.
107
+ :where: For a single where statement, provide a string. For multiple provide a list.
97
108
  The list of statements are "anded". If you need "or", embed it in one of your list items
98
109
  DO NOT provide the keyword 'where' - it is auto-added.
99
110
  ex: ["node_ip.ip = '1.2.3.4'", "node.switch = '10.233.0.5'"]
@@ -101,10 +112,10 @@ class UMnetdbBase:
101
112
  :group_by: A string representing a column name (or names) to group by
102
113
  :limit: An integer
103
114
 
104
- '''
115
+ """
105
116
 
106
117
  # First part of the sql statement is the 'select'
107
- distinct = 'distinct ' if distinct else ''
118
+ distinct = "distinct " if distinct else ""
108
119
  sql = f"select {distinct}" + ", ".join(select) + "\n"
109
120
 
110
121
  # Next is the table
@@ -118,7 +129,6 @@ class UMnetdbBase:
118
129
 
119
130
  # Next are the filters. They are 'anded'
120
131
  if where and isinstance(where, list):
121
-
122
132
  sql += "where\n"
123
133
  sql += " and\n".join(where) + "\n"
124
134
  elif where:
@@ -133,20 +143,20 @@ class UMnetdbBase:
133
143
 
134
144
  if limit:
135
145
  sql += f"limit {limit}\n"
136
-
146
+
137
147
  logger.debug(f"Generated SQL command:\n****\n{sql}\n****\n")
138
148
 
139
149
  return sql
140
150
 
141
- def _execute(self, sql:str, rows_as_dict:bool=True, fetch_one:bool=False):
142
- '''
151
+ def _execute(self, sql: str, rows_as_dict: bool = True, fetch_one: bool = False):
152
+ """
143
153
  Generic sqlalchemy "open a session, execute this sql command and give me all the results"
144
154
 
145
155
  NB This function is defined for legacy database classes that came from umnet-scripts.
146
156
  It's encouraged to use "self.session.execute" in other child methods, allowing
147
157
  scripts that import the child class to use the context manager and execute multiple
148
158
  mehtods within the same session.
149
- '''
159
+ """
150
160
  with self.engine.begin() as c:
151
161
  r = c.execute(text(sql))
152
162
 
@@ -158,8 +168,9 @@ class UMnetdbBase:
158
168
  else:
159
169
  return []
160
170
 
161
-
162
- def execute(self, sql:str, rows_as_dict:bool=True, fetch_one:bool=False) -> Union[list[dict],dict]:
171
+ def execute(
172
+ self, sql: str, rows_as_dict: bool = True, fetch_one: bool = False
173
+ ) -> Union[list[dict], dict]:
163
174
  """
164
175
  Executes a sqlalchemy command and gives all the results as a list of dicts, or as a dict
165
176
  if 'fetch_one' is set to true.
@@ -172,5 +183,5 @@ class UMnetdbBase:
172
183
 
173
184
  if fetch_one:
174
185
  return dict(result.fetchone())
175
-
176
- return [dict(r) for r in result.fetchall()]
186
+
187
+ return [dict(r) for r in result.fetchall()]
umnetdb_utils/umnetdb.py CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  from typing import List
3
2
  import logging
4
3
  import re
@@ -9,12 +8,9 @@ from .base import UMnetdbBase
9
8
 
10
9
 
11
10
  class UMnetdb(UMnetdbBase):
11
+ URL = "postgresql+psycopg://{UMNETDB_USER}:{UMNETDB_PASSWORD}@wintermute.umnet.umich.edu/umnetdb"
12
12
 
13
- URL="postgresql+psycopg://{UMNETDB_USER}:{UMNETDB_PASSWORD}@wintermute.umnet.umich.edu/umnetdb"
14
-
15
- def get_neighbors(
16
- self, device: str, known_devices_only: bool = True
17
- ) -> List[dict]:
13
+ def get_neighbors(self, device: str, known_devices_only: bool = True) -> List[dict]:
18
14
  """
19
15
  Gets a list of the neighbors of a particular device. If the port
20
16
  has a parent in the LAG table that is included as well.
@@ -35,12 +31,12 @@ class UMnetdb(UMnetdbBase):
35
31
  "n_d.name as remote_device",
36
32
  "n.remote_port",
37
33
  "l.parent",
38
- "n_l.parent as remote_parent"
39
- ]
34
+ "n_l.parent as remote_parent",
35
+ ]
40
36
  joins = [
41
- "join device n_d on n_d.hostname=n.remote_device",
42
- "left outer join lag l on l.device=n.device and l.member=n.port",
43
- "left outer join lag n_l on n_l.device=n_d.name and n_l.member=n.remote_port",
37
+ "join device n_d on n_d.hostname=n.remote_device",
38
+ "left outer join lag l on l.device=n.device and l.member=n.port",
39
+ "left outer join lag n_l on n_l.device=n_d.name and n_l.member=n.remote_port",
44
40
  ]
45
41
  else:
46
42
  select = [
@@ -48,7 +44,7 @@ class UMnetdb(UMnetdbBase):
48
44
  "coalesce(n_d.name, n.remote_device) as remote_device",
49
45
  "n.remote_port",
50
46
  "l.parent",
51
- "n_l.parent as remote_parent"
47
+ "n_l.parent as remote_parent",
52
48
  ]
53
49
  joins = [
54
50
  "left outer join device n_d on n_d.hostname=n.remote_device",
@@ -64,9 +60,7 @@ class UMnetdb(UMnetdbBase):
64
60
 
65
61
  return self.execute(query)
66
62
 
67
-
68
-
69
- def get_dlzone(self, zone_name:str) -> List[dict]:
63
+ def get_dlzone(self, zone_name: str) -> List[dict]:
70
64
  """
71
65
  Gets all devices within a DL zone based on walking the 'neighbors'
72
66
  table.
@@ -77,42 +71,52 @@ class UMnetdb(UMnetdbBase):
77
71
  device_cols = ["name", "ip", "version", "vendor", "model", "serial"]
78
72
 
79
73
  # step 1 is to find DLs in the database - we'll seed our zone with them
80
- query = self._build_select(select=device_cols, table="device",where=f"name similar to '(d-|dl-){zone_name}-(1|2)'")
74
+ query = self._build_select(
75
+ select=device_cols,
76
+ table="device",
77
+ where=f"name similar to '(d-|dl-){zone_name}-(1|2)'",
78
+ )
81
79
  dls = self.execute(query)
82
80
 
83
81
  if not dls:
84
82
  raise ValueError(f"No DLs found in umnetdb for zone {zone_name}")
85
83
 
86
- devices_by_name = {d['name']:d for d in dls}
84
+ devices_by_name = {d["name"]: d for d in dls}
87
85
 
88
86
  # now we'll look for neighbors on each device within the zone.
89
87
  # Note that outside of the DLs we only expect to find devices that start with
90
88
  # "s-" anything else is considered 'outside the zone'
91
89
  todo = list(devices_by_name.keys())
92
- while(len(todo) != 0):
93
-
90
+ while len(todo) != 0:
94
91
  device = todo.pop()
95
92
 
93
+ print(f"Processing device {device}")
96
94
  # note that by default this method only returns neighbors in the 'device' table,
97
95
  # any others are ignored
98
96
  neighs = self.get_neighbors(device)
99
97
  devices_by_name[device]["neighbors"] = {}
100
98
  for neigh in neighs:
101
-
102
- # only want 'd- or 'dl-' or 's-' devices
103
- if re.match(r"(dl?|s)-", neigh["remote_device"]):
104
-
99
+ print(f"EVALUATING {neigh}")
100
+ # only want 'd- or 'dl-' or 's-' devices, and we don't want out of band devices
101
+ if re.match(r"(dl?-|s-)", neigh["remote_device"]) and not re.match(
102
+ r"s-oob-", neigh["remote_device"]
103
+ ):
105
104
  # adding neighbor to local device's neighbor list
106
- devices_by_name[device]["neighbors"][neigh["port"]] = {k:v for k,v in neigh.items() if k != "port"}
105
+ devices_by_name[device]["neighbors"][neigh["port"]] = {
106
+ k: v for k, v in neigh.items() if k != "port"
107
+ }
107
108
 
108
109
  # if we haven't seen this neighbor yet, pull data from our device table for it, and
109
110
  # add it to our 'to do' list to pull its neighbors.
110
111
  if neigh["remote_device"] not in devices_by_name:
111
-
112
- query = self._build_select(select=device_cols, table="device", where=f"name = '{neigh['remote_device']}'")
112
+ query = self._build_select(
113
+ select=device_cols,
114
+ table="device",
115
+ where=f"name = '{neigh['remote_device']}'",
116
+ )
113
117
  neigh_device = self.execute(query, fetch_one=True)
114
118
  devices_by_name[neigh_device["name"]] = neigh_device
115
-
119
+
116
120
  todo.append(neigh_device["name"])
117
121
 
118
122
  return list(devices_by_name.values())
@@ -1,24 +1,31 @@
1
-
2
-
3
1
  from .base import UMnetdbBase
4
2
  from .utils import is_ip_address, is_mac_address
5
3
 
6
4
 
7
5
  class Umnetdisco(UMnetdbBase):
8
- URL="postgresql+psycopg://{NETDISCO_DB_USER}:{NETDISCO_DB_PASSWORD}@netdisco.umnet.umich.edu:5432/netdisco"
6
+ URL = "postgresql+psycopg://{NETDISCO_DB_USER}:{NETDISCO_DB_PASSWORD}@netdisco.umnet.umich.edu:5432/netdisco"
9
7
 
10
- def host_arp(self, host, start_time=None, end_time=None, limit=1, active_only=False):
11
- '''
12
- Does an ARP query against the netdisco db for a single host.
8
+ def host_arp(
9
+ self, host, start_time=None, end_time=None, limit=1, active_only=False
10
+ ):
11
+ """
12
+ Does an ARP query against the netdisco db for a single host.
13
13
  :host: A string representing a MAC address or an IPv4 address
14
- '''
14
+ """
15
15
 
16
16
  # what table are we querying?
17
- table = 'node_ip nip'
18
- joins = ['join node n on nip.mac = n.mac']
17
+ table = "node_ip nip"
18
+ joins = ["join node n on nip.mac = n.mac"]
19
19
 
20
20
  # define select statements
21
- select = ['nip.mac', 'nip.ip', 'n.switch', 'n.port', 'nip.time_first', 'nip.time_last']
21
+ select = [
22
+ "nip.mac",
23
+ "nip.ip",
24
+ "n.switch",
25
+ "n.port",
26
+ "nip.time_first",
27
+ "nip.time_last",
28
+ ]
22
29
 
23
30
  # First determine if this host is a MAC address or an IP
24
31
  where = []
@@ -31,7 +38,9 @@ class Umnetdisco(UMnetdbBase):
31
38
 
32
39
  # filter for specific start/end times if specified
33
40
  if start_time and end_time:
34
- where.append(f"n.time_last between timestamp '{start_time}' and timestamp '{end_time}'")
41
+ where.append(
42
+ f"n.time_last between timestamp '{start_time}' and timestamp '{end_time}'"
43
+ )
35
44
  elif start_time:
36
45
  where.append(f"n.time_last > timestamp '{start_time}'")
37
46
 
@@ -40,63 +49,73 @@ class Umnetdisco(UMnetdbBase):
40
49
  where.append("nip.active = true")
41
50
 
42
51
  # order by last seen
43
- sql = self._build_select(select, table, joins=joins, where=where, order_by="time_last", limit=limit)
52
+ sql = self._build_select(
53
+ select, table, joins=joins, where=where, order_by="time_last", limit=limit
54
+ )
44
55
  result = self._execute(sql)
45
56
 
46
57
  return result
47
58
 
48
59
  def arp_count(self, prefix=None, start_time=None, end_time=None, active_only=False):
49
- '''
60
+ """
50
61
  Queries the host data in netdisco based on prefix and start/end time.
51
62
  First any prefix greater than or equal to the inputted prefix is searched for
52
63
  (in the 'device_ip' table).
53
64
 
54
65
  Then the host table is queried.
55
- '''
56
-
66
+ """
67
+
57
68
  # We're counting all the host IPs by subnet
58
- select = ['count(distinct nip.ip)', 'dip.subnet']
59
- table = 'node_ip nip'
69
+ select = ["count(distinct nip.ip)", "dip.subnet"]
70
+ table = "node_ip nip"
60
71
 
61
72
  # postgres allows us to do a join based on an IPs (type inet) membership
62
73
  # of a subnet (type cidr)
63
- joins = ['join device_ip dip on nip.ip <<= dip.subnet']
74
+ joins = ["join device_ip dip on nip.ip <<= dip.subnet"]
64
75
 
65
76
  # grouping by subnet is what gives us per-subnet host counts
66
- group_by = 'dip.subnet'
77
+ group_by = "dip.subnet"
67
78
 
68
79
  # append all cli filtering options
69
80
  where = [f"dip.subnet <<= inet '{prefix}'"]
70
81
  if start_time and end_time:
71
- where.append(f"nip.time_last between timestamp '{start_time}' and timestamp '{end_time}'")
82
+ where.append(
83
+ f"nip.time_last between timestamp '{start_time}' and timestamp '{end_time}'"
84
+ )
72
85
  elif start_time:
73
86
  where.append(f"nip.time_last > timestamp '{start_time}'")
74
87
 
75
88
  if active_only:
76
89
  where.append("nip.active = true")
77
90
 
78
- sql = self._build_select(select, table, joins=joins, where=where, group_by=group_by)
91
+ sql = self._build_select(
92
+ select, table, joins=joins, where=where, group_by=group_by
93
+ )
79
94
  return self._execute(sql)
80
95
 
81
96
  def neighbors(self, device=None):
82
- '''
97
+ """
83
98
  Queries the device_ip table in netdisco to get neighbors of a device.
84
99
  If no device is specified, all neighbors are pulled.
85
100
  The device input can be an IPv4 address or an FQDN.
86
- '''
87
-
88
- select = [ 'dp.ip as local_ip',
89
- 'ld.dns as local_dns',
90
- 'dp.port as local_port',
91
- 'dp.remote_ip',
92
- 'rd.dns as remote_dns',
93
- 'dp.remote_port']
94
- table = 'device d'
95
- joins = ['join device_port dp on d.ip = dp.ip',
96
- 'join device ld on dp.ip = ld.ip',
97
- 'join device rd on dp.remote_ip = rd.ip']
98
-
99
- where = ['dp.remote_ip is not null']
101
+ """
102
+
103
+ select = [
104
+ "dp.ip as local_ip",
105
+ "ld.dns as local_dns",
106
+ "dp.port as local_port",
107
+ "dp.remote_ip",
108
+ "rd.dns as remote_dns",
109
+ "dp.remote_port",
110
+ ]
111
+ table = "device d"
112
+ joins = [
113
+ "join device_port dp on d.ip = dp.ip",
114
+ "join device ld on dp.ip = ld.ip",
115
+ "join device rd on dp.remote_ip = rd.ip",
116
+ ]
117
+
118
+ where = ["dp.remote_ip is not null"]
100
119
  if is_ip_address(device):
101
120
  where.append(f"d.ip = '{device}'")
102
121
  elif device:
@@ -106,37 +125,39 @@ class Umnetdisco(UMnetdbBase):
106
125
  return self._execute(sql)
107
126
 
108
127
  def get_devices(self, match_subnets=None, exclude_subnets=None):
109
- '''
128
+ """
110
129
  Queries netdisco for a list of devices.
111
130
  Optionally, limit it by a list of prefixes
112
- '''
131
+ """
113
132
 
114
- select = [ 'ip', 'dns', 'serial', 'model', 'vendor', 'os' ]
115
- table = 'device'
133
+ select = ["ip", "dns", "serial", "model", "vendor", "os"]
134
+ table = "device"
116
135
 
117
136
  where = []
118
137
  if match_subnets:
119
138
  where.append(" or\n".join([f"ip << inet '{s}'" for s in match_subnets]))
120
-
139
+
121
140
  if exclude_subnets:
122
- where.append("not\n" + " or\n".join([f"ip << inet '{s}'" for s in exclude_subnets]))
141
+ where.append(
142
+ "not\n" + " or\n".join([f"ip << inet '{s}'" for s in exclude_subnets])
143
+ )
123
144
 
124
145
  sql = self._build_select(select, table, where=where)
125
146
  return self._execute(sql)
126
147
 
127
148
  def get_device(self, name_or_ip):
128
- '''
149
+ """
129
150
  Pulls the device name, ip, model, and description (where version info often is)
130
151
  from the netinfo device table
131
- '''
152
+ """
132
153
  select = [
133
- "replace(lower(name), '.umnet.umich.edu', '') as name",
134
- "device.ip as ip",
135
- "model",
136
- "description",
137
- ]
154
+ "replace(lower(name), '.umnet.umich.edu', '') as name",
155
+ "device.ip as ip",
156
+ "model",
157
+ "description",
158
+ ]
138
159
  table = "device"
139
-
160
+
140
161
  if is_ip_address(name_or_ip):
141
162
  joins = ["join device_ip on device.ip=device_ip.alias"]
142
163
  where = [f"device_ip.alias='{name_or_ip}'"]
@@ -153,7 +174,7 @@ class Umnetdisco(UMnetdbBase):
153
174
  return None
154
175
 
155
176
  def get_port_and_host_data(self, name_or_ip):
156
- '''
177
+ """
157
178
  Pulls a list of all ports on the switch and all hosts seen on those ports.
158
179
  You will see multiple rows for an interface where multiple hosts have been seen.
159
180
 
@@ -168,21 +189,21 @@ class Umnetdisco(UMnetdbBase):
168
189
  - vlan the host was seen on
169
190
  - date last mac was seen
170
191
 
171
- '''
192
+ """
172
193
 
173
194
  # getting device by name or IP
174
195
  if is_ip_address(name_or_ip):
175
196
  device_ip = name_or_ip
176
197
  else:
177
198
  nd_device = self.get_device(name_or_ip)
178
- if not(nd_device):
179
- raise LookupError(f'Could not find {nd_device} in Netdisco')
180
- device_ip = nd_device['ip']
199
+ if not (nd_device):
200
+ raise LookupError(f"Could not find {nd_device} in Netdisco")
201
+ device_ip = nd_device["ip"]
181
202
 
182
203
  # The port description is in different fields depending on the platform, so we need to do
183
204
  # some if-else stuff that's not supported in our basic sql builer in the base class.
184
205
  # That's why we're just doing a long sting here
185
- sql = f'''
206
+ sql = f"""
186
207
  select
187
208
  dp.port,
188
209
  case
@@ -202,7 +223,7 @@ from device_port dp
202
223
  left outer join node on dp.port = node.port and dp.ip = node.switch
203
224
  left outer join node_ip on node.mac = node_ip.mac
204
225
  where dp.ip='{device_ip}'
205
- '''
226
+ """
206
227
 
207
228
  result = self._execute(sql)
208
229
 
@@ -210,7 +231,7 @@ where dp.ip='{device_ip}'
210
231
 
211
232
  def get_poe_data(self, name_or_ip):
212
233
  """
213
- Pulls PoE admin, status, class, and power columns from device_port_pwer
234
+ Pulls PoE admin, status, class, and power columns from device_port_pwer
214
235
  """
215
236
 
216
237
  # getting device by name or IP
@@ -218,16 +239,15 @@ where dp.ip='{device_ip}'
218
239
  device_ip = name_or_ip
219
240
  else:
220
241
  nd_device = self.get_device(name_or_ip)
221
- if not(nd_device):
222
- raise LookupError(f'Could not find {nd_device} in Netdisco')
223
- device_ip = nd_device['ip']
224
-
242
+ if not (nd_device):
243
+ raise LookupError(f"Could not find {nd_device} in Netdisco")
244
+ device_ip = nd_device["ip"]
245
+
225
246
  select = ["port", "admin", "status", "class", "power"]
226
247
  table = "device_port_power"
227
248
  where = [f"ip='{device_ip}'"]
228
249
 
229
-
230
250
  sql = self._build_select(select, table, where=where)
231
251
  result = self._execute(sql)
232
252
 
233
- return result
253
+ return result
@@ -3,25 +3,28 @@ from typing import Union, Optional
3
3
  from .base import UMnetdbBase
4
4
  from .utils import is_ip_address
5
5
 
6
+
6
7
  class UMnetequip(UMnetdbBase):
7
- '''
8
+ """
8
9
  This class wraps helpful equip db queries in python.
9
- '''
10
+ """
11
+
10
12
  URL = "oracle+oracledb://{EQUIP_DB_USERNAME}:{EQUIP_DB_PASSWORD}@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=equipdb-prod.umnet.umich.edu)(PORT=1521))(CONNECT_DATA=(SID=KANNADA)))"
11
13
 
12
14
  def get_devices_by_category(self, category, active_only=False):
13
- '''
15
+ """
14
16
  Queries equip db for devices by category. You can also
15
17
  specify if you only want active devices.
16
- '''
18
+ """
17
19
 
18
- select = [ 'eq.monitored_device',
19
- 'eq.rancid',
20
- 'eq.off_line',
21
- 'eq.dns_name',
22
- 'eq.ip_address',
23
- ]
24
- table = 'ARADMIN1.UMNET_EQUIPMENT eq'
20
+ select = [
21
+ "eq.monitored_device",
22
+ "eq.rancid",
23
+ "eq.off_line",
24
+ "eq.dns_name",
25
+ "eq.ip_address",
26
+ ]
27
+ table = "ARADMIN1.UMNET_EQUIPMENT eq"
25
28
 
26
29
  where = [f"eq.category = '{category}'"]
27
30
 
@@ -29,54 +32,55 @@ class UMnetequip(UMnetdbBase):
29
32
  # 1: RESERVE, 2:ACTIVE, 3:RETIRED
30
33
  if active_only:
31
34
  where.append("eq.status = 2")
32
-
35
+
33
36
  sql = self._build_select(select, table, where=where, distinct=True)
34
37
  return self._execute(sql)
35
38
 
36
-
37
39
  def get_device_type(self, ip):
38
- '''
40
+ """
39
41
  Queries equip db for a device by ip, returns the 'type' of the device.
40
42
  By type we mean the UMNET_EQUIPMENT_TYPE: ACCESS LAYER, DISTRIBUTION LAYER, UMD, etc
41
- '''
42
- select = [ 'types as type' ]
43
- table = 'ARADMIN1.UMNET_EQUIPMENT'
43
+ """
44
+ select = ["types as type"]
45
+ table = "ARADMIN1.UMNET_EQUIPMENT"
44
46
  where = [f"ip_address='{ip}'"]
45
47
 
46
48
  sql = self._build_select(select, table, where=where)
47
49
  return self._execute(sql)
48
50
 
49
- def get_devices_by_building_no(self,
50
- building_no:Union[int, list],
51
-
52
-
53
- active_only:bool=False,
54
- types:Optional[list]=None,
55
- location_info:bool=False,
56
- ):
51
+ def get_devices_by_building_no(
52
+ self,
53
+ building_no: Union[int, list],
54
+ active_only: bool = False,
55
+ types: Optional[list] = None,
56
+ location_info: bool = False,
57
+ ):
57
58
  """
58
59
  Queries equipdb for devices by building no. You can provide a single
59
60
  building number, or a list of numbers. You can specify if you want 'active only' devices
60
61
  (based on the 'status' field, defalut false) or you can limit to a certain device type (default all).
61
62
  You can also get the location info for each device via 'location_info' (default false)
62
63
  """
63
- select = [ 'dns_name',
64
- 'ip_address',
65
- 'model_no_ as model',
66
- 'types as device_type',
67
- 'rancid',
68
- 'off_line',
69
- ]
64
+ select = [
65
+ "dns_name",
66
+ "ip_address",
67
+ "model_no_ as model",
68
+ "types as device_type",
69
+ "rancid",
70
+ "off_line",
71
+ ]
70
72
  if location_info:
71
- select.extend([
72
- 'bldg as building_name',
73
- 'address as building_address',
74
- 'room as room_no',
75
- 'floor as floor',
76
- 'bldg_code__ as building_no',
77
- ])
78
-
79
- table = 'ARADMIN1.UMNET_EQUIPMENT eq'
73
+ select.extend(
74
+ [
75
+ "bldg as building_name",
76
+ "address as building_address",
77
+ "room as room_no",
78
+ "floor as floor",
79
+ "bldg_code__ as building_no",
80
+ ]
81
+ )
82
+
83
+ table = "ARADMIN1.UMNET_EQUIPMENT eq"
80
84
 
81
85
  where = []
82
86
  if isinstance(building_no, int):
@@ -99,59 +103,62 @@ class UMnetequip(UMnetdbBase):
99
103
  sql = self._build_select(select, table, where=where)
100
104
  return self._execute(sql)
101
105
 
102
- def get_device(self, name_or_ip:str, location_info:bool=False):
103
-
106
+ def get_device(self, name_or_ip: str, location_info: bool = False):
104
107
  if is_ip_address(name_or_ip):
105
108
  where = [f"ip_address='{name_or_ip}'"]
106
109
  else:
107
- name_or_ip = name_or_ip.replace(".umnet.umich.edu","")
110
+ name_or_ip = name_or_ip.replace(".umnet.umich.edu", "")
108
111
  where = [f"dns_name='{name_or_ip}'"]
109
112
 
110
- select = [ 'dns_name',
111
- 'ip_address',
112
- 'model_no_ as model',
113
- 'types as device_type',
114
- 'rancid',
115
- 'off_line',
116
- ]
113
+ select = [
114
+ "dns_name",
115
+ "ip_address",
116
+ "model_no_ as model",
117
+ "types as device_type",
118
+ "rancid",
119
+ "off_line",
120
+ ]
117
121
  if location_info:
118
- select.extend([
119
- 'bldg as building_name',
120
- 'address as building_address',
121
- 'room as room_no',
122
- 'floor as floor',
123
- 'bldg_code__ as building_no',
124
- ])
125
- table = 'ARADMIN1.UMNET_EQUIPMENT eq'
122
+ select.extend(
123
+ [
124
+ "bldg as building_name",
125
+ "address as building_address",
126
+ "room as room_no",
127
+ "floor as floor",
128
+ "bldg_code__ as building_no",
129
+ ]
130
+ )
131
+ table = "ARADMIN1.UMNET_EQUIPMENT eq"
126
132
 
127
133
  sql = self._build_select(select, table, where=where)
128
134
  return self._execute(sql)
129
135
 
130
136
  def get_all_devices(self):
131
- select = [ "bldg_code__ as bldg_code",
132
- "bldg",
133
- "room",
134
- "ip_address",
135
- "dns_name",
136
- "category",
137
- "types",
138
- "manufacturer",
139
- "serial_number",
140
- "billing_code_ as billing_code",
141
- "warehouse_item__ as warehouse_item",
142
- "st.descr as status",
143
- "sla_network",
144
- "address",
145
- "floor",
146
- "model_no_ as model_no",
147
- "customer_name",
148
- "mat_l_item_description",
149
- "rancid",
150
- "off_line",
151
- ]
152
- table = 'ARADMIN1.UMNET_EQUIPMENT eq'
137
+ select = [
138
+ "bldg_code__ as bldg_code",
139
+ "bldg",
140
+ "room",
141
+ "ip_address",
142
+ "dns_name",
143
+ "category",
144
+ "types",
145
+ "manufacturer",
146
+ "serial_number",
147
+ "billing_code_ as billing_code",
148
+ "warehouse_item__ as warehouse_item",
149
+ "st.descr as status",
150
+ "sla_network",
151
+ "address",
152
+ "floor",
153
+ "model_no_ as model_no",
154
+ "customer_name",
155
+ "mat_l_item_description",
156
+ "rancid",
157
+ "off_line",
158
+ ]
159
+ table = "ARADMIN1.UMNET_EQUIPMENT eq"
153
160
  joins = ["join ARADMIN1.UMNET_EQUIPMENT_STATUS st on st.idnum=eq.status"]
154
161
  where = ["eq.status != 3"]
155
162
 
156
- sql = self._build_select(select, table,where=where, joins=joins)
157
- return self._execute(sql)
163
+ sql = self._build_select(select, table, where=where, joins=joins)
164
+ return self._execute(sql)
@@ -1,14 +1,14 @@
1
-
2
-
3
1
  import ipaddress
4
2
  import re
5
3
  from .base import UMnetdbBase
6
4
  from .utils import is_ip_address, is_ip_network, is_mac_address
7
5
 
6
+
8
7
  class UMnetinfo(UMnetdbBase):
9
8
  """
10
9
  Wraps helpful netinfo db queries into python
11
10
  """
11
+
12
12
  URL = "oracle+oracledb://{NETINFO_USERNAME}:{NETINFO_PASSWORD}@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=kannada.web.itd.umich.edu)(PORT=1521))(CONNECT_DATA=(SID=KANNADA)))"
13
13
 
14
14
  def get_network_by_name(self, netname, active_only=False):
@@ -365,7 +365,6 @@ class UMnetinfo(UMnetdbBase):
365
365
 
366
366
  # dns names in the device table are fqdn and end in a dot (eg 'dl-arbl-1.umnet.umich.edu.')
367
367
  else:
368
-
369
368
  name_or_ip = name_or_ip.lower()
370
369
  if "." not in name_or_ip:
371
370
  name_or_ip += ".umnet.umich.edu"
@@ -417,7 +416,6 @@ class UMnetinfo(UMnetdbBase):
417
416
  # UMNET.ARPHIST stores MACs as strings without separators .:-, ie
418
417
  # 0010.abcd.1010 => '0010abcd1010'
419
418
  if is_mac_address(query):
420
-
421
419
  arphist_mac = re.sub(r"[\.\:\-]", "", query)
422
420
  where.append(f"arp.mac_addr = '{arphist_mac}'")
423
421
 
@@ -456,7 +454,6 @@ class UMnetinfo(UMnetdbBase):
456
454
  # isn't throwing anyone :-/
457
455
  processed_results = []
458
456
  for r in results:
459
-
460
457
  processed = {
461
458
  "ip": ipaddress.ip_address(r["address32bit"]),
462
459
  "mac": f"{r['mac_addr'][0:4]}.{r['mac_addr'][4:8]}.{r['mac_addr'][8:12]}",
@@ -486,4 +483,4 @@ class UMnetinfo(UMnetdbBase):
486
483
  sql = self._build_select(select, table, where=where, order_by="1,2")
487
484
  results = self._execute(sql)
488
485
 
489
- return results
486
+ return results
umnetdb_utils/utils.py CHANGED
@@ -1,5 +1,7 @@
1
1
  import ipaddress
2
2
  import re
3
+
4
+
3
5
  def is_ip_address(input_str, version=None):
4
6
  try:
5
7
  ip = ipaddress.ip_address(input_str)
@@ -11,8 +13,8 @@ def is_ip_address(input_str, version=None):
11
13
 
12
14
  return True
13
15
 
16
+
14
17
  def is_ip_network(input_str, version=None):
15
-
16
18
  # First check that this is a valid IP or network
17
19
  try:
18
20
  net = ipaddress.ip_network(input_str)
@@ -21,19 +23,20 @@ def is_ip_network(input_str, version=None):
21
23
 
22
24
  if version and version != net.version:
23
25
  return False
24
-
26
+
25
27
  return True
26
28
 
29
+
27
30
  def is_mac_address(input_str):
28
- '''
31
+ """
29
32
  Validates the input string as a mac address. Valid formats are
30
33
  XX:XX:XX:XX:XX:XX, XX-XX-XX-XX-XX-XX, XXXX.XXXX.XXXX
31
34
  where 'X' is a hexadecimal digit (upper or lowercase).
32
- '''
35
+ """
33
36
  mac = input_str.lower()
34
- if re.match(r'[0-9a-f]{2}([-:])[0-9a-f]{2}(\1[0-9a-f]{2}){4}$', mac):
37
+ if re.match(r"[0-9a-f]{2}([-:])[0-9a-f]{2}(\1[0-9a-f]{2}){4}$", mac):
35
38
  return True
36
- if re.match(r'[0-9a-f]{4}\.[0-9a-f]{4}\.[0-9a-f]{4}$', mac):
39
+ if re.match(r"[0-9a-f]{4}\.[0-9a-f]{4}\.[0-9a-f]{4}$", mac):
37
40
  return True
38
41
 
39
42
  return False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: umnetdb-utils
3
- Version: 0.1.4
3
+ Version: 0.1.5
4
4
  Summary: Helper classes for querying UMnet databases
5
5
  License: MIT
6
6
  Author: Amy Liebowitz
@@ -0,0 +1,10 @@
1
+ umnetdb_utils/__init__.py,sha256=YrT7drJ2MlU287z4--8EQI3fKLQZBlVxaCacU1yoDtg,132
2
+ umnetdb_utils/base.py,sha256=LWVhrbShhuYekXZxwvhhVUkH6DHd_wnJ2QZ_zw3fKqM,6139
3
+ umnetdb_utils/umnetdb.py,sha256=jHifl5eJXzye28mjLnfn7A7zwN2Y4YuKgrFvjV5MQBI,4953
4
+ umnetdb_utils/umnetdisco.py,sha256=D9MiF_QrYznYL_ozLUmD1ASzkGDs1jqZ_XksNV7wW3o,8018
5
+ umnetdb_utils/umnetequip.py,sha256=Nnh_YCcqGpYRp2KsS8qbAGJ9AgTeYB4umeYeP9_B-6A,5440
6
+ umnetdb_utils/umnetinfo.py,sha256=vqFvLbIu7Wu650RTqzaELpbKhq6y91fgBXM5Q_elRl4,18443
7
+ umnetdb_utils/utils.py,sha256=VLxfBKq1iOQVxTuySvxV4goE_jtX4jCshjf9a5ky1vI,989
8
+ umnetdb_utils-0.1.5.dist-info/METADATA,sha256=fPpx67_VnM0Xp6JGk13mRrxOte8wPAkau0O0PtM9vW0,1555
9
+ umnetdb_utils-0.1.5.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
10
+ umnetdb_utils-0.1.5.dist-info/RECORD,,
@@ -1,10 +0,0 @@
1
- umnetdb_utils/__init__.py,sha256=QJaytbr4ccKESiwaKjpf1b4b8s2cHNfCDdnCOs1tmoI,131
2
- umnetdb_utils/base.py,sha256=aC5oKSB5Ox9QtylmHV2uvEUVUx4CRLZTgH6HpQJ6DZo,6064
3
- umnetdb_utils/umnetdb.py,sha256=IxqpAcPZn71HVV_oL3ryvbKS7cupUsyM6rJSGk6NLaU,4554
4
- umnetdb_utils/umnetdisco.py,sha256=Z2XwT79jKO_avd3w_z99DDEdAikrfKxYm1JYHRWqvG4,7841
5
- umnetdb_utils/umnetequip.py,sha256=jOW5kvk0FXtdHv8PA4rYT_PWfLdMiq83Mjwmy-c1DN8,5701
6
- umnetdb_utils/umnetinfo.py,sha256=MH1YDW4OWtHD46qfYb5Pv40vPSbL0GrMNW5gAhdlihE,18445
7
- umnetdb_utils/utils.py,sha256=wU6QMYfofj7trX3QeqXty0btbGdhP_RUaSqA7QTflFM,991
8
- umnetdb_utils-0.1.4.dist-info/METADATA,sha256=JI2hwHkX3B8y-GLwS0yywAiFszcqUlkiWUyga0A2KaY,1555
9
- umnetdb_utils-0.1.4.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
10
- umnetdb_utils-0.1.4.dist-info/RECORD,,