d9a245be4fba8f64a980e1648168d255d028bd19
[plcapi.git] / PLC / Methods / Legacy / AddInterface.py
1 from PLC.Faults import *
2 from PLC.Auth import Auth
3 from PLC.Method import Method
4 from PLC.Parameter import Parameter, Mixed
5 from PLC.Table import Row
6
7 from PLC.Nodes import Node, Nodes
8 from PLC.Interfaces import Interface, Interfaces
9 from PLC.IpAddresses import IpAddress, IpAddresses
10 from PLC.Routes import Route, Routes
11 from PLC.TagTypes import TagTypes
12 from PLC.InterfaceTags import InterfaceTags
13 from PLC.Methods.AddInterfaceTag import AddInterfaceTag
14 from PLC.Methods.UpdateInterfaceTag import UpdateInterfaceTag
15 from PLC.Methods.UpdateNode import UpdateNode
16
17 cannot_update = ['interface_id', 'node_id']
18
19 legacy_interface_fields = {
20         'interface_id': Parameter(int, "Node interface identifier"),
21         'method': Parameter(str, "Addressing method (e.g., 'static' or 'dhcp')"),
22         'type': Parameter(str, "Address type (e.g., 'ipv4')"),
23         'ip': Parameter(str, "IP address", nullok = True),
24         'mac': Parameter(str, "MAC address", nullok = True),
25         'gateway': Parameter(str, "IP address of primary gateway", nullok = True),
26         'network': Parameter(str, "Subnet address", nullok = True),
27         'broadcast': Parameter(str, "Network broadcast address", nullok = True),
28         'netmask': Parameter(str, "Subnet mask", nullok = True),
29         'dns1': Parameter(str, "IP address of primary DNS server", nullok = True),
30         'dns2': Parameter(str, "IP address of secondary DNS server", nullok = True),
31         'bwlimit': Parameter(int, "Bandwidth limit", min = 0, nullok = True),
32         'hostname': Parameter(str, "(Optional) Hostname", nullok = True),
33         'node_id': Parameter(int, "Node associated with this interface"),
34         'is_primary': Parameter(bool, "Is the primary interface for this node"),
35         'interface_tag_ids' : Parameter([int], "List of interface settings"),
36         'last_updated': Parameter(int, "Date and time when the interface entry was last updated"),
37         }
38
39 class AddInterface(Method):
40     """
41
42     Adds a new network for a node. Any values specified in
43     interface_fields are used, otherwise defaults are
44     used.
45
46     If type is static, then ip, gateway, network, broadcast, netmask,
47     and dns1 must all be specified in interface_fields. If type is
48     dhcp, these parameters, even if specified, are ignored.
49
50     PIs and techs may only add interfaces to their own nodes. Admins may
51     add interfaces to any node.
52
53     Returns the new interface_id (> 0) if successful, faults otherwise.
54     """
55
56     roles = ['admin', 'pi', 'tech']
57
58     accepted_fields = Row.accepted_fields(cannot_update, legacy_interface_fields, exclude=True)
59     accepted_fields.update(Interface.tags)
60
61     accepts = [
62         Auth(),
63         Mixed(Node.fields['node_id'],
64               Node.fields['hostname']),
65         accepted_fields
66         ]
67
68     returns = Parameter(int, 'New interface_id (> 0) if successful')
69     
70     # needed for generating the doc and prevent conflicts in the xml ids
71     status = 'legacy'
72
73     def call(self, auth, node_id_or_hostname, interface_fields):
74
75         [native,tags,rejected]=Row.split_fields(interface_fields,[legacy_interface_fields,Interface.tags])
76
77         # type checking
78         native = Row.check_fields (native, self.accepted_fields)
79         if rejected:
80             raise PLCInvalidArgument, "Cannot add Interface with column(s) %r"%rejected
81
82         # Check if node exists
83         nodes = Nodes(self.api, [node_id_or_hostname])
84         if not nodes:
85             raise PLCInvalidArgument, "No such node %r"%node_id_or_hostname
86         node = nodes[0]
87
88         # Authenticated function
89         assert self.caller is not None
90
91         # If we are not an admin, make sure that the caller is a
92         # member of the site where the node exists.
93         if 'admin' not in self.caller['roles']:
94             if node['site_id'] not in self.caller['site_ids']:
95                 raise PLCPermissionDenied, "Not allowed to add an interface to the specified node"
96
97         # Add interface
98         interface = Interface(self.api, native)
99         interface['node_id'] = node['node_id']
100         # if this is the first interface, make it primary
101         if not node['interface_ids']:
102             interface['is_primary'] = True
103         interface.sync()
104
105         # Add IpAddress to conform with the new object model
106         if native['method'] == 'static':
107             address_fields = {}
108             address_fields['interface_id'] = interface['interface_id']
109             address_fields['ip_addr'] = native['ip']
110             address_fields['netmask'] = native['netmask']
111             address_fields['type'] = native['type']
112             ip_address = IpAddress(self.api, address_fields)
113             ip_address.sync()
114
115         # ADD DNS and ROUTES if this is a primary interface
116         if native['is_primary']:
117             route_fields = {}
118             route_fields['node_id'] = interface['node_id']
119             route_fields['interface_id'] = interface['interface_id']
120             route_fields['subnet'] = '0.0.0.0/0'
121             route_fields['next_hop'] = interface['gateway']
122             route = Route(self.api, route_fields)
123             route.sync()
124
125             dns = ""
126             if native.has_key('dns1'):
127                 dns += native['dns1']
128             if native.has_key('dns2'):
129                 dns += ",%s" % native['dns2']
130
131             if dns:
132                 UpdateNode(self.api).__call__(auth, interface['node_id'], {'dns':dns})
133             
134
135         # Logging variables
136         self.event_objects = { 'Node': [node['node_id']],
137                                'Interface' : [interface['interface_id']] }
138         self.message = "Interface %d added" % interface['interface_id']
139
140         for (tagname,value) in tags.iteritems():
141             # the tagtype instance is assumed to exist, just check that
142             if not TagTypes(self.api,{'tagname':tagname}):
143                 raise PLCInvalidArgument,"No such TagType %s"%tagname
144             interface_tags=InterfaceTags(self.api,{'tagname':tagname,'interface_id':interface['interface_id']})
145             if not interface_tags:
146                 AddInterfaceTag(self.api).__call__(auth,interface['interface_id'],tagname,value)
147             else:
148                 UpdateInterfaceTag(self.api).__call__(auth,interface_tags[0]['interface_tag_id'],value)
149
150         return interface['interface_id']