File: network.py

package info (click to toggle)
python-pypowervm 1.1.16%2Bdfsg1-3
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 7,356 kB
  • sloc: python: 29,449; xml: 174; makefile: 21; sh: 14
file content (1463 lines) | stat: -rw-r--r-- 53,743 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
# Copyright 2014, 2015 IBM Corp.
#
# All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

"""Wrappers for virtual networking objects."""

import copy

from oslo_log import log as logging

import pypowervm.const as c
import pypowervm.entities as ent
from pypowervm.i18n import _
import pypowervm.util as u
import pypowervm.wrappers.base_partition as bp
import pypowervm.wrappers.entry_wrapper as ewrap

LOG = logging.getLogger(__name__)

_PVID = 'PortVLANID'

_VNETS = 'VirtualNetworks'
_DRC_NAME = 'DynamicReconfigurationConnectorName'

_VSW_NAME = 'SwitchName'
_VSW_ID = 'SwitchID'
_VSW_MODE = 'SwitchMode'
_VSW_VIRT_NETS = _VNETS
VSW_DEFAULT_VSWITCH = 'ETHERNET0'
VSW_DEFAULT_VSWITCH_ID = 0
_VSW_DEFAULT_VSWITCH_API = 'ETHERNET0(Default)'
_VSW_EL_ORDER = (_VSW_ID, _VSW_MODE, _VSW_NAME, _VSW_VIRT_NETS)

SHARED_ETH_ADPT = 'SharedEthernetAdapter'

_NB_CONTROL_CHANNEL_ID = 'ControlChannelID'
_NB_FAILOVER = 'FailoverEnabled'
_NB_LOADBALANCE = 'LoadBalancingEnabled'
_NB_LGS = 'LoadGroups'
_NB_PVID = _PVID
NB_SEAS = 'SharedEthernetAdapters'
_NB_DEV_ID = 'UniqueDeviceID'
_NB_VNETS = _VNETS
_NB_LG = 'LoadGroup'
_NB_EL_ORDER = (_NB_CONTROL_CHANNEL_ID, _NB_FAILOVER, _NB_LOADBALANCE, _NB_LGS,
                _NB_PVID, NB_SEAS, _NB_DEV_ID, _NB_VNETS)

ETH_BACK_DEV = 'EthernetBackingDevice'

_SEA_VIO_HREF = 'AssignedVirtualIOServer'
_SEA_CONTROL_CHANNEL = 'ControlChannelInterfaceName'
_SEA_BACKING_DEV = 'BackingDeviceChoice'
_SEA_ETH_BACK_DEV = ETH_BACK_DEV
_SEA_HA_MODE = 'HighAvailabilityMode'
_SEA_DEV_NAME = 'DeviceName'
_SEA_JUMBO_FRAMES = 'JumboFramesEnabled'
_SEA_PVID = _PVID
_SEA_QOS_MODE = 'QualityOfServiceMode'
_SEA_QUEUE_SIZE = 'QueueSize'
_SEA_THREAD_MODE = 'ThreadModeEnabled'
_SEA_IP_INTERFACE = 'IPInterface'
_SEA_DEV_ID = 'UniqueDeviceID'
_SEA_LARGE_SEND = 'LargeSend'
_SEA_ADDRESS_TO_PING = 'AddressToPing'
_SEA_IIDP_SERVICE = 'IIDPService'
SEA_TRUNKS = 'TrunkAdapters'
_SEA_PRIMARY = 'IsPrimary'
_SEA_CONFIGURATION_STATE = 'ConfigurationState'
_SEA_EL_ORDER = (_SEA_VIO_HREF, _SEA_CONTROL_CHANNEL, _SEA_BACKING_DEV,
                 _SEA_HA_MODE, _SEA_DEV_NAME, _SEA_JUMBO_FRAMES, _SEA_PVID,
                 _SEA_QOS_MODE, _SEA_QUEUE_SIZE, _SEA_THREAD_MODE,
                 _SEA_IP_INTERFACE, _SEA_DEV_ID, _SEA_LARGE_SEND,
                 _SEA_ADDRESS_TO_PING, _SEA_IIDP_SERVICE, SEA_TRUNKS,
                 _SEA_PRIMARY, _SEA_CONFIGURATION_STATE)

_SEA_EBD_ADAPTER_ID = 'AdapterID'
_SEA_EBD_DESCRIPTION = 'Description'
_SEA_EBD_DEV_NAME = 'DeviceName'
_SEA_EBD_DEV_TYPE = 'DeviceType'
_SEA_EBD_DYN_CONN_NAME = _DRC_NAME
_SEA_EBD_PHYS_LOC = 'PhysicalLocation'
_SEA_EBD_UDID = 'UniqueDeviceID'
_SEA_EBD_ORDER = (_SEA_EBD_ADAPTER_ID, _SEA_EBD_DESCRIPTION,
                  _SEA_EBD_DEV_NAME, _SEA_EBD_DEV_TYPE,
                  _SEA_EBD_DYN_CONN_NAME, _SEA_EBD_PHYS_LOC,
                  _SEA_EBD_UDID)

_USE_NEXT_AVAIL_SLOT = 'UseNextAvailableSlotID'
_USE_NEXT_AVAIL_HIGH_SLOT = 'UseNextAvailableHighSlotID'

TA_ROOT = 'TrunkAdapter'
_TA_CONN_NAME = _DRC_NAME
_TA_LOC_CODE = 'LocationCode'
_TA_REQUIRED = 'RequiredAdapter'
_TA_VARIED_ON = 'VariedOn'
_TA_VIRTUAL_SLOT = 'VirtualSlotNumber'
_TA_USE_NEXT_AVAIL_SLOT = _USE_NEXT_AVAIL_SLOT
_TA_USE_NEXT_AVAIL_HIGH_SLOT = _USE_NEXT_AVAIL_HIGH_SLOT
_TA_ALLOWED_MAC = 'AllowedOperatingSystemMACAddresses'
_TA_MAC = 'MACAddress'
_TA_PVID = _PVID
_TA_QOS_PRI = 'QualityOfServicePriorityEnabled'
_TA_VLAN_IDS = 'TaggedVLANIDs'
_TA_TAG_SUPP = 'TaggedVLANSupported'
_TA_VS_ID = 'VirtualSwitchID'
_TA_DEV_NAME = 'DeviceName'
_TA_TRUNK_PRI = 'TrunkPriority'
_TA_ASSOC_VSWITCH = 'AssociatedVirtualSwitch'
_TA_EL_ORDER = (_TA_CONN_NAME, _TA_LOC_CODE, _TA_REQUIRED, _TA_VARIED_ON,
                _TA_USE_NEXT_AVAIL_SLOT, _TA_USE_NEXT_AVAIL_HIGH_SLOT,
                _TA_VIRTUAL_SLOT, _TA_ALLOWED_MAC, _TA_MAC, _TA_PVID,
                _TA_QOS_PRI, _TA_VLAN_IDS, _TA_TAG_SUPP, _TA_ASSOC_VSWITCH,
                _TA_VS_ID, _TA_DEV_NAME, _TA_TRUNK_PRI)

_LG_PVID = _PVID
_LG_TRUNKS = 'TrunkAdapters'
_LG_VNETS = _VNETS

_VNET_ASSOC_SW = 'AssociatedSwitch'
_VNET_NET_NAME = 'NetworkName'
_VNET_VLAN_ID = 'NetworkVLANID'
_VNET_SW_ID = 'VswitchID'
_VNET_TAG = 'TaggedNetwork'

_VADPT_TYPE = 'AdapterType'
_VADPT_DRC_NAME = _DRC_NAME
_VADPT_LOCATION_CODE = 'LocationCode'
_VADPT_LOC_PART_ID = 'LocalPartitionID'
_VADPT_REQUIRED = _TA_REQUIRED
_VADPT_VARIED_ON = _TA_VARIED_ON
_VADPT_USE_NEXT_AVAIL_SLOT = _USE_NEXT_AVAIL_SLOT
_VADPT_USE_NEXT_AVAIL_HIGH_SLOT = _USE_NEXT_AVAIL_HIGH_SLOT
_VADPT_SLOT_NUM = 'VirtualSlotNumber'
_VADPT_ENABLED = 'Enabled'
_VADPT_ALLOWED_MAC = _TA_ALLOWED_MAC
_VADPT_MAC_ADDR = _TA_MAC
_VADPT_IP_ADDR = 'IPAddress'
_VADPT_SUBNET_MASK = 'SubnetMask'
_VADPT_GATEWAY = 'Gateway'
_VADPT_PVID = _PVID
_VADPT_QOS_PRI = 'QualityOfServicePriority'
_VADPT_QOS_PRI_ENABLED = _TA_QOS_PRI
_VADPT_TAGGED_VLANS = 'TaggedVLANIDs'
_VADPT_TAGGED_VLAN_SUPPORT = 'TaggedVLANSupported'
_VADPT_VSI_MANAGER_ID = 'VirtualStationInterfaceManagerID'
_VADPT_VSI_TYPE_ID = 'VirtualStationInterfaceTypeID'
_VADPT_VSWITCH = 'AssociatedVirtualSwitch'
_VADPT_VSWITCH_ID = _TA_VS_ID
_VADPT_VSI_TYPE_VERSION = 'VirtualStationInterfaceTypeVersion'
_VADPT_DEV_NAME = 'DeviceName'
_VADPT_TRUNK_PRI = _TA_TRUNK_PRI
_VADPT_VNETS = _VNETS
_VADPT_MTU = "ConfiguredMTU"
_VADPT_OVS_BRIDGE = "OvsBridge"
_VADPT_OVS_EXT_IDS = "OvsPortExternalIds"
_VADPT_EL_ORDER = (
    _VADPT_TYPE, _VADPT_DRC_NAME, _VADPT_LOCATION_CODE, _VADPT_LOC_PART_ID,
    _VADPT_REQUIRED, _VADPT_VARIED_ON, _VADPT_USE_NEXT_AVAIL_SLOT,
    _VADPT_USE_NEXT_AVAIL_HIGH_SLOT, _VADPT_SLOT_NUM, _VADPT_ENABLED,
    _VADPT_ALLOWED_MAC, _VADPT_MAC_ADDR, _VADPT_PVID, _VADPT_QOS_PRI,
    _VADPT_QOS_PRI_ENABLED, _VADPT_TAGGED_VLANS, _VADPT_TAGGED_VLAN_SUPPORT,
    _VADPT_VSI_MANAGER_ID, _VADPT_VSI_TYPE_ID, _VADPT_VSWITCH,
    _VADPT_VSWITCH_ID, _VADPT_VSI_TYPE_VERSION, _VADPT_DEV_NAME,
    _VADPT_TRUNK_PRI, _VADPT_MTU, _VADPT_OVS_BRIDGE, _VADPT_OVS_EXT_IDS,
    _VADPT_VNETS)


class VSwitchMode(object):
    VEB = "Veb"
    VEPA = "Vepa"


class HAMode(object):
    FAILOVER = 'auto'
    LOAD_SHARING = 'sharing'
    DISABLED = 'disabled'


class SEAState(object):
    CONFIGURED = 'Configured'
    UNCONFIGURED = 'UnConfigured'
    INVALID = 'InvalidConfiguration'


def _order_by_pvid(adapters, pvid):
    """Orders a list of adapters.

    Takes in a set of adapter wrappers (either TrunkAdapter or LoadGroups) and
    returns a list where the adapter that matches the PVID is the first.
    :param adapters: The list of adapter wrappers (either TrunkAdapter or
                     LoadGroups).  Must have a pvid attribute.
    :param pvid: The pvid to match on.
    """
    resp_list = []
    for adapter in adapters:
        if adapter.pvid == pvid:
            # If the PVIDs match, put it to the front of the list
            resp_list.insert(0, adapter)
        else:
            # Otherwise, throw it on the back
            resp_list.append(adapter)
    return resp_list


@ewrap.EntryWrapper.pvm_type('VirtualSwitch', has_metadata=True,
                             child_order=_VSW_EL_ORDER)
class VSwitch(ewrap.EntryWrapper):
    """Wraps the Virtual Switch entries.

    The virtual switch in PowerVM is an independent plane of traffic.  If
    Ethernet packets are traveling on different virtual switches, the only
    time they can communicate is on the physical network plane (or if two
    logical adapters are bridged together).  They are important for data
    plane segregation.
    """

    @classmethod
    def bld(cls, adapter, name, switch_mode=VSwitchMode.VEB):
        """Creates a VSwitch that can be used for a create operation.

        :param adapter: A pypowervm.adapter.Adapter (for traits, etc.)
        :param name: The name for the virtual switch.  Must be unique.
        :param switch_mode: The mode of virtual switch (see VSwitchMode).
        :returns: The ElementWrapper that represents the new VSwitch.
        """
        vswitch = super(VSwitch, cls)._bld(adapter)
        vswitch.name = name
        vswitch.mode = switch_mode
        vswitch.vnet_uri_list = []
        return vswitch

    @property
    def name(self):
        """The name associated with the Virtual Switch."""
        name = self._get_val_str(_VSW_NAME)
        if name == _VSW_DEFAULT_VSWITCH_API:
            return VSW_DEFAULT_VSWITCH
        return name

    @name.setter
    def name(self, new_name):
        self.set_parm_value(_VSW_NAME, new_name)

    @property
    def switch_id(self):
        """The internal ID (not UUID) for the Virtual Switch."""
        return self._get_val_int(_VSW_ID)

    @property
    def mode(self):
        """The mode that the switch is in (ex. Veb).

        This is a string value that represents one of the values in the
        VSwitchMode enumeration.
        """
        return self._get_val_str(_VSW_MODE)

    @mode.setter
    def mode(self, new_mode):
        self.set_parm_value(_VSW_MODE, new_mode)

    @property
    def vnet_uri_list(self):
        """Returns a list of the Virtual Network URIs.

        If the vnet_aware trait (see traits.py) is set to False, the user
        should not modify this.  Virtual Networks become 'realized' off of
        the system's VLANs/vSwitches.  However, if set to True, one can add
        a Virtual Network to the vSwitch before it is used.

        The task classes (cna.py and network_bridger.py) should abstract the
        user away from these deviations in traits.
        """
        uri_resp_list = list(self.get_href(u.xpath(_LG_VNETS, c.LINK)))
        return ewrap.ActionableList(uri_resp_list, self.__update_uri_list)

    @vnet_uri_list.setter
    def vnet_uri_list(self, new_list):
        self.__update_uri_list(new_list)

    def __update_uri_list(self, new_list):
        new_vnet_elem = self._bld_link_list(_VSW_VIRT_NETS, new_list)
        self.inject(new_vnet_elem)


@ewrap.EntryWrapper.pvm_type('NetworkBridge', child_order=_NB_EL_ORDER)
class NetBridge(ewrap.EntryWrapper):
    """Wrapper object for the NetBridge entry.

    A NetworkBridge represents an aggregate entity comprising Shared
    Ethernet Adapters.  If Failover or Load-Balancing is in use, the
    Network Bridge will have two identically structured Shared Ethernet
    Adapters belonging to different Virtual I/O Servers.
    """

    @classmethod
    def bld(cls, adapter, pvid, vios_to_backing_adpts, vswitch,
            load_balance=False):
        """Create the NetBridge entry that can be used for a create operation.

        This is used when creating a NetBridge.

        :param adapter: A pypowervm.adapter.Adapter (for traits, etc.)
        :param pvid: The primary VLAN ID (ex. 1) for the Network Bridge.
        :param vios_to_backing_adpts: An argument containing a list of tuples
                                      between VIOS href and the VIOS backing
                                      trunk adapter names for 1 or 2 VIOS
                                      servers, depending whether failover is
                                      required.
        :param vswitch: The vswitch wrapper to retrieve ID and href.
        :param load_balance: (Optional) If set to True, the load balancing will
                             be enabled across the two SEAs.  This does
                             require multiple backing adapters (two SEAs).
        :returns: A new NetBridge EntryWrapper that represents the new
                  NetBridge.
        """
        nb = super(NetBridge, cls)._bld(adapter)

        if not vios_to_backing_adpts:
            raise ValueError()

        # Set required failover flag based on number of VIOSs. True for 2,
        # False for 1. We can determine this based on the number of provided
        # backing adpts. If its 0 we should throw an exception, 1 means only
        # 1 VIOS, 2 or more means we are defaulting to failover.
        nb._failover(len(vios_to_backing_adpts) != 1)

        # Set required load balancing flag.
        nb.load_balance = load_balance

        # Collection must be set based on schema requirements.
        nb.replace_list(_NB_LGS, [])

        # Set PVID to user provided value.
        nb._pvid(pvid)

        # There should never be more than two VIOSes specified.  However, the
        # API should validate this.  Therefore we can loop through and assert
        # that only the first VIOS is the primary.
        primary = True
        nb.seas = []
        for vio_tuple in vios_to_backing_adpts:
            nb.seas.append(SEA.bld(adapter, pvid, vio_tuple[0], vio_tuple[1],
                                   vswitch, primary=primary))
            primary = False

        return nb

    @property
    def pvid(self):
        """Returns the Primary VLAN ID of the Network Bridge."""
        return self._get_val_int(_NB_PVID)

    def _pvid(self, value):
        """Private setter for the PVID used by Network Bridge builder."""
        self.set_parm_value(_NB_PVID, value)

    @property
    def vnet_uri_list(self):
        """Returns a list of the Virtual Network URIs.

        This is a READ-ONLY list.  Modification should take place through the
        LoadGroup vnet_uri_list.  As the LoadGroups are modified,
        this list will be dynamically updated.
        """
        return self.get_href(u.xpath(_NB_VNETS, c.LINK))

    def _rebuild_vnet_list(self):
        """A callback from the Load Group to rebuild the virtual network list.

        Needed due to the API using both the LoadGroup and Network Bridge
        as a source.  But usage at an API level should be through Load
        Groups.
        """
        # Find all the children Virtual Networks.
        search = u.xpath(_NB_LGS, _NB_LG, _NB_VNETS, c.LINK)
        new_vnets = copy.deepcopy(self.element.findall(search))
        # Find and replace the current element.
        cur_vnets = self.element.find(_NB_VNETS)
        self.element.replace(
            cur_vnets, ent.Element(_NB_VNETS, self.adapter,
                                   children=new_vnets))

    @property
    def seas(self):
        """Returns a list of SEA wrappers."""
        return ewrap.WrapperElemList(self.entry.element.find(NB_SEAS), SEA)

    @seas.setter
    def seas(self, new_list):
        self.replace_list(NB_SEAS, new_list)

    @property
    def load_balance(self):
        """If set to True, the Load Balancing is enabled on the bridge.

        Requires two SEAs (sharing is done across the SEAs).
        """
        return self._get_val_bool(_NB_LOADBALANCE, False)

    @load_balance.setter
    def load_balance(self, value):
        self.set_parm_value(_NB_LOADBALANCE, u.sanitize_bool_for_api(value))

    @property
    def load_grps(self):
        """Returns the load groups.  The first in the list is the primary."""
        lg_list = [LoadGroup.wrap(x, nb_root=self) for x in
                   self.element.findall(u.xpath(_NB_LGS, _NB_LG))]
        temp_list = _order_by_pvid(lg_list, self.pvid)
        return ewrap.ActionableList(temp_list, self._load_grps)

    @load_grps.setter
    def load_grps(self, new_list):
        self._load_grps(new_list)

    def _load_grps(self, new_list):
        self.replace_list(_NB_LGS, new_list)

    @property
    def vswitch_id(self):
        return self.seas[0].primary_adpt.vswitch_id

    @property
    def arbitrary_pvids(self):
        """Lists all of the network bridges' arbitrary PVIDs.

        An arbitrary PVID is a 'primary VLAN ID' attached to an additional
        Load Group or Trunk Adapter.  These typically do not send traffic
        through them, and are placeholder VLANs required by the backing
        'additional' Trunk Adapters.

        :return: List of arbitrary PVIDs
        """
        if self.traits.vnet_aware:
            return [x.pvid for x in self.load_grps[1:]]
        else:
            return [x.pvid for x in self.seas[0].addl_adpts]

    def list_vlans(self, pvid=True, arbitrary=False):
        """Lists all of the VLANs on the Network Bridge.

        :param pvid: True if the primary VLAN ID should be included in the
                     response.  Defaults to True.
        :param arbitrary: If True, the arbitrary PVIDs (see arbitrary_pvids
                          property) will be included in the response.

        :response: A list of all the VLANs.
        """
        resp = []
        if self.traits.vnet_aware:
            # Loop through all load groups (even primary) and add the VLANs.
            for ld_grp in self.load_grps:
                trunk = ld_grp.trunk_adapters[0]
                if arbitrary:
                    resp.append(trunk.pvid)
                resp.extend(trunk.tagged_vlans)

            # Depending on if the arbitrary flag was set, the primary VLAN may
            # be in already.  This is odd logic here...but keeps the code
            # efficient.
            if not pvid and arbitrary:
                resp.remove(self.pvid)
            elif pvid and not arbitrary:
                resp.append(self.pvid)
        else:
            # Loop through the first SEA's trunks
            sea = self.seas[0]
            if pvid:
                resp.append(sea.primary_adpt.pvid)
            resp.extend(sea.primary_adpt.tagged_vlans)

            for trunk in sea.addl_adpts:
                if arbitrary:
                    resp.append(trunk.pvid)
                resp.extend(trunk.tagged_vlans)

        return resp

    def supports_vlan(self, vlan):
        """Determines if the VLAN can flow through the Network Bridge.

        The VLAN can flow through if either of the following applies:
         - It is the primary VLAN of the primary Load Group
         - It is an additional VLAN on any Load Group

        Therefore, the inverse is true and the VLAN is not supported by the
        Network Bridge if the following:
         - The VLAN is not on the Network Bridge
         - The VLAN is a primary VLAN on a NON-primary Load Group

        :param vlan: The VLAN to query for.  Can be a string or a number.
        :returns: True or False based on the previous criteria.
        """
        # Make sure we're using string
        vlan = int(vlan)

        return vlan in self.list_vlans()

    def _failover(self, value):
        """Private setter for the failover attr.

        Determined by backing adapters on NetworkBridge creation.
        """
        self.set_parm_value(_NB_FAILOVER, u.sanitize_bool_for_api(value))

    @property
    def control_channel_id(self):
        """Returns the control channel interface ID.  Can be None."""
        return self._get_val_int(_NB_CONTROL_CHANNEL_ID)


@ewrap.ElementWrapper.pvm_type(SHARED_ETH_ADPT, has_metadata=True,
                               child_order=_SEA_EL_ORDER)
class SEA(ewrap.ElementWrapper):
    """Represents the Shared Ethernet Adapter within a NetworkBridge."""

    @classmethod
    def bld(cls, adapter, pvid, vios_href, adpt_name, vswitch, primary=True):
        """Create the SEA entry that can be used for NetBridge creation.

        :param adapter: A pypowervm.adapter.Adapter (for traits, etc.)
        :param pvid: The primary VLAN ID (ex. 1) for the Network Bridge.
        :param vios_href: The Assigned VIOS href.
        :param adpt_name: Name of the physical adapter or ether channel that
                          will back the SEA.
        :param vswitch: The vswitch wrapper to retrieve ID and href.
        :param primary: Used in a dual Virtual I/O Server environment.  If
                        set to True, indicates it is running on the I/O Server
                        that the traffic should run through by default.  False
                        indicates it is the SEA on the fail over Virtual I/O
                        Server.  If single Virtual I/O Server environment,
                        always set this to True.
        :returns: A new SEA ElementWrapper that represents the new SEA.
        """
        sea = super(SEA, cls)._bld(adapter)
        sea._pvid(pvid)

        sea._vio_uri(vios_href)
        sea._backing_device(EthernetBackingDevice.bld(adapter, adpt_name))

        trunk_pri = 1 if primary else 2
        sea._primary_adpt(TrunkAdapter.bld(adapter, pvid, [], vswitch,
                                           trunk_pri=trunk_pri))
        sea._is_primary(primary)

        return sea

    @property
    def pvid(self):
        """Returns the Primary VLAN ID of the Shared Ethernet Adapter."""
        return self._get_val_int(_SEA_PVID)

    def _pvid(self, value):
        self.set_parm_value(_SEA_PVID, value)

    @property
    def dev_name(self):
        return self._get_val_str(_SEA_DEV_NAME)

    @property
    def vio_uri(self):
        """The URI to the corresponding VIOS."""
        return self.get_href(_SEA_VIO_HREF, one_result=True)

    def _vio_uri(self, value):
        self.set_href(_SEA_VIO_HREF, value)

    @property
    def ha_mode(self):
        """Returns the high availability mode of the SEA.

        See the HAMode Enumeration.
        """
        # This is a read only attribute.  We default to None in the
        # event this payload was created by a bld method (indicating
        # that we do not know).  All subsequent reads will have this
        # attribute.
        return self._get_val_str(_SEA_HA_MODE, None)

    @property
    def is_primary(self):
        """Returns if this is the primary SEA.

        Only valuable in dual Virtual I/O Server environments where a
        NetBridge spans multiple I/O Servers.  The primary SEA is the one
        the traffic runs through by default unless in a fail over scenario.
        """
        return self._get_val_bool(_SEA_PRIMARY)

    def _is_primary(self, val):
        self.set_parm_value(_SEA_PRIMARY, u.sanitize_bool_for_api(val))

    @property
    def addl_adpts(self):
        """Non-primary TrunkAdapters on this Shared Ethernet Adapter.

        If the vnet_aware trait (see traits.py) is set to True, then the
        modification of a Network Bridge should be driven via the LoadGroup.
        If set to False, the LoadGroups simply reflect the state of the
        system and can't be used for modification.

        In those scenarios, modification should be done directly against the
        Trunk Adapters.

        :return: List of TrunkAdapter wrappers.  May be the empty list.
        """
        # TODO(thorst): Second return unreachable!  Use self.traits.vnet_aware.
        return ewrap.ActionableList(self._get_trunks()[1:],
                                    self._addl_adpts)
        return tuple(self._get_trunks()[1:])

    @addl_adpts.setter
    def addl_adpts(self, value):
        self._addl_adpts(value)

    def _addl_adpts(self, value):
        """Sets the additional Trunk Adapters on this SEA."""
        # TODO(thorst): Condition on self.traits.vnet_aware.
        new_list = [self.primary_adpt]
        new_list.extend(value)
        self.replace_list(SEA_TRUNKS, new_list)

    @property
    def primary_adpt(self):
        """Returns the primary TrunkAdapter for this SEA. """
        all_trunks = self._get_trunks()
        return all_trunks[0] if all_trunks else None

    def _primary_adpt(self, value):
        new_list = [value]
        if self._get_trunks() and len(self._get_trunks()) > 1:
            # Drop the original primary adapter.
            new_list.extend(self._get_trunks()[1:])

        self.replace_list(SEA_TRUNKS, new_list)

    def _get_trunks(self):
        """Returns all of the trunk adapters.

        The first is the primary adapter.  All others are the additional
        adapters.
        """
        # It is not expected that the API will return the adapters such that
        # the first is the primary.  Yet to reduce complexity in the other
        # methods that work with the trunks, the returned value from here
        # will order it as such.
        trunk_elem_list = [TrunkAdapter.wrap(x) for x in
                           self.element.findall(u.xpath(SEA_TRUNKS, TA_ROOT))]
        return _order_by_pvid(trunk_elem_list, self.pvid)

    @property
    def backing_device(self):
        """The BackingDeviceChoice for this SEA."""
        elem = self.element.find(_SEA_BACKING_DEV)
        if elem is None:
            return None
        return ewrap.ElementWrapper.wrap(elem[0])

    def _backing_device(self, eth_back_dev):
        """The BackingDeviceChoice for this SEA.

        :param eth_back_dev: The EthernetBackingDevice for this
                             BackingDeviceChoice.
        """
        stor_elem = ent.Element(_SEA_BACKING_DEV, self.adapter, attrib={},
                                children=[])
        stor_elem.inject(eth_back_dev.element)
        self.inject(stor_elem)

    @property
    def control_channel(self):
        """Returns the control channel interface name.

        This may be None, indicating the lack of a control channel.  Control
        channels are no longer required for a network bridge to be redundant.
        """
        return self._get_val_str(_SEA_CONTROL_CHANNEL)

    @property
    def configuration_state(self):
        """Returns the configuration state.  May be None.

        Refer to SEAState for valid values.
        """
        return self._get_val_str(_SEA_CONFIGURATION_STATE)

    def contains_device(self, dev_name):
        """Returns if one of the child adapters is owned by this SEA.

        A child adapter is either the primary adapter, control channel, or
        is one of the additional adapters.

        :param dev_name: The name of the child device.
        :return: True if owned by this SEA, False otherwise.
        """
        if self.control_channel == dev_name:
            return True

        # If this SEA has no trunk adapters, primary_adpt will be None
        if self.primary_adpt and (self.primary_adpt.dev_name == dev_name):
            return True

        return dev_name in [x.dev_name for x in self.addl_adpts]


@ewrap.ElementWrapper.pvm_type('TrunkAdapter', child_order=_TA_EL_ORDER)
class TrunkAdapter(ewrap.ElementWrapper):
    """Represents a Trunk Adapter, either within a LoadGroup or a SEA."""

    @classmethod
    def bld(cls, adapter, pvid, vlan_ids, vswitch, trunk_pri=1):
        """Create the TrunkAdapter element that can be used for SEA creation.

        The returned adapter uses the "next available high slot" option,
        meaning that the API will attempt to assign the next available slot
        number that's higher than all the existing assigned slot numbers.

        :param adapter: A pypowervm.adapter.Adapter (for traits, etc.)
        :param pvid: The primary VLAN ID (ex. 1) for the Network Bridge.
        :param vlan_ids: Additional VLAN ids for the trunk adapters.
        :param vswitch: The vswitch wrapper to retrieve ID and href.
        :param trunk_pri: Trunk priority of this adapter. Defaults to 1.
        :returns: A new TrunkAdapter ElementWrapper that represents the new
                  TrunkAdapter.
        """
        ta = super(TrunkAdapter, cls)._bld(adapter)

        ta._required(True)
        ta.pvid = pvid
        ta.tagged_vlans = vlan_ids
        ta.has_tag_support = True if vlan_ids else False
        ta._vswitch_id(vswitch.switch_id)
        ta._trunk_pri(trunk_pri)

        # UseNextAvailableSlotID field - High only if available
        unasi_field = (_TA_USE_NEXT_AVAIL_HIGH_SLOT
                       if adapter.traits.has_high_slot
                       else _TA_USE_NEXT_AVAIL_SLOT)
        ta.set_parm_value(unasi_field, u.sanitize_bool_for_api(True))
        ta._associated_vswitch_uri(vswitch.related_href)

        return ta

    @property
    def pvid(self):
        """Returns the Primary VLAN ID of the Trunk Adapter."""
        return self._get_val_int(_TA_PVID)

    @pvid.setter
    def pvid(self, value):
        self.set_parm_value(_TA_PVID, value)

    @property
    def dev_name(self):
        """Returns the name of the device as represented by the hosting VIOS.

        If RMC is down, will not be available.
        """
        return self._get_val_str(_TA_DEV_NAME, 'Unknown')

    @property
    def has_tag_support(self):
        """Does this Trunk Adapter support Tagged VLANs passing through it?"""
        return self._get_val_bool(_TA_TAG_SUPP)

    @has_tag_support.setter
    def has_tag_support(self, new_val):
        self.set_parm_value(_TA_TAG_SUPP, u.sanitize_bool_for_api(new_val))

    @property
    def tagged_vlans(self):
        """Returns the tagged VLAN IDs that are allowed to pass through.

        Assumes has_tag_support() returns True.  If not, an empty list will
        be returned.
        """
        addl_vlans = self._get_val_str(_TA_VLAN_IDS, '')
        list_data = []
        if addl_vlans != '':
            list_data = [int(i) for i in addl_vlans.split(' ')]

        def update_list(new_list):
            data = ' '.join([str(j) for j in new_list])
            self.set_parm_value(_TA_VLAN_IDS, data)

        return ewrap.ActionableList(list_data, update_list)

    @tagged_vlans.setter
    def tagged_vlans(self, new_list):
        data = ' '.join([str(i) for i in new_list])
        self.set_parm_value(_TA_VLAN_IDS, data)

    @property
    def vswitch_id(self):
        """Returns the virtual switch identifier."""
        return self._get_val_int(_TA_VS_ID)

    def _vswitch_id(self, value):
        self.set_parm_value(_TA_VS_ID, value)

    @property
    def trunk_pri(self):
        """Returns the trunk priority of the adapter."""
        return self._get_val_int(_TA_TRUNK_PRI)

    def _trunk_pri(self, value):
        self.set_parm_value(_TA_TRUNK_PRI, value)

    def _required(self, value):
        self.set_parm_value(_TA_REQUIRED, u.sanitize_bool_for_api(value))

    @property
    def virtual_slot_number(self):
        """Returns the virtual slot number for this adapter."""
        return self._get_val_int(_TA_VIRTUAL_SLOT)

    @property
    def associated_vswitch_uri(self):
        """Returns the associated vswitch href."""
        return self.get_href(u.xpath(_TA_ASSOC_VSWITCH, c.LINK),
                             one_result=True)

    def _associated_vswitch_uri(self, href):
        self.set_href(u.xpath(_TA_ASSOC_VSWITCH, c.LINK), href)

    @property
    def varied_on(self):
        """Returns the VariedOn property."""
        return self._get_val_bool(_TA_VARIED_ON)

    @property
    def loc_code(self):
        """Returns the LocationCode property."""
        return self._get_val_str(_TA_LOC_CODE)

    @property
    def vios_id(self):
        """Determines and returns the VIOS ID from the loc_code.

        :return: int representing the short ID of the associated VIOS.
        """
        return u.part_id_by_loc_code(self.loc_code)


@ewrap.ElementWrapper.pvm_type('LoadGroup', has_metadata=True)
class LoadGroup(ewrap.ElementWrapper):
    """Load Group (how the I/O load should be distributed) for a Network Bridge.

    If using failover or load balancing, then the Load Group will have pairs of
    Trunk Adapters, each with their own unique Trunk Priority.
    """

    @classmethod
    def bld(cls, adapter, pvid, vnet_uris):
        """Create the LoadGroup element that can be used for a create operation.

        This is used when adding a Load Group to a NetBridge.

        :param adapter: A pypowervm.adapter.Adapter (for traits, etc.)
        :param pvid: The primary VLAN ID (ex. 1) for the Load Group.
        :param vnet_uris: The virtual network URI list (mapping to each
                          additional VLAN/vswitch combo).
        :returns: A new LoadGroup ElementWrapper that represents the new
                  LoadGroup.
        """
        lg = super(LoadGroup, cls)._bld(adapter)
        lg._pvid(pvid)
        lg.vnet_uri_list.extend(vnet_uris)
        return lg

    @classmethod
    def wrap(cls, element, **kwargs):
        wrap = super(LoadGroup, cls).wrap(element)

        # If created from a Network Bridge this will be set.  Else it will
        # be None (ex. crt_load_group method)
        wrap._nb_root = kwargs.get('nb_root')
        return wrap

    @property
    def pvid(self):
        """Returns the Primary VLAN ID of the Load Group."""
        return self._get_val_int(_LG_PVID)

    def _pvid(self, new_pvid):
        self.set_parm_value(_LG_PVID, new_pvid)

    @property
    def trunk_adapters(self):
        """Returns the Trunk Adapters for the Load Group.

        There is either one (no redundancy/wrap balancing) or two (typically
        the case in a multi VIOS scenario).

        :return: list of TrunkAdapter objects.
        """
        return ewrap.WrapperElemList(self.element.find(_LG_TRUNKS),
                                     TrunkAdapter)

    @trunk_adapters.setter
    def trunk_adapters(self, new_list):
        self.replace_list(_LG_TRUNKS, new_list)

    @property
    def vnet_uri_list(self):
        """Returns a list of the Virtual Network URIs.

        If the vnet_aware trait (see traits.py) is set, then the addition
        of VLANs is driven via virtual networks rather than straight VLAN
        modification.  This uri list is what drives the modification.

        If the trait is set to false, then the modification should be driven
        via the trunk adapters on the SEA directly.  This list will also
        be empty.

        The task classes (cna.py and network_bridger.py) should abstract the
        user away from these deviations in traits.
        """
        uri_resp_list = list(self.get_href(u.xpath(_LG_VNETS, c.LINK)))
        return ewrap.ActionableList(uri_resp_list, self.__update_uri_list)

    @vnet_uri_list.setter
    def vnet_uri_list(self, new_list):
        self.__update_uri_list(new_list)

    def __update_uri_list(self, new_list):
        new_vnet_elem = self._bld_link_list(_VSW_VIRT_NETS, new_list)
        old_elems = self.element.find(_LG_VNETS)
        # This is a bug where the API isn't returning vnets if just a PVID
        # on additional VEA
        if old_elems is not None:
            self.element.replace(old_elems, new_vnet_elem)
        else:
            self.element.append(new_vnet_elem)

        # If the Network Bridge was set, tell it to rebuild its VirtualNetwork
        # list.
        try:
            self._nb_root._rebuild_vnet_list()
        except AttributeError:
            # Network Bridge was not set - ignore
            pass

    @property
    def tagged_vlans(self):
        """The VLANs supported by this Load Group.  Does not include PVID."""
        return self.trunk_adapters[0].tagged_vlans


@ewrap.EntryWrapper.pvm_type('VirtualNetwork')
class VNet(ewrap.EntryWrapper):
    """The overall definition of a VLAN network within the hypervisor."""

    @classmethod
    def bld(cls, adapter, name, vlan_id, vswitch_uri, tagged):
        """Creates a VirtualNetwork that can be used for a create operation.

        This is used when creating a new Virtual Network within the system

        :param adapter: A pypowervm.adapter.Adapter (for traits, etc.)
        :param name: The name for the virtual network.
        :param vlan_id: The VLAN identifier (1 to 4094) for the network.
        :param vswitch_uri: The URI that points to the appropriate vSwitch.
        :param tagged: True if packets should have VLAN tags when they leave
                       the system.  False if tags should only be on the packets
                       while in the system (but tag-less when on the physical
                       network).
        :returns: The ElementWrapper that represents the new VirtualNetwork.
        """
        vnet = super(VNet, cls)._bld(adapter)
        # Assignment order matters
        vnet.associated_switch_uri = vswitch_uri
        vnet.name = name
        vnet.vlan = vlan_id
        vnet.tagged = tagged
        return vnet

    @property
    def associated_switch_uri(self):
        return self.get_href(_VNET_ASSOC_SW, one_result=True)

    @associated_switch_uri.setter
    def associated_switch_uri(self, uri):
        self.set_href(_VNET_ASSOC_SW, uri)

    @property
    def name(self):
        return self._get_val_str(_VNET_NET_NAME)

    @name.setter
    def name(self, value):
        self.set_parm_value(_VNET_NET_NAME, value)

    @property
    def vlan(self):
        return self._get_val_int(_VNET_VLAN_ID)

    @vlan.setter
    def vlan(self, vlan_id):
        self.set_parm_value(_VNET_VLAN_ID, vlan_id)

    @property
    def vswitch_id(self):
        """The vSwitch identifier (int).  0 through 15 (max number vSwitches).

        Is not a UUID.
        """
        return self._get_val_int(_VNET_SW_ID)

    @property
    def tagged(self):
        """If True, the VLAN tag is preserved when the packet leaves system."""
        return self._get_val_bool(_VNET_TAG)

    @tagged.setter
    def tagged(self, is_tagged):
        self.set_parm_value(_VNET_TAG, u.sanitize_bool_for_api(is_tagged))


@ewrap.EntryWrapper.pvm_type('ClientNetworkAdapter',
                             child_order=_VADPT_EL_ORDER)
class CNA(ewrap.EntryWrapper):
    """Wrapper object for ClientNetworkAdapter schema."""

    @classmethod
    def bld(cls, adapter, pvid, vswitch_href, slot_num=None, mac_addr=None,
            addl_tagged_vlans=None, trunk_pri=None, dev_name=None,
            ovs_bridge=None, ovs_ext_ids=None, configured_mtu=None):
        """Creates a fresh CNA EntryWrapper.

        This is used when creating a new CNA for a partition.  This can be PUT
        to LogicalPartition/<UUID>/ClientNetworkAdapter or to
        VirtualIOServer/<UUID>/ClientNetworkAdapter.

        :param adapter: A pypowervm.adapter.Adapter (for traits, etc.)
        :param pvid: The Primary VLAN ID to use.
        :param vswitch_href: The URI that points to the Virtual Switch that
                             will support this adapter.
        :param slot_num: The Slot on the Client LPAR that should be used.  This
                         defaults to 'None', which means the next available
                         high slot will be used (the API will attempt to assign
                         the next available slot number that's higher than all
                         the existing assigned slot numbers.
        :param mac_addr: Optional user specified mac address to use.  If left
                         as None, the system will generate one.
        :param addl_tagged_vlans: A set of additional tagged VLANs that can be
                                  passed through this adapter (with client VLAN
                                  adapters).

                                  Input should be a list of int (or int string)
                                  Example: [51, 52, 53]
                                  Note: The limit is ~18 additional VLANs
        :param trunk_pri: Optional TrunkPriority integer that, if specified,
                          will create this wrapper as a trunk.
        :param dev_name: (Optional, Default: None) Can only be set if the CNA
                         is being created against the Management VM.  Can be
                         used to specify what the device name should be for the
                         CNA on the Management VM.  Ignored for all other LPAR
                         types.
        :param ovs_bridge: (Optional, Default: None) If hosting through mgmt
                           partition, this attribute specifies which Open
                           vSwitch to connect to.
                           This assumes that Open vSwitch is installed and
                           active on the mgmt partition.
        :param ovs_ext_ids: (Optional, Default: None) A comma-delimited list of
                            key=value pairs in string format.
                            Ex. iface-id=abc123,iface-status=active
                            This sets a dictionary of values on the Interface
                            element within Open vSwitch.
                            This assumes that Open vSwitch is installed and
                            active on the mgmt partition.
        :param configured_mtu: (Optional, Default: None) Sets the MTU on the
                               adapter. May only be valid if adapter is being
                               created against mgmt partition.
        :returns: A CNA EntryWrapper that can be used for create.
        """
        cna = super(CNA, cls)._bld(adapter)
        # Assignment order matters
        if slot_num is not None:
            cna._slot(slot_num)
        else:
            cna._use_next_avail_slot_id = True

        if mac_addr is not None:
            cna.mac = mac_addr

        #  The primary VLAN ID
        cna.pvid = pvid

        # Additional VLANs
        if addl_tagged_vlans is not None:
            cna.tagged_vlans = addl_tagged_vlans
            cna.is_tagged_vlan_supported = True
        else:
            cna.is_tagged_vlan_supported = False

        # vSwitch URI
        cna.vswitch_uri = vswitch_href

        # Set the device name if not None
        if dev_name:
            cna._dev_name(dev_name)

        if ovs_bridge is not None:
            cna.ovs_bridge = ovs_bridge
            if ovs_ext_ids is not None:
                cna.ovs_ext_ids = ovs_ext_ids

        if configured_mtu is not None:
            cna.configured_mtu = configured_mtu

        # If a trunk priority is specified, set it. It will make this CNA
        # build out a trunk adapter. However, if it is not specified, we
        # do not want to set it as we don't want to include the element in
        # the payload we send for the CNA creation.
        if trunk_pri:
            cna._trunk_pri(trunk_pri)

        return cna

    def create(self, parent_type=None, parent_uuid=None, timeout=-1,
               parent=None, **kwargs):
        """Override to ensure default slot setting is correct.

        Create the CNA as specified *except*:

        If UseNextAvailableHighSlot is True (i.e. slot number was not given);
        and the parent is a VIOS or the management partition;
        then change to UseNextAvailableSlot (not High).

        This is because VIOS and the management partition don't care about slot
        ordering, and their longevity increases the probability of running out
        of slot space if we use 'High'.

        :param parent_type: See superclass.
        :param parent_uuid: See superclass.
        :param timeout: See superclass.
        :param parent: See superclass.
        """
        # These checks are quick, so do them first.
        el2d = self._find(_TA_USE_NEXT_AVAIL_HIGH_SLOT)
        # If UseNextAvailableHighSlot is present *and* True.
        if el2d is not None and self._use_next_avail_slot_id:
            # If we have the parent wrapper, we don't have to GET. Otherwise...
            if parent is None:
                if any(val is None for val in (parent_type, parent_uuid)):
                    raise ValueError(_("Invalid parent spec for CNA.create."))
                # If the parent_type isn't a wrapper class, get it
                if type(parent_type) is str:
                    parent_type = ewrap.Wrapper._pvm_object_registry[
                        parent_type]['entry']
                # Aaaand get the parent
                parent = parent_type.get(self.adapter, uuid=parent_uuid)
            # Now we can find out whether the parent is VIOS or mgmt.
            if parent.env == bp.LPARType.VIOS or parent.is_mgmt_partition:
                # Delete the existing UNAHSI field
                self.element.remove(el2d)
                # Aaaand add the UNASI field
                self.set_parm_value(_TA_USE_NEXT_AVAIL_SLOT,
                                    u.sanitize_bool_for_api(True))

        # Superclass does the real work.
        return super(CNA, self).create(
            parent_type=parent_type, parent_uuid=parent_uuid, timeout=timeout,
            parent=parent, **kwargs)

    @property
    def slot(self):
        return self._get_val_int(_VADPT_SLOT_NUM)

    def _slot(self, sid):
        self.set_parm_value(_VADPT_SLOT_NUM, sid)

    @property
    def lpar_id(self):
        """Returns the Local Partition ID for this adapter."""
        return self._get_val_int(_VADPT_LOC_PART_ID)

    @property
    def _use_next_avail_slot_id(self):
        """Use next available (high) slot ID, true or false."""
        # We could be using either next-available-slot field. If either is set,
        # it counts.
        return any(self._get_val_bool(unasi_field)
                   for unasi_field in
                   (_TA_USE_NEXT_AVAIL_HIGH_SLOT, _TA_USE_NEXT_AVAIL_SLOT))

    @_use_next_avail_slot_id.setter
    def _use_next_avail_slot_id(self, unasi):
        """Use next available (high) slot ID.

        :param unasi: Boolean value to set (True or False)
        """
        # NOTE(efried): We'd like to set this to not-HIGH for VIOS/mgmt, but in
        # bld(), we don't know what kind of parent we have.
        unasi_field = (_TA_USE_NEXT_AVAIL_HIGH_SLOT
                       if self.traits.has_high_slot
                       else _TA_USE_NEXT_AVAIL_SLOT)
        self.set_parm_value(unasi_field, u.sanitize_bool_for_api(unasi))

    @property
    def mac(self):
        """Returns the Mac Address for the adapter.

        Typical format would be: AABBCCDDEEFF
        The API returns a format with no colons and is upper cased.
        """
        return self._get_val_str(_VADPT_MAC_ADDR)

    @property
    def vsi_type_id(self):
        """Returns the virtual station interface type id."""
        return self._get_val_str(_VADPT_VSI_TYPE_ID)

    @property
    def vsi_type_version(self):
        """Returns the virtual station interface version."""
        return self._get_val_str(_VADPT_VSI_TYPE_VERSION)

    @property
    def vsi_type_manager_id(self):
        """Returns the virtual station interface manager id."""
        return self._get_val_str(_VADPT_VSI_MANAGER_ID)

    @mac.setter
    def mac(self, new_val):
        new_mac = u.sanitize_mac_for_api(new_val)
        self.set_parm_value(_VADPT_MAC_ADDR, new_mac)

    @property
    def pvid(self):
        """Returns the Port VLAN ID (int value)."""
        return self._get_val_int(_VADPT_PVID)

    @pvid.setter
    def pvid(self, new_val):
        self.set_parm_value(_VADPT_PVID, new_val)

    @property
    def enabled(self):
        """Returns the enabled state (boolean value).

        A CNA is always created enabled=true.  However, certain migration
        operations of an LPAR (ex. migration via OpenStack when using Open
        vSwitch) will cause the client's CNA to be disabled. This method can be
        used to check the state of the adapter.
        """
        return self._get_val_bool(_VADPT_ENABLED)

    @enabled.setter
    def enabled(self, new_val):
        self.set_parm_value(_VADPT_ENABLED,
                            u.sanitize_bool_for_api(new_val))

    @property
    def loc_code(self):
        """The device's location code."""
        return self._get_val_str(_VADPT_LOCATION_CODE)

    @property
    def tagged_vlans(self):
        """Returns a list of additional VLANs on this adapter.

        Only valid if tagged vlan support is on.
        """
        addl_vlans = self._get_val_str(_VADPT_TAGGED_VLANS, '')
        list_data = []
        if addl_vlans != '':
            list_data = [int(i) for i in addl_vlans.split(' ')]

        def update_list(new_list):
            data = ' '.join([str(j) for j in new_list])
            self.set_parm_value(_VADPT_TAGGED_VLANS, data)

        return ewrap.ActionableList(list_data, update_list)

    @tagged_vlans.setter
    def tagged_vlans(self, new_list):
        data = ' '.join([str(i) for i in new_list])
        self.set_parm_value(_VADPT_TAGGED_VLANS, data)

    @property
    def is_tagged_vlan_supported(self):
        """Returns if addl tagged VLANs are supported (bool value)."""
        return self._get_val_bool(_VADPT_TAGGED_VLAN_SUPPORT)

    @is_tagged_vlan_supported.setter
    def is_tagged_vlan_supported(self, new_val):
        """Parameter new_val is a bool (True or False)."""
        self.set_parm_value(_VADPT_TAGGED_VLAN_SUPPORT,
                            u.sanitize_bool_for_api(new_val))

    @property
    def vswitch_uri(self):
        """Returns the URI for the associated vSwitch."""
        return self.get_href(u.xpath(_VADPT_VSWITCH, c.LINK), one_result=True)

    @vswitch_uri.setter
    def vswitch_uri(self, new_val):
        self.set_href(u.xpath(_VADPT_VSWITCH, c.LINK), new_val)

    @property
    def vswitch_id(self):
        """Returns the ID (typically 0-15) for the virtual switch."""
        return self._get_val_int(_VADPT_VSWITCH_ID)

    @property
    def dev_name(self):
        """Returns the name of the device (if available).

        If RMC is down, will not be available.
        """
        return self._get_val_str(_VADPT_DEV_NAME, 'Unknown')

    def _dev_name(self, value):
        """Sets the device name.

        This is only available for devices running on the Management VM.  If
        set for any other LPARs, it will be ignored.

        :param value: The device name.
        """
        self.set_parm_value(_VADPT_DEV_NAME, value)

    @property
    def trunk_pri(self):
        """Returns the Trunk Priority for the adapter.

        :returns: None if this is not a Trunk Adapter, priority otherwise.
        """
        return self._get_val_int(_TA_TRUNK_PRI)

    def _trunk_pri(self, new_val):
        self.set_parm_value(_TA_TRUNK_PRI, new_val)

    @property
    def is_trunk(self):
        """Returns if this adapter was created with a trunk priority.

        If the adapter was created without a trunk priority, it is just a
        client network adapter. However, if it was given a trunk priority on
        creation, it is a wrapper for a trunk adapter.
        """
        return self.trunk_pri is not None

    @property
    def configured_mtu(self):
        """The MTU of the adapter.

        May only be valid if adapter is being created against mgmt partition.
        """
        return self._get_val_int(_VADPT_MTU)

    @configured_mtu.setter
    def configured_mtu(self, value):
        self.set_parm_value(_VADPT_MTU, value, attrib=c.ATTR_KSV160)

    @property
    def ovs_bridge(self):
        """The Open vSwitch bridge it is connected to.  Otherwise None."""
        return self._get_val_str(_VADPT_OVS_BRIDGE)

    @ovs_bridge.setter
    def ovs_bridge(self, value):
        self.set_parm_value(_VADPT_OVS_BRIDGE, value, attrib=c.ATTR_KSV160)

    @property
    def ovs_ext_ids(self):
        """If connected to an Open vSwitch, returns the external ids.

        This is a comma-delimited list of key=value pairs.
        Ex:
          'iface-id=123asdf,iface-status=active'

        This maps directly to the Open vSwitch Interface object's 'external_id'
        field.
        """
        return self._get_val_str(_VADPT_OVS_EXT_IDS)

    @ovs_ext_ids.setter
    def ovs_ext_ids(self, value):
        self.set_parm_value(_VADPT_OVS_EXT_IDS, value, attrib=c.ATTR_KSV160)

    @ewrap.Wrapper.xag_property(c.XAG.ADV)
    def ip_address(self):
        """Returns the IP Address of the network interface.

        Typical format would be: 255.255.255.255 (IPv4)
        and ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff (IPv6)
        or other short forms of IPv6 address
        """
        return self._get_val_str(_VADPT_IP_ADDR)

    @ewrap.Wrapper.xag_property(c.XAG.ADV)
    def subnet_mask(self):
        """Returns the subnet mask of the network interface.

        Typical format would be: 255.255.255.0 (IPv4)
        and ffff:ffff:ffff:ffff:: (IPv6)
        or other forms of IPv6 address
        """
        return self._get_val_str(_VADPT_SUBNET_MASK)

    @ewrap.Wrapper.xag_property(c.XAG.ADV)
    def gateway(self):
        """Returns the gateway of the network interface.

        Typical format would be: 10.0.0.1 (IPv4)
        and cafe::1 (IPv6)
        or other forms of IPv6 address
        """
        return self._get_val_str(_VADPT_GATEWAY)


@ewrap.ElementWrapper.pvm_type(_SEA_ETH_BACK_DEV, has_metadata=True,
                               child_order=_SEA_EBD_ORDER)
class EthernetBackingDevice(ewrap.ElementWrapper):
    """Represents the SEA EthernetBackingDevice."""

    @classmethod
    def bld(cls, adapter, dev_name):
        """Creates the EthernetBackingDevice element.

        :param adapter: A pypowervm.adapter.Adapter (for traits, etc.)
        :param dev_name: The device name (e.g. eth0).
        :returns: The EthernetBackingDevice element for SEAs.
        """
        cfg = super(EthernetBackingDevice, cls)._bld(adapter)
        cfg._dev_name(dev_name)

        # This is required by the schema, setting it to 1
        # just for legacy support.
        cfg._adapter_id(1)

        return cfg

    @property
    def dev_name(self):
        return self._get_val_str(_SEA_DEV_NAME)

    def _dev_name(self, dev_name):
        self.set_parm_value(_SEA_DEV_NAME, str(dev_name))

    @property
    def adapter_id(self):
        return self._get_val_int(_SEA_EBD_ADAPTER_ID)

    def _adapter_id(self, value):
        # TODO(IBM) remove this once the schema no longer requires it.
        return self.set_parm_value(_SEA_EBD_ADAPTER_ID, value)