Browse Source

Command for managing DVS on vCenter server.

Adds "ovsvapp-mange-dvs", a cli command to
manage Distributed Virtual Switches on a
vCenter server.

Change-Id: I1cd0235d19a7d7e024ac2694cd548b2c1261f5e3
Adolfo Duarte 1 year ago
parent
commit
c6b0155d6c

+ 173
- 0
networking_vsphere/tests/unit/utils/test_vim_objects.py View File

@@ -15,8 +15,10 @@
15 15
 #
16 16
 
17 17
 from mock import MagicMock
18
+from mock import mock
18 19
 from mock import patch
19 20
 
21
+import networking_vsphere
20 22
 from networking_vsphere.common import constants as const
21 23
 from networking_vsphere.utils import vim_objects
22 24
 from oslotest import base
@@ -72,3 +74,174 @@ class TestVcenterProxy(TestBase):
72 74
             'HostSystem',
73 75
             const.VIM_MAX_OBJETS
74 76
         )
77
+
78
+
79
+class TestDistributedVirtualSwitch(TestBase):
80
+    def setUp(self):
81
+        super(TestDistributedVirtualSwitch, self).setUp()
82
+        self.sut = vim_objects.DistributedVirtualSwitch(
83
+            'test_dvs',
84
+            vcenter_ip='127.0.0.1',
85
+            vcenter_port=443,
86
+            vcenter_password='test',
87
+            host_names=[],
88
+            pnic_devices=[
89
+                'vmnic1',
90
+                'vmnic2']
91
+        )
92
+        self.sut.host_names = ['HostSystem1', 'HostSystem2']
93
+        self.sut.connect_to_vcenter()
94
+
95
+    @mock.patch.object(networking_vsphere.utils.vim_objects.VcenterProxy,
96
+                       'get_type')
97
+    @mock.patch.object(networking_vsphere.utils.vim_objects.VcenterProxy,
98
+                       'get_used_pnics_keys_in_host')
99
+    @mock.patch.object(networking_vsphere.utils.vim_objects.VcenterProxy,
100
+                       'get_all_pnic_keys_in_host')
101
+    @mock.patch.object(networking_vsphere.utils.vim_objects.VcenterProxy,
102
+                       'get_mob_by_name')
103
+    def test_create_spec(self, mocked_get_by_name, mocked_all_keys,
104
+                         mocked_used_keys,
105
+                         mocked_get_type):
106
+        key1 = 'key-vim.host.PhysicalNic-vmnic1'
107
+        key2 = 'key-vim.host.PhysicalNic-vmnic2'
108
+        key3 = 'key-vim.host.PhysicalNic-vmnic3'
109
+        mocked_all_keys.return_value = {key1, key2, key3}
110
+        mocked_used_keys.return_value = {key2}
111
+        mocked_result = MagicMock()
112
+        mocked_get_type.return_value = mocked_result
113
+        mocked_host = MagicMock()
114
+        mocked_host.obj = MagicMock()
115
+        mocked_get_by_name.return_value = MagicMock()
116
+        self.sut.hosts = [mocked_host]
117
+        self.assertEqual(self.sut.create_spec, mocked_result)
118
+        for _type in [
119
+            'DVSCreateSpec',
120
+            'DistributedVirtualSwitchProductSpec',
121
+            'VMwareDVSConfigSpec',
122
+            'ConfigSpecOperation',
123
+            'DistributedVirtualSwitchHostMemberPnicBacking',
124
+            'DistributedVirtualSwitchHostMemberPnicSpec',
125
+            'DVSNameArrayUplinkPortPolicy'
126
+        ]:
127
+            mocked_get_type.assert_any_call(_type)
128
+
129
+    @mock.patch.object(networking_vsphere.utils.vim_objects.VcenterProxy,
130
+                       'get_type')
131
+    @mock.patch.object(networking_vsphere.utils.vim_objects.VcenterProxy,
132
+                       'get_used_pnics_keys_in_host')
133
+    @mock.patch.object(networking_vsphere.utils.vim_objects.VcenterProxy,
134
+                       'get_all_pnic_keys_in_host')
135
+    @mock.patch.object(networking_vsphere.utils.vim_objects.VcenterProxy,
136
+                       'get_mob_by_name')
137
+    def test_config_spec(self, mocked_get_mob_by_name, mocked_all_keys,
138
+                         mocked_used_keys,
139
+                         mocked_get_type):
140
+        key1 = 'key-vim.host.PhysicalNic-vmnic1'
141
+        key2 = 'key-vim.host.PhysicalNic-vmnic2'
142
+        key3 = 'key-vim.host.PhysicalNic-vmnic3'
143
+        mocked_all_keys.return_value = {key1, key2, key3}
144
+        mocked_used_keys.return_value = {key2}
145
+        mocked_result = MagicMock()
146
+        mocked_get_type.return_value = mocked_result
147
+        mocked_host = MagicMock()
148
+        mocked_host.obj = MagicMock()
149
+        mocked_get_mob_by_name.return_value = MagicMock()
150
+        self.sut.hosts = [mocked_host]
151
+        spec = self.sut.config_spec
152
+        self.assertEqual(spec, mocked_result)
153
+        for _type in [
154
+            'VMwareDVSConfigSpec',
155
+            'ConfigSpecOperation',
156
+            'DistributedVirtualSwitchHostMemberPnicBacking',
157
+            'DistributedVirtualSwitchHostMemberPnicSpec',
158
+            'DVSNameArrayUplinkPortPolicy'
159
+        ]:
160
+            mocked_get_type.assert_any_call(_type)
161
+            self.assertEqual(spec.name, self.sut.name)
162
+            self.assertEqual(spec.description, self.sut.description)
163
+            self.assertEqual(spec.maxPorts, self.sut.max_ports)
164
+            self.assertEqual(spec.maxMtu, self.sut.max_mtu)
165
+
166
+    @mock.patch.object(networking_vsphere.utils.vim_objects.VcenterProxy,
167
+                       'get_type')
168
+    @mock.patch.object(networking_vsphere.utils.vim_objects.VcenterProxy,
169
+                       'get_used_pnics_keys_in_host')
170
+    @mock.patch.object(networking_vsphere.utils.vim_objects.VcenterProxy,
171
+                       'get_all_pnic_keys_in_host')
172
+    def test_list_of_member_hosts_specs(self, mocked_all_keys,
173
+                                        mocked_used_keys,
174
+                                        mocked_get_type):
175
+        key1 = 'key-vim.host.PhysicalNic-vmnic1'
176
+        key2 = 'key-vim.host.PhysicalNic-vmnic2'
177
+        key3 = 'key-vim.host.PhysicalNic-vmnic3'
178
+        mocked_all_keys.return_value = {key1, key2, key3}
179
+        mocked_used_keys.return_value = {key2}
180
+        mocked_result = MagicMock()
181
+        mocked_host = MagicMock()
182
+        mocked_host.obj = MagicMock()
183
+        self.sut.hosts = [mocked_host]
184
+
185
+        mocked_get_type.return_value = mocked_result
186
+        results = self.sut.list_of_host_member_config_specs
187
+        self.assertEqual(len(results),
188
+                         len(self.sut.hosts)
189
+                         )
190
+        for _type in [
191
+            'ConfigSpecOperation',
192
+            'DistributedVirtualSwitchHostMemberPnicBacking',
193
+            'DistributedVirtualSwitchHostMemberPnicSpec',
194
+        ]:
195
+            mocked_get_type.assert_any_call(_type)
196
+
197
+    @mock.patch.object(networking_vsphere.utils.vim_objects.VcenterProxy,
198
+                       'get_type')
199
+    def test_host_member_pnic_backing(self, mocked_get_type):
200
+        mocked_result = MagicMock()
201
+        mocked_result.__len__.return_value = 1
202
+        mocked_get_type.return_value = mocked_result
203
+        results = self.sut.host_member_pnic_backing(['vmnic1'])
204
+
205
+        for _type in [
206
+            'DistributedVirtualSwitchHostMemberPnicBacking',
207
+            'DistributedVirtualSwitchHostMemberPnicSpec',
208
+        ]:
209
+            mocked_get_type.assert_any_call(_type)
210
+
211
+        self.assertEqual(len(results), 1)
212
+
213
+    @mock.patch.object(networking_vsphere.utils.vim_objects.VcenterProxy,
214
+                       'get_type')
215
+    def test_host_member_pnic_spec(self, mocked_get_type):
216
+        mocked_result = MagicMock()
217
+        mocked_result.__len__.return_value = 1
218
+        mocked_get_type.return_value = mocked_result
219
+        results = self.sut.host_member_pnic_spec(['vmnic1'])
220
+        self.assertEqual(len(results), 1)
221
+        mocked_get_type.assert_any_call(
222
+            'DistributedVirtualSwitchHostMemberPnicSpec')
223
+
224
+    @mock.patch.object(networking_vsphere.utils.vim_objects.VcenterProxy,
225
+                       'get_type')
226
+    def test_uplink_port_policy(self, mocked_get_type):
227
+        mocked_result = MagicMock()
228
+        mocked_result.__len__.return_value = 1
229
+        mocked_get_type.return_value = mocked_result
230
+        results = self.sut.uplink_port_policy
231
+        self.assertEqual(len(results), 1)
232
+        mocked_get_type.assert_any_call('DVSNameArrayUplinkPortPolicy')
233
+        self.assertEqual(len(results.uplinkPortName),
234
+                         len(self.sut.pnic_devices))
235
+
236
+    def test_uplink_port_names(self):
237
+        self.assertEqual(self.sut.uplink_port_names,
238
+                         ['dvUplink0', 'dvUplink1'])
239
+        self.sut.pnic_devices = []
240
+        self.assertEqual(self.sut.uplink_port_names, ['dvUplink'])
241
+
242
+    @mock.patch.object(networking_vsphere.utils.vim_objects.VcenterProxy,
243
+                       'get_mob_by_name')
244
+    def test_datacenter(self, mocked_get_mob_by_name):
245
+        self.assertIsNotNone(self.sut.datacenter)
246
+        mocked_get_mob_by_name.assert_called_with('Datacenter',
247
+                                                  self.sut.datacenter_name)

+ 117
- 0
networking_vsphere/utils/ovsvapp_manage_vcenter.py View File

@@ -0,0 +1,117 @@
1
+# (c) Copyright 2017 SUSE LLC
2
+#
3
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
4
+# not use this file except in compliance with the License. You may obtain
5
+# a copy of the License at
6
+#
7
+# http://www.apache.org/licenses/LICENSE-2.0
8
+#
9
+# Unless required by applicable law or agreed to in writing, software
10
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
+# License for the specific language governing permissions and limitations
13
+# under the License.
14
+#
15
+
16
+
17
+import argparse
18
+
19
+from networking_vsphere.utils import vim_objects
20
+
21
+
22
+def dvs():
23
+
24
+    parser = argparse.ArgumentParser()
25
+
26
+    parser.add_argument("dvs_name", type=str,
27
+                        help="Name to use for creating the DVS")
28
+
29
+    parser.add_argument("vcenter_user", type=str,
30
+                        help="Username to be used for connecting to vCenter")
31
+
32
+    parser.add_argument("vcenter_password", type=str,
33
+                        help="Password to be used for connecting to vCenter")
34
+
35
+    parser.add_argument("vcenter_ip", type=str,
36
+                        help="IP address to be used for connecting "
37
+                             "to vCenter")
38
+
39
+    parser.add_argument("datacenter_name", type=str,
40
+                        help="Name of data center where the DVS will be "
41
+                             "created")
42
+
43
+    parser.add_argument("--tcp", type=int, dest='vcenter_port',
44
+                        metavar='tcp_port',
45
+                        help="TCP port to be used for connecting to vCenter",
46
+                        default=443)
47
+
48
+    parser.add_argument("--pnic_devices", nargs='+', dest='pnic_devices',
49
+                        metavar='pnic_devices',
50
+                        help="Space separated list of PNIC devices for DVS",
51
+                        default=[])
52
+
53
+    parser.add_argument("--max_mtu", type=int, dest='max_mtu',
54
+                        metavar='max_mtu',
55
+                        help="MTU to be used by the DVS",
56
+                        default=1500)
57
+
58
+    parser.add_argument("--host_names", nargs='+', dest='host_names',
59
+                        metavar='host_names',
60
+                        help="Space separated list of ESX hosts to add to DVS",
61
+                        default=[])
62
+
63
+    parser.add_argument("--description", type=str, dest='description',
64
+                        metavar='description',
65
+                        help="DVS description",
66
+                        default="")
67
+
68
+    parser.add_argument("--max_ports", type=int, dest='max_ports',
69
+                        metavar='max_ports',
70
+                        help="Maximum number of ports allowed on DVS",
71
+                        default=3000)
72
+
73
+    parser.add_argument("--cluster_name", type=str, dest='cluster_name',
74
+                        metavar='cluster_name',
75
+                        help="Cluster name to use for DVS",
76
+                        default=None)
77
+
78
+    parser.add_argument("--create", action='store_true',
79
+                        help="Create DVS on vCenter")
80
+
81
+    parser.add_argument("--display_spec", action='store_true',
82
+                        help="Print create spec of DVS"
83
+                        )
84
+
85
+    parser.add_argument("-v", action='store_true',
86
+                        help="Verbose output")
87
+
88
+    args = parser.parse_args()
89
+
90
+    _dvs = vim_objects.DistributedVirtualSwitch(
91
+        dvs_name=args.dvs_name,
92
+        vcenter_user=args.vcenter_user,
93
+        vcenter_password=args.vcenter_password,
94
+        vcenter_ip=args.vcenter_ip,
95
+        vcenter_port=args.vcenter_port,
96
+        datacenter_name=args.datacenter_name,
97
+        pnic_devices=args.pnic_devices,
98
+        max_mtu=args.max_mtu,
99
+        host_names=args.host_names,
100
+        description=args.description,
101
+        max_ports=args.max_ports,
102
+        cluster_name=args.cluster_name)
103
+
104
+    if args.display_spec or args.create:
105
+        _dvs.connect_to_vcenter()
106
+
107
+    if args.display_spec:
108
+        print(_dvs.create_spec)
109
+
110
+    if args.create:
111
+        print("Attempting to create switch...")
112
+        _dvs.create_on_vcenter()
113
+        print("Success")
114
+
115
+    if args.v:
116
+        print("DVS Configuration: ")
117
+        print(_dvs)

+ 195
- 5
networking_vsphere/utils/vim_objects.py View File

@@ -30,11 +30,8 @@ class VcenterProxy(object):
30 30
         self.vcenter_password = vcenter_password
31 31
         self.cf = None
32 32
         self.session = None
33
-        self._connected = False
34 33
 
35 34
     def connect_to_vcenter(self):
36
-        self._connected = False
37
-
38 35
         self.session = api.VMwareAPISession(self.vcenter_ip,
39 36
                                             self.vcenter_user,
40 37
                                             self.vcenter_password,
@@ -43,7 +40,6 @@ class VcenterProxy(object):
43 40
                                             port=self.vcenter_port,
44 41
                                             create_session=True
45 42
                                             )
46
-        self._connected = True
47 43
         self.cf = self.session.vim.client.factory
48 44
 
49 45
     def get_type(self, type_name):
@@ -92,8 +88,202 @@ class VcenterProxy(object):
92 88
                                        self.session.vim,
93 89
                                        moref,
94 90
                                        property_name
95
-                                       )
91
+                                       )[0]
96 92
 
97 93
     @staticmethod
98 94
     def make_moref(value, type_):
99 95
         return vim_util.get_moref(value, type_)
96
+
97
+    def get_vswitches_from_host(self, host):
98
+        """Returns vswitches from host
99
+
100
+        :param host: mob
101
+        :return:  list
102
+        """
103
+        return self.get_property(host.obj,
104
+                                 "config.network.vswitch")
105
+
106
+    def get_proxyswitches_from_host(self, host):
107
+        """Returns proxy switches in host
108
+
109
+        :param host:
110
+        :return:  list
111
+        """
112
+        return self.get_property(host.obj,
113
+                                 "config.network.proxySwitch")
114
+
115
+    def get_host_pnics(self, host):
116
+        """Returns list of pnics in host
117
+
118
+        :param host: host
119
+        :return: returns a list of pnic mobs
120
+        """
121
+        return self.get_property(host.obj, "config.network.pnic")
122
+
123
+    def get_used_pnics_keys_in_host(self, host):
124
+        """Returns keys pointing to used pnics in host
125
+
126
+        :param host: mob
127
+        :return: a set of keys pointing to pnics not used in the host
128
+        """
129
+        used_pnics = []
130
+        for vswitch in self.get_vswitches_from_host(host):
131
+            if hasattr(vswitch, "pnic"):
132
+                used_pnics += vswitch.pnic
133
+        for pswitch in self.get_proxyswitches_from_host(host):
134
+            if hasattr(pswitch, "pnic"):
135
+                used_pnics += pswitch.pnic
136
+        return set(used_pnics)
137
+
138
+    def get_all_pnic_keys_in_host(self, host):
139
+        """Returns keys pointing to pnics in host
140
+
141
+        :param host:
142
+        :return: a set of keys pointing to all pnics on host
143
+        """
144
+        return {pnic.key for pnic in self.get_host_pnics(host)}
145
+
146
+    def get_free_pnics_keys_in_host(self, host):
147
+        """Returns keys pointing to free pnics in host
148
+
149
+        :param host: mob
150
+        :return: a set of free (not in use) pnic keys for host
151
+        """
152
+        _used = self.get_used_pnics_keys_in_host(host)
153
+        _all = self.get_all_pnic_keys_in_host(host)
154
+        return _all.difference(_used)
155
+
156
+
157
+class DistributedVirtualSwitch(VcenterProxy):
158
+
159
+    def __str__(self):
160
+
161
+        return '\n'.join(["{} = '{}'".format(key, self.__dict__[key])
162
+                          for key in self.__dict__])
163
+
164
+    __repr__ = __str__
165
+
166
+    def __init__(self, dvs_name, pnic_devices=None, max_mtu=1500,
167
+                 host_names=None,
168
+                 description=None, max_ports=3000, datacenter_name=None,
169
+                 cluster_name=None,
170
+                 **kwargs):
171
+        super(DistributedVirtualSwitch, self).__init__(dvs_name, **kwargs)
172
+        self.type = 'dvSwitch'
173
+        if pnic_devices is None:
174
+            pnic_devices = []
175
+        self.pnic_devices = {"key-vim.host.PhysicalNic-" + device
176
+                             for device in pnic_devices}
177
+        self.max_mtu = max_mtu
178
+        self.host_names = host_names
179
+        self.description = description
180
+        self.max_ports = max_ports
181
+        self.datacenter_name = datacenter_name
182
+        self.cluster_name = cluster_name
183
+        self.hosts = None
184
+
185
+    def collect_data_from_vcenter(self):
186
+
187
+        # Lets get hosts ready
188
+        if self.session is None or len(self.host_names) == 0:
189
+            self.hosts = []
190
+        else:
191
+            self.hosts = [self.get_mob_by_name("HostSystem", host_name)
192
+                          for host_name in self.host_names]
193
+
194
+    @property
195
+    def create_spec(self):
196
+        spec = self.get_type('DVSCreateSpec')
197
+        spec.productInfo = self.get_type('DistributedVirtualSwitchProductSpec')
198
+        spec.productInfo.version = ''.join([const.MIN_SUPPORTED_VERSION, '.0'])
199
+        spec.configSpec = self.config_spec
200
+        return spec
201
+
202
+    @property
203
+    def config_spec(self):
204
+        if self.session is None:
205
+            return None
206
+        self.collect_data_from_vcenter()
207
+        spec = self.get_type('VMwareDVSConfigSpec')
208
+        spec.name = self.name
209
+        spec.description = self.description
210
+        spec.maxPorts = self.max_ports
211
+        spec.maxMtu = self.max_mtu
212
+        spec.uplinkPortPolicy = self.uplink_port_policy
213
+        spec.host = self.list_of_host_member_config_specs
214
+        return spec
215
+
216
+    @property
217
+    def list_of_host_member_config_specs(self):
218
+        if self.hosts is None:
219
+            return []
220
+        return [self.host_member_config_spec_for(host) for host in self.hosts]
221
+
222
+    def host_member_config_spec_for(self, host):
223
+        spec = self.get_type("DistributedVirtualSwitchHostMemberConfigSpec")
224
+        spec.operation = self.get_type("ConfigSpecOperation").add
225
+        spec.host = host.obj
226
+        spec.backing = self.get_host_member_backing(host)
227
+        return spec
228
+
229
+    def get_host_member_backing(self, host):
230
+        available_devices = self.get_available_pnic_devices(host)
231
+        if available_devices:
232
+            return self.host_member_pnic_backing(available_devices)
233
+
234
+    def get_available_pnic_devices(self, host):
235
+        _free = self.get_free_pnics_keys_in_host(host)
236
+
237
+        return _free.intersection(self.pnic_devices)
238
+
239
+    def host_member_pnic_backing(self, p_devices):
240
+        pnic_backing = self.get_type(
241
+            'DistributedVirtualSwitchHostMemberPnicBacking')
242
+        pnic_backing.pnicSpec = [self.host_member_pnic_spec(device)
243
+                                 for device in p_devices]
244
+        return pnic_backing
245
+
246
+    def host_member_pnic_spec(self, device):
247
+        spec = self.get_type('DistributedVirtualSwitchHostMemberPnicSpec')
248
+        spec.pnicDevice = device
249
+        return spec
250
+
251
+    @property
252
+    def uplink_port_policy(self):
253
+        policy = self.get_type('DVSNameArrayUplinkPortPolicy')
254
+        policy.uplinkPortName = self.uplink_port_names
255
+        return policy
256
+
257
+    @property
258
+    def uplink_port_names(self):
259
+        if self.pnic_devices:
260
+            return [''.join(['dvUplink', str(c)]) for c, nic in
261
+                    enumerate(self.pnic_devices)]
262
+        return ['dvUplink']
263
+
264
+    @property
265
+    def datacenter(self):
266
+        return self.get_mob_by_name('Datacenter', self.datacenter_name)
267
+
268
+    @property
269
+    def networkfolder(self):
270
+        _dc_network = self.get_property(self.datacenter.obj, "networkFolder")
271
+        return self.get_mob_by_mobid('Folder', _dc_network.value)
272
+
273
+    @property
274
+    def networkfolder_moref(self):
275
+        return self.networkfolder.obj
276
+
277
+    def create_on_vcenter(self):
278
+        """Creates DVS on vCenter server
279
+
280
+        :raises VimException, VimFaultException, VimAttributeException,
281
+                 VimSessionOverLoadException, VimConnectionException
282
+
283
+        """
284
+        task = self.session.invoke_api(self.session.vim,
285
+                                       'CreateDVS_Task',
286
+                                       self.networkfolder_moref,
287
+                                       spec=self.create_spec)
288
+
289
+        self.session.wait_for_task(task)

+ 1
- 0
setup.cfg View File

@@ -39,6 +39,7 @@ console_scripts =
39 39
     neutron-ovsvapp-db-manage = networking_vsphere.db.migration.cli:main
40 40
     neutron-ovsvapp-agent-monitor = networking_vsphere.monitor.monitor:main
41 41
     neutron-dvs-agent = networking_vsphere.agent.dvs_neutron_agent:main
42
+    ovsvapp-manage-dvs = networking_vsphere.utils.ovsvapp_manage_vcenter:dvs
42 43
 neutron.ml2.mechanism_drivers =
43 44
     ovsvapp = networking_vsphere.ml2.ovsvapp_mech_driver:OVSvAppAgentMechanismDriver
44 45
     vmware_dvs = networking_vsphere.ml2.dvs_mechanism_driver:VMwareDVSMechanismDriver

Loading…
Cancel
Save