X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=PLC%2FMethods%2FBootUpdateNode.py;h=5606ee80bdab863548c7bfc01ab46d7fac0a2311;hb=c379c0fff5edc592cc5d5d647c7fadb91317db87;hp=271da9f245e7dfafc9a0193b98d2bd74f2454968;hpb=dd6f6871abdf5412d908dc31777ce88b09192efe;p=plcapi.git diff --git a/PLC/Methods/BootUpdateNode.py b/PLC/Methods/BootUpdateNode.py index 271da9f..5606ee8 100644 --- a/PLC/Methods/BootUpdateNode.py +++ b/PLC/Methods/BootUpdateNode.py @@ -1,11 +1,14 @@ +import time + from PLC.Faults import * from PLC.Method import Method from PLC.Parameter import Parameter, Mixed -from PLC.Auth import Auth, BootAuth +from PLC.Auth import Auth, BootAuth, SessionAuth from PLC.Nodes import Node, Nodes -from PLC.NodeNetworks import NodeNetwork, NodeNetworks +from PLC.Interfaces import Interface, Interfaces +from PLC.Timestamp import * -can_update = lambda (field, value): field in \ +can_update = lambda field_value: field_value[0] in \ ['method', 'mac', 'gateway', 'network', 'broadcast', 'netmask', 'dns1', 'dns2'] @@ -19,46 +22,94 @@ class BootUpdateNode(Method): roles = ['node'] - nodenetwork_fields = dict(filter(can_update, NodeNetwork.fields.items())) + interface_fields = dict(list(filter(can_update, list(Interface.fields.items())))) accepts = [ - BootAuth(), + Mixed(BootAuth(), SessionAuth()), {'boot_state': Node.fields['boot_state'], - 'primary_network': nodenetwork_fields, - 'ssh_host_key': Node.fields['ssh_rsa_key']} - ] + 'primary_network': interface_fields, + ### BEWARE that the expected formerly did not match the native Node field + # support both for now + 'ssh_rsa_key': Node.fields['ssh_rsa_key'], + 'ssh_host_key': Node.fields['ssh_rsa_key'], + }] returns = Parameter(int, '1 if successful') def call(self, auth, node_fields): + + if not isinstance(self.caller, Node): + raise PLCInvalidArgument("Caller is expected to be a node") + + node = self.caller + + # log this event only if a change occured + # otherwise the db gets spammed with meaningless entries + changed_fields = [] # Update node state - if node_fields.has_key('boot_state'): - self.caller['boot_state'] = node_fields['boot_state'] - if node_fields.has_key('ssh_host_key'): - self.caller['ssh_rsa_key'] = node_fields['ssh_host_key'] - - # Update primary node network state - if node_fields.has_key('primary_network'): - primary_network = node_fields['primary_network'] - - if 'nodenetwork_id' not in primary_network: - raise PLCInvalidArgument, "Node network not specified" - if primary_network['nodenetwork_id'] not in self.caller['nodenetwork_ids']: - raise PLCInvalidArgument, "Node network not associated with calling node" - - nodenetworks = NodeNetworks(self.api, [primary_network['nodenetwork_id']]) - if not nodenetworks: - raise PLCInvalidArgument, "No such node network" - nodenetwork = nodenetworks[0] - - if not nodenetwork['is_primary']: - raise PLCInvalidArgument, "Not the primary node network on record" - - nodenetwork_fields = dict(filter(can_update, primary_network.items())) - nodenetwork.update(nodenetwork_fields) - nodenetwork.sync(commit = False) - - self.caller.sync(commit = True) - self.message = "Node updated: %s" % ", ".join(node_fields.keys()) + if 'boot_state' in node_fields: + if node['boot_state'] != node_fields['boot_state']: changed_fields.append('boot_state') + node['boot_state'] = node_fields['boot_state'] + ### for legacy BootManager + if 'ssh_host_key' in node_fields: + if node['ssh_rsa_key'] != node_fields['ssh_host_key']: changed_fields.append('ssh_rsa_key') + node['ssh_rsa_key'] = node_fields['ssh_host_key'] + if 'ssh_rsa_key' in node_fields: + if node['ssh_rsa_key'] != node_fields['ssh_rsa_key']: changed_fields.append('ssh_rsa_key') + node['ssh_rsa_key'] = node_fields['ssh_rsa_key'] + + # Update primary interface state + if 'primary_network' in node_fields: + primary_network = node_fields['primary_network'] + + if 'interface_id' not in primary_network: + raise PLCInvalidArgument("Interface not specified") + if primary_network['interface_id'] not in node['interface_ids']: + raise PLCInvalidArgument("Interface not associated with calling node") + + interfaces = Interfaces(self.api, [primary_network['interface_id']]) + if not interfaces: + raise PLCInvalidArgument("No such interface %r"%interface_id) + interface = interfaces[0] + + if not interface['is_primary']: + raise PLCInvalidArgument("Not the primary interface on record") + + interface_fields = dict(list(filter(can_update, list(primary_network.items())))) + for field in interface_fields: + if interface[field] != primary_network[field] : changed_fields.append('Interface.'+field) + interface.update(interface_fields) + interface.sync(commit = False) + + current_time = int(time.time()) + + # ONLY UPDATE ONCE when the boot_state flag and ssh_rsa_key flag are NOT passed + if 'boot_state' not in node_fields and 'ssh_rsa_key' not in node_fields: + + # record times spent on and off line by comparing last_contact with previous value of last_boot + if node['last_boot'] and node['last_contact']: + # last_boot is when the machine last called this API function. + # last_contact is the last time NM or RLA pinged the API. + node['last_time_spent_online'] = node['last_contact'] - node['last_boot'] + node['last_time_spent_offline'] = current_time - Timestamp.cast_long(node['last_contact']) + + node.update_readonly_int('last_time_spent_online') + node.update_readonly_int('last_time_spent_offline') + changed_fields.append('last_time_spent_online') + changed_fields.append('last_time_spent_offline') + + # indicate that node has booted & contacted PLC. + node.update_last_contact() + node.update_last_boot() + + # if last_pcu_reboot is within 20 minutes of current_time, accept that the PCU is responsible + if node['last_pcu_reboot'] and Timestamp.cast_long(node['last_pcu_reboot']) >= current_time - 60*20: + node.update_last_pcu_confirmation(commit=False) + + node.sync(commit = True) + + if changed_fields: + self.message = "Boot updated: %s" % ", ".join(changed_fields) + self.event_objects = { 'Node' : [node['node_id']] } return 1