Merge "[docs] Transition to local docs build job."
[yardstick.git] / yardstick / benchmark / contexts / standalone / model.py
1 # Copyright (c) 2016-2017 Intel Corporation
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain 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,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 import os
16 import re
17 import time
18 import uuid
19 import random
20 import logging
21 import errno
22
23 from netaddr import IPNetwork
24 import xml.etree.ElementTree as ET
25
26 from yardstick import ssh
27 from yardstick.common import constants
28 from yardstick.common import exceptions
29 from yardstick.common import yaml_loader
30 from yardstick.network_services.utils import PciAddress
31 from yardstick.network_services.helpers.cpu import CpuSysCores
32
33
34 LOG = logging.getLogger(__name__)
35
36 VM_TEMPLATE = """
37 <domain type="kvm">
38   <name>{vm_name}</name>
39   <uuid>{random_uuid}</uuid>
40   <memory unit="MB">{memory}</memory>
41   <currentMemory unit="MB">{memory}</currentMemory>
42   <memoryBacking>
43     <hugepages />
44   </memoryBacking>
45   <vcpu cpuset='{cpuset}'>{vcpu}</vcpu>
46  {cputune}
47   <os>
48     <type arch="x86_64" machine="pc-i440fx-xenial">hvm</type>
49     <boot dev="hd" />
50   </os>
51   <features>
52     <acpi />
53     <apic />
54     <pae />
55   </features>
56   <cpu mode='host-passthrough'>
57     <topology cores="{cpu}" sockets="{socket}" threads="{threads}" />
58     <numa>
59        <cell id='0' cpus='{numa_cpus}' memory='{memory}' unit='MB' memAccess='shared'/>
60     </numa>
61   </cpu>
62   <clock offset="utc">
63     <timer name="rtc" tickpolicy="catchup" />
64     <timer name="pit" tickpolicy="delay" />
65     <timer name="hpet" present="no" />
66   </clock>
67   <on_poweroff>destroy</on_poweroff>
68   <on_reboot>restart</on_reboot>
69   <on_crash>restart</on_crash>
70   <devices>
71     <emulator>/usr/bin/kvm-spice</emulator>
72     <disk device="disk" type="file">
73       <driver name="qemu" type="qcow2" />
74       <source file="{vm_image}"/>
75       <target bus="virtio" dev="vda" />
76     </disk>
77     <graphics autoport="yes" listen="0.0.0.0" port="-1" type="vnc" />
78     <interface type="bridge">
79       <mac address='{mac_addr}'/>
80       <source bridge="br-int" />
81       <model type='virtio'/>
82     </interface>
83     <serial type='pty'>
84       <target port='0'/>
85     </serial>
86     <console type='pty'>
87       <target type='serial' port='0'/>
88     </console>
89   </devices>
90 </domain>
91 """
92
93 USER_DATA_TEMPLATE = """
94 cat > {user_file} <<EOF
95 #cloud-config
96 preserve_hostname: false
97 hostname: {host}
98 users:
99 {user_config}
100 EOF
101 """
102
103 NETWORK_DATA_TEMPLATE = """
104 cat > {network_file} <<EOF
105 #cloud-config
106 version: 2
107 ethernets:
108   ens3:
109     match:
110       mac_address: {mac_address}
111     addresses:
112       - {ip_address}
113 EOF
114 """
115
116 WAIT_FOR_BOOT = 30
117
118
119 class Libvirt(object):
120     """ This class handles all the libvirt updates to lauch VM
121     """
122
123     @staticmethod
124     def check_if_vm_exists_and_delete(vm_name, connection):
125         cmd_template = "virsh list --name | grep -i %s"
126         status = connection.execute(cmd_template % vm_name)[0]
127         if status == 0:
128             LOG.info("VM '%s' is already present... destroying", vm_name)
129             connection.execute("virsh destroy %s" % vm_name)
130
131     @staticmethod
132     def virsh_create_vm(connection, cfg):
133         LOG.info('VM create, XML config: %s', cfg)
134         status, _, error = connection.execute('virsh create %s' % cfg)
135         if status:
136             raise exceptions.LibvirtCreateError(error=error)
137
138     @staticmethod
139     def virsh_destroy_vm(vm_name, connection):
140         LOG.info('VM destroy, VM name: %s', vm_name)
141         status, _, error = connection.execute('virsh destroy %s' % vm_name)
142         if status:
143             LOG.warning('Error destroying VM %s. Error: %s', vm_name, error)
144
145     @staticmethod
146     def _add_interface_address(interface, pci_address):
147         """Add a PCI 'address' XML node
148
149         <address type='pci' domain='0x0000' bus='0x00' slot='0x08'
150          function='0x0'/>
151
152         Refence: https://software.intel.com/en-us/articles/
153                  configure-sr-iov-network-virtual-functions-in-linux-kvm
154         """
155         vm_pci = ET.SubElement(interface, 'address')
156         vm_pci.set('type', 'pci')
157         vm_pci.set('domain', '0x{}'.format(pci_address.domain))
158         vm_pci.set('bus', '0x{}'.format(pci_address.bus))
159         vm_pci.set('slot', '0x{}'.format(pci_address.slot))
160         vm_pci.set('function', '0x{}'.format(pci_address.function))
161         return vm_pci
162
163     @classmethod
164     def add_ovs_interface(cls, vpath, port_num, vpci, vports_mac, xml_str):
165         """Add a DPDK OVS 'interface' XML node in 'devices' node
166
167         <devices>
168             <interface type='vhostuser'>
169                 <mac address='00:00:00:00:00:01'/>
170                 <source type='unix' path='/usr/local/var/run/openvswitch/
171                  dpdkvhostuser0' mode='client'/>
172                 <model type='virtio'/>
173                 <driver queues='4'>
174                     <host mrg_rxbuf='off'/>
175                 </driver>
176                 <address type='pci' domain='0x0000' bus='0x00' slot='0x03'
177                  function='0x0'/>
178             </interface>
179             ...
180         </devices>
181
182         Reference: http://docs.openvswitch.org/en/latest/topics/dpdk/
183                    vhost-user/
184         """
185
186         vhost_path = ('{0}/var/run/openvswitch/dpdkvhostuser{1}'.
187                       format(vpath, port_num))
188         root = ET.fromstring(xml_str)
189         pci_address = PciAddress(vpci.strip())
190         device = root.find('devices')
191
192         interface = ET.SubElement(device, 'interface')
193         interface.set('type', 'vhostuser')
194         mac = ET.SubElement(interface, 'mac')
195         mac.set('address', vports_mac)
196
197         source = ET.SubElement(interface, 'source')
198         source.set('type', 'unix')
199         source.set('path', vhost_path)
200         source.set('mode', 'client')
201
202         model = ET.SubElement(interface, 'model')
203         model.set('type', 'virtio')
204
205         driver = ET.SubElement(interface, 'driver')
206         driver.set('queues', '4')
207
208         host = ET.SubElement(driver, 'host')
209         host.set('mrg_rxbuf', 'off')
210
211         cls._add_interface_address(interface, pci_address)
212
213         return ET.tostring(root)
214
215     @classmethod
216     def add_sriov_interfaces(cls, vm_pci, vf_pci, vf_mac, xml_str):
217         """Add a SR-IOV 'interface' XML node in 'devices' node
218
219         <devices>
220            <interface type='hostdev' managed='yes'>
221              <source>
222                <address type='pci' domain='0x0000' bus='0x00' slot='0x03'
223                 function='0x0'/>
224              </source>
225              <mac address='52:54:00:6d:90:02'>
226              <address type='pci' domain='0x0000' bus='0x02' slot='0x04'
227               function='0x1'/>
228            </interface>
229            ...
230          </devices>
231
232         Reference: https://access.redhat.com/documentation/en-us/
233             red_hat_enterprise_linux/6/html/
234             virtualization_host_configuration_and_guest_installation_guide/
235             sect-virtualization_host_configuration_and_guest_installation_guide
236             -sr_iov-how_sr_iov_libvirt_works
237         """
238
239         root = ET.fromstring(xml_str)
240         device = root.find('devices')
241
242         interface = ET.SubElement(device, 'interface')
243         interface.set('managed', 'yes')
244         interface.set('type', 'hostdev')
245
246         mac = ET.SubElement(interface, 'mac')
247         mac.set('address', vf_mac)
248
249         source = ET.SubElement(interface, 'source')
250         pci_address = PciAddress(vf_pci.strip())
251         cls._add_interface_address(source, pci_address)
252
253         pci_vm_address = PciAddress(vm_pci.strip())
254         cls._add_interface_address(interface, pci_vm_address)
255
256         return ET.tostring(root)
257
258     @staticmethod
259     def create_snapshot_qemu(connection, index, base_image):
260         """Create the snapshot image for a VM using a base image
261
262         :param connection: SSH connection to the remote host
263         :param index: index of the VM to be spawn
264         :param base_image: path of the VM base image in the remote host
265         :return: snapshot image path
266         """
267         vm_image = '/var/lib/libvirt/images/%s.qcow2' % index
268         connection.execute('rm -- "%s"' % vm_image)
269         status, _, _ = connection.execute('test -r %s' % base_image)
270         if status:
271             if not os.access(base_image, os.R_OK):
272                 raise exceptions.LibvirtQemuImageBaseImageNotPresent(
273                     vm_image=vm_image, base_image=base_image)
274             # NOTE(ralonsoh): done in two steps to avoid root permission
275             # issues.
276             LOG.info('Copy %s from execution host to remote host', base_image)
277             file_name = os.path.basename(os.path.normpath(base_image))
278             connection.put_file(base_image, '/tmp/%s' % file_name)
279             status, _, error = connection.execute(
280                 'mv -- "/tmp/%s" "%s"' % (file_name, base_image))
281             if status:
282                 raise exceptions.LibvirtQemuImageCreateError(
283                     vm_image=vm_image, base_image=base_image, error=error)
284
285         LOG.info('Convert image %s to %s', base_image, vm_image)
286         qemu_cmd = ('qemu-img create -f qcow2 -o backing_file=%s %s' %
287                     (base_image, vm_image))
288         status, _, error = connection.execute(qemu_cmd)
289         if status:
290             raise exceptions.LibvirtQemuImageCreateError(
291                 vm_image=vm_image, base_image=base_image, error=error)
292         return vm_image
293
294     @classmethod
295     def build_vm_xml(cls, connection, flavor, vm_name, index, cdrom_img):
296         """Build the XML from the configuration parameters"""
297         memory = flavor.get('ram', '4096')
298         extra_spec = flavor.get('extra_specs', {})
299         cpu = extra_spec.get('hw:cpu_cores', '2')
300         socket = extra_spec.get('hw:cpu_sockets', '1')
301         threads = extra_spec.get('hw:cpu_threads', '2')
302         vcpu = int(cpu) * int(threads)
303         numa_cpus = '0-%s' % (vcpu - 1)
304         hw_socket = flavor.get('hw_socket', '0')
305         cpuset = Libvirt.pin_vcpu_for_perf(connection, hw_socket)
306
307         cputune = extra_spec.get('cputune', '')
308         mac = StandaloneContextHelper.get_mac_address(0x00)
309         image = cls.create_snapshot_qemu(connection, index,
310                                          flavor.get("images", None))
311         vm_xml = VM_TEMPLATE.format(
312             vm_name=vm_name,
313             random_uuid=uuid.uuid4(),
314             mac_addr=mac,
315             memory=memory, vcpu=vcpu, cpu=cpu,
316             numa_cpus=numa_cpus,
317             socket=socket, threads=threads,
318             vm_image=image, cpuset=cpuset, cputune=cputune)
319
320         # Add CD-ROM device
321         vm_xml = Libvirt.add_cdrom(cdrom_img, vm_xml)
322
323         return vm_xml, mac
324
325     @staticmethod
326     def update_interrupts_hugepages_perf(connection):
327         connection.execute("echo 1 > /sys/module/kvm/parameters/allow_unsafe_assigned_interrupts")
328         connection.execute("echo never > /sys/kernel/mm/transparent_hugepage/enabled")
329
330     @classmethod
331     def pin_vcpu_for_perf(cls, connection, socket='0'):
332         threads = ""
333         sys_obj = CpuSysCores(connection)
334         soc_cpu = sys_obj.get_core_socket()
335         sys_cpu = int(soc_cpu["cores_per_socket"])
336         socket = str(socket)
337         cores = "%s-%s" % (soc_cpu[socket][0], soc_cpu[socket][sys_cpu - 1])
338         if int(soc_cpu["thread_per_core"]) > 1:
339             threads = "%s-%s" % (soc_cpu[socket][sys_cpu], soc_cpu[socket][-1])
340         cpuset = "%s,%s" % (cores, threads)
341         return cpuset
342
343     @classmethod
344     def write_file(cls, file_name, xml_str):
345         """Dump a XML string to a file"""
346         root = ET.fromstring(xml_str)
347         et = ET.ElementTree(element=root)
348         et.write(file_name, encoding='utf-8', method='xml')
349
350     @classmethod
351     def add_cdrom(cls, file_path, xml_str):
352         """Add a CD-ROM disk XML node in 'devices' node
353
354         <devices>
355             <disk type='file' device='cdrom'>
356               <driver name='qemu' type='raw'/>
357               <source file='/var/lib/libvirt/images/data.img'/>
358               <target dev='hdb'/>
359               <readonly/>
360             </disk>
361             ...
362         </devices>
363         """
364
365         root = ET.fromstring(xml_str)
366         device = root.find('devices')
367
368         disk = ET.SubElement(device, 'disk')
369         disk.set('type', 'file')
370         disk.set('device', 'cdrom')
371
372         driver = ET.SubElement(disk, 'driver')
373         driver.set('name', 'qemu')
374         driver.set('type', 'raw')
375
376         source = ET.SubElement(disk, 'source')
377         source.set('file', file_path)
378
379         target = ET.SubElement(disk, 'target')
380         target.set('dev', 'hdb')
381
382         ET.SubElement(disk, 'readonly')
383         return ET.tostring(root)
384
385     @staticmethod
386     def gen_cdrom_image(connection, file_path, vm_name, vm_user, key_filename, mac, ip):
387         """Generate ISO image for CD-ROM """
388
389         user_config = ["    - name: {user_name}",
390                        "      ssh_authorized_keys:",
391                        "        - {pub_key_str}"]
392         if vm_user != "root":
393             user_config.append("      sudo: ALL=(ALL) NOPASSWD:ALL")
394
395         meta_data = "/tmp/meta-data"
396         user_data = "/tmp/user-data"
397         network_data = "/tmp/network-config"
398         with open(".".join([key_filename, "pub"]), "r") as pub_key_file:
399             pub_key_str = pub_key_file.read().rstrip()
400         user_conf = os.linesep.join(user_config).format(pub_key_str=pub_key_str, user_name=vm_user)
401
402         cmd_lst = [
403             "touch %s" % meta_data,
404             USER_DATA_TEMPLATE.format(user_file=user_data, host=vm_name, user_config=user_conf),
405             NETWORK_DATA_TEMPLATE.format(network_file=network_data, mac_address=mac,
406                                          ip_address=ip),
407             "genisoimage -output {0} -volid cidata -joliet -r {1} {2} {3}".format(file_path,
408                                                                                   meta_data,
409                                                                                   user_data,
410                                                                                   network_data),
411             "rm {0} {1} {2}".format(meta_data, user_data, network_data),
412         ]
413         for cmd in cmd_lst:
414             LOG.info(cmd)
415             status, _, error = connection.execute(cmd)
416             if status:
417                 raise exceptions.LibvirtQemuImageCreateError(error=error)
418
419
420 class StandaloneContextHelper(object):
421     """ This class handles all the common code for standalone
422     """
423     def __init__(self):
424         self.file_path = None
425         super(StandaloneContextHelper, self).__init__()
426
427     @staticmethod
428     def install_req_libs(connection, extra_pkgs=None):
429         extra_pkgs = extra_pkgs or []
430         pkgs = ["qemu-kvm", "libvirt-bin", "bridge-utils", "numactl", "fping", "genisoimage"]
431         pkgs.extend(extra_pkgs)
432         cmd_template = "dpkg-query -W --showformat='${Status}\\n' \"%s\"|grep 'ok installed'"
433         for pkg in pkgs:
434             if connection.execute(cmd_template % pkg)[0]:
435                 connection.execute("apt-get update")
436                 connection.execute("apt-get -y install %s" % pkg)
437
438     @staticmethod
439     def get_kernel_module(connection, pci, driver):
440         if not driver:
441             out = connection.execute("lspci -k -s %s" % pci)[1]
442             driver = out.split("Kernel modules:").pop().strip()
443         return driver
444
445     @classmethod
446     def get_nic_details(cls, connection, networks, dpdk_devbind):
447         for key, ports in networks.items():
448             if key == "mgmt":
449                 continue
450
451             phy_ports = ports['phy_port']
452             phy_driver = ports.get('phy_driver', None)
453             driver = cls.get_kernel_module(connection, phy_ports, phy_driver)
454
455             # Make sure that ports are bound to kernel drivers e.g. i40e/ixgbe
456             bind_cmd = "{dpdk_devbind} --force -b {driver} {port}"
457             lshw_cmd = "lshw -c network -businfo | grep '{port}'"
458             link_show_cmd = "ip -s link show {interface}"
459
460             cmd = bind_cmd.format(dpdk_devbind=dpdk_devbind,
461                                   driver=driver, port=ports['phy_port'])
462             connection.execute(cmd)
463
464             out = connection.execute(lshw_cmd.format(port=phy_ports))[1]
465             interface = out.split()[1]
466
467             connection.execute(link_show_cmd.format(interface=interface))
468
469             ports.update({
470                 'interface': str(interface),
471                 'driver': driver
472             })
473         LOG.info(networks)
474
475         return networks
476
477     @staticmethod
478     def get_virtual_devices(connection, pci):
479         cmd = "cat /sys/bus/pci/devices/{0}/virtfn0/uevent"
480         output = connection.execute(cmd.format(pci))[1]
481
482         pattern = "PCI_SLOT_NAME=({})".format(PciAddress.PCI_PATTERN_STR)
483         m = re.search(pattern, output, re.MULTILINE)
484
485         pf_vfs = {}
486         if m:
487             pf_vfs = {pci: m.group(1).rstrip()}
488
489         LOG.info("pf_vfs:\n%s", pf_vfs)
490
491         return pf_vfs
492
493     def parse_pod_file(self, file_path, nfvi_role='Sriov'):
494         self.file_path = file_path
495         nodes = []
496         nfvi_host = []
497         try:
498             cfg = yaml_loader.read_yaml_file(self.file_path)
499         except IOError as io_error:
500             if io_error.errno != errno.ENOENT:
501                 raise
502             self.file_path = os.path.join(constants.YARDSTICK_ROOT_PATH,
503                                           file_path)
504             cfg = yaml_loader.read_yaml_file(self.file_path)
505
506         nodes.extend([node for node in cfg["nodes"] if str(node["role"]) != nfvi_role])
507         nfvi_host.extend([node for node in cfg["nodes"] if str(node["role"]) == nfvi_role])
508         if not nfvi_host:
509             raise("Node role is other than SRIOV")
510
511         host_mgmt = {'user': nfvi_host[0]['user'],
512                      'ip': str(IPNetwork(nfvi_host[0]['ip']).ip),
513                      'password': nfvi_host[0]['password'],
514                      'ssh_port': nfvi_host[0].get('ssh_port', 22),
515                      'key_filename': nfvi_host[0].get('key_filename')}
516
517         return [nodes, nfvi_host, host_mgmt]
518
519     @staticmethod
520     def get_mac_address(end=0x7f):
521         mac = [0x52, 0x54, 0x00,
522                random.randint(0x00, end),
523                random.randint(0x00, 0xff),
524                random.randint(0x00, 0xff)]
525         mac_address = ':'.join('%02x' % x for x in mac)
526         return mac_address
527
528     @staticmethod
529     def get_mgmt_ip(connection, mac, cidr, node):
530         mgmtip = None
531         times = 10
532         while not mgmtip and times:
533             connection.execute("fping -c 1 -g %s > /dev/null 2>&1" % cidr)
534             out = connection.execute("ip neighbor | grep '%s'" % mac)[1]
535             LOG.info("fping -c 1 -g %s > /dev/null 2>&1", cidr)
536             if out.strip():
537                 mgmtip = str(out.split(" ")[0]).strip()
538                 client = ssh.SSH.from_node(node, overrides={"ip": mgmtip})
539                 client.wait()
540                 break
541
542             time.sleep(WAIT_FOR_BOOT)  # FixMe: How to find if VM is booted?
543             times = times - 1
544         return mgmtip
545
546     @classmethod
547     def wait_for_vnfs_to_start(cls, connection, servers, nodes):
548         for node in nodes:
549             vnf = servers[node["name"]]
550             mgmtip = vnf["network_ports"]["mgmt"]["cidr"]
551             ip = cls.get_mgmt_ip(connection, node["mac"], mgmtip, node)
552             if ip:
553                 node["ip"] = ip
554         return nodes
555
556     @classmethod
557     def check_update_key(cls, connection, node, vm_name, id_name, cdrom_img, mac):
558         # Generate public/private keys if private key file is not provided
559         user_name = node.get('user')
560         if not user_name:
561             node['user'] = 'root'
562             user_name = node.get('user')
563         if not node.get('key_filename'):
564             key_filename = ''.join(
565                 [constants.YARDSTICK_ROOT_PATH,
566                  'yardstick/resources/files/yardstick_key-',
567                  id_name, '-', vm_name])
568             ssh.SSH.gen_keys(key_filename)
569             node['key_filename'] = key_filename
570         # Update image with public key
571         key_filename = node.get('key_filename')
572         ip_netmask = "{0}/{1}".format(node.get('ip'), node.get('netmask'))
573         Libvirt.gen_cdrom_image(connection, cdrom_img, vm_name, user_name, key_filename, mac,
574                                 ip_netmask)
575         return node
576
577
578 class Server(object):
579     """ This class handles geting vnf nodes
580     """
581
582     @staticmethod
583     def build_vnf_interfaces(vnf, ports):
584         interfaces = {}
585         index = 0
586
587         for key, vfs in vnf["network_ports"].items():
588             if key == "mgmt":
589                 mgmt_cidr = IPNetwork(vfs['cidr'])
590                 continue
591
592             vf = ports[vfs[0]]
593             ip = IPNetwork(vf['cidr'])
594             interfaces.update({
595                 key: {
596                     'vpci': vf['vpci'],
597                     'driver': "%svf" % vf['driver'],
598                     'local_mac': vf['mac'],
599                     'dpdk_port_num': index,
600                     'local_ip': str(ip.ip),
601                     'netmask': str(ip.netmask)
602                     },
603             })
604             index = index + 1
605
606         return mgmt_cidr, interfaces
607
608     @classmethod
609     def generate_vnf_instance(cls, flavor, ports, ip, key, vnf, mac):
610         mgmt_cidr, interfaces = cls.build_vnf_interfaces(vnf, ports)
611
612         result = {
613             "ip": str(mgmt_cidr.ip),
614             "netmask": str(mgmt_cidr.netmask),
615             "mac": mac,
616             "host": ip,
617             "user": flavor.get('user', 'root'),
618             "interfaces": interfaces,
619             "routing_table": [],
620             # empty IPv6 routing table
621             "nd_route_tbl": [],
622             "name": key, "role": key
623         }
624
625         try:
626             result['key_filename'] = flavor['key_filename']
627         except KeyError:
628             pass
629
630         try:
631             result['password'] = flavor['password']
632         except KeyError:
633             pass
634         LOG.info(result)
635         return result
636
637
638 class OvsDeploy(object):
639     """ This class handles deploy of ovs dpdk
640     Configuration: ovs_dpdk
641     """
642
643     OVS_DEPLOY_SCRIPT = "ovs_deploy.bash"
644
645     def __init__(self, connection, bin_path, ovs_properties):
646         self.connection = connection
647         self.bin_path = bin_path
648         self.ovs_properties = ovs_properties
649
650     def prerequisite(self):
651         pkgs = ["git", "build-essential", "pkg-config", "automake",
652                 "autotools-dev", "libltdl-dev", "cmake", "libnuma-dev",
653                 "libpcap-dev"]
654         StandaloneContextHelper.install_req_libs(self.connection, pkgs)
655
656     def ovs_deploy(self):
657         ovs_deploy = os.path.join(constants.YARDSTICK_ROOT_PATH,
658                                   "yardstick/resources/scripts/install/",
659                                   self.OVS_DEPLOY_SCRIPT)
660         if os.path.isfile(ovs_deploy):
661             self.prerequisite()
662             remote_ovs_deploy = os.path.join(self.bin_path, self.OVS_DEPLOY_SCRIPT)
663             LOG.info(remote_ovs_deploy)
664             self.connection.put(ovs_deploy, remote_ovs_deploy)
665
666             http_proxy = os.environ.get('http_proxy', '')
667             ovs_details = self.ovs_properties.get("version", {})
668             ovs = ovs_details.get("ovs", "2.6.0")
669             dpdk = ovs_details.get("dpdk", "16.11.1")
670
671             cmd = "sudo -E %s --ovs='%s' --dpdk='%s' -p='%s'" % (remote_ovs_deploy,
672                                                                  ovs, dpdk, http_proxy)
673             exit_status, _, stderr = self.connection.execute(cmd)
674             if exit_status:
675                 raise exceptions.OVSDeployError(stderr=stderr)