Add support for storing and using lock_reason
lock_reason is simple optional text for admins to use to store why
they've locked a port in VLANd. Helpful as reminders later on!
Changes:
* Added database schema V2, with upgrade code to go with it. Adds the
"lock_reason" field in the port table.
* Deal with changes to all the database methods dealing with the
port table. set_port_is_locked() now takes an extra parameter for
lock_reason.
* API call db.set_port_is_locked() updated to match
* admin.py updated to match - allow optional setting of lock_reason
when locking a port, and print the reason when displaying port
details too
* In the web interface when hovering over a locked port, now also
display lock_reason if it's available, or "unknown reason"
otherwise
Change-Id: I31abc7308fd95e264729abc32ee855ac327a9abd
diff --git a/admin.py b/admin.py
index ca13a0d..706d24b 100755
--- a/admin.py
+++ b/admin.py
@@ -51,7 +51,7 @@
switch[1])
def dump_port(port):
- print "port_id:%d name:%s switch_id:%d locked:%s mode:%s base_vlan_id:%d current_vlan_id:%d number:%d trunk_id:%s" % (
+ print "port_id:%d name:%s switch_id:%d locked:%s mode:%s base_vlan_id:%d current_vlan_id:%d number:%d trunk_id:%s lock_reason:%s" % (
int(port[0]),
port[1],
int(port[2]),
@@ -60,7 +60,8 @@
int(port[5]),
int(port[6]),
int(port[7]),
- 'None' if (TRUNK_ID_NONE == port[8]) else port[8])
+ 'None' if (TRUNK_ID_NONE == port[8]) else port[8],
+ port[9])
def dump_vlan(vlan):
print "vlan_id:%d name:%s tag:%d is_base_vlan:%s, creation_time:%s" % (
@@ -249,6 +250,9 @@
p_lock_port.add_argument('--port_id',
required = True,
help = "The ID of the port to lock")
+p_lock_port.add_argument('--reason',
+ required = False,
+ help = "(Optional) The reason for locking the port")
p_unlock_port = sp.add_parser("unlock_port",
help = "Unlock the settings on a port")
p_unlock_port.set_defaults(which = "unlock_port")
@@ -714,7 +718,8 @@
{'command':'db.set_port_is_locked',
'data':
{'port_id': args.port_id,
- 'is_locked': True}})
+ 'is_locked': True,
+ 'lock_reason': args.reason}})
print "Locked port_id %d" % port_id
except InputError as inst:
print 'Failed: %s' % inst
@@ -728,7 +733,8 @@
{'command':'db.set_port_is_locked',
'data':
{'port_id': args.port_id,
- 'is_locked': False}})
+ 'is_locked': False,
+ 'lock_reason': ""}})
print "Unlocked port_id %d" % port_id
except InputError as inst:
print 'Failed: %s' % inst
diff --git a/db/db.py b/db/db.py
index ec8e1f4..003f973 100644
--- a/db/db.py
+++ b/db/db.py
@@ -30,8 +30,12 @@
# no version!) at startup, it will auto-migrate to the latest version
#
# Version 0: Base, no version found
+#
# Version 1: No changes, except adding the version and coping with upgrade
-DATABASE_SCHEMA_VERSION = 1
+#
+# Version 2: Add "lock_reason" field in the port table, and code to deal with
+# it
+DATABASE_SCHEMA_VERSION = 2
if __name__ == '__main__':
vlandpath = os.path.abspath(os.path.normpath(os.path.dirname(sys.argv[0])))
@@ -95,7 +99,13 @@
logging.info("Upgrading database to match schema version 1")
sql = "ALTER TABLE state ADD schema_version INTEGER"
self.cursor.execute(sql)
- logging.info("Sschema version 1 upgrade successful")
+ logging.info("Schema version 1 upgrade successful")
+
+ if current_db_version < 2:
+ logging.info("Upgrading database to match schema version 2")
+ sql = "ALTER TABLE port ADD lock_reason VARCHAR(64)"
+ self.cursor.execute(sql)
+ logging.info("Schema version 2 upgrade successful")
sql = "INSERT INTO state (last_modified, schema_version) VALUES (%s, %s)"
data = (datetime.datetime.now(), DATABASE_SCHEMA_VERSION)
@@ -159,9 +169,10 @@
raise InputError("Already have a port %d on switch ID %d" % (int(number), int(switch_id)))
try:
- sql = "INSERT INTO port (name, number, switch_id, is_locked, is_trunk, current_vlan_id, base_vlan_id, trunk_id) VALUES (%s, %s, %s, %s, %s, %s, %s, %s) RETURNING port_id"
+ sql = "INSERT INTO port (name, number, switch_id, is_locked, lock_reason, is_trunk, current_vlan_id, base_vlan_id, trunk_id) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING port_id"
data = (name, number, switch_id,
- False, False,
+ False, "",
+ False,
current_vlan_id, base_vlan_id, TRUNK_ID_NONE)
self.cursor.execute(sql, data)
port_id = self.cursor.fetchone()[0]
@@ -633,20 +644,20 @@
# the admin interface, and will stop API users from modifying
# settings on the port. Use this to lock down ports that are used
# for PDUs and other core infrastructure
- def set_port_is_locked(self, port_id, is_locked):
+ def set_port_is_locked(self, port_id, is_locked, lock_reason=""):
port = self.get_port_by_id(port_id)
if port is None:
raise NotFoundError("Port ID %d does not exist" % int(port_id))
try:
- sql = "UPDATE port SET is_locked=%s WHERE port_id=%s RETURNING port_id"
- data = (is_locked, port_id)
+ sql = "UPDATE port SET is_locked=%s, lock_reason=%s WHERE port_id=%s RETURNING port_id"
+ data = (is_locked, lock_reason, port_id)
self.cursor.execute(sql, data)
port_id = self.cursor.fetchone()[0]
self.cursor.execute("UPDATE state SET last_modified=%s", (datetime.datetime.now(),))
self.connection.commit()
except:
self.connection.rollback()
- raise
+ raise InputError("lock failed on Port ID %d" % int(port_id))
return port_id
# Set the mode of a port in the database. Valid values for mode
diff --git a/util.py b/util.py
index 090e06e..9474211 100644
--- a/util.py
+++ b/util.py
@@ -193,7 +193,9 @@
elif command == 'db.delete_port':
ret = db.delete_port(data['port_id'])
elif command == 'db.set_port_is_locked':
- ret = db.set_port_is_locked(data['port_id'], data['is_locked'])
+ ret = db.set_port_is_locked(data['port_id'],
+ data['is_locked'],
+ data['lock_reason'])
elif command == 'db.set_base_vlan':
ret = db.set_base_vlan(data['port_id'], data['base_vlan_id'])
elif command == 'db.delete_trunk':
diff --git a/visualisation/visualisation.py b/visualisation/visualisation.py
index e73c860..fd24435 100644
--- a/visualisation/visualisation.py
+++ b/visualisation/visualisation.py
@@ -239,7 +239,13 @@
page.append('<div class="port" id="port%d.%d">' % (vlan.vlan_id, port.port_id))
page.append('Port ID: %d Port number: %d<br>' % (port.port_id, port.number))
if port.is_locked:
- page.append('Locked<br>')
+ page.append('Locked - ')
+ if (port.lock_reason is not None
+ and len(port.lock_reason) > 1):
+ page.append(port.lock_reason)
+ else:
+ page.append('unknown reason')
+ page.append('<br>')
if port.is_trunk:
page.append('Trunk')
if port.trunk_id != -1: