5d2774d01383e9b8360d84be347dcd3dec8ab520
[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
71     def call(self, auth, node_id_or_hostname, interface_fields):
72
73         [native,tags,rejected]=Row.split_fields(interface_fields,[legacy_interface_fields,Interface.tags])
74
75         # type checking
76         native = Row.check_fields (native, self.accepted_fields)
77         if rejected:
78             raise PLCInvalidArgument, "Cannot add Interface with column(s) %r"%rejected
79
80         # Check if node exists
81         nodes = Nodes(self.api, [node_id_or_hostname])
82         if not nodes:
83             raise PLCInvalidArgument, "No such node %r"%node_id_or_hostname
84         node = nodes[0]
85
86         # Authenticated function
87         assert self.caller is not None
88
89         # If we are not an admin, make sure that the caller is a
90         # member of the site where the node exists.
91         if 'admin' not in self.caller['roles']:
92             if node['site_id'] not in self.caller['site_ids']:
93                 raise PLCPermissionDenied, "Not allowed to add an interface to the specified node"
94
95         # Add interface
96         interface = Interface(self.api, native)
97         interface['node_id'] = node['node_id']
98         # if this is the first interface, make it primary
99         if not node['interface_ids']:
100             interface['is_primary'] = True
101         interface.sync()
102
103         # Add IpAddress to conform with the new object model
104         if native['method'] == 'static':
105             address_fields = {}
106             address_fields['interface_id'] = interface['interface_id']
107             address_fields['ip_addr'] = native['ip']
108             address_fields['netmask'] = native['netmask']
109             address_fields['type'] = native['type']
110             ip_address = IpAddress(self.api, address_fields)
111             ip_address.sync()
112
113         # ADD DNS and ROUTES if this is a primary interface
114         if native['is_primary']:
115             route_fields = {}
116             route_fields['node_id'] = interface['node_id']
117             route_fields['interface_id'] = interface['interface_id']
118             route_fields['subnet'] = '0.0.0.0/0'
119             route_fields['next_hop'] = interface['gateway']
120             route = Route(self.api, route_fields)
121             route.sync()
122
123             dns = ""
124             if native.has_key('dns1'):
125                 dns += native['dns1']
126             if native.has_key('dns2'):
127                 dns += ",%s" % native['dns2']
128
129             if dns:
130                 UpdateNode(self.api).__call__(auth, interface['node_id'], {'dns':dns})
131             
132
133         # Logging variables
134         self.event_objects = { 'Node': [node['node_id']],
135                                'Interface' : [interface['interface_id']] }
136         self.message = "Interface %d added" % interface['interface_id']
137
138         for (tagname,value) in tags.iteritems():
139             # the tagtype instance is assumed to exist, just check that
140             if not TagTypes(self.api,{'tagname':tagname}):
141                 raise PLCInvalidArgument,"No such TagType %s"%tagname
142             interface_tags=InterfaceTags(self.api,{'tagname':tagname,'interface_id':interface['interface_id']})
143             if not interface_tags:
144                 AddInterfaceTag(self.api).__call__(auth,interface['interface_id'],tagname,value)
145             else:
146                 UpdateInterfaceTag(self.api).__call__(auth,interface_tags[0]['interface_tag_id'],value)
147
148         return interface['interface_id']