fix PLCAPI doc that was whining about duplicate ids in docbook xml output
[plcapi.git] / PLC / Methods / Legacy / AddNode.py
1 # $Id$
2 # $URL$
3 from PLC.Faults import *
4 from PLC.Auth import Auth
5 from PLC.Method import Method
6 from PLC.Parameter import Parameter, Mixed
7 from PLC.Table import Row
8 from PLC.Namespace import hostname_to_hrn
9 from PLC.Peers import Peers
10 from PLC.Sites import Site, 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
16
17 can_update = ['hostname', 'node_type', 'boot_state', 'model', 'version']
18
19 legacy_node_fields = {
20     'node_id': Parameter(int, "Node identifier"),
21     'node_type': Parameter(str,"Node type",max=20),
22     'hostname': Parameter(str, "Fully qualified hostname", max = 255),
23     'site_id': Parameter(int, "Site at which this node is located"),
24     'boot_state': Parameter(str, "Boot state", max = 20),
25     'run_level': Parameter(str, "Run level", max = 20),
26     'model': Parameter(str, "Make and model of the actual machine", max = 255, nullok = True),
27     'boot_nonce': Parameter(str, "(Admin only) Random value generated by the node at last boot", max = 128),
28     'version': Parameter(str, "Apparent Boot CD version", max = 64),
29     'ssh_rsa_key': Parameter(str, "Last known SSH host key", max = 1024),
30     'date_created': Parameter(int, "Date and time when node entry was created", ro = True),
31     'last_updated': Parameter(int, "Date and time when node entry was created", ro = True),
32     'last_contact': Parameter(int, "Date and time when node last contacted plc", ro = True),
33     'last_boot': Parameter(int, "Date and time when node last booted", ro = True),
34     'last_download': Parameter(int, "Date and time when node boot image was created", ro = True),
35     'last_pcu_reboot': Parameter(int, "Date and time when PCU reboot was attempted", ro = True),
36     'last_pcu_confirmation': Parameter(int, "Date and time when PCU reboot was confirmed", ro = True),
37     'last_time_spent_online': Parameter(int, "Length of time the node was last online before shutdown/failure", ro = True),
38     'last_time_spent_offline': Parameter(int, "Length of time the node was last offline after failure and before reboot", ro = True),
39     'verified': Parameter(bool, "Whether the node configuration is verified correct", ro=False),
40     'key': Parameter(str, "(Admin only) Node key", max = 256),
41     'session': Parameter(str, "(Admin only) Node session value", max = 256, ro = True),
42     'interface_ids': Parameter([int], "List of network interfaces that this node has"),
43     'conf_file_ids': Parameter([int], "List of configuration files specific to this node"),
44     # 'root_person_ids': Parameter([int], "(Admin only) List of people who have root access to this node"),
45     'slice_ids': Parameter([int], "List of slices on this node"),
46     'slice_ids_whitelist': Parameter([int], "List of slices allowed on this node"),
47     'pcu_ids': Parameter([int], "List of PCUs that control this node"),
48     'ports': Parameter([int], "List of PCU ports that this node is connected to"),
49     'peer_id': Parameter(int, "Peer to which this node belongs", nullok = True),
50     'peer_node_id': Parameter(int, "Foreign node identifier at peer", nullok = True),
51     'node_tag_ids' : Parameter ([int], "List of tags attached to this node"),
52     'nodegroup_ids': Parameter([int], "List of node groups that this node is in"),
53     }
54
55
56 class AddNode(Method):
57     """
58     Adds a new node. Any values specified in node_fields are used,
59     otherwise defaults are used.
60
61     PIs and techs may only add nodes to their own sites. Admins may
62     add nodes to any site.
63
64     Returns the new node_id (> 0) if successful, faults otherwise.
65     """
66
67     roles = ['admin', 'pi', 'tech']
68
69     accepted_fields = Row.accepted_fields(can_update,legacy_node_fields)
70     accepted_fields.update(Node.tags)
71
72     accepts = [
73         Auth(),
74         Mixed(Site.fields['site_id'],
75               Site.fields['login_base']),
76         accepted_fields
77         ]
78
79     returns = Parameter(int, 'New node_id (> 0) if successful')
80
81     # needed for generating the doc and prevent conflicts in the xml ids
82     status = 'legacy'
83
84     def call(self, auth, site_id_or_login_base, node_fields):
85
86         [native,tags,rejected]=Row.split_fields(node_fields,[legacy_node_fields,Node.tags])
87
88         # type checking
89         native = Row.check_fields(native, self.accepted_fields)
90         if rejected:
91             raise PLCInvalidArgument, "Cannot add Node with column(s) %r"%rejected
92
93         # Get site information
94         sites = Sites(self.api, [site_id_or_login_base])
95         if not sites:
96             raise PLCInvalidArgument, "No such site"
97
98         site = sites[0]
99
100         # Authenticated function
101         assert self.caller is not None
102
103         # If we are not an admin, make sure that the caller is a
104         # member of the site.
105         if 'admin' not in self.caller['roles']:
106             if site['site_id'] not in self.caller['site_ids']:
107                 assert self.caller['person_id'] not in site['person_ids']
108                 raise PLCPermissionDenied, "Not allowed to add nodes to specified site"
109             else:
110                 assert self.caller['person_id'] in site['person_ids']
111
112         node = Node(self.api, native)
113         node['site_id'] = site['site_id']
114         node.sync()
115
116         # since hostname was specified lets add the 'hrn' node tag
117         root_auth = self.api.config.PLC_HRN_ROOT
118         login_base = site['login_base']
119         tags['hrn'] = hostname_to_hrn(root_auth, login_base, node['hostname'])
120
121         for (tagname,value) in tags.iteritems():
122             # the tagtype instance is assumed to exist, just check that
123             tag_types = TagTypes(self.api,{'tagname':tagname})
124             if not tag_types:
125                 raise PLCInvalidArgument,"No such TagType %s"%tagname
126             tag_type = tag_types[0] 
127             node_tags=NodeTags(self.api,{'tagname':tagname,'node_id':node['node_id']})
128             if not node_tags:
129                 node_tag = NodeTag(self.api)
130                 node_tag['node_id'] = node['node_id']
131                 node_tag['tag_type_id'] = tag_type['tag_type_id']
132                 node_tag['tagname']  = tagname
133                 node_tag['value'] = value
134                 node_tag.sync()
135             else:
136                 node_tag = node_tags[0]
137                 node_tag['value'] = value
138                 node_tag.sync() 
139
140         self.event_objects = {'Site': [site['site_id']],
141                               'Node': [node['node_id']]}
142         self.message = "Node %d=%s created" % (node['node_id'],node['hostname'])
143
144         return node['node_id']