diff options
-rwxr-xr-x | clover/tools/jmeter/build_master.sh | 16 | ||||
-rwxr-xr-x | clover/tools/jmeter/build_slave.sh | 16 | ||||
-rw-r--r-- | clover/tools/jmeter/jmeter-master/Dockerfile | 29 | ||||
-rwxr-xr-x | clover/tools/jmeter/jmeter-master/grpc/build_proto.sh | 11 | ||||
-rw-r--r-- | clover/tools/jmeter/jmeter-master/grpc/jmeter.proto | 43 | ||||
-rw-r--r-- | clover/tools/jmeter/jmeter-master/grpc/jmeter_pb2.py | 291 | ||||
-rw-r--r-- | clover/tools/jmeter/jmeter-master/grpc/jmeter_pb2_grpc.py | 80 | ||||
-rw-r--r-- | clover/tools/jmeter/jmeter-master/grpc/jmeter_server.py | 118 | ||||
-rwxr-xr-x | clover/tools/jmeter/jmeter-master/process/grpc_process.sh | 11 | ||||
-rw-r--r-- | clover/tools/jmeter/jmeter-master/tests/jmx.template | 135 | ||||
-rw-r--r-- | clover/tools/jmeter/jmeter-slave/Dockerfile | 27 | ||||
-rw-r--r-- | clover/tools/jmeter/rmi_keystore.jks | bin | 0 -> 2190 bytes | |||
-rw-r--r-- | clover/tools/jmeter/yaml/manifest.template | 45 | ||||
-rw-r--r-- | clover/tools/jmeter/yaml/render_master.py | 67 | ||||
-rw-r--r-- | clover/tools/jmeter/yaml/render_slave.py | 67 |
15 files changed, 956 insertions, 0 deletions
diff --git a/clover/tools/jmeter/build_master.sh b/clover/tools/jmeter/build_master.sh new file mode 100755 index 0000000..5c5459a --- /dev/null +++ b/clover/tools/jmeter/build_master.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# + +IMAGE_PATH=${IMAGE_PATH:-"localhost:5000"} +IMAGE_NAME=${IMAGE_NAME:-"clover-jmeter-master"} + +docker build -f jmeter-master/Dockerfile -t $IMAGE_NAME . +docker tag $IMAGE_NAME $IMAGE_PATH/$IMAGE_NAME +docker push $IMAGE_PATH/$IMAGE_NAME diff --git a/clover/tools/jmeter/build_slave.sh b/clover/tools/jmeter/build_slave.sh new file mode 100755 index 0000000..1651c55 --- /dev/null +++ b/clover/tools/jmeter/build_slave.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# + +IMAGE_PATH=${IMAGE_PATH:-"localhost:5000"} +IMAGE_NAME=${IMAGE_NAME:-"clover-jmeter-slave"} + +docker build -f jmeter-slave/Dockerfile -t $IMAGE_NAME . +docker tag $IMAGE_NAME $IMAGE_PATH/$IMAGE_NAME +docker push $IMAGE_PATH/$IMAGE_NAME diff --git a/clover/tools/jmeter/jmeter-master/Dockerfile b/clover/tools/jmeter/jmeter-master/Dockerfile new file mode 100644 index 0000000..da0e474 --- /dev/null +++ b/clover/tools/jmeter/jmeter-master/Dockerfile @@ -0,0 +1,29 @@ +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +FROM java:8 + +RUN wget http://apache.mirrors.hoobly.com//jmeter/binaries/apache-jmeter-4.0.tgz +RUN tar -xvzf apache-jmeter-4.0.tgz +RUN rm apache-jmeter-4.0.tgz +RUN mv apache-jmeter-4.0 /jmeter +ENV JMETER_HOME /jmeter +ENV PATH $JMETER_HOME/bin:$PATH +COPY rmi_keystore.jks $JMETER_HOME/bin + +RUN apt update && apt install -y python-setuptools python-dev python-pip +RUN python -m pip install --upgrade pip +RUN python -m pip install enum34 futures cython +RUN python -m pip install grpcio protobuf jinja2 + +WORKDIR /jmeter/bin + +COPY jmeter-master/process process +COPY jmeter-master/grpc grpc +COPY jmeter-master/tests tests + +CMD ./process/grpc_process.sh no_init diff --git a/clover/tools/jmeter/jmeter-master/grpc/build_proto.sh b/clover/tools/jmeter/jmeter-master/grpc/build_proto.sh new file mode 100755 index 0000000..52dfd0a --- /dev/null +++ b/clover/tools/jmeter/jmeter-master/grpc/build_proto.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# + +python -m grpc_tools.protoc -I./ --python_out=. --grpc_python_out=. jmeter.proto diff --git a/clover/tools/jmeter/jmeter-master/grpc/jmeter.proto b/clover/tools/jmeter/jmeter-master/grpc/jmeter.proto new file mode 100644 index 0000000..7213faa --- /dev/null +++ b/clover/tools/jmeter/jmeter-master/grpc/jmeter.proto @@ -0,0 +1,43 @@ +// Copyright (c) Authors of Clover +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Apache License, Version 2.0 +// which accompanies this distribution, and is available at +// http://www.apache.org/licenses/LICENSE-2.0 + +syntax = "proto3"; + +package jmeter; + +// The controller service definition. +service Controller { + + rpc GenTest (ConfigJmeter) returns (JmeterReply) {} + rpc StartTest (TestParams) returns (JmeterReply) {} + rpc GetResults (JResults) returns (JmeterReply) {} +} + +message TestParams { + string num_slaves = 1; + string test_plan = 2; + string slave_ips = 3; +} + +message ConfigJmeter { + string url_list = 1; + string num_threads = 2; + string url_names = 3; + string url_protocols = 4; + string url_methods = 5; + string loops = 6; + string ramp_time = 7; +} + +message JmeterReply { + string message = 1; +} + +message JResults { + string r_format = 1; + string r_file = 2; +} diff --git a/clover/tools/jmeter/jmeter-master/grpc/jmeter_pb2.py b/clover/tools/jmeter/jmeter-master/grpc/jmeter_pb2.py new file mode 100644 index 0000000..e4a75fd --- /dev/null +++ b/clover/tools/jmeter/jmeter-master/grpc/jmeter_pb2.py @@ -0,0 +1,291 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: jmeter.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='jmeter.proto', + package='jmeter', + syntax='proto3', + serialized_pb=_b('\n\x0cjmeter.proto\x12\x06jmeter\"F\n\nTestParams\x12\x12\n\nnum_slaves\x18\x01 \x01(\t\x12\x11\n\ttest_plan\x18\x02 \x01(\t\x12\x11\n\tslave_ips\x18\x03 \x01(\t\"\x96\x01\n\x0c\x43onfigJmeter\x12\x10\n\x08url_list\x18\x01 \x01(\t\x12\x13\n\x0bnum_threads\x18\x02 \x01(\t\x12\x11\n\turl_names\x18\x03 \x01(\t\x12\x15\n\rurl_protocols\x18\x04 \x01(\t\x12\x13\n\x0burl_methods\x18\x05 \x01(\t\x12\r\n\x05loops\x18\x06 \x01(\t\x12\x11\n\tramp_time\x18\x07 \x01(\t\"\x1e\n\x0bJmeterReply\x12\x0f\n\x07message\x18\x01 \x01(\t\",\n\x08JResults\x12\x10\n\x08r_format\x18\x01 \x01(\t\x12\x0e\n\x06r_file\x18\x02 \x01(\t2\xb3\x01\n\nController\x12\x36\n\x07GenTest\x12\x14.jmeter.ConfigJmeter\x1a\x13.jmeter.JmeterReply\"\x00\x12\x36\n\tStartTest\x12\x12.jmeter.TestParams\x1a\x13.jmeter.JmeterReply\"\x00\x12\x35\n\nGetResults\x12\x10.jmeter.JResults\x1a\x13.jmeter.JmeterReply\"\x00\x62\x06proto3') +) + + + + +_TESTPARAMS = _descriptor.Descriptor( + name='TestParams', + full_name='jmeter.TestParams', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='num_slaves', full_name='jmeter.TestParams.num_slaves', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='test_plan', full_name='jmeter.TestParams.test_plan', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='slave_ips', full_name='jmeter.TestParams.slave_ips', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=24, + serialized_end=94, +) + + +_CONFIGJMETER = _descriptor.Descriptor( + name='ConfigJmeter', + full_name='jmeter.ConfigJmeter', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='url_list', full_name='jmeter.ConfigJmeter.url_list', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='num_threads', full_name='jmeter.ConfigJmeter.num_threads', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='url_names', full_name='jmeter.ConfigJmeter.url_names', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='url_protocols', full_name='jmeter.ConfigJmeter.url_protocols', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='url_methods', full_name='jmeter.ConfigJmeter.url_methods', index=4, + number=5, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='loops', full_name='jmeter.ConfigJmeter.loops', index=5, + number=6, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='ramp_time', full_name='jmeter.ConfigJmeter.ramp_time', index=6, + number=7, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=97, + serialized_end=247, +) + + +_JMETERREPLY = _descriptor.Descriptor( + name='JmeterReply', + full_name='jmeter.JmeterReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='message', full_name='jmeter.JmeterReply.message', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=249, + serialized_end=279, +) + + +_JRESULTS = _descriptor.Descriptor( + name='JResults', + full_name='jmeter.JResults', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='r_format', full_name='jmeter.JResults.r_format', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='r_file', full_name='jmeter.JResults.r_file', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=281, + serialized_end=325, +) + +DESCRIPTOR.message_types_by_name['TestParams'] = _TESTPARAMS +DESCRIPTOR.message_types_by_name['ConfigJmeter'] = _CONFIGJMETER +DESCRIPTOR.message_types_by_name['JmeterReply'] = _JMETERREPLY +DESCRIPTOR.message_types_by_name['JResults'] = _JRESULTS +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +TestParams = _reflection.GeneratedProtocolMessageType('TestParams', (_message.Message,), dict( + DESCRIPTOR = _TESTPARAMS, + __module__ = 'jmeter_pb2' + # @@protoc_insertion_point(class_scope:jmeter.TestParams) + )) +_sym_db.RegisterMessage(TestParams) + +ConfigJmeter = _reflection.GeneratedProtocolMessageType('ConfigJmeter', (_message.Message,), dict( + DESCRIPTOR = _CONFIGJMETER, + __module__ = 'jmeter_pb2' + # @@protoc_insertion_point(class_scope:jmeter.ConfigJmeter) + )) +_sym_db.RegisterMessage(ConfigJmeter) + +JmeterReply = _reflection.GeneratedProtocolMessageType('JmeterReply', (_message.Message,), dict( + DESCRIPTOR = _JMETERREPLY, + __module__ = 'jmeter_pb2' + # @@protoc_insertion_point(class_scope:jmeter.JmeterReply) + )) +_sym_db.RegisterMessage(JmeterReply) + +JResults = _reflection.GeneratedProtocolMessageType('JResults', (_message.Message,), dict( + DESCRIPTOR = _JRESULTS, + __module__ = 'jmeter_pb2' + # @@protoc_insertion_point(class_scope:jmeter.JResults) + )) +_sym_db.RegisterMessage(JResults) + + + +_CONTROLLER = _descriptor.ServiceDescriptor( + name='Controller', + full_name='jmeter.Controller', + file=DESCRIPTOR, + index=0, + options=None, + serialized_start=328, + serialized_end=507, + methods=[ + _descriptor.MethodDescriptor( + name='GenTest', + full_name='jmeter.Controller.GenTest', + index=0, + containing_service=None, + input_type=_CONFIGJMETER, + output_type=_JMETERREPLY, + options=None, + ), + _descriptor.MethodDescriptor( + name='StartTest', + full_name='jmeter.Controller.StartTest', + index=1, + containing_service=None, + input_type=_TESTPARAMS, + output_type=_JMETERREPLY, + options=None, + ), + _descriptor.MethodDescriptor( + name='GetResults', + full_name='jmeter.Controller.GetResults', + index=2, + containing_service=None, + input_type=_JRESULTS, + output_type=_JMETERREPLY, + options=None, + ), +]) +_sym_db.RegisterServiceDescriptor(_CONTROLLER) + +DESCRIPTOR.services_by_name['Controller'] = _CONTROLLER + +# @@protoc_insertion_point(module_scope) diff --git a/clover/tools/jmeter/jmeter-master/grpc/jmeter_pb2_grpc.py b/clover/tools/jmeter/jmeter-master/grpc/jmeter_pb2_grpc.py new file mode 100644 index 0000000..4df110d --- /dev/null +++ b/clover/tools/jmeter/jmeter-master/grpc/jmeter_pb2_grpc.py @@ -0,0 +1,80 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +import grpc + +import jmeter_pb2 as jmeter__pb2 + + +class ControllerStub(object): + """The controller service definition. + """ + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.GenTest = channel.unary_unary( + '/jmeter.Controller/GenTest', + request_serializer=jmeter__pb2.ConfigJmeter.SerializeToString, + response_deserializer=jmeter__pb2.JmeterReply.FromString, + ) + self.StartTest = channel.unary_unary( + '/jmeter.Controller/StartTest', + request_serializer=jmeter__pb2.TestParams.SerializeToString, + response_deserializer=jmeter__pb2.JmeterReply.FromString, + ) + self.GetResults = channel.unary_unary( + '/jmeter.Controller/GetResults', + request_serializer=jmeter__pb2.JResults.SerializeToString, + response_deserializer=jmeter__pb2.JmeterReply.FromString, + ) + + +class ControllerServicer(object): + """The controller service definition. + """ + + def GenTest(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def StartTest(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def GetResults(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_ControllerServicer_to_server(servicer, server): + rpc_method_handlers = { + 'GenTest': grpc.unary_unary_rpc_method_handler( + servicer.GenTest, + request_deserializer=jmeter__pb2.ConfigJmeter.FromString, + response_serializer=jmeter__pb2.JmeterReply.SerializeToString, + ), + 'StartTest': grpc.unary_unary_rpc_method_handler( + servicer.StartTest, + request_deserializer=jmeter__pb2.TestParams.FromString, + response_serializer=jmeter__pb2.JmeterReply.SerializeToString, + ), + 'GetResults': grpc.unary_unary_rpc_method_handler( + servicer.GetResults, + request_deserializer=jmeter__pb2.JResults.FromString, + response_serializer=jmeter__pb2.JmeterReply.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'jmeter.Controller', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) diff --git a/clover/tools/jmeter/jmeter-master/grpc/jmeter_server.py b/clover/tools/jmeter/jmeter-master/grpc/jmeter_server.py new file mode 100644 index 0000000..cef180c --- /dev/null +++ b/clover/tools/jmeter/jmeter-master/grpc/jmeter_server.py @@ -0,0 +1,118 @@ +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +from concurrent import futures +from jinja2 import Template +from urlparse import urlparse +import time +import sys +import grpc +import subprocess +import pickle +import logging +import jmeter_pb2 +import jmeter_pb2_grpc + + +_ONE_DAY_IN_SECONDS = 60 * 60 * 24 +GRPC_PORT = '[::]:50054' + + +class Controller(jmeter_pb2_grpc.ControllerServicer): + + def __init__(self, init_jmeter): + logging.basicConfig(filename='jmeter_server.log', + level=logging.DEBUG) + self.jmeter = 0 + if init_jmeter == 'init': + print('init test') + + def GenTest(self, r, context): + try: + out_file = 'tests/test.jmx' + template_file = 'tests/jmx.template' + unames = pickle.loads(r.url_names) + umethods = pickle.loads(r.url_methods) + ulist = [] + for url in pickle.loads(r.url_list): + u = urlparse(url) + d = {} + d['domain'] = u.hostname + if u.port: + d['port'] = u.port + else: + d['port'] = 80 + if u.path == '': + d['path'] = '/' + else: + d['path'] = u.path + ulist.append(d) + + with open(template_file) as f: + tmpl = Template(f.read()) + output = tmpl.render( + num_threads=r.num_threads, + url_names=unames, + url_methods=umethods, + ramp_time=r.ramp_time, + loops=r.loops, + url_list=ulist + ) + with open(out_file, "wb") as fh: + fh.write(output) + msg = 'Generated test plan' + except Exception as e: + logging.debug(e) + msg = "Failed to generate test plan" + return jmeter_pb2.JmeterReply(message=msg) + + def StartTest(self, r, context): + try: + if r.num_slaves == '0': + self.jmeter = subprocess.Popen( + ["jmeter", "-n", "-t", "tests/test.jmx", "-l", + "default.jtl"], shell=False) + else: + slave_arg = "-R" + r.slave_ips + self.jmeter = subprocess.Popen( + ["jmeter", "-n", "-t", "tests/test.jmx", slave_arg, "-l", + "default.jtl"], shell=False) + msg = "Started jmeter on pid: {}".format(self.jmeter.pid) + except Exception as e: + logging.debug(e) + msg = e + return jmeter_pb2.JmeterReply(message=msg) + + def GetResults(self, r, context): + try: + if r.r_file == 'log': + r_file = 'jmeter.log' + else: + r_file = 'default.jtl' + f = open(r_file, 'r') + msg = "Retrieved all results\n" + f.read() + except Exception as e: + logging.debug(e) + msg = "Failed to retrieve results" + return jmeter_pb2.JmeterReply(message=msg) + + +def serve(init_jmeter): + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + jmeter_pb2_grpc.add_ControllerServicer_to_server( + Controller(init_jmeter), server) + server.add_insecure_port(GRPC_PORT) + server.start() + try: + while True: + time.sleep(_ONE_DAY_IN_SECONDS) + except KeyboardInterrupt: + server.stop(0) + + +if __name__ == '__main__': + serve(sys.argv[1]) diff --git a/clover/tools/jmeter/jmeter-master/process/grpc_process.sh b/clover/tools/jmeter/jmeter-master/process/grpc_process.sh new file mode 100755 index 0000000..0f14d7c --- /dev/null +++ b/clover/tools/jmeter/jmeter-master/process/grpc_process.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# + +python grpc/jmeter_server.py test1 diff --git a/clover/tools/jmeter/jmeter-master/tests/jmx.template b/clover/tools/jmeter/jmeter-master/tests/jmx.template new file mode 100644 index 0000000..1a6fa95 --- /dev/null +++ b/clover/tools/jmeter/jmeter-master/tests/jmx.template @@ -0,0 +1,135 @@ +<?xml version="1.0" encoding="UTF-8"?> +<jmeterTestPlan version="1.2" properties="4.0" jmeter="4.0 r1823414"> + <hashTree> + <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true"> + <stringProp name="TestPlan.comments"></stringProp> + <boolProp name="TestPlan.functional_mode">false</boolProp> + <boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp> + <boolProp name="TestPlan.serialize_threadgroups">false</boolProp> + <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="TestPlan.user_define_classpath"></stringProp> + </TestPlan> + <hashTree> + <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="ThreadGroup" enabled="true"> + <stringProp name="ThreadGroup.on_sample_error">continue</stringProp> + <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="ThreadGroup" enabled="true"> + <boolProp name="LoopController.continue_forever">false</boolProp> + <intProp name="LoopController.loops">{{ loops }}</intProp> + </elementProp> + <stringProp name="ThreadGroup.num_threads">{{ num_threads }}</stringProp> + <stringProp name="ThreadGroup.ramp_time">{{ ramp_time }}</stringProp> + <longProp name="ThreadGroup.start_time">1385457190000</longProp> + <longProp name="ThreadGroup.end_time">1385457190000</longProp> + <boolProp name="ThreadGroup.scheduler">true</boolProp> + <stringProp name="ThreadGroup.duration">60</stringProp> + <stringProp name="ThreadGroup.delay"/> + <boolProp name="ThreadGroup.delayedStart">true</boolProp> + </ThreadGroup> + <hashTree> + {%- for u in url_list %} + <HTTPSampler guiclass="HttpTestSampleGui" testclass="HTTPSampler" testname="{{ url_names[loop.index0] }}" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="{{ url_names[loop.index0] }}" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain">{{ u['domain'] }}</stringProp> + <stringProp name="HTTPSampler.port">{{ u['port'] }}</stringProp> + <stringProp name="HTTPSampler.connect_timeout"/> + <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.protocol">http</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">{{ u['path'] }}</stringProp> + <stringProp name="HTTPSampler.method">{{ url_methods[loop.index0] }}</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="HTTPSampler.implementation"/> + </HTTPSampler> + <hashTree/> + {%- endfor %} + + + </hashTree> + <ResultCollector guiclass="SummaryReport" testclass="ResultCollector" testname="Summary Report" enabled="true"> + <boolProp name="ResultCollector.error_logging">false</boolProp> + <objProp> + <name>saveConfig</name> + <value class="SampleSaveConfiguration"> + <time>true</time> + <latency>true</latency> + <timestamp>true</timestamp> + <success>true</success> + <label>true</label> + <code>true</code> + <message>true</message> + <threadName>true</threadName> + <dataType>true</dataType> + <encoding>false</encoding> + <assertions>true</assertions> + <subresults>true</subresults> + <responseData>false</responseData> + <samplerData>false</samplerData> + <xml>false</xml> + <fieldNames>true</fieldNames> + <responseHeaders>false</responseHeaders> + <requestHeaders>false</requestHeaders> + <responseDataOnError>false</responseDataOnError> + <saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage> + <assertionsResultsToSave>0</assertionsResultsToSave> + <bytes>true</bytes> + <sentBytes>true</sentBytes> + <threadCounts>true</threadCounts> + <idleTime>true</idleTime> + <connectTime>true</connectTime> + </value> + </objProp> + <stringProp name="filename">/jmeter/bin/results2</stringProp> + </ResultCollector> + <hashTree/> + + + <ResultCollector guiclass="StatVisualizer" testclass="ResultCollector" testname="Aggregate Report" enabled="true"> + <boolProp name="ResultCollector.error_logging">false</boolProp> + <objProp> + <name>saveConfig</name> + <value class="SampleSaveConfiguration"> + <time>true</time> + <latency>true</latency> + <timestamp>true</timestamp> + <success>true</success> + <label>true</label> + <code>true</code> + <message>true</message> + <threadName>true</threadName> + <dataType>true</dataType> + <encoding>false</encoding> + <assertions>true</assertions> + <subresults>true</subresults> + <responseData>false</responseData> + <samplerData>false</samplerData> + <xml>false</xml> + <fieldNames>true</fieldNames> + <responseHeaders>false</responseHeaders> + <requestHeaders>false</requestHeaders> + <responseDataOnError>false</responseDataOnError> + <saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage> + <assertionsResultsToSave>0</assertionsResultsToSave> + <bytes>true</bytes> + <sentBytes>true</sentBytes> + <threadCounts>true</threadCounts> + <idleTime>true</idleTime> + <connectTime>true</connectTime> + </value> + </objProp> + <stringProp name="filename">/jmeter/bin/results_aggregate</stringProp> + </ResultCollector> + <hashTree/> + + + </hashTree> + </hashTree> +</jmeterTestPlan> diff --git a/clover/tools/jmeter/jmeter-slave/Dockerfile b/clover/tools/jmeter/jmeter-slave/Dockerfile new file mode 100644 index 0000000..b5ccbcd --- /dev/null +++ b/clover/tools/jmeter/jmeter-slave/Dockerfile @@ -0,0 +1,27 @@ +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +FROM java:8 + +RUN wget http://apache.mirrors.hoobly.com//jmeter/binaries/apache-jmeter-4.0.tgz +RUN tar -xvzf apache-jmeter-4.0.tgz +RUN rm apache-jmeter-4.0.tgz +RUN mv apache-jmeter-4.0 /jmeter +ENV JMETER_HOME /jmeter +ENV PATH $JMETER_HOME/bin:$PATH +RUN mkdir /share +COPY rmi_keystore.jks $JMETER_HOME/bin + +WORKDIR $JMETER_HOME +# Ports to be exposed from the container for JMeter Master +RUN mkdir scripts + +EXPOSE 1099 + +WORKDIR /jmeter/bin + +CMD ./jmeter-server diff --git a/clover/tools/jmeter/rmi_keystore.jks b/clover/tools/jmeter/rmi_keystore.jks Binary files differnew file mode 100644 index 0000000..f503361 --- /dev/null +++ b/clover/tools/jmeter/rmi_keystore.jks diff --git a/clover/tools/jmeter/yaml/manifest.template b/clover/tools/jmeter/yaml/manifest.template new file mode 100644 index 0000000..01a4595 --- /dev/null +++ b/clover/tools/jmeter/yaml/manifest.template @@ -0,0 +1,45 @@ +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: {{ deploy_name }} + labels: + app: {{ deploy_name }} +spec: +{%- if replica_count %} + replicas: 3{% endif %} + template: + metadata: + labels: + app: {{ deploy_name }} + spec: + containers: + - name: {{ deploy_name }} + image: {{ image_path }}/{{ image_name }}:{{ image_tag }} + ports: +{%- if grpc_port %} + - containerPort: {{ grpc_port }}{% endif %} + - containerPort: {{ rmi_port }} + - containerPort: {{ http_port }} + - containerPort: {{ ssl_port }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ deploy_name }} + labels: + app: {{ deploy_name }} +spec: + ports: +{%- if grpc_port %} + - port: {{ grpc_port }} + name: grpc{% endif %} + - port: {{ rmi_port }} + name: rmi + - port: {{ http_port }} + name: http + - port: {{ ssl_port }} + name: https + selector: + app: {{ deploy_name }} +--- diff --git a/clover/tools/jmeter/yaml/render_master.py b/clover/tools/jmeter/yaml/render_master.py new file mode 100644 index 0000000..884ee81 --- /dev/null +++ b/clover/tools/jmeter/yaml/render_master.py @@ -0,0 +1,67 @@ +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +import argparse + +from jinja2 import Template + + +def render_yaml(args): + template_file = 'manifest.template' + out_file = args['deploy_name'] + '.yaml' + + try: + with open(template_file) as f: + tmpl = Template(f.read()) + output = tmpl.render( + image_path=args['image_path'], + image_name=args['image_name'], + image_tag=args['image_tag'], + deploy_name=args['deploy_name'], + grpc_port=args['grpc_port'], + ssl_port=args['ssl_port'], + rmi_port=args['rmi_port'], + http_port=args['http_port'] + ) + with open(out_file, "wb") as fh: + fh.write(output) + return "Generated manifest for {}".format(args['deploy_name']) + except Exception as e: + print(e) + return "Unable to generate manifest for {}".format( + args['deploy_name']) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument( + '--image_name', default='clover-jmeter-master', + help='The image name to use') + parser.add_argument( + '--image_path', default='localhost:5000', + help='The path to the image to use') + parser.add_argument( + '--image_tag', default='latest', + help='The image tag to use') + parser.add_argument( + '--deploy_name', default='clover-jmeter-master', + help='The k8s deploy name to use') + parser.add_argument( + '--rmi_port', default='1099', + help='The master-slave remote method invocation port') + parser.add_argument( + '--http_port', default='80', + help='HTTP data-plane traffic') + parser.add_argument( + '--grpc_port', default='50054', + help='The GRPC server port for management') + parser.add_argument( + '--ssl_port', default='443', + help='HTTPS data-plane traffic') + + args = parser.parse_args() + print(render_yaml(vars(args))) diff --git a/clover/tools/jmeter/yaml/render_slave.py b/clover/tools/jmeter/yaml/render_slave.py new file mode 100644 index 0000000..cd9fd91 --- /dev/null +++ b/clover/tools/jmeter/yaml/render_slave.py @@ -0,0 +1,67 @@ +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +import argparse + +from jinja2 import Template + + +def render_yaml(args): + template_file = 'manifest.template' + out_file = args['deploy_name'] + '.yaml' + + try: + with open(template_file) as f: + tmpl = Template(f.read()) + output = tmpl.render( + image_path=args['image_path'], + image_name=args['image_name'], + image_tag=args['image_tag'], + deploy_name=args['deploy_name'], + replica_count=args['replica_count'], + ssl_port=args['ssl_port'], + rmi_port=args['rmi_port'], + http_port=args['http_port'] + ) + with open(out_file, "wb") as fh: + fh.write(output) + return "Generated manifest for {}".format(args['deploy_name']) + except Exception as e: + print(e) + return "Unable to generate manifest for {}".format( + args['deploy_name']) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument( + '--image_name', default='clover-jmeter-slave', + help='The image name to use') + parser.add_argument( + '--image_path', default='localhost:5000', + help='The path to the image to use') + parser.add_argument( + '--image_tag', default='latest', + help='The image tag to use') + parser.add_argument( + '--deploy_name', default='clover-jmeter-slave', + help='The k8s deploy name to use') + parser.add_argument( + '--rmi_port', default='1099', + help='The master-slave remote method invocation port') + parser.add_argument( + '--http_port', default='80', + help='HTTP data-plane traffic') + parser.add_argument( + '--replica_count', default='3', + help='Number of replicas in slave deployment') + parser.add_argument( + '--ssl_port', default='443', + help='HTTPS data-plane traffic') + + args = parser.parse_args() + print(render_yaml(vars(args))) |