3 from PLC.Faults import *
4 from PLC.Method import Method
5 from PLC.Parameter import Parameter, Mixed
6 from PLC.Table import Row
7 from PLC.Auth import Auth
8 from PLC.Namespace import hostname_to_hrn
9 from PLC.Peers import Peers
10 from PLC.Sites import Sites
11 from PLC.Nodes import Node, Nodes
12 from PLC.TagTypes import TagTypes
13 from PLC.NodeTags import NodeTags, NodeTag
14 from PLC.Methods.AddNodeTag import AddNodeTag
15 from PLC.Methods.UpdateNodeTag import UpdateNodeTag
17 admin_only = [ 'key', 'session', 'boot_nonce', 'site_id']
18 can_update = ['hostname', 'node_type', 'boot_state', 'model', 'version'] + admin_only
20 legacy_node_fields = {
21 'node_id': Parameter(int, "Node identifier"),
22 'node_type': Parameter(str,"Node type",max=20),
23 'hostname': Parameter(str, "Fully qualified hostname", max = 255),
24 'site_id': Parameter(int, "Site at which this node is located"),
25 'boot_state': Parameter(str, "Boot state", max = 20),
26 'run_level': Parameter(str, "Run level", max = 20),
27 'model': Parameter(str, "Make and model of the actual machine", max = 255, nullok = True),
28 'boot_nonce': Parameter(str, "(Admin only) Random value generated by the node at last boot", max = 128),
29 'version': Parameter(str, "Apparent Boot CD version", max = 64),
30 'ssh_rsa_key': Parameter(str, "Last known SSH host key", max = 1024),
31 'date_created': Parameter(int, "Date and time when node entry was created", ro = True),
32 'last_updated': Parameter(int, "Date and time when node entry was created", ro = True),
33 'last_contact': Parameter(int, "Date and time when node last contacted plc", ro = True),
34 'last_boot': Parameter(int, "Date and time when node last booted", ro = True),
35 'last_download': Parameter(int, "Date and time when node boot image was created", ro = True),
36 'last_pcu_reboot': Parameter(int, "Date and time when PCU reboot was attempted", ro = True),
37 'last_pcu_confirmation': Parameter(int, "Date and time when PCU reboot was confirmed", ro = True),
38 'last_time_spent_online': Parameter(int, "Length of time the node was last online before shutdown/failure", ro = True),
39 'last_time_spent_offline': Parameter(int, "Length of time the node was last offline after failure and before reboot", ro = True),
40 'verified': Parameter(bool, "Whether the node configuration is verified correct", ro=False),
41 'key': Parameter(str, "(Admin only) Node key", max = 256),
42 'session': Parameter(str, "(Admin only) Node session value", max = 256, ro = True),
43 'interface_ids': Parameter([int], "List of network interfaces that this node has"),
44 'conf_file_ids': Parameter([int], "List of configuration files specific to this node"),
45 # 'root_person_ids': Parameter([int], "(Admin only) List of people who have root access to this node"),
46 'slice_ids': Parameter([int], "List of slices on this node"),
47 'slice_ids_whitelist': Parameter([int], "List of slices allowed on this node"),
48 'pcu_ids': Parameter([int], "List of PCUs that control this node"),
49 'ports': Parameter([int], "List of PCU ports that this node is connected to"),
50 'peer_id': Parameter(int, "Peer to which this node belongs", nullok = True),
51 'peer_node_id': Parameter(int, "Foreign node identifier at peer", nullok = True),
52 'node_tag_ids' : Parameter ([int], "List of tags attached to this node"),
53 'nodegroup_ids': Parameter([int], "List of node groups that this node is in"),
57 class UpdateNode(Method):
59 Updates a node. Only the fields specified in node_fields are
60 updated, all other fields are left untouched.
62 PIs and techs can update only the nodes at their sites. Only
63 admins can update the key, session, and boot_nonce fields.
65 Returns 1 if successful, faults otherwise.
68 roles = ['admin', 'pi', 'tech']
70 accepted_fields = Row.accepted_fields(can_update,legacy_node_fields)
71 # xxx check the related_fields feature
72 accepted_fields.update(Node.related_fields)
73 accepted_fields.update(Node.tags)
77 Mixed(Node.fields['node_id'],
78 Node.fields['hostname']),
82 returns = Parameter(int, '1 if successful')
84 # needed for generating the doc and prevent conflicts in the xml ids
87 def call(self, auth, node_id_or_hostname, node_fields):
89 # split provided fields
90 [native,related,tags,rejected] = Row.split_fields(node_fields,[legacy_node_fields,Node.related_fields,Node.tags])
93 native = Row.check_fields (native, self.accepted_fields)
95 raise PLCInvalidArgument, "Cannot update Node column(s) %r"%rejected
97 # Authenticated function
98 assert self.caller is not None
100 # Remove admin only fields
101 if 'admin' not in self.caller['roles']:
102 for key in admin_only:
103 if native.has_key(key):
106 # Get account information
107 nodes = Nodes(self.api, [node_id_or_hostname])
109 raise PLCInvalidArgument, "No such node %r"%node_id_or_hostname
112 if node['peer_id'] is not None:
113 raise PLCInvalidArgument, "Not a local node %r"%node_id_or_hostname
115 # If we are not an admin, make sure that the caller is a
116 # member of the site at which the node is located.
117 if 'admin' not in self.caller['roles']:
118 if node['site_id'] not in self.caller['site_ids']:
119 raise PLCPermissionDenied, "Not allowed to delete nodes from specified site"
121 # Make requested associations
122 for (k,v) in related.iteritems():
123 node.associate(auth, k,v)
126 node.update_last_updated(commit=False)
127 node.sync(commit=True)
129 # if hostname was modifed make sure to update the hrn
131 if 'hostname' in native:
132 root_auth = self.api.config.PLC_HRN_ROOT
133 # sub auth is the login base of this node's site
134 sites = Sites(self.api, node['site_id'], ['login_base'])
136 login_base = site['login_base']
137 tags['hrn'] = hostname_to_hrn(root_auth, login_base, node['hostname'])
139 for (tagname,value) in tags.iteritems():
140 # the tagtype instance is assumed to exist, just check that
141 tag_types = TagTypes(self.api,{'tagname':tagname})
143 raise PLCInvalidArgument,"No such TagType %s"%tagname
144 tag_type = tag_types[0]
145 node_tags=NodeTags(self.api,{'tagname':tagname,'node_id':node['node_id']})
147 node_tag = NodeTag(self.api)
148 node_tag['node_id'] = node['node_id']
149 node_tag['tag_type_id'] = tag_type['tag_type_id']
150 node_tag['tagname'] = tagname
151 node_tag['value'] = value
154 node_tag = node_tags[0]
155 node_tag['value'] = value
158 self.event_objects = {'Node': [node['node_id']]}
159 if 'hostname' in node:
160 self.message = 'Node %s updated'%node['hostname']
162 self.message = 'Node %d updated'%node['node_id']
163 self.message += " [%s]." % (", ".join(node_fields.keys()),)
164 if 'boot_state' in node_fields.keys():
165 self.message += ' boot_state updated to %s' % node_fields['boot_state']