From 76a8fef4246077155c393b8c396c5c31c43b4ac3 Mon Sep 17 00:00:00 2001 From: Roni Dover Date: Tue, 19 Apr 2016 12:08:28 +0300 Subject: [PATCH 01/10] fixing some driver issues --- src/dockerhostdriver.py | 43 +++++++++++++++++++++++++++++------------ src/drivermetadata.xml | 4 ++-- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/dockerhostdriver.py b/src/dockerhostdriver.py index 0436c16..c21144f 100755 --- a/src/dockerhostdriver.py +++ b/src/dockerhostdriver.py @@ -42,9 +42,7 @@ def destroy(self, context, ports): :return The JSON response and HTTP status code :rtype str """ - vm_info_json =context.remote_endpoints[0].app_context.deployed_app_json - vm_info_obj = json.loads(vm_info_json) - uid = vm_info_obj['vmdetails']['uid'] + uid = self._get_vm_uui(context) log = "" address = context.resource.address response = requests.post('{address}/containers/{uid}/stop'.format(address=address, uid=uid) ) @@ -55,6 +53,12 @@ def destroy(self, context, ports): session.DeleteResource(context.remote_endpoints[0].name) return log + def _get_vm_uui(self, context): + vm_info_json = context.remote_endpoints[0].app_context.deployed_app_json + vm_info_obj = json.loads(vm_info_json) + uid = vm_info_obj['vmdetails']['uid'] + return uid + def deploy_image(self, context, image, env, port_config): """ Deploys a container from an image @@ -77,7 +81,7 @@ def deploy_image(self, context, image, env, port_config): address = context.resource.address response = None try: - response = requests.post('{address}:4000/containers/create'.format(address=address), + response = requests.post('{address}/containers/create'.format(address=address), create_request_data) except Exception as e: @@ -145,12 +149,9 @@ def _get_api_session(self, context): port=context.connectivity.cloudshell_api_port) - def inspect(self, context, ports ): address = context.resource.address - vm_info_json =context.remote_endpoints[0].app_context.deployed_app_json - vm_info_obj = json.loads(vm_info_json) - uid = vm_info_obj['vmdetails']['uid'] + uid = self._get_vm_uui(context) response = requests.get('{address}/containers/{uid}/json'.format(address=address, uid=uid) ) result = response.json() return result @@ -159,13 +160,11 @@ def inspect(self, context, ports ): # the name is by the Qualisystems conventions def show_logs(self, context, ports): address = context.resource.address - vm_info_json =context.remote_endpoints[0].app_context.deployed_app_json - vm_info_obj = json.loads(vm_info_json) - uid = vm_info_obj['vmdetails']['uid'] + uid = self._get_vm_uui(context) response = requests.get('{address}/containers/{uid}/logs?stdout=1'.format(address=address, uid=uid) ) return response.content - def power_on(self, context, vm_uuid, resource_fullname): + def power_on(self, context, vm_uuid): uid = vm_uuid log = "" address = context.resource.address @@ -174,6 +173,26 @@ def power_on(self, context, vm_uuid, resource_fullname): log+=str(response.status_code) + ": " + response.content return log + + def PowerOn(self, context, ports): + """ + Powers off the remote vm + :param models.QualiDriverModels.ResourceRemoteCommandContext context: the context the command runs on + :param list[string] ports: the ports of the connection between the remote resource and the local resource, NOT IN USE!!! + """ + uid = self._get_vm_uui(context) + self.power_on(context,uid) + + + def PowerOff(self, context, ports): + uid = self._get_vm_uui(context) + address = context.resource.address + requests.post('{address}/containers/{uid}/stop'.format(address=address, uid=uid)) + + # the name is by the Qualisystems conventions + def PowerCycle(self, context, ports, delay): + pass + def _wrapInParenthesis(self, value): value = value.strip() if not value.startswith('"'): diff --git a/src/drivermetadata.xml b/src/drivermetadata.xml index 2576996..828aa9f 100755 --- a/src/drivermetadata.xml +++ b/src/drivermetadata.xml @@ -7,7 +7,7 @@ - + @@ -22,7 +22,7 @@ - + From 72ca8ec538cce99da21e4d345fca72d17672fe08 Mon Sep 17 00:00:00 2001 From: Roni Dover Date: Sat, 7 May 2016 22:39:56 +0300 Subject: [PATCH 02/10] updated driver --- scripts/docker_deployment.py | 10 ++++--- src/dockerhostdriver.py | 52 ++++++++++++++++++++++++++++-------- src/drivermetadata.xml | 52 +++++++++++++++++------------------- 3 files changed, 73 insertions(+), 41 deletions(-) diff --git a/scripts/docker_deployment.py b/scripts/docker_deployment.py index 0ce4fc5..d3bd7c6 100644 --- a/scripts/docker_deployment.py +++ b/scripts/docker_deployment.py @@ -1,9 +1,12 @@ -import cloudshell.api.cloudshell_scripts_helpers as helpers +import cloudshell.helpers.scripts.cloudshell_scripts_helpers as helpers from cloudshell.api.cloudshell_api import * import os +import json + reservation_id = helpers.get_reservation_context_details().id service_details = helpers.get_resource_context_details() +dict = helpers.get_resource_context_details_dict() docker_host = service_details.attributes['Docker Host'] image = service_details.attributes['Docker Image'] environment = service_details.attributes['Container Env'] @@ -12,10 +15,11 @@ api = helpers.get_api_session() -api.WriteMessageToReservationOutput(reservation_id,"Starting deploy") +api.WriteMessageToReservationOutput(reservation_id,"Deploying App..." ) result = api.ExecuteCommand(reservationId=reservation_id, targetName=docker_host, targetType="Resource", commandName="deploy_image", - commandInputs=[InputNameValue("image", image),InputNameValue("env", environment), + commandInputs=[InputNameValue("app_name",dict["appData"]["name"]), + InputNameValue("image", image),InputNameValue("env", environment), InputNameValue("port_config", ports)]) api.WriteMessageToReservationOutput(reservation_id,"Deploy done:" + result.Output) diff --git a/src/dockerhostdriver.py b/src/dockerhostdriver.py index c21144f..c0b6adf 100755 --- a/src/dockerhostdriver.py +++ b/src/dockerhostdriver.py @@ -3,7 +3,7 @@ import requests from cloudshell.api.cloudshell_api import CloudShellAPISession, ResourceAttributesUpdateRequest, AttributeNameValue from cloudshell.shell.core.resource_driver_interface import ResourceDriverInterface - +import uuid class DockerHostDriver (ResourceDriverInterface): def __init__(self): @@ -42,28 +42,33 @@ def destroy(self, context, ports): :return The JSON response and HTTP status code :rtype str """ - uid = self._get_vm_uui(context) - log = "" - address = context.resource.address - response = requests.post('{address}/containers/{uid}/stop'.format(address=address, uid=uid) ) - log+=str(response.status_code) + ": " + response.content - response = requests.delete('{address}/containers/{uid}'.format(address=address, uid=uid) ) - log = log + '\n' + str(response.status_code) + ": " + response.content + log = self.destroy_vm_only(context,ports) session = CloudShellAPISession(host=context.connectivity.server_address,token_id=context.connectivity.admin_auth_token,domain='Global') session.DeleteResource(context.remote_endpoints[0].name) return log + + def _get_vm_uui(self, context): vm_info_json = context.remote_endpoints[0].app_context.deployed_app_json vm_info_obj = json.loads(vm_info_json) uid = vm_info_obj['vmdetails']['uid'] return uid - def deploy_image(self, context, image, env, port_config): + def _get_remote_name(self, context): + """ + :type context: cloudshell.shell.core.driver_context.ResourceRemoteCommandContext + + """ + return context.remote_endpoints[0].name + + def deploy_image(self, context, app_name, image, env, port_config): """ Deploys a container from an image :param context: This is the execution context automatically injected by CloudShell when running this command :type context: cloudshell.shell.core.driver_context.ResourceCommandContext + :param app_name: The name of the app being deployed + :type app_name: str :param image: The docker image to create the container from :type image: str :param env: Environment variables to use for the container, provided as comma separated list of name=value @@ -92,10 +97,31 @@ def deploy_image(self, context, image, env, port_config): "response" + response.content) container_id = response.json()["Id"] + app_unique_name = app_name + "_" + str(uuid.uuid4())[0:4] + result = '{ "vm_name" : "%s", "vm_uuid" : "%s", "cloud_provider_resource_name" : "%s"}' % (app_unique_name ,container_id, context.resource.name) - str = '{ "vm_name" : "%s", "vm_uuid" : "%s", "cloud_provider_resource_name" : "%s"}' % (image.replace('/','_').replace(':','_') ,container_id, context.resource.name) + return json.loads(result) + + + + def destroy_vm_only(self, context, ports): + """ + Destroys only the containers (without the associated CloudShell resource) For internal purposes only + :param context: This is the execution context automatically injected by CloudShell when running this command + :type context: cloudshell.shell.core.driver_context.ResourceRemoteCommandContext + :param ports: OBSOLETE - information on the remote ports + :type ports: str + + """ + uid = self._get_vm_uui(context) + log = "" + address = context.resource.address + response = requests.post('{address}/containers/{uid}/stop'.format(address=address, uid=uid)) + log += str(response.status_code) + ": " + response.content + response = requests.delete('{address}/containers/{uid}'.format(address=address, uid=uid)) + log = log + '\n' + str(response.status_code) + ": " + response.content + return log - return json.loads(str) # the name is by the Qualisystems conventions def remote_refresh_ip(self, context, ports): @@ -182,12 +208,16 @@ def PowerOn(self, context, ports): """ uid = self._get_vm_uui(context) self.power_on(context,uid) + self._get_api_session(context).SetResourceLiveStatus( self._get_remote_name(context),'Online') + def PowerOff(self, context, ports): uid = self._get_vm_uui(context) address = context.resource.address requests.post('{address}/containers/{uid}/stop'.format(address=address, uid=uid)) + self._get_api_session(context).SetResourceLiveStatus( self._get_remote_name(context),'Offline') + # the name is by the Qualisystems conventions def PowerCycle(self, context, ports, delay): diff --git a/src/drivermetadata.xml b/src/drivermetadata.xml index 828aa9f..7397d32 100755 --- a/src/drivermetadata.xml +++ b/src/drivermetadata.xml @@ -3,39 +3,37 @@ - - + + + + + - - - - - - + + - - - - - - - - - - - - - - + - - + + + + + + + + + + + + + - - - + + + From 6568c427c45a1c8287e14bcb61be5f2623f3619d Mon Sep 17 00:00:00 2001 From: Roni Dover Date: Wed, 18 May 2016 17:39:30 +0300 Subject: [PATCH 03/10] Create .travis.yml --- .travis.yml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..bfc79f7 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,9 @@ +language: python +python: +- '2.7' +install: +- pip install nose +- pip install coveralls +script: +- nosetests +after_success: coveralls From d9ce5c19034b523e8a23066a823d6de516f5bf11 Mon Sep 17 00:00:00 2001 From: Roni Dover Date: Wed, 18 May 2016 20:18:19 +0300 Subject: [PATCH 04/10] adding some tests --- tests/__init__.py | 0 tests/docker_driver_tests.py | 10 ++++++++++ 2 files changed, 10 insertions(+) create mode 100644 tests/__init__.py create mode 100644 tests/docker_driver_tests.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/docker_driver_tests.py b/tests/docker_driver_tests.py new file mode 100644 index 0000000..2ca76e8 --- /dev/null +++ b/tests/docker_driver_tests.py @@ -0,0 +1,10 @@ +from unittest import TestCase + +from dockerhostdriver import DockerHostDriver + + +class DescribeDockerHostDriver (TestCase): + + def test_it_can_be_initialized(self): + driver = DockerHostDriver() + self.assertIsNone(driver,msg="Can't allow that can we") \ No newline at end of file From f1e8348396236a83d4567a9576362410e9995d5c Mon Sep 17 00:00:00 2001 From: Roni Dover Date: Wed, 18 May 2016 20:18:19 +0300 Subject: [PATCH 05/10] adding some tests --- tests/__init__.py | 0 tests/docker_driver_tests.py | 10 ++++++++++ 2 files changed, 10 insertions(+) create mode 100644 tests/__init__.py create mode 100644 tests/docker_driver_tests.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/docker_driver_tests.py b/tests/docker_driver_tests.py new file mode 100644 index 0000000..2ca76e8 --- /dev/null +++ b/tests/docker_driver_tests.py @@ -0,0 +1,10 @@ +from unittest import TestCase + +from dockerhostdriver import DockerHostDriver + + +class DescribeDockerHostDriver (TestCase): + + def test_it_can_be_initialized(self): + driver = DockerHostDriver() + self.assertIsNone(driver,msg="Can't allow that can we") \ No newline at end of file From 7fd612478d173217394b8d4a297138eb30da3efd Mon Sep 17 00:00:00 2001 From: Roni Dover Date: Wed, 18 May 2016 20:28:12 +0300 Subject: [PATCH 06/10] Update .travis.yml --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index bfc79f7..6327cfb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ python: install: - pip install nose - pip install coveralls +- pip install -r src/requirements.txt script: - nosetests after_success: coveralls From 523f295b3748d26ed78e9f49d121f4bb493b2d2a Mon Sep 17 00:00:00 2001 From: Roni Dover Date: Wed, 18 May 2016 20:28:55 +0300 Subject: [PATCH 07/10] install requirements --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index bfc79f7..6327cfb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ python: install: - pip install nose - pip install coveralls +- pip install -r src/requirements.txt script: - nosetests after_success: coveralls From f236cbada4cd3fb7840ed6c62dba541cb8fe183b Mon Sep 17 00:00:00 2001 From: Roni Dover Date: Wed, 18 May 2016 20:31:13 +0300 Subject: [PATCH 08/10] fixing tests --- tests/docker_driver_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/docker_driver_tests.py b/tests/docker_driver_tests.py index 2ca76e8..a5dc26d 100644 --- a/tests/docker_driver_tests.py +++ b/tests/docker_driver_tests.py @@ -7,4 +7,4 @@ class DescribeDockerHostDriver (TestCase): def test_it_can_be_initialized(self): driver = DockerHostDriver() - self.assertIsNone(driver,msg="Can't allow that can we") \ No newline at end of file + self.assertIsNotNone(driver, "Driver can't be none") \ No newline at end of file From 66ef123936ec3168399ea2165a379f1f952a0183 Mon Sep 17 00:00:00 2001 From: Roni Dover Date: Wed, 18 May 2016 21:02:15 +0300 Subject: [PATCH 09/10] Update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6327cfb..500c7db 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,5 +6,5 @@ install: - pip install coveralls - pip install -r src/requirements.txt script: -- nosetests +- nosetests --include it_ after_success: coveralls From 1e75dfcba1dce32e6283798252c4db9c62f4b8f6 Mon Sep 17 00:00:00 2001 From: Roni Dover Date: Tue, 24 May 2016 16:18:30 +0300 Subject: [PATCH 10/10] tests session --- src/dockerhostdriver.py | 3 +- src/refresh_ip_request.py | 3 ++ tests/docker_driver_tests.py | 61 ++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 src/refresh_ip_request.py create mode 100644 tests/docker_driver_tests.py diff --git a/src/dockerhostdriver.py b/src/dockerhostdriver.py index c0b6adf..f818480 100755 --- a/src/dockerhostdriver.py +++ b/src/dockerhostdriver.py @@ -6,7 +6,7 @@ import uuid class DockerHostDriver (ResourceDriverInterface): - def __init__(self): + def __init__(self, domain_logic): pass # Initialize the driver session, this function is called everytime a new instance of the driver is created @@ -135,6 +135,7 @@ def remote_refresh_ip(self, context, ports): """ json = self.inspect(context,ports) ip = json["Node"]["IP"] + matching_resources = \ self._get_api_session(context).FindResources(attributeValues=[AttributeNameValue("Private IP", ip)])\ .Resources diff --git a/src/refresh_ip_request.py b/src/refresh_ip_request.py new file mode 100644 index 0000000..661319b --- /dev/null +++ b/src/refresh_ip_request.py @@ -0,0 +1,3 @@ +class RefreshIpRequest: + def __init__(self,resource_name): + self.resource_name = resource_name diff --git a/tests/docker_driver_tests.py b/tests/docker_driver_tests.py new file mode 100644 index 0000000..6476b42 --- /dev/null +++ b/tests/docker_driver_tests.py @@ -0,0 +1,61 @@ +from unittest import TestCase +from mock import Mock +from dockerhostdriver import DockerHostDriver +from refresh_ip_request import RefreshIpRequest +from cloudshell.shell.core.context import ResourceCommandContext + + +class CloudShellContextObjectStub(object): + def __init__(self, resource_name): + self.resource = "" + + +class DockerNewDriver(object): + def __init__(self, docker_api_service, cloudshell_service): + self.docker_api_service = docker_api_service + self.cloudshell_service=cloudshell_service + + def refresh_ip(self): + ip = self.docker_api_service.get_container_ip() + self.cloudshell_service.set_resource_address(ip) + + +class StubDockerApiService: + + def get_container_ip(self): + return self.ip_to_return + + +class DescribeRefreshIPOnDockerHostDriver (TestCase): + + + def it_gets_the_ip_from_the_docker_host(self): + docker_api_service = Mock() + cloudshell_service = Mock() + + driver = DockerNewDriver(docker_api_service,cloudshell_service).refresh_ip() + docker_api_service.get_container_ip.assert_called() + + def it_updates_cloudshell_with_the_ip_it_got_from_the_docker_host(self): + docker_api_service = Mock(spec=StubDockerApiService) + docker_api_service.get_container_ip.return_value="192.12.33.22" + print docker_api_service.get_container_ip() + cloudshell_service = Mock() + + DockerNewDriver(docker_api_service,cloudshell_service).refresh_ip() + cloudshell_service.set_resource_address.assert_called_once_with("192.12.33.22") + + + # + # + # + # + # + # + # def it_converts_the_cloudshell_execution_context_to_a_refresh_ip_request(self): + # mock_domain_logic = Mock() + # driver = DockerHostDriver(mock_domain_logic) + # driver.remote_refresh_ip(context=CloudShellContextObjectStub("resource1"), ports="") + # mock_domain_logic.refresh_ip.assert_called_once_with(RefreshIpRequest("resource1")) + # self.assertTrue(False) +