diff options
| author | Monis Khan <mkhan@redhat.com> | 2017-02-16 16:03:01 -0500 | 
|---|---|---|
| committer | Monis Khan <mkhan@redhat.com> | 2017-02-21 17:07:25 -0500 | 
| commit | d20f526eb7adb27abd8d5c41c1e14c0eb22b0736 (patch) | |
| tree | 602535782ce8e5c63ee5a4f1beea743d90785c0c /roles/lib_openshift/src | |
| parent | b718955622da88c875aa5814fd87bcb3f53599f6 (diff) | |
| download | openshift-d20f526eb7adb27abd8d5c41c1e14c0eb22b0736.tar.gz openshift-d20f526eb7adb27abd8d5c41c1e14c0eb22b0736.tar.bz2 openshift-d20f526eb7adb27abd8d5c41c1e14c0eb22b0736.tar.xz openshift-d20f526eb7adb27abd8d5c41c1e14c0eb22b0736.zip | |
Add SDNValidator Module
Signed-off-by: Monis Khan <mkhan@redhat.com>
Diffstat (limited to 'roles/lib_openshift/src')
| -rw-r--r-- | roles/lib_openshift/src/ansible/oc_sdnvalidator.py | 24 | ||||
| -rw-r--r-- | roles/lib_openshift/src/class/oc_sdnvalidator.py | 58 | ||||
| -rw-r--r-- | roles/lib_openshift/src/doc/sdnvalidator | 27 | ||||
| -rw-r--r-- | roles/lib_openshift/src/sources.yml | 10 | ||||
| -rwxr-xr-x | roles/lib_openshift/src/test/unit/oc_sdnvalidator.py | 481 | 
5 files changed, 600 insertions, 0 deletions
| diff --git a/roles/lib_openshift/src/ansible/oc_sdnvalidator.py b/roles/lib_openshift/src/ansible/oc_sdnvalidator.py new file mode 100644 index 000000000..e91417d63 --- /dev/null +++ b/roles/lib_openshift/src/ansible/oc_sdnvalidator.py @@ -0,0 +1,24 @@ +# pylint: skip-file +# flake8: noqa + +def main(): +    ''' +    ansible oc module for validating OpenShift SDN objects +    ''' + +    module = AnsibleModule( +        argument_spec=dict( +            kubeconfig=dict(default='/etc/origin/master/admin.kubeconfig', type='str'), +        ), +        supports_check_mode=False, +    ) + + +    rval = OCSDNValidator.run_ansible(module.params) +    if 'failed' in rval: +        module.fail_json(**rval) + +    module.exit_json(**rval) + +if __name__ == '__main__': +    main() diff --git a/roles/lib_openshift/src/class/oc_sdnvalidator.py b/roles/lib_openshift/src/class/oc_sdnvalidator.py new file mode 100644 index 000000000..da923337b --- /dev/null +++ b/roles/lib_openshift/src/class/oc_sdnvalidator.py @@ -0,0 +1,58 @@ +# pylint: skip-file +# flake8: noqa + +# pylint: disable=too-many-instance-attributes +class OCSDNValidator(OpenShiftCLI): +    ''' Class to wrap the oc command line tools ''' + +    def __init__(self, kubeconfig): +        ''' Constructor for OCSDNValidator ''' +        # namespace has no meaning for SDN validation, hardcode to 'default' +        super(OCSDNValidator, self).__init__('default', kubeconfig) + +    def get(self, kind, invalid_filter): +        ''' return SDN information ''' + +        rval = self._get(kind) +        if rval['returncode'] != 0: +            return False, rval, [] + +        return True, rval, filter(invalid_filter, rval['results'][0]['items']) + +    # pylint: disable=too-many-return-statements +    @staticmethod +    def run_ansible(params): +        ''' run the idempotent ansible code + +            params comes from the ansible portion of this module +        ''' + +        sdnvalidator = OCSDNValidator(params['kubeconfig']) +        all_invalid = {} +        failed = False + +        checks = ( +            ( +                'hostsubnet', +                lambda x: x['metadata']['name'] != x['host'], +                u'hostsubnets where metadata.name != host', +            ), +            ( +                'netnamespace', +                lambda x: x['metadata']['name'] != x['netname'], +                u'netnamespaces where metadata.name != netname', +            ), +        ) + +        for resource, invalid_filter, invalid_msg in checks: +            success, rval, invalid = sdnvalidator.get(resource, invalid_filter) +            if not success: +                return {'failed': True, 'msg': 'Failed to GET {}.'.format(resource), 'state': 'list', 'results': rval} +            if invalid: +                failed = True +                all_invalid[invalid_msg] = invalid + +        if failed: +            return {'failed': True, 'msg': 'All SDN objects are not valid.', 'state': 'list', 'results': all_invalid} + +        return {'msg': 'All SDN objects are valid.'} diff --git a/roles/lib_openshift/src/doc/sdnvalidator b/roles/lib_openshift/src/doc/sdnvalidator new file mode 100644 index 000000000..0b1862ed1 --- /dev/null +++ b/roles/lib_openshift/src/doc/sdnvalidator @@ -0,0 +1,27 @@ +# flake8: noqa +# pylint: skip-file + +DOCUMENTATION = ''' +--- +module: oc_sdnvalidator +short_description: Validate SDN objects +description: +  - Validate SDN objects +options: +  kubeconfig: +    description: +    - The path for the kubeconfig file to use for authentication +    required: false +    default: /etc/origin/master/admin.kubeconfig +    aliases: [] +author: +- "Mo Khan <monis@redhat.com>" +extends_documentation_fragment: [] +''' + +EXAMPLES = ''' +oc_version: +- name: get oc sdnvalidator +  sdnvalidator: +  register: oc_sdnvalidator +''' diff --git a/roles/lib_openshift/src/sources.yml b/roles/lib_openshift/src/sources.yml index fca1f4818..8445c6fac 100644 --- a/roles/lib_openshift/src/sources.yml +++ b/roles/lib_openshift/src/sources.yml @@ -166,3 +166,13 @@ oc_version.py:  - lib/base.py  - class/oc_version.py  - ansible/oc_version.py + +oc_sdnvalidator.py: +- doc/generated +- doc/license +- lib/import.py +- doc/sdnvalidator +- ../../lib_utils/src/class/yedit.py +- lib/base.py +- class/oc_sdnvalidator.py +- ansible/oc_sdnvalidator.py diff --git a/roles/lib_openshift/src/test/unit/oc_sdnvalidator.py b/roles/lib_openshift/src/test/unit/oc_sdnvalidator.py new file mode 100755 index 000000000..49e2aadb2 --- /dev/null +++ b/roles/lib_openshift/src/test/unit/oc_sdnvalidator.py @@ -0,0 +1,481 @@ +#!/usr/bin/env python2 +''' + Unit tests for oc sdnvalidator +''' +# To run +# ./oc_sdnvalidator.py +# +# .... +# ---------------------------------------------------------------------- +# Ran 4 tests in 0.002s +# +# OK + +import os +import sys +import unittest +import mock + +# Removing invalid variable names for tests so that I can +# keep them brief +# pylint: disable=invalid-name,no-name-in-module +# Disable import-error b/c our libraries aren't loaded in jenkins +# pylint: disable=import-error +# place class in our python path +module_path = os.path.join('/'.join(os.path.realpath(__file__).split('/')[:-4]), 'library')  # noqa: E501 +sys.path.insert(0, module_path) +from oc_sdnvalidator import OCSDNValidator  # noqa: E402 + + +class OCSDNValidatorTest(unittest.TestCase): +    ''' +     Test class for OCSDNValidator +    ''' + +    @mock.patch('oc_sdnvalidator.Utils.create_tmpfile_copy') +    @mock.patch('oc_sdnvalidator.OCSDNValidator._run') +    def test_no_data(self, mock_cmd, mock_tmpfile_copy): +        ''' Testing when both SDN objects are empty ''' + +        # Arrange + +        # run_ansible input parameters +        params = { +            'kubeconfig': '/etc/origin/master/admin.kubeconfig', +        } + +        empty = '''{ +    "apiVersion": "v1", +    "items": [], +    "kind": "List", +    "metadata": {}, +    "resourceVersion": "", +    "selfLink": "" +}''' + +        # Return values of our mocked function call. These get returned once per call. +        mock_cmd.side_effect = [ +            # First call to mock +            (0, empty, ''), + +            # Second call to mock +            (0, empty, ''), +        ] + +        mock_tmpfile_copy.side_effect = [ +            '/tmp/mocked_kubeconfig', +        ] + +        # Act +        results = OCSDNValidator.run_ansible(params) + +        # Assert +        self.assertNotIn('failed', results) +        self.assertEqual(results['msg'], 'All SDN objects are valid.') + +        # Making sure our mock was called as we expected +        mock_cmd.assert_has_calls([ +            mock.call(['oc', '-n', 'default', 'get', 'hostsubnet', '-o', 'json'], None), +            mock.call(['oc', '-n', 'default', 'get', 'netnamespace', '-o', 'json'], None), +        ]) + +    @mock.patch('oc_sdnvalidator.Utils.create_tmpfile_copy') +    @mock.patch('oc_sdnvalidator.OCSDNValidator._run') +    def test_error_code(self, mock_cmd, mock_tmpfile_copy): +        ''' Testing when both we fail to get SDN objects ''' + +        # Arrange + +        # run_ansible input parameters +        params = { +            'kubeconfig': '/etc/origin/master/admin.kubeconfig', +        } + +        # Return values of our mocked function call. These get returned once per call. +        mock_cmd.side_effect = [ +            # First call to mock +            (1, '', 'Error.'), +        ] + +        mock_tmpfile_copy.side_effect = [ +            '/tmp/mocked_kubeconfig', +        ] + +        error_results = { +            'returncode': 1, +            'stderr': 'Error.', +            'stdout': '', +            'cmd': 'oc -n default get hostsubnet -o json', +            'results': [{}] +        } + +        # Act +        results = OCSDNValidator.run_ansible(params) + +        # Assert +        self.assertTrue(results['failed']) +        self.assertEqual(results['msg'], 'Failed to GET hostsubnet.') +        self.assertEqual(results['state'], 'list') +        self.assertEqual(results['results'], error_results) + +        # Making sure our mock was called as we expected +        mock_cmd.assert_has_calls([ +            mock.call(['oc', '-n', 'default', 'get', 'hostsubnet', '-o', 'json'], None), +        ]) + +    @mock.patch('oc_sdnvalidator.Utils.create_tmpfile_copy') +    @mock.patch('oc_sdnvalidator.OCSDNValidator._run') +    def test_valid_both(self, mock_cmd, mock_tmpfile_copy): +        ''' Testing when both SDN objects are valid ''' + +        # Arrange + +        # run_ansible input parameters +        params = { +            'kubeconfig': '/etc/origin/master/admin.kubeconfig', +        } + +        valid_hostsubnet = '''{ +    "apiVersion": "v1", +    "items": [ +        { +            "apiVersion": "v1", +            "host": "bar0", +            "hostIP": "1.1.1.1", +            "kind": "HostSubnet", +            "metadata": { +                "creationTimestamp": "2017-02-16T18:47:09Z", +                "name": "bar0", +                "namespace": "", +                "resourceVersion": "986", +                "selfLink": "/oapi/v1/hostsubnetsbar0", +                "uid": "528dbb41-f478-11e6-aae0-507b9dac97ff" +            }, +            "subnet": "1.1.0.0/24" +        }, +        { +            "apiVersion": "v1", +            "host": "bar1", +            "hostIP": "1.1.1.1", +            "kind": "HostSubnet", +            "metadata": { +                "creationTimestamp": "2017-02-16T18:47:18Z", +                "name": "bar1", +                "namespace": "", +                "resourceVersion": "988", +                "selfLink": "/oapi/v1/hostsubnetsbar1", +                "uid": "57710d84-f478-11e6-aae0-507b9dac97ff" +            }, +            "subnet": "1.1.0.0/24" +        }, +        { +            "apiVersion": "v1", +            "host": "bar2", +            "hostIP": "1.1.1.1", +            "kind": "HostSubnet", +            "metadata": { +                "creationTimestamp": "2017-02-16T18:47:26Z", +                "name": "bar2", +                "namespace": "", +                "resourceVersion": "991", +                "selfLink": "/oapi/v1/hostsubnetsbar2", +                "uid": "5c59a28c-f478-11e6-aae0-507b9dac97ff" +            }, +            "subnet": "1.1.0.0/24" +        } +    ], +    "kind": "List", +    "metadata": {}, +    "resourceVersion": "", +    "selfLink": "" +    }''' + +        valid_netnamespace = '''{ +    "apiVersion": "v1", +    "items": [ +        { +            "apiVersion": "v1", +            "kind": "NetNamespace", +            "metadata": { +                "creationTimestamp": "2017-02-16T18:45:16Z", +                "name": "foo0", +                "namespace": "", +                "resourceVersion": "959", +                "selfLink": "/oapi/v1/netnamespacesfoo0", +                "uid": "0f1c85b2-f478-11e6-aae0-507b9dac97ff" +            }, +            "netid": 100, +            "netname": "foo0" +        }, +        { +            "apiVersion": "v1", +            "kind": "NetNamespace", +            "metadata": { +                "creationTimestamp": "2017-02-16T18:45:26Z", +                "name": "foo1", +                "namespace": "", +                "resourceVersion": "962", +                "selfLink": "/oapi/v1/netnamespacesfoo1", +                "uid": "14effa0d-f478-11e6-aae0-507b9dac97ff" +            }, +            "netid": 100, +            "netname": "foo1" +        }, +        { +            "apiVersion": "v1", +            "kind": "NetNamespace", +            "metadata": { +                "creationTimestamp": "2017-02-16T18:45:36Z", +                "name": "foo2", +                "namespace": "", +                "resourceVersion": "965", +                "selfLink": "/oapi/v1/netnamespacesfoo2", +                "uid": "1aabdf84-f478-11e6-aae0-507b9dac97ff" +            }, +            "netid": 100, +            "netname": "foo2" +        } +    ], +    "kind": "List", +    "metadata": {}, +    "resourceVersion": "", +    "selfLink": "" +    }''' + +        # Return values of our mocked function call. These get returned once per call. +        mock_cmd.side_effect = [ +            # First call to mock +            (0, valid_hostsubnet, ''), + +            # Second call to mock +            (0, valid_netnamespace, ''), +        ] + +        mock_tmpfile_copy.side_effect = [ +            '/tmp/mocked_kubeconfig', +        ] + +        # Act +        results = OCSDNValidator.run_ansible(params) + +        # Assert +        self.assertNotIn('failed', results) +        self.assertEqual(results['msg'], 'All SDN objects are valid.') + +        # Making sure our mock was called as we expected +        mock_cmd.assert_has_calls([ +            mock.call(['oc', '-n', 'default', 'get', 'hostsubnet', '-o', 'json'], None), +            mock.call(['oc', '-n', 'default', 'get', 'netnamespace', '-o', 'json'], None), +        ]) + +    @mock.patch('oc_sdnvalidator.Utils.create_tmpfile_copy') +    @mock.patch('oc_sdnvalidator.OCSDNValidator._run') +    def test_invalid_both(self, mock_cmd, mock_tmpfile_copy): +        ''' Testing when both SDN objects are invalid ''' + +        # Arrange + +        # run_ansible input parameters +        params = { +            'kubeconfig': '/etc/origin/master/admin.kubeconfig', +        } + +        invalid_hostsubnet = '''{ +    "apiVersion": "v1", +    "items": [ +        { +            "apiVersion": "v1", +            "host": "bar0", +            "hostIP": "1.1.1.1", +            "kind": "HostSubnet", +            "metadata": { +                "creationTimestamp": "2017-02-16T18:47:09Z", +                "name": "bar0", +                "namespace": "", +                "resourceVersion": "986", +                "selfLink": "/oapi/v1/hostsubnetsbar0", +                "uid": "528dbb41-f478-11e6-aae0-507b9dac97ff" +            }, +            "subnet": "1.1.0.0/24" +        }, +        { +            "apiVersion": "v1", +            "host": "bar1", +            "hostIP": "1.1.1.1", +            "kind": "HostSubnet", +            "metadata": { +                "creationTimestamp": "2017-02-16T18:47:18Z", +                "name": "bar1", +                "namespace": "", +                "resourceVersion": "988", +                "selfLink": "/oapi/v1/hostsubnetsbar1", +                "uid": "57710d84-f478-11e6-aae0-507b9dac97ff" +            }, +            "subnet": "1.1.0.0/24" +        }, +        { +            "apiVersion": "v1", +            "host": "bar2", +            "hostIP": "1.1.1.1", +            "kind": "HostSubnet", +            "metadata": { +                "creationTimestamp": "2017-02-16T18:47:26Z", +                "name": "bar2", +                "namespace": "", +                "resourceVersion": "991", +                "selfLink": "/oapi/v1/hostsubnetsbar2", +                "uid": "5c59a28c-f478-11e6-aae0-507b9dac97ff" +            }, +            "subnet": "1.1.0.0/24" +        }, +        { +            "apiVersion": "v1", +            "host": "baz1", +            "hostIP": "1.1.1.1", +            "kind": "HostSubnet", +            "metadata": { +                "creationTimestamp": "2017-02-16T18:47:49Z", +                "name": "baz0", +                "namespace": "", +                "resourceVersion": "996", +                "selfLink": "/oapi/v1/hostsubnetsbaz0", +                "uid": "69f75f87-f478-11e6-aae0-507b9dac97ff" +            }, +            "subnet": "1.1.0.0/24" +        } +    ], +    "kind": "List", +    "metadata": {}, +    "resourceVersion": "", +    "selfLink": "" +}''' + +        invalid_netnamespace = '''{ +    "apiVersion": "v1", +    "items": [ +        { +            "apiVersion": "v1", +            "kind": "NetNamespace", +            "metadata": { +                "creationTimestamp": "2017-02-16T18:45:52Z", +                "name": "bar0", +                "namespace": "", +                "resourceVersion": "969", +                "selfLink": "/oapi/v1/netnamespacesbar0", +                "uid": "245d416e-f478-11e6-aae0-507b9dac97ff" +            }, +            "netid": 100, +            "netname": "bar1" +        }, +        { +            "apiVersion": "v1", +            "kind": "NetNamespace", +            "metadata": { +                "creationTimestamp": "2017-02-16T18:45:16Z", +                "name": "foo0", +                "namespace": "", +                "resourceVersion": "959", +                "selfLink": "/oapi/v1/netnamespacesfoo0", +                "uid": "0f1c85b2-f478-11e6-aae0-507b9dac97ff" +            }, +            "netid": 100, +            "netname": "foo0" +        }, +        { +            "apiVersion": "v1", +            "kind": "NetNamespace", +            "metadata": { +                "creationTimestamp": "2017-02-16T18:45:26Z", +                "name": "foo1", +                "namespace": "", +                "resourceVersion": "962", +                "selfLink": "/oapi/v1/netnamespacesfoo1", +                "uid": "14effa0d-f478-11e6-aae0-507b9dac97ff" +            }, +            "netid": 100, +            "netname": "foo1" +        }, +        { +            "apiVersion": "v1", +            "kind": "NetNamespace", +            "metadata": { +                "creationTimestamp": "2017-02-16T18:45:36Z", +                "name": "foo2", +                "namespace": "", +                "resourceVersion": "965", +                "selfLink": "/oapi/v1/netnamespacesfoo2", +                "uid": "1aabdf84-f478-11e6-aae0-507b9dac97ff" +            }, +            "netid": 100, +            "netname": "foo2" +        } +    ], +    "kind": "List", +    "metadata": {}, +    "resourceVersion": "", +    "selfLink": "" +}''' + +        invalid_results = { +            'hostsubnets where metadata.name != host': [{ +                'apiVersion': 'v1', +                'host': 'baz1', +                'hostIP': '1.1.1.1', +                'kind': 'HostSubnet', +                'metadata': { +                    'creationTimestamp': '2017-02-16T18:47:49Z', +                    'name': 'baz0', +                    'namespace': '', +                    'resourceVersion': '996', +                    'selfLink': '/oapi/v1/hostsubnetsbaz0', +                    'uid': '69f75f87-f478-11e6-aae0-507b9dac97ff' +                }, +                'subnet': '1.1.0.0/24' +            }], +            'netnamespaces where metadata.name != netname': [{ +                'apiVersion': 'v1', +                'kind': 'NetNamespace', +                'metadata': { +                    'creationTimestamp': '2017-02-16T18:45:52Z', +                    'name': 'bar0', +                    'namespace': '', +                    'resourceVersion': '969', +                    'selfLink': '/oapi/v1/netnamespacesbar0', +                    'uid': '245d416e-f478-11e6-aae0-507b9dac97ff' +                }, +                'netid': 100, +                'netname': 'bar1' +            }], +        } + +        # Return values of our mocked function call. These get returned once per call. +        mock_cmd.side_effect = [ +            # First call to mock +            (0, invalid_hostsubnet, ''), + +            # Second call to mock +            (0, invalid_netnamespace, ''), +        ] + +        mock_tmpfile_copy.side_effect = [ +            '/tmp/mocked_kubeconfig', +        ] + +        # Act +        results = OCSDNValidator.run_ansible(params) + +        # Assert +        self.assertTrue(results['failed']) +        self.assertEqual(results['msg'], 'All SDN objects are not valid.') +        self.assertEqual(results['state'], 'list') +        self.assertEqual(results['results'], invalid_results) + +        # Making sure our mock was called as we expected +        mock_cmd.assert_has_calls([ +            mock.call(['oc', '-n', 'default', 'get', 'hostsubnet', '-o', 'json'], None), +            mock.call(['oc', '-n', 'default', 'get', 'netnamespace', '-o', 'json'], None), +        ]) + + +if __name__ == '__main__': +    unittest.main() | 
