diff options
Diffstat (limited to 'tools/docker/deployment/interactive')
4 files changed, 403 insertions, 0 deletions
diff --git a/tools/docker/deployment/interactive/controller/Dockerfile b/tools/docker/deployment/interactive/controller/Dockerfile new file mode 100644 index 00000000..3d9fca42 --- /dev/null +++ b/tools/docker/deployment/interactive/controller/Dockerfile @@ -0,0 +1,21 @@ +FROM python:3.6 +LABEL maintainer="sridhar.rao@spirent.com" + +ENV GRPC_PYTHON_VERSION 1.4.0 +RUN apt-get update && apt-get -y install python3-pip +RUN pip3 install grpcio==${GRPC_PYTHON_VERSION} grpcio-tools==${GRPC_PYTHON_VERSION} +RUN pip3 install paramiko +RUN pip3 install chainmap +RUN pip3 install oslo.utils +RUN pip3 install scp + +WORKDIR /usr/src/app + +COPY ./vsperf ./vsperf + +VOLUME ["/usr/src/app/vsperf"] + +CMD ["python3", "./vsperf/vsperf_controller.py"] + + + diff --git a/tools/docker/deployment/interactive/controller/vsperf/__init__.py b/tools/docker/deployment/interactive/controller/vsperf/__init__.py new file mode 100644 index 00000000..ad0ebec3 --- /dev/null +++ b/tools/docker/deployment/interactive/controller/vsperf/__init__.py @@ -0,0 +1 @@ +#### Empty diff --git a/tools/docker/deployment/interactive/controller/vsperf/vsperf_controller.py b/tools/docker/deployment/interactive/controller/vsperf/vsperf_controller.py new file mode 100644 index 00000000..b192c493 --- /dev/null +++ b/tools/docker/deployment/interactive/controller/vsperf/vsperf_controller.py @@ -0,0 +1,360 @@ +# Copyright 2018-19 Spirent Communications. +# +# 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. + +# pylint: disable=R0902 +# Sixteen is reasonable instance attributes +# pylint: disable=W0221 +""" +VSPER docker-controller. +""" + +import io +import time +from concurrent import futures +import grpc + +from proto import vsperf_pb2 +from proto import vsperf_pb2_grpc +from utils import ssh + +_ONE_DAY_IN_SECONDS = 60 * 60 * 24 + + +# pylint: disable=too-few-public-methods,no-self-use +class PseudoFile(io.RawIOBase): + """ + Handle ssh command output. + """ + + def write(self, chunk): + """ + Write to file + """ + if "error" in chunk: + return + with open("./output.txt", "a") as fref: + fref.write(chunk) + + +class VsperfController(vsperf_pb2_grpc.ControllerServicer): + """ + Main Controller Class + """ + + def __init__(self): + """ + Initialization + """ + self.client = None + self.dut = None + self.dut_check = None + self.tgen_check = None + self.user = None + self.pwd = None + self.tgen_client = None + self.tgen = None + self.tgen_user = None + self.tgenpwd = None + self.tgen_conf = None + self.scenario = None + self.hpmax = None + self.hprequested = None + self.tgen_ip_address = None + self.trex_conf = None + # Default TGen is T-Rex + self.trex_conffile = "trex_cfg.yml" + self.collectd_conffile = "collectd.conf" + + def setup(self): + """ + Performs Setup of the client. + """ + # Just connect to VM. + self.client = ssh.SSH(host=self.dut, user=self.user, + password=self.pwd) + self.client.wait() + + def install_vsperf(self): + """ + Perform actual installation + """ + download_cmd = "git clone https://gerrit.opnfv.org/gerrit/vswitchperf" + self.client.run(download_cmd) + install_cmd = "cd vswitchperf/systems ; " + install_cmd += "echo '{}' | sudo -S ./build_base_machine.sh ".format( + self.pwd) + #install_cmd += "./build_base_machine.sh" + self.client.run(install_cmd) + + def VsperfInstall(self, request, context): + """ + Handle VSPERF install command from client + """ + # print("Installing VSPERF") + if self.dut_check != 0: + return vsperf_pb2.StatusReply(message="DUT-Host is not Connected [!]" \ + "\nMake sure to establish connection with" \ + " DUT-Host.") + vsperf_check_cmd = "source ~/vsperfenv/bin/activate ; cd vswitchperf* && ./vsperf --help" + vsperf_check_cmd_result = str(self.client.execute(vsperf_check_cmd)[1]) + vsperf_verify_list = [ + 'usage', + 'positional arguments', + 'optional arguments', + 'test selection options', + 'test behavior options'] + for idx, i in enumerate(vsperf_verify_list, start=1): + if str(i) in vsperf_check_cmd_result: + if idx < 5: + continue + elif idx == 5: + return vsperf_pb2.StatusReply( + message="VSPERF is Already Installed on DUT-Host") + self.install_vsperf() + return vsperf_pb2.StatusReply(message="VSPERF Successfully Installed DUT-Host") + + def HostConnect(self, request, context): + """ + Handle host connectivity command from client + """ + self.dut = request.ip + self.user = request.uname + self.pwd = request.pwd + self.setup() + check_cmd = "ls -l" + self.dut_check = int(self.client.execute(check_cmd)[0]) + return vsperf_pb2.StatusReply(message="Successfully Connected") + + def save_chunks_to_file(self, chunks, filename): + """ + Write the output to file + """ + with open(filename, 'wb') as fref: + for chunk in chunks: + fref.write(chunk.Content) + +###### Traffic Generator Related functions #### + def TGenHostConnect(self, request, context): + """ + Connect to TGen-Node + """ + self.tgen = request.ip + self.tgen_user = request.uname + self.tgenpwd = request.pwd + self.tgen_setup() + check_tgen_cmd = "ls" + self.tgen_check = int(self.tgen_client.execute(check_tgen_cmd)[0]) + return vsperf_pb2.StatusReply(message="Successfully Connected") + + def tgen_setup(self): + """ + Setup the T-Gen Client + """ + # Just connect to VM. + self.tgen_client = ssh.SSH(host=self.tgen, user=self.tgen_user, + password=self.tgenpwd) + self.tgen_client.wait() + + def TGenInstall(self, request, context): + """ + Install Traffic generator on the node. + """ + if self.tgen_check != 0: + return vsperf_pb2.StatusReply(message="TGen-Host is not Connected [!]" \ + "\nMake sure to establish connection with" \ + " TGen-Host.") + kill_cmd = "pkill -f t-rex" + self.tgen_client.send_command(kill_cmd) + tgen_start_cmd = "cd trex_2.37/scripts && ./t-rex-64 -f cap2/dns.yaml -d 100 -m 1 --nc" + tgen_start_cmd_result = int(self.tgen_client.execute(tgen_start_cmd)[0]) + kill_cmd = "pkill -f t-rex" + self.tgen_client.send_command(kill_cmd) + if tgen_start_cmd_result == 0: + return vsperf_pb2.StatusReply( + message="Traffic Generetor has T-rex Installed") + download_cmd = "git clone https://github.com/cisco-system-traffic-generator/trex-core" + self.tgen_client.run(download_cmd) + install_cmd = "cd trex-core/linux_dpdk ; ./b configure ; ./b build" + self.tgen_client.run(install_cmd) + # before you setup your trex_cfg.yml make sure to do sanity check + # NIC PICs and establish route between your DUT and Test Device. + return vsperf_pb2.StatusReply(message="Traffic Generetor has now T-rex Installed") + + def TGenUploadConfigFile(self, request, context): + """ + Handle upload config-file command from client + """ + if self.tgen_check != 0: + return vsperf_pb2.StatusReply(message="TGen-Host is not Connected [!]" \ + "\nMake sure to establish connection with" \ + " TGen-Host.") + filename = self.trex_conffile + self.save_chunks_to_file(request, filename) + check_trex_config_cmd = "echo {} | sudo -S find /etc -maxdepth 1 -name trex_cfg.yaml".\ + format(self.tgenpwd) + check_test_result = str( + self.tgen_client.execute(check_trex_config_cmd)[1]) + if "trex_cfg.yaml" in check_test_result: + self.tgen_client.run("rm -f /etc/trex_cfg.yaml") + self.upload_tgen_config() + self.tgen_client.run( + "echo {} | sudo -S mv ~/trex_cfg.yaml /etc/".format(self.tgenpwd), pty=True) + return vsperf_pb2.UploadStatus(Message="Successfully Uploaded", + Code=1) + + def upload_tgen_config(self): + """ + Perform file upload. + """ + self.tgen_client.put_file(self.trex_conffile, '/root/trex_cfg.yaml') + +# Tool-Chain related Functions####3 + + def install_collectd(self): + """ + installation of the collectd + """ + check_collectd_config_cmd = "find /opt -maxdepth 1 -name 'collectd'" + check_test_result = str( + self.client.execute(check_collectd_config_cmd)[1]) + if "collectd" in check_test_result: + pass + else: + download_cmd = "git clone https://github.com/collectd/collectd.git" + self.client.run(download_cmd) + build_cmd = "cd collectd ; " + build_cmd += "./build.sh" + self.client.run(build_cmd) + config_cmd = "cd collectd ; ./configure --enable-syslog " + config_cmd += "--enable-logfile --enable-hugepages --enable-debug ; " + self.client.run(config_cmd) + install_cmd = "cd collectd ; make ; " + install_cmd += "echo '{}' | sudo -S make install".format(self.pwd) + self.client.run(install_cmd, pty=True) + + def CollectdInstall(self, request, context): + """ + Install Collectd on DUT + """ + if self.dut_check != 0: + return vsperf_pb2.StatusReply(message="DUT-Host is not Connected [!]" \ + "\nMake sure to establish connection with" \ + " DUT-Host.") + self.install_collectd() + return vsperf_pb2.StatusReply( + message="Collectd Successfully Installed on DUT-Host") + + def upload_collectd_config(self): + """ + Perform file upload. + """ + self.client.put_file(self.collectd_conffile, '~/collectd.conf') + move_cmd = "echo '{}' | sudo -S mv ~/collectd.conf /opt/collectd/etc".format( + self.pwd) + self.client.run(move_cmd, pty=True) + + def CollectdUploadConfig(self, request, context): + """ + Upload collectd config-file on DUT + """ + if self.dut_check != 0: + return vsperf_pb2.StatusReply(message="DUT-Host is not Connected [!]" \ + "\nMake sure to establish connection with" \ + " DUT-Host.") + filename = self.collectd_conffile + self.save_chunks_to_file(request, filename) + self.upload_collectd_config() + return vsperf_pb2.UploadStatus( + Message="Successfully Collectd Configuration Uploaded", Code=1) + +###System Configuration related functions### + + def DutHugepageConfig(self, request, context): + """ + Configure the DUT system hugepage parameter from client + """ + if self.dut_check != 0: + return vsperf_pb2.StatusReply(message="DUT-Host is not Connected [!]" \ + "\nMake sure to establish connection with" \ + " DUT-Host.") + self.hpmax = int(request.hpmax) + self.hprequested = int(request.hprequested) + hugepage_cmd = "echo '{}' | sudo -S mkdir -p /mnt/huge ; ".format( + self.pwd) + hugepage_cmd += "echo '{}' | sudo -S mount -t hugetlbfs nodev /mnt/huge".format( + self.pwd) + self.client.run(hugepage_cmd, pty=True) + hp_nr_cmd = "cat /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages" + hp_free_cmd = "cat /sys/devices/system/node/node0/hugepages/hugepages-2048kB/free_hugepages" + hp_nr = int(self.client.execute(hp_nr_cmd)[1]) + hp_free = int(self.client.execute(hp_free_cmd)[1]) + if hp_free <= self.hprequested: + hp_nr_new = hp_nr + (self.hprequested - hp_free) + if hp_nr_new > self.hpmax: + hp_nr_new = self.hpmax + + nr_hugepage_cmd = "echo '{}' | sudo -S bash -c \"echo 'vm.nr_hugepages={}' >>".\ + format(self.pwd, hp_nr_new) + nr_hugepage_cmd += " /etc/sysctl.conf\"" + self.client.run(nr_hugepage_cmd, pty=True) + + dict_cmd = "cat /sys/devices/system/node/node1/hugepages/hugepages-2048kB/nr_hugepages" + dict_check = int(self.client.execute(dict_cmd)[0]) + if dict_check == 0: + node1_hugepage_cmd = "echo '{}' | sudo -s bash -c \"echo 0 >".format(self.pwd) + node1_hugepage_cmd += " /sys/devices/system/node/node1/" + node1_hugepage_cmd += "hugepages/hugepages-2048kB/nr_hugepages\"" + return vsperf_pb2.StatusReply( + message="DUT-Host system configured with {} No of Hugepages".format(hp_nr_new)) + + def CheckDependecies(self, request, context): + """ + Check and Install required packages on DUT + """ + if self.dut_check != 0: + return vsperf_pb2.StatusReply(message="DUT-Host is not Connected [!]" \ + "\nMake sure to establish connection with" \ + " DUT-Host.") + packages = ['python34-tkinter', 'sysstat', 'bc'] + for pkg in packages: + # pkg_check_cmd = "dpkg -s {}".format(pkg) for ubuntu + pkg_check_cmd = "rpm -q {}".format(pkg) + pkg_cmd_response = self.client.execute(pkg_check_cmd)[0] + if pkg_cmd_response == 1: + install_pkg_cmd = "echo '{}' | sudo -S yum install -y {}".format( + self.pwd, pkg) + #install_pkg_cmd = "echo '{}' | sudo -S apt-get install -y {}".format(self.pwd,pkg) + self.client.run(install_pkg_cmd, pty=True) + + return vsperf_pb2.StatusReply(message="Python34-tkinter, sysstat and bc Packages"\ + "are now Installed") + +def serve(): + """ + Start servicing the client + """ + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + vsperf_pb2_grpc.add_ControllerServicer_to_server( + VsperfController(), server) + server.add_insecure_port('[::]:50051') + server.start() + try: + while True: + time.sleep(_ONE_DAY_IN_SECONDS) + except (SystemExit, KeyboardInterrupt, MemoryError, RuntimeError): + server.stop(0) + + +if __name__ == "__main__": + serve() diff --git a/tools/docker/deployment/interactive/docker-compose.yml b/tools/docker/deployment/interactive/docker-compose.yml new file mode 100644 index 00000000..cbf894c5 --- /dev/null +++ b/tools/docker/deployment/interactive/docker-compose.yml @@ -0,0 +1,21 @@ +version: '2' + +services: + deploy: + build: + context: ./controller + volumes: + - ./controller/vsperf:/vsperf + ports: + - 50051:50051 + + + + + + + + + + + |