Security hardening, multi-site portability, and a documentation and API-surface sweep
This commit is contained in:
1
plugins/network/services/__init__.py
Normal file
1
plugins/network/services/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Network plugin services."""
|
||||
70
plugins/network/services/assetnumbers.py
Normal file
70
plugins/network/services/assetnumbers.py
Normal file
@@ -0,0 +1,70 @@
|
||||
"""Generate a network device's asset number from its type prefix and name.
|
||||
|
||||
Every network device on this fleet already follows one convention, applied by
|
||||
hand: AP-<name>, SW-<name>, SVR-<name>, IDF-<name>. This turns that into
|
||||
something the create path does, so the same value is not typed twice.
|
||||
|
||||
Generation only fills a BLANK asset number. A device that carries a real
|
||||
identifier of its own - a vendor tag, a controller name, a serial - keeps it.
|
||||
That is the platform rule: adopt an external identifier where one exists, derive
|
||||
one only where none does.
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
|
||||
# Characters that must not reach a unique business key. assetnumber ends up in
|
||||
# URLs and in joins other subsystems make, and the existing data already shows
|
||||
# why this matters: IDF-Telco-Demarc-#1 came from the name 'Telco Demarc #1',
|
||||
# carrying a '#' into an identifier.
|
||||
_SEPARATORS = re.compile(r'[\s_/\\]+')
|
||||
_ILLEGAL = re.compile(r'[^A-Za-z0-9.-]')
|
||||
_RUNS = re.compile(r'-{2,}')
|
||||
|
||||
|
||||
def sanitize(value):
|
||||
"""A name reduced to something safe to use as an identifier.
|
||||
|
||||
Whitespace and slashes become single dashes; anything outside
|
||||
letters/digits/dot/dash is dropped rather than transliterated, because a
|
||||
guessed transliteration in a business key is worse than a shorter one.
|
||||
"""
|
||||
text = (value or '').strip()
|
||||
if not text:
|
||||
return ''
|
||||
text = _SEPARATORS.sub('-', text)
|
||||
text = _ILLEGAL.sub('', text)
|
||||
text = _RUNS.sub('-', text)
|
||||
return text.strip('-')
|
||||
|
||||
|
||||
def generate(prefix, name):
|
||||
"""'<PREFIX>-<name>', or the bare name when the type has no prefix.
|
||||
|
||||
Returns '' when there is nothing to build from, so the caller can fall back
|
||||
to demanding an explicit value rather than inventing one.
|
||||
|
||||
Does NOT stack an existing prefix: a name already starting with the prefix
|
||||
(IDF-03 under type IDF) is returned as-is, because IDF-IDF-03 is nobody's
|
||||
intent. Matched case-insensitively on the prefix plus its dash.
|
||||
"""
|
||||
cleanname = sanitize(name)
|
||||
if not cleanname:
|
||||
return ''
|
||||
cleanprefix = sanitize(prefix).upper()
|
||||
if not cleanprefix:
|
||||
return cleanname
|
||||
if cleanname.upper().startswith(cleanprefix + '-'):
|
||||
return cleanname
|
||||
return '{}-{}'.format(cleanprefix, cleanname)
|
||||
|
||||
|
||||
def generate_for_type(networkdevicetypeid, name):
|
||||
"""Same, looking the prefix up from the device type. '' if no type given."""
|
||||
if not networkdevicetypeid:
|
||||
return sanitize(name)
|
||||
from shopdb.api import db
|
||||
from ..models import NetworkDeviceType
|
||||
|
||||
devicetype = db.session.get(NetworkDeviceType, networkdevicetypeid)
|
||||
return generate(devicetype.prefix if devicetype else None, name)
|
||||
Reference in New Issue
Block a user