| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you 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. |
| """ BVT tests for extension custom actions lifecycle functionalities |
| """ |
| # Import Local Modules |
| from marvin.cloudstackTestCase import cloudstackTestCase |
| from marvin.cloudstackAPI import listManagementServers |
| from marvin.cloudstackException import CloudstackAPIException |
| from marvin.lib.base import (Extension, |
| ExtensionCustomAction) |
| from marvin.lib.common import (get_zone) |
| from marvin.lib.utils import (random_gen) |
| from marvin.sshClient import SshClient |
| from nose.plugins.attrib import attr |
| # Import System modules |
| import logging |
| import random |
| import string |
| import time |
| |
| _multiprocess_shared_ = True |
| |
| class TestExtensions(cloudstackTestCase): |
| |
| @classmethod |
| def setUpClass(cls): |
| testClient = super(TestExtensions, cls).getClsTestClient() |
| cls.apiclient = testClient.getApiClient() |
| cls.services = testClient.getParsedTestDataConfig() |
| |
| # Get Zone |
| cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests()) |
| |
| cls.mgtSvrDetails = cls.config.__dict__["mgtSvr"][0].__dict__ |
| |
| cls._cleanup = [] |
| cls.logger = logging.getLogger('TestExtensions') |
| cls.logger.setLevel(logging.DEBUG) |
| |
| cls.resource_name_suffix = random_gen() |
| cls.extension = Extension.create( |
| cls.apiclient, |
| name=f"ext-{cls.resource_name_suffix}", |
| type='Orchestrator' |
| ) |
| cls._cleanup.append(cls.extension) |
| |
| @classmethod |
| def tearDownClass(cls): |
| super(TestExtensions, cls).tearDownClass() |
| |
| def setUp(self): |
| self.cleanup = [] |
| |
| def tearDown(self): |
| super(TestExtensions, self).tearDown() |
| |
| def popItemFromCleanup(self, item_id): |
| for idx, x in enumerate(self.cleanup): |
| if x.id == item_id: |
| self.cleanup.pop(idx) |
| break |
| |
| @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") |
| def test_01_extension_create_custom_action(self): |
| name = random_gen() |
| details = [{}] |
| details[0]['abc'] = 'xyz' |
| parameters = [{}] |
| parameter0 = { |
| 'name': 'Name', |
| 'type': 'STRING', |
| 'validationformat': 'NONE', |
| 'required': True |
| } |
| parameters[0] = parameter0 |
| self.custom_action = ExtensionCustomAction.create( |
| self.apiclient, |
| extensionid=self.extension.id, |
| name=name, |
| details=details, |
| parameters=parameters |
| ) |
| self.cleanup.append(self.custom_action) |
| self.assertEqual( |
| name, |
| self.custom_action.name, |
| "Check custom action name failed" |
| ) |
| self.assertEqual( |
| self.extension.id, |
| self.custom_action.extensionid, |
| "Check custom action extension ID failed" |
| ) |
| self.assertEqual( |
| 'VirtualMachine', |
| self.custom_action.resourcetype, |
| "Check custom action resorucetype failed" |
| ) |
| self.assertFalse( |
| self.custom_action.enabled, |
| "Check custom action enabled failed" |
| ) |
| self.assertTrue( |
| self.custom_action.allowedroletypes is not None and len(self.custom_action.allowedroletypes) == 1 and self.custom_action.allowedroletypes[0] == 'Admin', |
| "Check custom action allowedroletypes failed" |
| ) |
| self.assertEqual( |
| 3, |
| self.custom_action.timeout, |
| "Check custom action timeout failed" |
| ) |
| action_details = self.custom_action.details.__dict__ |
| for k, v in details[0].items(): |
| self.assertIn(k, action_details, f"Key '{k}' should be present in details") |
| self.assertEqual(v, action_details[k], f"Value for key '{k}' should be '{v}'") |
| self.assertTrue( |
| self.custom_action.parameters is not None and len(self.custom_action.parameters) == 1, |
| "Check custom action parameters count failed" |
| ) |
| parameter_result = self.custom_action.parameters[0].__dict__ |
| for k, v in parameter0.items(): |
| self.assertIn(k, parameter_result, f"Key '{k}' should be present in the parameter") |
| self.assertEqual(v, parameter_result[k], f"Parameter property for key '{k}' should be '{v}'") |
| |
| actions = self.extension.list_custom_actions(self.apiclient) |
| self.assertTrue( |
| actions is not None and len(actions) == 1 and actions[0].id == self.custom_action.id, |
| "Check extension custom actions failed" |
| ) |
| |
| @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") |
| def test_02_extension_create_custom_action_name_fail(self): |
| name = random_gen() |
| self.custom_action = ExtensionCustomAction.create( |
| self.apiclient, |
| extensionid=self.extension.id, |
| name=name |
| ) |
| try: |
| self.custom_action1 = ExtensionCustomAction.create( |
| self.apiclient, |
| extensionid=self.extension.id, |
| name=name |
| ) |
| self.cleanup.append(self.custom_action1) |
| self.fail(f"Same name: {name} custom action created twice") |
| except CloudstackAPIException: pass |
| |
| @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") |
| def test_03_extension_create_custom_action_resourcetype_fail(self): |
| try: |
| self.custom_action = ExtensionCustomAction.create( |
| self.apiclient, |
| extensionid=self.extension.id, |
| name=random_gen(), |
| resourcetype='Host' |
| ) |
| self.cleanup.append(self.custom_action) |
| self.fail(f"Unknown resourcetype: {type} custom action created") |
| except CloudstackAPIException: pass |
| |
| @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") |
| def test_04_extension_update_custom_action(self): |
| details = [{}] |
| details[0]['abc'] = 'xyz' |
| parameters = [{}] |
| parameter0 = { |
| 'name': 'Name', |
| 'type': 'STRING', |
| 'validationformat': 'NONE', |
| 'required': True |
| } |
| parameters[0] = parameter0 |
| self.custom_action = ExtensionCustomAction.create( |
| self.apiclient, |
| extensionid=self.extension.id, |
| name=random_gen(), |
| details=details, |
| parameters=parameters |
| ) |
| self.cleanup.append(self.custom_action) |
| |
| details[0]['bca'] = 'yzx' |
| description = 'Updated test description' |
| parameter1 = { |
| 'name': 'COUNT', |
| 'type': 'NUMBER', |
| 'validationformat': 'NONE', |
| 'required': False, |
| 'valueoptions': '10,100,1000' |
| } |
| parameters.append(parameter1) |
| updated_action = self.custom_action.update( |
| self.apiclient, |
| description=description, |
| enabled=True, |
| details=details, |
| parameters=parameters)['extensioncustomaction'] |
| self.assertTrue( |
| updated_action.enabled, |
| "Check custom action enabled failed" |
| ) |
| self.assertEqual( |
| description, |
| updated_action.description, |
| "Check custom action description failed" |
| ) |
| action_details = updated_action.details.__dict__ |
| for k, v in details[0].items(): |
| self.assertIn(k, action_details, f"Key '{k}' should be present in details") |
| self.assertEqual(v, action_details[k], f"Value for key '{k}' should be '{v}'") |
| self.assertTrue( |
| updated_action.parameters is not None and len(updated_action.parameters) == 2, |
| "Check custom action parameters count failed" |
| ) |
| for idx, param in enumerate(parameters): |
| parameter_result = updated_action.parameters[idx].__dict__ |
| for k, v in param.items(): |
| self.assertIn(k, parameter_result, f"Key '{k}' should be present in the parameter") |
| actual = ','.join(str(x) for x in parameter_result[k]) if k == 'valueoptions' else parameter_result[k] |
| self.assertEqual(v, actual, f"Parameter property for key '{k}' should be '{v}'") |
| |
| @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") |
| def test_05_extension_run_custom_action_invalid_resource_type_fail(self): |
| name = random_gen() |
| self.custom_action = ExtensionCustomAction.create( |
| self.apiclient, |
| extensionid=self.extension.id, |
| name=name, |
| enabled=True |
| ) |
| self.cleanup.append(self.custom_action) |
| try: |
| self.custom_action.run( |
| self.apiclient, |
| resourcetype='Host', |
| resourceid='abcd' |
| ) |
| self.fail(f"Invalid resource custom action: {name} ran successfully") |
| except Exception as e: |
| msg = str(e) |
| if msg.startswith("Job failed") == False or "Internal error running action" not in msg == False: |
| self.fail(f"Unknown exception occurred: {e}") |
| pass |
| |
| @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") |
| def test_06_extension_run_custom_action_fail(self): |
| name = random_gen() |
| self.custom_action = ExtensionCustomAction.create( |
| self.apiclient, |
| extensionid=self.extension.id, |
| name=name, |
| enabled=True |
| ) |
| self.cleanup.append(self.custom_action) |
| try: |
| self.custom_action.run( |
| self.apiclient, |
| resourceid='abcd' |
| ) |
| self.fail(f"Invalid resource custom action: {name} ran successfully") |
| except Exception as e: |
| msg = str(e) |
| if msg.startswith("Job failed") == False or "Invalid action" not in msg == False: |
| self.fail(f"Unknown exception occurred: {e}") |
| pass |