statements about a node's configuration state.
Added:
Node.last_boot -- updated by BootUpdateNode()
Node.last_download -- updated by GetBootMedium()
Node.last_pcu_reboot -- updated by RebootNodeWithPCU()
Node.last_pcu_confirmation -- updated by BootUpdateNode()
Interface.last_updated -- updated by UpdateInterface()
PCU.last_updated -- updated by UpdatePCU()
With these timestamps we can implement isValid() routines that indicate
whether the DB state is consistent with respect to the external deployment.
Previously, the DB state could be inconsistent, but there was no way to
identify this until runtime. These checks should make it possible to validate
the DB state prior to runtime.
Also, isValid() is different from whether a node is online. A node can be
offline and have a valid DB state.
For instance (in pseudocode):
def Node.isValid():
return Node.last_download < Node.last_boot &&
Node.PCU.isValid(Node) &&
Node.Interface.isValid(Node)
def PCU.isValid(Node):
return PCU.last_updated < Node.last_pcu_confirmation
def Interface.isValid(Node):
return Interface.last_updated < Node.last_download
In other words, if the primary interface for a node is updated more recently
than a bootimage is downloaded, then the DB configuration is out of sync with
the node deployment.
If the PCU is updated more recently than the last confirmed use of the PCU,
then the DB configuration is potentially out of sync with the deployment.
If the Node has downloaded a boot image prior to rebooting, then the DB config
is potentially out of sync with the deployment.
All of these states can be flagged in the GUI to help a technical contact
known the status of his configuration and deployment more easily. The
recommended steps to take should be straight forward based on the conditions
above. i.e. Create new boot image, reboot node, verify PCU, etc.
'node_id': Parameter(int, "Node associated with this interface"),
'is_primary': Parameter(bool, "Is the primary interface for this node"),
'interface_tag_ids' : Parameter([int], "List of interface settings"),
+ 'last_updated': Parameter(int, "Date and time when node entry was created", ro = True),
}
view_tags_name = "view_interface_tags"
if 'ip' not in self or not self['ip']:
raise PLCInvalidArgument, "For ipmi method, ip is required"
+ validate_last_updated = Row.validate_timestamp
+
+ def update_timestamp(self, col_name, commit = True):
+ """
+ Update col_name field with current time
+ """
+
+ assert 'interface_id' in self
+ assert self.table_name
+
+ self.api.db.do("UPDATE %s SET %s = CURRENT_TIMESTAMP " % (self.table_name, col_name) + \
+ " where interface_id = %d" % (self['interface_id']) )
+ self.sync(commit)
+
+ def update_last_updated(self, commit = True):
+ self.update_timestamp('last_updated', commit)
+
+
class Interfaces(Table):
"""
Representation of row(s) from the interfaces table in the
# $Id$
# $URL$
+import time
+
from PLC.Faults import *
from PLC.Method import Method
from PLC.Parameter import Parameter, Mixed
from PLC.Auth import Auth, BootAuth, SessionAuth
from PLC.Nodes import Node, Nodes
from PLC.Interfaces import Interface, Interfaces
+from PLC.Timestamp import *
can_update = lambda (field, value): field in \
['method', 'mac', 'gateway', 'network',
# indicate that node has booted & contacted PLC.
node.update_last_contact()
+ node.update_last_boot()
+
+ current_time = int(time.time())
+ # if last_pcu_reboot is within 20 minutes of current_time, accept that the PCU is responsible
+ if Timestamp.cast_long(node['last_pcu_reboot']) >= current_time - 60*20:
+ node.update_last_pcu_confirmation(commit=False)
node.sync(commit = True)
# renew the key and save it on the database
if renew_key:
node['key'] = compute_key()
+ node.update_last_download(commit=False)
node.sync()
# Generate node configuration file suitable for BootCD
# p = pcu['ports'][i]
ret = reboot.reboot_api(node, pcu, testrun)
+ node.update_last_pcu_reboot(commit=True) # commits new timestamp to node
+
self.event_objects = {'Node': [node['node_id']]}
self.message = "RebootNodeWithPCU called"
raise PLCPermissionDenied, "Not allowed to update interface"
interface.update(native)
+ interface.update_last_updated(commit=False)
interface.sync()
for (tagname,value) in tags.iteritems():
raise PLCPermissionDenied, "Not allowed to update that PCU"
pcu.update(pcu_fields)
+ pcu.update_last_updated(commit=False)
pcu.sync()
# Logging variables
'date_created': Parameter(int, "Date and time when node entry was created", ro = True),
'last_updated': Parameter(int, "Date and time when node entry was created", ro = True),
'last_contact': Parameter(int, "Date and time when node last contacted plc", ro = True),
+ 'last_boot': Parameter(int, "Date and time when node last booted", ro = True),
+ 'last_download': Parameter(int, "Date and time when node boot image was created", ro = True),
+ 'last_pcu_reboot': Parameter(int, "Date and time when PCU reboot was attempted", ro = True),
+ 'last_pcu_confirmation': Parameter(int, "Date and time when PCU reboot was confirmed", ro = True),
'verified': Parameter(bool, "Whether the node configuration is verified correct", ro=False),
'key': Parameter(str, "(Admin only) Node key", max = 256),
'session': Parameter(str, "(Admin only) Node session value", max = 256, ro = True),
validate_date_created = Row.validate_timestamp
validate_last_updated = Row.validate_timestamp
validate_last_contact = Row.validate_timestamp
+ validate_last_boot = Row.validate_timestamp
+ validate_last_download = Row.validate_timestamp
+ validate_last_pcu_reboot = Row.validate_timestamp
+ validate_last_pcu_confirmation = Row.validate_timestamp
- def update_last_contact(self, commit = True):
+ def update_timestamp(self, col_name, commit = True):
"""
- Update last_contact field with current time
+ Update col_name field with current time
"""
assert 'node_id' in self
assert self.table_name
- self.api.db.do("UPDATE %s SET last_contact = CURRENT_TIMESTAMP " % (self.table_name) + \
- " where node_id = %d" % ( self['node_id']) )
+ self.api.db.do("UPDATE %s SET %s = CURRENT_TIMESTAMP " % (self.table_name, col_name) + \
+ " where node_id = %d" % (self['node_id']) )
self.sync(commit)
+ def update_last_boot(self, commit = True):
+ self.update_timestamp('last_boot', commit)
+ def update_last_download(self, commit = True):
+ self.update_timestamp('last_download', commit)
+ def update_last_pcu_reboot(self, commit = True):
+ self.update_timestamp('last_pcu_reboot', commit)
+ def update_last_pcu_confirmation(self, commit = True):
+ self.update_timestamp('last_pcu_confirmation', commit)
+ def update_last_contact(self, commit = True):
+ self.update_timestamp('last_contact', commit)
def update_last_updated(self, commit = True):
- """
- Update last_updated field with current time
- """
-
- assert 'node_id' in self
- assert self.table_name
-
- self.api.db.do("UPDATE %s SET last_updated = CURRENT_TIMESTAMP " % (self.table_name) + \
- " where node_id = %d" % (self['node_id']) )
- self.sync(commit)
+ self.update_timestamp('last_updated', commit)
def update_tags(self, tags):
from PLC.Shell import Shell
'model': Parameter(str, "PCU model string", max = 32, nullok = True),
'node_ids': Parameter([int], "List of nodes that this PCU controls"),
'ports': Parameter([int], "List of the port numbers that each node is connected to"),
+ 'last_updated': Parameter(int, "Date and time when node entry was created", ro = True),
}
def validate_ip(self, ip):
raise PLCInvalidArgument, "Invalid IP address " + ip
return ip
+ validate_last_updated = Row.validate_timestamp
+
+ def update_timestamp(self, col_name, commit = True):
+ """
+ Update col_name field with current time
+ """
+
+ assert 'pcu_id' in self
+ assert self.table_name
+
+ self.api.db.do("UPDATE %s SET %s = CURRENT_TIMESTAMP " % (self.table_name, col_name) + \
+ " where pcu_id = %d" % (self['pcu_id']) )
+ self.sync(commit)
+
+ def update_last_updated(self, commit = True):
+ self.update_timestamp('last_updated', commit)
+
def add_node(self, node, port, commit = True):
"""
Add node to existing PCU.
-- Timestamps
date_created timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
last_updated timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ last_download timestamp without time zone,
+ last_pcu_reboot timestamp without time zone,
+ last_pcu_confirmation timestamp without time zone,
last_contact timestamp without time zone
) WITH OIDS;
CREATE INDEX nodes_hostname_idx ON nodes (hostname);
dns1 text, -- Primary DNS server
dns2 text, -- Secondary DNS server
bwlimit integer, -- Bandwidth limit in bps
- hostname text -- Hostname of this interface
+ hostname text, -- Hostname of this interface
+ last_updated timestamp without time zone -- When the interface was last updated
) WITH OIDS;
CREATE INDEX interfaces_node_id_idx ON interfaces (node_id);
interfaces.dns2,
interfaces.bwlimit,
interfaces.hostname,
+CAST(date_part('epoch', interfaces.last_updated) AS bigint) AS last_updated,
COALESCE((SELECT interface_tag_ids FROM interface_tags WHERE interface_tags.interface_id = interfaces.interface_id), '{}') AS interface_tag_ids
FROM interfaces;
username text, -- Username, if applicable
"password" text, -- Password, if applicable
model text, -- Model, e.g. BayTech or iPal
+ last_updated timestamp without time zone,
notes text -- Random notes
) WITH OIDS;
CREATE INDEX pcus_site_id_idx ON pcus (site_id);
CAST(date_part('epoch', nodes.date_created) AS bigint) AS date_created,
CAST(date_part('epoch', nodes.last_updated) AS bigint) AS last_updated,
CAST(date_part('epoch', nodes.last_contact) AS bigint) AS last_contact,
+CAST(date_part('epoch', nodes.last_boot) AS bigint) AS last_boot,
+CAST(date_part('epoch', nodes.last_download) AS bigint) AS last_download,
+CAST(date_part('epoch', nodes.last_pcu_reboot) AS bigint) AS last_pcu_reboot,
+CAST(date_part('epoch', nodes.last_pcu_confirmation) AS bigint) AS last_pcu_confirmation,
peer_node.peer_id,
peer_node.peer_node_id,
COALESCE((SELECT interface_ids FROM node_interfaces
FROM conf_files;
--------------------------------------------------------------------------------
+DROP VIEW view_pcus;
CREATE OR REPLACE VIEW view_pcus AS
SELECT
-pcus.*,
+pcus.pcu_id,
+pcus.site_id,
+pcus.hostname,
+pcus.ip,
+pcus.protocol,
+pcus.username,
+pcus.password,
+pcus.model,
+pcus.notes,
+CAST(date_part('epoch', pcus.last_updated) AS bigint) AS last_updated,
COALESCE((SELECT node_ids FROM pcu_nodes WHERE pcu_nodes.pcu_id = pcus.pcu_id), '{}') AS node_ids,
COALESCE((SELECT ports FROM pcu_nodes WHERE pcu_nodes.pcu_id = pcus.pcu_id), '{}') AS ports
FROM pcus;
+
--------------------------------------------------------------------------------
CREATE OR REPLACE VIEW view_sites AS
SELECT