summaryrefslogtreecommitdiff
path: root/framework/virt_resource.py
blob: b830f4e3203ec52ab274e8d6039ceca9137127ec (plain)
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
#!/usr/bin/python
# BSD LICENSE
#
# Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
#   * Redistributions of source code must retain the above copyright
#     notice, this list of conditions and the following disclaimer.
#   * Redistributions in binary form must reproduce the above copyright
#     notice, this list of conditions and the following disclaimer in
#     the documentation and/or other materials provided with the
#     distribution.
#   * Neither the name of Intel Corporation nor the names of its
#     contributors may be used to endorse or promote products derived
#     from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from random import randint

from utils import get_obj_funcs

INIT_FREE_PORT = 6060


class VirtResource(object):

    """
    Class handle dut resource, like cpu, memory, net devices
    """

    def __init__(self, dut):
        self.dut = dut

        self.cores = [int(core['thread']) for core in dut.cores]
        # initialized unused cores
        self.unused_cores = self.cores[:]
        # initialized used cores
        self.used_cores = [-1] * len(self.unused_cores)

        self.ports_info = dut.ports_info
        # initialized unused ports
        self.ports = [port['pci'] for port in dut.ports_info]
        self.unused_ports = self.ports[:]
        # initialized used ports
        self.used_ports = ['unused'] * len(self.unused_ports)

        # initialized vf ports
        self.vfs_info = []
        self.vfs = []
        self.unused_vfs = []
        self.used_vfs = []

        # save allocated cores and related vm
        self.allocated_info = {}

    def __port_used(self, pci):
        index = self.ports.index(pci)
        self.used_ports[index] = pci
        self.unused_ports[index] = 'used'

    def __port_unused(self, pci):
        index = self.ports.index(pci)
        self.unused_ports[index] = pci
        self.used_ports[index] = 'unused'

    def __port_on_socket(self, pci, socket):
        for port in self.ports_info:
            if port['pci'] == pci:
                if socket is -1:
                    return True

                if port['numa'] == socket:
                    return True
                else:
                    return False

        return False

    def __vf_used(self, pci):
        index = self.vfs.index(pci)
        self.used_vfs[index] = pci
        self.unused_vfs[index] = 'used'

    def __vf_unused(self, pci):
        index = self.vfs.index(pci)
        self.used_vfs[index] = 'unused'
        self.unused_vfs[index] = pci

    def __core_used(self, core):
        core = int(core)
        index = self.cores.index(core)
        self.used_cores[index] = core
        self.unused_cores[index] = -1

    def __core_unused(self, core):
        core = int(core)
        index = self.cores.index(core)
        self.unused_cores[index] = core
        self.used_cores[index] = -1

    def __core_on_socket(self, core, socket):
        for dut_core in self.dut.cores:
            if int(dut_core['thread']) == core:
                if socket is -1:
                    return True

                if int(dut_core['socket']) == socket:
                    return True
                else:
                    return False

        return False

    def __core_isused(self, core):
        index = self.cores.index(core)
        if self.used_cores[index] != -1:
            return True
        else:
            return False

    def reserve_cpu(self, coremask=''):
        """
        Reserve dpdk used cpus by mask
        """
        val = int(coremask, base=16)
        cpus = []
        index = 0
        while val != 0:
            if val & 0x1:
                cpus.append(index)

            val = val >> 1
            index += 1

        for cpu in cpus:
            self.__core_used(cpu)

    def alloc_cpu(self, vm='', number=-1, socket=-1, corelist=None):
        """
        There're two options for request cpu resouce for vm.
        If number is not -1, just allocate cpu from not used cores.
        If list is not None, will allocate cpu after checked.
        """
        cores = []

        if vm == '':
            print "Alloc cpu request vitual machine name!!!"
            return cores

        # if vm has been alloacted cores, just return them
        if self.__vm_has_resource(vm, 'cores'):
            return self.allocated_info[vm]['cores']

        if number != -1:
            for core in self.unused_cores:
                if core != -1 and number != 0:
                    if self.__core_on_socket(core, socket) is True:
                        self.__core_used(core)
                        cores.append(str(core))
                        number = number - 1
            if number != 0:
                print "Can't allocated requested cpu!!!"

        if corelist is not None:
            for core in corelist:
                if self.__core_isused(int(core)) is True:
                    print "Core %s has been used!!!" % core
                else:
                    if self.__core_on_socket(int(core), socket) is True:
                        self.__core_used(int(core))
                        cores.append(core)

        if vm not in self.allocated_info:
            self.allocated_info[vm] = {}

        self.allocated_info[vm]['cores'] = cores
        return cores

    def __vm_has_resource(self, vm, resource=''):
        if vm == '':
            self.dut.logger.info("VM name cannt be NULL!!!")
            raise Exception("VM name cannt be NULL!!!")
        if vm not in self.allocated_info:
            self.dut.logger.info(
                "There is no resource allocated to VM [%s]." % vm)
            return False
        if resource == '':
            return True
        if resource not in self.allocated_info[vm]:
            self.dut.logger.info(
                "There is no resource [%s] allocated to VM [%s] " %
                (resource, vm))
            return False
        return True

    def free_cpu(self, vm):
        if self.__vm_has_resource(vm, 'cores'):
            for core in self.allocated_info[vm]['cores']:
                self.__core_unused(core)
            self.allocated_info[vm].pop('cores')

    def alloc_pf(self, vm='', number=-1, socket=-1, pflist=[]):
        """
        There're two options for request pf devices for vm.
        If number is not -1, just allocate pf device from not used pfs.
        If list is not None, will allocate pf devices after checked.
        """
        ports = []

        if number != -1:
            for pci in self.unused_ports:
                if pci != 'unused' and number != 0:
                    if self.__port_on_socket(pci, socket) is True:
                        self.__port_used(pci)
                        ports.append(pci)
                        number = number - 1
            if number != 0:
                print "Can't allocated requested PF devices!!!"

        if pflist is not None:
            for pci in pflist:
                if self.__port_isused(pci) is True:
                    print "Port %s has been used!!!" % pci
                else:
                    if self.__port_on_socket(pci, socket) is True:
                        self.__port_used(core)
                        ports.append(core)

        if vm not in self.allocated_info:
            self.allocated_info[vm] = {}

        self.allocated_info[vm]['ports'] = ports
        return ports

    def free_pf(self, vm):
        if self.__vm_has_resource(vm, 'ports'):
            for pci in self.allocated_info[vm]['ports']:
                self.__port_unused(pci)
            self.allocated_info[vm].pop('ports')

    def alloc_vf_from_pf(self, vm='', pf_pci='', number=-1, vflist=[]):
        """
        There're two options for request vf devices of pf device.
        If number is not -1, just allocate vf device from not used vfs.
        If list is not None, will allocate vf devices after checked.
        """
        vfs = []
        if vm == '':
            print "Alloc VF request vitual machine name!!!"
            return vfs

        if pf_pci == '':
            print "Alloc VF request PF pci address!!!"
            return vfs

        for vf_info in self.vfs_info:
            if vf_info['pf_pci'] == pf_pci:
                if vf_info['pci'] in vflist:
                    vfs.append(vf_info['pci'])
                    continue

                if number > 0:
                    vfs.append(vf_info['pci'])
                    number = number - 1

        for vf in vfs:
            self.__vf_used(vf)

        if vm not in self.allocated_info:
            self.allocated_info[vm] = {}

        self.allocated_info[vm]['vfs'] = vfs
        return vfs

    def free_vf(self, vm):
        if self.__vm_has_resource(vm, 'vfs'):
            for pci in self.allocated_info[vm]['vfs']:
                self.__vf_unused(pci)
            self.allocated_info[vm].pop('vfs')

    def add_vf_on_pf(self, pf_pci='', vflist=[]):
        """
        Add vf devices generated by specified pf devices.
        """
        # add vfs into vf info list
        vfs = []
        for vf in vflist:
            if vf not in self.vfs:
                self.vfs_info.append({'pci': vf, 'pf_pci': pf_pci})
                vfs.append(vf)
        used_vfs = ['unused'] * len(vflist)
        self.unused_vfs += vfs
        self.used_vfs += used_vfs
        self.vfs += vfs

    def del_vf_on_pf(self, pf_pci='', vflist=[]):
        """
        Remove vf devices generated by specified pf devices.
        """
        vfs = []
        for vf in vflist:
            for vfs_info in self.vfs_info:
                if vfs_info['pci'] == vf:
                    vfs.append(vf)

        for vf in vfs:
            try:
                index = self.vfs.index(vf)
            except:
                continue
            del self.vfs_info[index]
            del self.unused_vfs[index]
            del self.used_vfs[index]
            del self.vfs[index]

    def alloc_port(self, vm=''):
        """
        Allocate unused host port for vm
        """
        if vm == '':
            print "Alloc host port request vitual machine name!!!"
            return None

        port_start = INIT_FREE_PORT + randint(1, 100)
        port_step = randint(1, 10)
        port = None
        count = 20
        while True:
            if self.dut.check_port_occupied(port_start) is False:
                port = port_start
                break
            count -= 1
            if count < 0:
                print 'No available port on the host!!!'
                break
            port_start += port_step

        if vm not in self.allocated_info:
            self.allocated_info[vm] = {}

        self.allocated_info[vm]['hostport'] = port
        return port

    def free_port(self, vm):
        if self.__vm_has_resource(vm, 'hostport'):
            self.allocated_info[vm].pop('hostport')

    def alloc_vnc_num(self, vm=''):
        """
        Allocate unused host VNC display number for VM.
        """
        if vm == '':
            print "Alloc vnc display number request vitual machine name!!!"
            return None

        max_vnc_display_num = self.dut.get_maximal_vnc_num()
        free_vnc_display_num = max_vnc_display_num + 1

        if vm not in self.allocated_info:
            self.allocated_info[vm] = {}

        self.allocated_info[vm]['vnc_display_num'] = free_vnc_display_num

        return free_vnc_display_num

    def free_vnc_num(self, vm):
        if self.__vm_has_resource(vm, 'vnc_display_num'):
            self.allocated_info[vm].pop('vnc_display_num')

    def free_all_resource(self, vm):
        """
        Free all resource VM has been allocated.
        """
        self.free_port(vm)
        self.free_vnc_num(vm)
        self.free_vf(vm)
        self.free_pf(vm)
        self.free_cpu(vm)

        if self.__vm_has_resource(vm):
            self.allocated_info.pop(vm)

    def get_cpu_on_vm(self, vm=''):
        """
        Return core list on specifid VM.
        """
        if vm in self.allocated_info:
            if "cores" in self.allocated_info[vm]:
                return self.allocated_info[vm]['cores']

    def get_vfs_on_vm(self, vm=''):
        """
        Return vf device list on specifid VM.
        """
        if vm in self.allocated_info:
            if 'vfs' in self.allocated_info[vm]:
                return self.allocated_info[vm]['vfs']

    def get_pfs_on_vm(self, vm=''):
        """
        Return pf device list on specifid VM.
        """
        if vm in self.allocated_info:
            if 'ports' in self.allocated_info[vm]:
                return self.allocated_info[vm]['ports']


class simple_dut(object):

    def __init__(self):
        self.ports_info = []
        self.cores = []

    def check_port_occupied(self, port):
        return False

if __name__ == "__main__":
    dut = simple_dut()
    dut.cores = [{'thread': '1', 'socket': '0'}, {'thread': '2', 'socket': '0'},
                 {'thread': '3', 'socket': '0'}, {'thread': '4', 'socket': '0'},
                 {'thread': '5', 'socket': '0'}, {'thread': '6', 'socket': '0'},
                 {'thread': '7', 'socket': '1'}, {'thread': '8', 'socket': '1'},
                 {'thread': '9', 'socket': '1'}, {'thread': '10', 'socket': '1'},
                 {'thread': '11', 'socket': '1'}, {'thread': '12', 'socket': '1'}]

    dut.ports_info = [{'intf': 'p786p1', 'source': 'cfg', 'mac': '90:e2:ba:69:e5:e4',
                       'pci': '08:00.0', 'numa': 0, 'ipv6': 'fe80::92e2:baff:fe69:e5e4',
                       'peer': 'IXIA:6.5', 'type': '8086:10fb'},
                      {'intf': 'p786p2', 'source': 'cfg', 'mac': '90:e2:ba:69:e5:e5',
                       'pci': '08:00.1', 'numa': 0, 'ipv6': 'fe80::92e2:baff:fe69:e5e5',
                       'peer': 'IXIA:6.6', 'type': '8086:10fb'},
                      {'intf': 'p787p1', 'source': 'cfg', 'mac': '90:e2:ba:69:e5:e6',
                       'pci': '84:00.0', 'numa': 1, 'ipv6': 'fe80::92e2:baff:fe69:e5e6',
                       'peer': 'IXIA:6.7', 'type': '8086:10fb'},
                      {'intf': 'p787p2', 'source': 'cfg', 'mac': '90:e2:ba:69:e5:e7',
                       'pci': '84:00.1', 'numa': 1, 'ipv6': 'fe80::92e2:baff:fe69:e5e7',
                       'peer': 'IXIA:6.8', 'type': '8086:10fb'}]

    virt_pool = VirtResource(dut)
    print "Alloc two PF devices on socket 1 from VM"
    print virt_pool.alloc_pf(vm='test1', number=2, socket=1)

    virt_pool.add_vf_on_pf(pf_pci='08:00.0', vflist=[
                           '08:10.0', '08:10.2', '08:10.4', '08:10.6'])
    virt_pool.add_vf_on_pf(pf_pci='08:00.1', vflist=[
                           '08:10.1', '08:10.3', '08:10.5', '08:10.7'])
    print "Add VF devices to resource pool"
    print virt_pool.vfs_info

    print "Alloc VF device from resource pool"
    print virt_pool.alloc_vf_from_pf(vm='test1', pf_pci='08:00.0', number=2)
    print virt_pool.used_vfs
    print "Alloc VF device from resource pool"
    print virt_pool.alloc_vf_from_pf(vm='test2', pf_pci='08:00.1', vflist=['08:10.3', '08:10.5'])
    print virt_pool.used_vfs

    print "Del VF devices from resource pool"
    virt_pool.del_vf_on_pf(pf_pci='08:00.0', vflist=['08:10.4', '08:10.2'])
    print virt_pool.vfs_info

    virt_pool.reserve_cpu('e')
    print "Reserve three cores from resource pool"
    print virt_pool.unused_cores
    print "Alloc two cores on socket1 for VM-test1"
    print virt_pool.alloc_cpu(vm="test1", number=2, socket=1)
    print "Alloc two cores in list for VM-test2"
    print virt_pool.alloc_cpu(vm="test2", corelist=['4', '5'])
    print "Alloc two cores for VM-test3"
    print virt_pool.alloc_cpu(vm="test3", number=2)
    print "Alloc port for VM-test1"
    print virt_pool.alloc_port(vm='test1')
    print "Alloc information after allcated"
    print virt_pool.allocated_info

    print "Get cores on VM-test1"
    print virt_pool.get_cpu_on_vm("test1")
    print "Get pfs on VM-test1"
    print virt_pool.get_pfs_on_vm("test1")
    print "Get vfs on VM-test2"
    print virt_pool.get_vfs_on_vm("test2")