Merge lp:~blake-rouse/maas/rack-nic-fabric-change into lp:~maas-committers/maas/trunk

Proposed by Blake Rouse
Status: Merged
Approved by: Blake Rouse
Approved revision: no longer in the source branch.
Merged at revision: 4725
Proposed branch: lp:~blake-rouse/maas/rack-nic-fabric-change
Merge into: lp:~maas-committers/maas/trunk
Prerequisite: lp:~blake-rouse/maas/fix-1551378
Diff against target: 484 lines (+273/-60)
6 files modified
src/maasserver/models/interface.py (+3/-2)
src/maasserver/models/signals/interfaces.py (+89/-19)
src/maasserver/models/signals/nodes.py (+5/-5)
src/maasserver/models/signals/tests/test_interfaces.py (+101/-0)
src/maasserver/models/tests/test_interface.py (+55/-23)
src/maasserver/testing/factory.py (+20/-11)
To merge this branch: bzr merge lp:~blake-rouse/maas/rack-nic-fabric-change
Reviewer Review Type Date Requested Status
Mike Pontillo (community) Approve
Review via email: mp+287852@code.launchpad.net

Commit message

Fix forms to only allow physical and bond interfaces to be on untagged vlans. Fix forms to only allow VLAN interface to be on the same fabric as its parent interface. Migrate subnets and child VLAN interfaces that are attached to a controllers interface when the interfaces fabric is changed.

To post a comment you must log in.
Revision history for this message
Blake Rouse (blake-rouse) wrote :

This is only the backend part of this. I need to modify the API and UI to allow only the fabric to be changed on the controller interfaces. All other API's for controllers should not be allowed.

Revision history for this message
Mike Pontillo (mpontillo) wrote :

I disagree with this change. We have received feedback from the field team that this set of assumptions is not working for customers.

First, the use of the term "untagged" is very misleading. It is a term to describe a *switch port*, not a term to describe an attribute of a VLAN. A switch can have many ports in a fabric, some of which are "untagged" in (for example) the VLAN with VID 10, some of which are untagged in (for example) the VLAN with VID 20. The first thing we should do is rename that. (perhaps "default" would be better, but it's more like "unknown" - it's *one of* the 4094 VLANs, but we aren't sure which one.)

Assuming we're using the correct terminology (and then, the idea that a single fabric only ever has one "untagged" VLAN is out of the way), think about the case of the multiple different VLANs being "untagged" on different switch ports. Users MUST be able to change the model so that physical interfaces can appear on ports with a known VID. (Otherwise, you're forced to model it as two separate fabrics - and therefore you cannot support the VLAN interface creation feature in MAAS.)

This also breaks HA in the case where an inter-switch link uses tagged VLANs to aggregate multiple racks, and expects each rack controller to demultiplex the VLAN tags.

I understand and agree with the vision that MAAS will work better if all the switch ports are configured the same. However, I am opposed to the idea that we should force the model to conform to that. (yes, it should be the default, well-supported way to do things in MAAS, but we should not prevent MAAS from working if it doesn't match reality.)

review: Disapprove
Revision history for this message
Blake Rouse (blake-rouse) wrote :

I have removed the code that validations the untagged VLANs. Please give it another look. Thanks.

Revision history for this message
Mike Pontillo (mpontillo) wrote :

I'm good with this branch now; thanks.

For the record, only validations I had a problem with in revision 4710 were these:
 - "A physical interface can only belong to an untagged VLAN."
 - "A bond interface can only belong to an untagged VLAN."

(Again, in my view, we have received direction that we are not modeling switches, therefore it would be inappropriate to enforce those restrictions.)

The following other validations are perfectly reasonable (necessary, even):
 - "A VLAN interface can only belong to a tagged VLAN."
 - "A VLAN interface can only belong to a tagged VLAN on the same fabric as its parent interface."
 - "All [bond] (sic) parents must belong to the same VLAN."

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/models/interface.py'
2--- src/maasserver/models/interface.py 2016-02-25 16:46:49 +0000
3+++ src/maasserver/models/interface.py 2016-03-03 21:19:22 +0000
4@@ -714,11 +714,12 @@
5 # Nothing to do, already has links.
6 return
7 else:
8- # Use an associated subnet if it exists, else it will just be a
9+ # Use an associated subnet if it exists and its on the same VLAN
10+ # the interface is currently connected, else it will just be a
11 # LINK_UP without a subnet.
12 discovered_address = self.ip_addresses.filter(
13 alloc_type=IPADDRESS_TYPE.DISCOVERED,
14- subnet__isnull=False).first()
15+ subnet__vlan=self.vlan).first()
16 if discovered_address is not None:
17 subnet = discovered_address.subnet
18 else:
19
20=== modified file 'src/maasserver/models/signals/interfaces.py'
21--- src/maasserver/models/signals/interfaces.py 2016-01-20 00:07:16 +0000
22+++ src/maasserver/models/signals/interfaces.py 2016-03-03 21:19:22 +0000
23@@ -7,19 +7,29 @@
24 "signals",
25 ]
26
27+from django.db.models.signals import post_save
28 from maasserver.enum import (
29 INTERFACE_TYPE,
30 IPADDRESS_TYPE,
31+ NODE_TYPE,
32 )
33 from maasserver.models import (
34 BondInterface,
35 Interface,
36 PhysicalInterface,
37+ VLAN,
38 VLANInterface,
39 )
40 from maasserver.utils.signals import SignalsManager
41
42
43+INTERFACE_CLASSES = [
44+ Interface,
45+ PhysicalInterface,
46+ BondInterface,
47+ VLANInterface,
48+]
49+
50 signals = SignalsManager()
51
52
53@@ -48,12 +58,10 @@
54 ip_address, clearing_config=True)
55
56
57-signals.watch_fields(
58- interface_enabled_or_disabled,
59- Interface, ['enabled'], delete=False)
60-signals.watch_fields(
61- interface_enabled_or_disabled,
62- PhysicalInterface, ['enabled'], delete=False)
63+for klass in INTERFACE_CLASSES:
64+ signals.watch_fields(
65+ interface_enabled_or_disabled,
66+ klass, ['enabled'], delete=False)
67
68
69 def interface_mtu_params_update(instance, old_values, **kwargs):
70@@ -114,19 +122,81 @@
71 parent.save()
72
73
74-signals.watch_fields(
75- interface_mtu_params_update,
76- Interface, ['params'], delete=False)
77-signals.watch_fields(
78- interface_mtu_params_update,
79- PhysicalInterface, ['params'], delete=False)
80-signals.watch_fields(
81- interface_mtu_params_update,
82- BondInterface, ['params'], delete=False)
83-signals.watch_fields(
84- interface_mtu_params_update,
85- VLANInterface, ['params'], delete=False)
86-
87+for klass in INTERFACE_CLASSES:
88+ signals.watch_fields(
89+ interface_mtu_params_update,
90+ klass, ['params'], delete=False)
91+
92+
93+def update_bond_parents(sender, instance, created, **kwargs):
94+ """Update bond parents when interface created."""
95+ if instance.type == INTERFACE_TYPE.BOND:
96+ for parent in instance.parents.all():
97+ # Make sure the parent has not links as well, just to be sure.
98+ parent.clear_all_links(clearing_config=True)
99+ if parent.vlan != instance.vlan:
100+ parent.vlan = instance.vlan
101+ parent.save()
102+
103+
104+for klass in INTERFACE_CLASSES:
105+ signals.watch(
106+ post_save, update_bond_parents,
107+ sender=klass)
108+
109+
110+def interface_vlan_update(instance, old_values, **kwargs):
111+ """When an interfaces VLAN is changed we need to do the following.
112+
113+ * If its a controller move all assigned subnets to the new VLAN. This
114+ is done because the subnets defined are discovered on the
115+ controller and an administrator cannot change them.
116+ * If its a machine or device then we need to remove all links if the
117+ VLAN is different.
118+ """
119+ new_vlan_id = instance.vlan_id
120+ [old_vlan_id] = old_values
121+ if new_vlan_id == old_vlan_id:
122+ # Nothing changed do nothing.
123+ return
124+ if instance.node is None:
125+ # Not assigned to a node. Nothing to do.
126+ return
127+
128+ new_vlan = instance.vlan
129+ if instance.node.node_type in (
130+ NODE_TYPE.REGION_CONTROLLER,
131+ NODE_TYPE.RACK_CONTROLLER,
132+ NODE_TYPE.REGION_AND_RACK_CONTROLLER):
133+ # Interface VLAN was changed on a controller. Move all linked subnets
134+ # to that new VLAN.
135+ for ip_address in instance.ip_addresses.all():
136+ if ip_address.subnet is not None:
137+ ip_address.subnet.vlan = new_vlan
138+ ip_address.subnet.save()
139+
140+ # If any children are VLAN interfaces then we need to move those
141+ # VLANs into the same fabric as the parent.
142+ for rel in instance.children_relationships.all():
143+ if rel.child.type == INTERFACE_TYPE.VLAN:
144+ new_child_vlan, _ = VLAN.objects.get_or_create(
145+ fabric=new_vlan.fabric, vid=rel.child.vlan.vid)
146+ rel.child.vlan = new_child_vlan
147+ rel.child.save()
148+ # No need to update the IP addresses here this function
149+ # will be called again because the child has been saved.
150+
151+ else:
152+ # Interface VLAN was changed on a machine or device. Remove all its
153+ # links except the DISCOVERED ones.
154+ instance.ip_addresses.exclude(
155+ alloc_type=IPADDRESS_TYPE.DISCOVERED).delete()
156+
157+
158+for klass in INTERFACE_CLASSES:
159+ signals.watch_fields(
160+ interface_vlan_update,
161+ klass, ['vlan_id'], delete=False)
162
163 # Enable all signals by default.
164 signals.enable()
165
166=== modified file 'src/maasserver/models/signals/nodes.py'
167--- src/maasserver/models/signals/nodes.py 2016-02-26 21:50:26 +0000
168+++ src/maasserver/models/signals/nodes.py 2016-03-03 21:19:22 +0000
169@@ -43,10 +43,10 @@
170 if owner_id_new != owner_id_old:
171 NodeKey.objects.clear_token_for_node(node)
172
173-for kclass in NODE_CLASSES:
174+for klass in NODE_CLASSES:
175 signals.watch_fields(
176 clear_nodekey_when_owner_changes,
177- kclass, ['owner_id'], delete=False)
178+ klass, ['owner_id'], delete=False)
179
180
181 def create_services_on_node_type_change(node, old_values, deleted=False):
182@@ -62,13 +62,13 @@
183 if created:
184 Service.objects.create_services_for(instance)
185
186-for kclass in NODE_CLASSES:
187+for klass in NODE_CLASSES:
188 signals.watch_fields(
189 create_services_on_node_type_change,
190- kclass, ['node_type'], delete=False)
191+ klass, ['node_type'], delete=False)
192 signals.watch(
193 post_save, create_services_on_create,
194- sender=kclass)
195+ sender=klass)
196
197
198 # Enable all signals by default.
199
200=== modified file 'src/maasserver/models/signals/tests/test_interfaces.py'
201--- src/maasserver/models/signals/tests/test_interfaces.py 2016-02-26 18:39:26 +0000
202+++ src/maasserver/models/signals/tests/test_interfaces.py 2016-03-03 21:19:22 +0000
203@@ -10,6 +10,7 @@
204 from maasserver.enum import (
205 INTERFACE_TYPE,
206 IPADDRESS_TYPE,
207+ NODE_TYPE,
208 )
209 from maasserver.testing.factory import factory
210 from maasserver.testing.testcase import MAASServerTestCase
211@@ -123,3 +124,103 @@
212 self.assertEqual({
213 'mtu': bond_mtu,
214 }, reload_object(physical3_interface).params)
215+
216+
217+class TestUpdateBondParents(MAASServerTestCase):
218+
219+ def test__updates_bond_parents(self):
220+ parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
221+ parent2 = factory.make_Interface(
222+ INTERFACE_TYPE.PHYSICAL, node=parent1.node)
223+ bond = factory.make_Interface(
224+ INTERFACE_TYPE.BOND, parents=[parent1, parent2])
225+ self.assertEqual(bond.vlan, reload_object(parent1).vlan)
226+ self.assertEqual(bond.vlan, reload_object(parent2).vlan)
227+
228+ def test__update_bond_clears_parent_links(self):
229+ parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
230+ parent2 = factory.make_Interface(
231+ INTERFACE_TYPE.PHYSICAL, node=parent1.node)
232+ static_ip = factory.make_StaticIPAddress(interface=parent1)
233+ factory.make_Interface(
234+ INTERFACE_TYPE.BOND, parents=[parent1, parent2])
235+ self.assertIsNone(reload_object(static_ip))
236+
237+
238+class TestInterfaceVLANUpdateNotController(MAASServerTestCase):
239+
240+ scenarios = (
241+ ("machine", {
242+ "maker": factory.make_Node,
243+ }),
244+ ("device", {
245+ "maker": factory.make_Device,
246+ }),
247+ )
248+
249+ def test__removes_links(self):
250+ node = self.maker()
251+ interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)
252+ static_ip = factory.make_StaticIPAddress(interface=interface)
253+ discovered_ip = factory.make_StaticIPAddress(
254+ alloc_type=IPADDRESS_TYPE.DISCOVERED, interface=interface)
255+ new_fabric = factory.make_Fabric()
256+ new_vlan = new_fabric.get_default_vlan()
257+ interface.vlan = new_vlan
258+ interface.save()
259+ self.assertIsNone(reload_object(static_ip))
260+ self.assertIsNotNone(reload_object(discovered_ip))
261+
262+
263+class TestInterfaceVLANUpdateController(MAASServerTestCase):
264+
265+ scenarios = (
266+ ("region", {
267+ "maker": factory.make_RegionController,
268+ }),
269+ ("rack", {
270+ "maker": factory.make_RackController,
271+ }),
272+ ("region-rack", {
273+ "maker": lambda: factory.make_Node(
274+ node_type=NODE_TYPE.REGION_AND_RACK_CONTROLLER)
275+ }),
276+ )
277+
278+ def test__moves_link_subnets_to_same_vlan(self):
279+ node = self.maker()
280+ interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)
281+ subnet = factory.make_Subnet(vlan=interface.vlan)
282+ factory.make_StaticIPAddress(
283+ subnet=subnet, interface=interface)
284+ new_fabric = factory.make_Fabric()
285+ new_vlan = new_fabric.get_default_vlan()
286+ interface.vlan = new_vlan
287+ interface.save()
288+ self.assertEquals(new_vlan, reload_object(subnet).vlan)
289+
290+ def test__moves_children_vlans_to_same_fabric(self):
291+ node = self.maker()
292+ parent = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)
293+ subnet = factory.make_Subnet(vlan=parent.vlan)
294+ factory.make_StaticIPAddress(
295+ subnet=subnet, interface=parent)
296+ old_vlan = factory.make_VLAN(fabric=parent.vlan.fabric)
297+ vlan_interface = factory.make_Interface(
298+ INTERFACE_TYPE.VLAN, vlan=old_vlan, parents=[parent])
299+ vlan_subnet = factory.make_Subnet(vlan=old_vlan)
300+ factory.make_StaticIPAddress(
301+ subnet=vlan_subnet, interface=vlan_interface)
302+ new_fabric = factory.make_Fabric()
303+ new_vlan = new_fabric.get_default_vlan()
304+ parent.vlan = new_vlan
305+ parent.save()
306+ self.assertEquals(new_vlan, reload_object(subnet).vlan)
307+ vlan_interface = reload_object(vlan_interface)
308+ self.assertEquals(
309+ (new_fabric.id, old_vlan.vid),
310+ (vlan_interface.vlan.fabric.id, vlan_interface.vlan.vid))
311+ vlan_subnet = reload_object(vlan_subnet)
312+ self.assertEquals(
313+ (new_fabric.id, old_vlan.vid),
314+ (vlan_subnet.vlan.fabric.id, vlan_subnet.vlan.vid))
315
316=== modified file 'src/maasserver/models/tests/test_interface.py'
317--- src/maasserver/models/tests/test_interface.py 2016-02-27 02:31:24 +0000
318+++ src/maasserver/models/tests/test_interface.py 2016-03-03 21:19:22 +0000
319@@ -241,31 +241,51 @@
320 ["id:%s" % iface1.id, "id:%s" % iface2.id]), [iface1, iface2])
321
322 def test__filter_by_specifiers_matches_vid(self):
323- iface1 = factory.make_Interface()
324- iface2 = factory.make_Interface()
325- self.assertItemsEqual(
326- Interface.objects.filter_by_specifiers(
327- "vid:%s" % iface1.vlan.vid), [iface1])
328- self.assertItemsEqual(
329- Interface.objects.filter_by_specifiers(
330- "vid:%s" % iface2.vlan.vid), [iface2])
331- self.assertItemsEqual(
332- Interface.objects.filter_by_specifiers(
333- ["vid:%s" % iface1.vlan.vid, "vid:%s" % iface2.vlan.vid]),
334+ fabric1 = factory.make_Fabric()
335+ parent1 = factory.make_Interface(
336+ INTERFACE_TYPE.PHYSICAL, vlan=fabric1.get_default_vlan())
337+ vlan1 = factory.make_VLAN(fabric=fabric1)
338+ iface1 = factory.make_Interface(
339+ INTERFACE_TYPE.VLAN, vlan=vlan1, parents=[parent1])
340+ fabric2 = factory.make_Fabric()
341+ parent2 = factory.make_Interface(
342+ INTERFACE_TYPE.PHYSICAL, vlan=fabric2.get_default_vlan())
343+ vlan2 = factory.make_VLAN(fabric=fabric2)
344+ iface2 = factory.make_Interface(
345+ INTERFACE_TYPE.VLAN, vlan=vlan2, parents=[parent2])
346+ self.assertItemsEqual(
347+ Interface.objects.filter_by_specifiers(
348+ "vid:%s" % vlan1.vid), [iface1])
349+ self.assertItemsEqual(
350+ Interface.objects.filter_by_specifiers(
351+ "vid:%s" % vlan2.vid), [iface2])
352+ self.assertItemsEqual(
353+ Interface.objects.filter_by_specifiers(
354+ ["vid:%s" % vlan1.vid, "vid:%s" % vlan2.vid]),
355 [iface1, iface2])
356
357 def test__filter_by_specifiers_matches_vlan(self):
358- iface1 = factory.make_Interface()
359- iface2 = factory.make_Interface()
360- self.assertItemsEqual(
361- Interface.objects.filter_by_specifiers(
362- "vlan:%s" % iface1.vlan.vid), [iface1])
363- self.assertItemsEqual(
364- Interface.objects.filter_by_specifiers(
365- "vlan:%s" % iface2.vlan.vid), [iface2])
366- self.assertItemsEqual(
367- Interface.objects.filter_by_specifiers(
368- ["vlan:%s" % iface1.vlan.vid, "vlan:%s" % iface2.vlan.vid]),
369+ fabric1 = factory.make_Fabric()
370+ parent1 = factory.make_Interface(
371+ INTERFACE_TYPE.PHYSICAL, vlan=fabric1.get_default_vlan())
372+ vlan1 = factory.make_VLAN(fabric=fabric1)
373+ iface1 = factory.make_Interface(
374+ INTERFACE_TYPE.VLAN, vlan=vlan1, parents=[parent1])
375+ fabric2 = factory.make_Fabric()
376+ parent2 = factory.make_Interface(
377+ INTERFACE_TYPE.PHYSICAL, vlan=fabric2.get_default_vlan())
378+ vlan2 = factory.make_VLAN(fabric=fabric2)
379+ iface2 = factory.make_Interface(
380+ INTERFACE_TYPE.VLAN, vlan=vlan2, parents=[parent2])
381+ self.assertItemsEqual(
382+ Interface.objects.filter_by_specifiers(
383+ "vlan:%s" % vlan1.vid), [iface1])
384+ self.assertItemsEqual(
385+ Interface.objects.filter_by_specifiers(
386+ "vlan:%s" % vlan2.vid), [iface2])
387+ self.assertItemsEqual(
388+ Interface.objects.filter_by_specifiers(
389+ ["vlan:%s" % vlan1.vid, "vlan:%s" % vlan2.vid]),
390 [iface1, iface2])
391
392 def test__filter_by_specifiers_matches_subnet_specifier(self):
393@@ -1401,7 +1421,7 @@
394 1, interface.ip_addresses.count(),
395 "Should only have one IP address assigned.")
396
397- def test__creates_link_up_to_discovered_subnet(self):
398+ def test__creates_link_up_to_discovered_subnet_on_same_vlan(self):
399 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
400 subnet = factory.make_Subnet(vlan=interface.vlan)
401 factory.make_StaticIPAddress(
402@@ -1413,6 +1433,18 @@
403 self.assertIsNone(link_ip.ip)
404 self.assertEqual(subnet, link_ip.subnet)
405
406+ def test__creates_link_up_to_no_subnet_when_on_different_vlan(self):
407+ interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
408+ subnet = factory.make_Subnet()
409+ factory.make_StaticIPAddress(
410+ alloc_type=IPADDRESS_TYPE.DISCOVERED, ip="",
411+ subnet=subnet, interface=interface)
412+ interface.ensure_link_up()
413+ link_ip = interface.ip_addresses.filter(
414+ alloc_type=IPADDRESS_TYPE.STICKY).first()
415+ self.assertIsNone(link_ip.ip)
416+ self.assertIsNone(link_ip.subnet)
417+
418 def test__creates_link_up_to_no_subnet(self):
419 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
420 interface.ensure_link_up()
421
422=== modified file 'src/maasserver/testing/factory.py'
423--- src/maasserver/testing/factory.py 2016-03-02 13:42:26 +0000
424+++ src/maasserver/testing/factory.py 2016-03-03 21:19:22 +0000
425@@ -413,14 +413,7 @@
426 subnet=subnet, interface=rack_interface)
427 else:
428 ip_address = existing_static_ips[0]
429- current_ip_addresses = [
430- sip.ip
431- for sip in StaticIPAddress.objects.filter(
432- subnet=ip_address.subnet)
433- if sip.ip
434- ]
435- bmc_ip_address = self.pick_ip_in_Subnet(
436- ip_address.subnet, but_not=current_ip_addresses)
437+ bmc_ip_address = self.pick_ip_in_Subnet(ip_address.subnet)
438 node.power_parameters = {
439 "power_address": "qemu+ssh://user@%s/system" % bmc_ip_address
440 }
441@@ -610,8 +603,12 @@
442 if vlan is None and subnet is not None:
443 vlan = subnet.vlan
444 if vlan is None:
445+ if fabric is None:
446+ fabric = factory.make_Fabric()
447+ vlan = fabric.get_default_vlan()
448 dhcp_on = with_dhcp_rack_primary or with_dhcp_rack_secondary
449- vlan = self.make_VLAN(fabric=fabric, dhcp_on=dhcp_on)
450+ vlan.dhcp_on = dhcp_on
451+ vlan.save()
452 if subnet is None:
453 subnet = self.make_Subnet(vlan=vlan, cidr=cidr)
454 boot_interface = self.make_Interface(
455@@ -661,7 +658,7 @@
456 secondary_rack = vlan.secondary_rack
457 vlan.secondary_rack = secondary_rack
458 vlan.save()
459- return node
460+ return reload_object(node)
461
462 UNDEFINED = float('NaN')
463
464@@ -860,7 +857,19 @@
465 if iftype is None:
466 iftype = INTERFACE_TYPE.PHYSICAL
467 if vlan is None:
468- vlan = self.make_VLAN(fabric=fabric)
469+ if fabric is not None:
470+ if iftype == INTERFACE_TYPE.VLAN:
471+ vlan = self.make_VLAN(fabric=fabric)
472+ else:
473+ vlan = fabric.get_default_vlan()
474+ else:
475+ if iftype == INTERFACE_TYPE.VLAN and parents:
476+ vlan = self.make_VLAN(fabric=parents[0].vlan.fabric)
477+ elif iftype == INTERFACE_TYPE.BOND and parents:
478+ vlan = parents[0].vlan
479+ else:
480+ fabric = self.make_Fabric()
481+ vlan = fabric.get_default_vlan()
482 if (mac_address is None and
483 iftype in [
484 INTERFACE_TYPE.PHYSICAL,