# $Id$ # $URL$ from PLC.Faults import * from PLC.Method import Method from PLC.Parameter import Parameter, Mixed from PLC.Table import Row from PLC.Auth import Auth from PLC.Namespace import hostname_to_hrn from PLC.Peers import Peers from PLC.Sites import Sites from PLC.Nodes import Node, Nodes from PLC.TagTypes import TagTypes from PLC.NodeTags import NodeTags, NodeTag from PLC.Methods.AddNodeTag import AddNodeTag from PLC.Methods.UpdateNodeTag import UpdateNodeTag admin_only = [ 'key', 'session', 'boot_nonce', 'site_id'] can_update = ['hostname', 'node_type', 'boot_state', 'model', 'version'] + admin_only legacy_node_fields = { 'node_id': Parameter(int, "Node identifier"), 'node_type': Parameter(str,"Node type",max=20), 'hostname': Parameter(str, "Fully qualified hostname", max = 255), 'site_id': Parameter(int, "Site at which this node is located"), 'boot_state': Parameter(str, "Boot state", max = 20), 'run_level': Parameter(str, "Run level", max = 20), 'model': Parameter(str, "Make and model of the actual machine", max = 255, nullok = True), 'boot_nonce': Parameter(str, "(Admin only) Random value generated by the node at last boot", max = 128), 'version': Parameter(str, "Apparent Boot CD version", max = 64), 'ssh_rsa_key': Parameter(str, "Last known SSH host key", max = 1024), '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), 'last_time_spent_online': Parameter(int, "Length of time the node was last online before shutdown/failure", ro = True), 'last_time_spent_offline': Parameter(int, "Length of time the node was last offline after failure and before reboot", 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), 'interface_ids': Parameter([int], "List of network interfaces that this node has"), 'conf_file_ids': Parameter([int], "List of configuration files specific to this node"), # 'root_person_ids': Parameter([int], "(Admin only) List of people who have root access to this node"), 'slice_ids': Parameter([int], "List of slices on this node"), 'slice_ids_whitelist': Parameter([int], "List of slices allowed on this node"), 'pcu_ids': Parameter([int], "List of PCUs that control this node"), 'ports': Parameter([int], "List of PCU ports that this node is connected to"), 'peer_id': Parameter(int, "Peer to which this node belongs", nullok = True), 'peer_node_id': Parameter(int, "Foreign node identifier at peer", nullok = True), 'node_tag_ids' : Parameter ([int], "List of tags attached to this node"), 'nodegroup_ids': Parameter([int], "List of node groups that this node is in"), } class UpdateNode(Method): """ Updates a node. Only the fields specified in node_fields are updated, all other fields are left untouched. PIs and techs can update only the nodes at their sites. Only admins can update the key, session, and boot_nonce fields. Returns 1 if successful, faults otherwise. """ roles = ['admin', 'pi', 'tech'] accepted_fields = Row.accepted_fields(can_update,legacy_node_fields) # xxx check the related_fields feature accepted_fields.update(Node.related_fields) accepted_fields.update(Node.tags) accepts = [ Auth(), Mixed(Node.fields['node_id'], Node.fields['hostname']), accepted_fields ] returns = Parameter(int, '1 if successful') # needed for generating the doc and prevent conflicts in the xml ids status = 'legacy' def call(self, auth, node_id_or_hostname, node_fields): # split provided fields [native,related,tags,rejected] = Row.split_fields(node_fields,[legacy_node_fields,Node.related_fields,Node.tags]) # type checking native = Row.check_fields (native, self.accepted_fields) if rejected: raise PLCInvalidArgument, "Cannot update Node column(s) %r"%rejected # Authenticated function assert self.caller is not None # Remove admin only fields if 'admin' not in self.caller['roles']: for key in admin_only: if native.has_key(key): del native[key] # Get account information nodes = Nodes(self.api, [node_id_or_hostname]) if not nodes: raise PLCInvalidArgument, "No such node %r"%node_id_or_hostname node = nodes[0] if node['peer_id'] is not None: raise PLCInvalidArgument, "Not a local node %r"%node_id_or_hostname # If we are not an admin, make sure that the caller is a # member of the site at which the node is located. if 'admin' not in self.caller['roles']: if node['site_id'] not in self.caller['site_ids']: raise PLCPermissionDenied, "Not allowed to delete nodes from specified site" # Make requested associations for (k,v) in related.iteritems(): node.associate(auth, k,v) node.update(native) node.update_last_updated(commit=False) node.sync(commit=True) # if hostname was modifed make sure to update the hrn # tag if 'hostname' in native: root_auth = self.api.config.PLC_HRN_ROOT # sub auth is the login base of this node's site sites = Sites(self.api, node['site_id'], ['login_base']) site = sites[0] login_base = site['login_base'] tags['hrn'] = hostname_to_hrn(root_auth, login_base, node['hostname']) for (tagname,value) in tags.iteritems(): # the tagtype instance is assumed to exist, just check that tag_types = TagTypes(self.api,{'tagname':tagname}) if not tag_types: raise PLCInvalidArgument,"No such TagType %s"%tagname tag_type = tag_types[0] node_tags=NodeTags(self.api,{'tagname':tagname,'node_id':node['node_id']}) if not node_tags: node_tag = NodeTag(self.api) node_tag['node_id'] = node['node_id'] node_tag['tag_type_id'] = tag_type['tag_type_id'] node_tag['tagname'] = tagname node_tag['value'] = value node_tag.sync() else: node_tag = node_tags[0] node_tag['value'] = value node_tag.sync() # Logging variables self.event_objects = {'Node': [node['node_id']]} if 'hostname' in node: self.message = 'Node %s updated'%node['hostname'] else: self.message = 'Node %d updated'%node['node_id'] self.message += " [%s]." % (", ".join(node_fields.keys()),) if 'boot_state' in node_fields.keys(): self.message += ' boot_state updated to %s' % node_fields['boot_state'] return 1