Meraki organization module (#38773)

* Initial commit

Query an organization within Meraki. No support is in place for managing
or creating yet

* Change output_level method and make the state parameter required.

* Implemented listing all organizations

- Updated documentation
- Parse results and return all organizations
- Parse results and return specified organization

* Framework for creating an organization

- Documentation example for organization creation
- Framework exists for creating organizations, pending PR 36809
- Created functions for HTTP calls
- Renamed from dashboard.meraki.com to api.meraki.com
- Added required_if for state

* Remove absent state

- Meraki API does not support deleting an organization so absent is removed
- Updated documentation to call it state instead of status

* Small change to documentation

* Support all parameters associated to organization

- Added all parameters needed for all organization actions.
- None of the added ones work at this time.
- Added documentation for clone.

* Integration test for meraki_organization module

* Rename module to meraki for porting to module utility

* Meraki documentation fragment

- Created initial documentation fragment for Meraki modules

* Add meraki module utility to branch. Formerly was on a separate branch.

* CRU support for Meraki organization module

* CRU is supported for Meraki organizations
* There is no DELETE function for organizations in the API
* This code is very messy and needs cleanup
* Create and Update actions don't show status as updated, must fix

* Added Meraki module utility to module utility documentation list

* Added support for organization cloning
* Renamed use_ssl to use_https
* Removed define_method()
* Removed is_org()
* Added is_org_valid() which does all org sanity checks

* Fixes for ansibot
- Changed default of use_proxy from true to false
- Removed some commented out code
- Updated documentation

* Changes for ansibot
- Removed requirement for state parameter. I may readd this.
- Updated formatting

diff --git a/lib/ansible/module_utils/network/meraki/meraki.py b/lib/ansible/module_utils/network/meraki/meraki.py
index 3acd3d1038..395ac7c4b4 100644
--- a/lib/ansible/module_utils/network/meraki/meraki.py
+++ b/lib/ansible/module_utils/network/meraki/meraki.py
@@ -42,7 +42,7 @@ def meraki_argument_spec():
     return dict(auth_key=dict(type='str', no_log=True, fallback=(env_fallback, ['MERAKI_KEY'])),
                 host=dict(type='str', default='api.meraki.com'),
                 name=dict(type='str'),
-                state=dict(type='str', choices=['present', 'absent', 'query'], required=True),
+                state=dict(type='str', choices=['present', 'absent', 'query']),
                 use_proxy=dict(type='bool', default=False),
                 use_https=dict(type='bool', default=True),
                 validate_certs=dict(type='bool', default=True),
diff --git a/lib/ansible/modules/network/meraki/meraki_organization.py b/lib/ansible/modules/network/meraki/meraki_organization.py
index 923d969366..3789be91d6 100644
--- a/lib/ansible/modules/network/meraki/meraki_organization.py
+++ b/lib/ansible/modules/network/meraki/meraki_organization.py
@@ -20,11 +20,9 @@ short_description: Manage organizations in the Meraki cloud
 version_added: "2.6"
 description:
 - Allows for creation, management, and visibility into organizations within Meraki
-
 notes:
 - More information about the Meraki API can be found at U(https://dashboard.meraki.com/api_docs).
 - Some of the options are likely only used for developers within Meraki
-
 options:
     name:
         description:
@@ -32,21 +30,18 @@ options:
         - If C(clone) is specified, C(name) is the name of the new organization.
     state:
         description:
-        - Create or query organizations
-        choices: ['query', 'present']
+        - Create or modify an organization
+        choices: ['present', 'query']
     clone:
         description:
         - Organization to clone to a new organization.
-        type: string
     org_name:
         description:
         - Name of organization.
         - Used when C(name) should refer to another object.
-        type: string
     org_id:
         description:
         - ID of organization
-
 author:
     - Kevin Breit (@kbreit)
 extends_documentation_fragment: meraki
@@ -86,7 +81,6 @@ RETURN = '''
 response:
     description: Data returned from Meraki dashboard.
     type: dict
-    state: query
     returned: info
 '''

@@ -103,6 +97,7 @@ def main():
     argument_spec = meraki_argument_spec()
     argument_spec.update(clone=dict(type='str'),
+                         state=dict(type='str', choices=['present', 'query']),
                          )

@@ -125,11 +120,9 @@ def main():

     meraki.function = 'organizations'
     meraki.params['follow_redirects'] = 'all'
-    meraki.required_if=[
-                           ['state', 'present', ['name']],
-                           ['clone', ['name']],
-                           # ['vpn_PublicIP', ['name']],
-                       ]
+    meraki.required_if = [['state', 'present', ['name']],
+                          ['clone', ['name']],
+                          ]

     create_urls = {'organizations': '/organizations',
                    }
@@ -162,23 +155,16 @@ def main():

-
-    # method = None
-    # org_id = None
-
-
-    # meraki.fail_json(msg=meraki.is_org_valid(meraki.get_orgs(), org_name='AnsibleTestOrg'))
-
     if meraki.params['state'] == 'query':
-      if meraki.params['name'] is None:  # Query all organizations, no matter what
-        orgs = meraki.get_orgs()
-        meraki.result['organization'] = orgs
-      elif meraki.params['name'] is not None:  # Query by organization name
-        module.warn('All matching organizations will be returned, even if there are duplicate named organizations')
-        orgs = meraki.get_orgs()
-        for o in orgs:
-          if o['name'] == meraki.params['name']:
-            meraki.result['organization'] = o
+        if meraki.params['name'] is None:  # Query all organizations, no matter what
+            orgs = meraki.get_orgs()
+            meraki.result['organization'] = orgs
+        elif meraki.params['name'] is not None:  # Query by organization name
+            module.warn('All matching organizations will be returned, even if there are duplicate named organizations')
+            orgs = meraki.get_orgs()
+            for o in orgs:
+                if o['name'] == meraki.params['name']:
+                    meraki.result['organization'] = o
     elif meraki.params['state'] == 'present':
         if meraki.params['clone'] is not None:  # Cloning
             payload = {'name': meraki.params['name']}
@@ -193,7 +179,10 @@ def main():
             payload = {'name': meraki.params['name'],
                        'id': meraki.params['org_id'],
                        }
-            meraki.result['response'] = json.loads(meraki.request(meraki.construct_path('update', org_id=meraki.params['org_id']), payload=json.dumps(payload), method='PUT'))
+            meraki.result['response'] = json.loads(meraki.request(meraki.construct_path('update',
+                                                                                        org_id=meraki.params['org_id']),
+                                                                  payload=json.dumps(payload),
+                                                                  method='PUT'))

diff --git a/lib/ansible/utils/module_docs_fragments/meraki.py b/lib/ansible/utils/module_docs_fragments/meraki.py
index e268d02e68..3569d83b99 100644
--- a/lib/ansible/utils/module_docs_fragments/meraki.py
+++ b/lib/ansible/utils/module_docs_fragments/meraki.py
@@ -35,6 +35,7 @@ options:
         description:
         - Set amount of debug output during module execution
         choices: ['normal', 'debug']
+        default: 'normal'
     timeout:
         description:
         - Time to timeout for HTTP requests.
diff --git a/test/integration/targets/meraki_organization/aliases b/test/integration/targets/meraki_organization/aliases
new file mode 100644
index 0000000000..ad7ccf7ada
--- /dev/null
+++ b/test/integration/targets/meraki_organization/aliases
@@ -0,0 +1 @@
+unsupported

* Formatting fix

* Minor updates due to testing
- Made state required again
- Improved formatting for happier PEP8
- request() now sets instance method

* Fix reporting of the result

* Enhance idempotency checks
- Remove merging functionality as the proposed should be used
- Do check and reverse check to look for differences

* Rewrote and added additional integration tests. This isn't done.

* Updated is_update_required method:
- Original and proposed data is passed to method
- Added ignored_keys list so it can be skipped if needed

* Changes per comments from dag
- Optionally assign function on class instantiation
- URLs now have {} for substitution method
- Move auth_key check to module utility
- Remove is_new and get_existing
- Minor changes to documentation

* Enhancements for future modules and organization
- Rewrote construct_path method for simplicity
- Increased support for network functionality to be committed

* Changes based on Dag feedback and to debug problems

* Minor fixes for validitation testing

* Small changes for dag and Ansibot
- Changed how auth_key is processed
- Removed some commented lines
- Updated documentation fragment, but that may get reverted

* Remove blank line and comment

* Improvements for testing and code simplification
- Added network integration tests
- Modified error handling in request()
	- More testing to come on this
- Rewrote construct_path again. Very simple now.

* Remove trailing whitespace

* Small changes based on dag's response

* Removed certain sections from exit_json and fail_json as they're old
This commit is contained in:
Kevin Breit 2018-05-03 17:01:05 -05:00 committed by Dag Wieers
parent c2d7347819
commit c8d287fece
9 changed files with 588 additions and 0 deletions

View file

@ -50,6 +50,7 @@ The following is a list of module_utils files and a general description. The mod
- network/iosxr/iosxr.py - Definitions and helper functions for modules that manage Cisco IOS-XR networking devices.
- network/ironware/ironware.py - Module support utilities for managing Brocade IronWare devices.
- network/junos/junos.py - Definitions and helper functions for modules that manage Junos networking devices.
- network/meraki/meraki.py - Utilities specifically for the Meraki network modules.
- network/netscaler/netscaler.py - Utilities specifically for the netscaler network modules.
- network/nso/nso.py - Utilities for modules that work with Cisco NSO.
- network/nxos/nxos.py - Contains definitions and helper functions specific to Cisco NXOS networking devices.

View file

@ -0,0 +1,263 @@
# -*- coding: utf-8 -*-
# This code is part of Ansible, but is an independent component
# This particular file snippet, and this file snippet only, is BSD licensed.
# Modules you write using this snippet, which is embedded dynamically by Ansible
# still belong to the author of the module, and may assign their own license
# to the complete work.
# Copyright: (c) 2018, Kevin Breit <kevin.breit@kevinbreit.net>
# All rights reserved.
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import os
from ansible.module_utils.basic import AnsibleModule, json, env_fallback
from ansible.module_utils.urls import fetch_url
from ansible.module_utils._text import to_native, to_bytes, to_text
def meraki_argument_spec():
return dict(auth_key=dict(type='str', no_log=True, fallback=(env_fallback, ['MERAKI_KEY'])),
host=dict(type='str', default='api.meraki.com'),
use_proxy=dict(type='bool', default=False),
use_https=dict(type='bool', default=True),
validate_certs=dict(type='bool', default=True),
output_level=dict(type='str', default='normal', choices=['normal', 'debug']),
timeout=dict(type='int', default=30),
org_name=dict(type='str', aliases=['organization']),
org_id=dict(type='str'),
)
class MerakiModule(object):
def __init__(self, module, function=None):
self.module = module
self.params = module.params
self.result = dict(changed=False)
self.headers = dict()
self.function = function
# normal output
self.existing = None
# info output
self.config = dict()
self.original = None
self.proposed = dict()
self.merged = None
# debug output
self.filter_string = ''
self.method = None
self.path = None
self.response = None
self.status = None
self.url = None
# If URLs need to be modified or added for specific purposes, use .update() on the url_catalog dictionary
self.get_urls = {'organizations': '/organizations',
'networks': '/organizations/{org_id}/networks',
'admins': '/organizations/{org_id}/admins',
'configTemplates': '/organizations/{org_id}/configTemplates',
'samlRoles': '/organizations/{org_id}/samlRoles',
'ssids': '/networks/{net_id}/ssids',
'groupPolicies': '/networks/{net_id}/groupPolicies',
'staticRoutes': '/networks/{net_id}/staticRoutes',
'vlans': '/networks/{net_id}/vlans',
'devices': '/networks/{net_id}/devices',
}
self.get_one_urls = {'organizations': '/organizations/{org_id}',
'networks': '/networks/{net_id}',
}
# Module should add URLs which are required by the module
self.url_catalog = {'get_all': self.get_urls,
'get_one': self.get_one_urls,
'create': None,
'update': None,
'delete': None,
'misc': None,
}
if self.module._debug or self.params['output_level'] == 'debug':
self.module.warn('Enable debug output because ANSIBLE_DEBUG was set or output_level is set to debug.')
# TODO: This needs to be tested
self.module.required_if = [('state', 'present', ['org_name']),
('state', 'absent', ['org_name']),
]
# self.module.mutually_exclusive = [('org_id', 'org_name'),
# ]
self.modifiable_methods = ['POST', 'PUT', 'DELETE']
self.headers = {'Content-Type': 'application/json',
'X-Cisco-Meraki-API-Key': module.params['auth_key'],
}
def define_protocol(self):
''' Set protocol based on use_https parameters '''
if self.params['use_https'] is True:
self.params['protocol'] = 'https'
else:
self.params['protocol'] = 'http'
def is_update_required(self, original, proposed):
''' Compare original and proposed data to see if an update is needed '''
is_changed = False
ignored_keys = ('id', 'organizationId')
# self.fail_json(msg="Update required check", original=original, proposed=proposed)
for k, v in original.items():
try:
if k not in ignored_keys:
if v != proposed[k]:
is_changed = True
except KeyError:
if v != '':
is_changed = True
for k, v in proposed.items():
try:
if k not in ignored_keys:
if v != original[k]:
is_changed = True
except KeyError:
if v != '':
is_changed = True
return is_changed
def get_orgs(self):
''' Downloads all organizations '''
return json.loads(self.request('/organizations', method='GET'))
def is_org_valid(self, data, org_name=None, org_id=None):
''' Checks whether a specific org exists and is duplicated '''
''' If 0, doesn't exist. 1, exists and not duplicated. >1 duplicated '''
org_count = 0
if org_name is not None:
for o in data:
if o['name'] == org_name:
org_count += 1
if org_id is not None:
for o in data:
if o['id'] == org_id:
org_count += 1
return org_count
def get_org_id(self, org_name):
''' Returns an organization id based on organization name, only if unique
If org_id is specified as parameter, return that instead of a lookup
'''
orgs = self.get_orgs()
if self.params['org_id'] is not None:
if self.is_org_valid(orgs, org_id=self.params['org_id']) is True:
return self.params['org_id']
org_count = self.is_org_valid(orgs, org_name=org_name)
if org_count == 0:
self.fail_json(msg='There are no organizations with the name {org_name}'.format(org_name=org_name))
if org_count > 1:
self.fail_json(msg='There are multiple organizations with the name {org_name}'.format(org_name=org_name))
elif org_count == 1:
for i in orgs:
if org_name == i['name']:
# self.fail_json(msg=i['id'])
return str(i['id'])
def get_net(self, org_name, net_name, data=None):
''' Return network information '''
if not data:
org_id = self.get_org_id(org_name)
path = '/organizations/{org_id}/networks/{net_id}'.format(org_id=org_id, net_id=self.get_net_id(org_name=org_name, net_name=net_name, data=data))
return json.loads(self.request('GET', path))
else:
for n in data:
if n['name'] == net_name:
return n
def get_net_id(self, org_name=None, net_name=None, data=None):
''' Return network id from lookup or existing data '''
if not data:
self.fail_json(msg='Must implement lookup')
for n in data:
if n['name'] == net_name:
return n['id']
self.fail_json(msg='No network found with the name {0}'.format(net_name))
def construct_path(self, action, function=None, org_id=None, net_id=None, org_name=None):
built_path = None
if function is None:
built_path = self.url_catalog[action][self.function]
else:
self.function = function
built_path = self.url_catalog[action][function]
if org_name:
org_id = self.get_org_id(org_name)
built_path = built_path.format(org_id=org_id, net_id=net_id)
return built_path
def request(self, path, method=None, payload=None):
''' Generic HTTP method for Meraki requests '''
self.path = path
self.define_protocol()
if method is not None:
self.method = method
self.url = '{protocol}://{host}/api/v0/{path}'.format(path=self.path.lstrip('/'), **self.params)
resp, info = fetch_url(self.module, self.url,
headers=self.headers,
data=payload,
method=self.method,
timeout=self.params['timeout'],
use_proxy=self.params['use_proxy'],
)
self.response = info['msg']
self.status = info['status']
if self.status >= 300:
self.fail_json(msg='Request failed for {url}: {status} - {msg}'.format(**info))
return to_native(resp.read())
def exit_json(self, **kwargs):
self.result['response'] = self.response
self.result['status'] = self.status
# Return the gory details when we need it
if self.params['output_level'] == 'debug':
self.result['method'] = self.method
self.result['url'] = self.url
self.result.update(**kwargs)
self.module.exit_json(**self.result)
def fail_json(self, msg, **kwargs):
self.result['response'] = self.response
self.result['status'] = self.status
if self.params['output_level'] == 'debug':
if self.url is not None:
self.result['method'] = self.method
self.result['url'] = self.url
self.result.update(**kwargs)
self.module.fail_json(msg=msg, **self.result)

View file

@ -0,0 +1,206 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2018, Kevin Breit (@kbreit) <kevin.breit@kevinbreit.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'
}
DOCUMENTATION = r'''
---
module: meraki_organization
short_description: Manage organizations in the Meraki cloud
version_added: "2.6"
description:
- Allows for creation, management, and visibility into organizations within Meraki.
notes:
- More information about the Meraki API can be found at U(https://dashboard.meraki.com/api_docs).
- Some of the options are likely only used for developers within Meraki.
options:
state:
description:
- Create or modify an organization.
choices: ['present', 'query']
default: present
clone:
description:
- Organization to clone to a new organization.
org_name:
description:
- Name of organization.
- If C(clone) is specified, C(org_name) is the name of the new organization.
aliases: [ name, organization ]
org_id:
description:
- ID of organization.
aliases: [ id ]
author:
- Kevin Breit (@kbreit)
extends_documentation_fragment: meraki
'''
EXAMPLES = r'''
- name: Create a new organization named YourOrg
meraki_organization:
auth_key: abc12345
org_name: YourOrg
state: present
delegate_to: localhost
- name: Query information about all organizations associated to the user
meraki_organization:
auth_key: abc12345
state: query
delegate_to: localhost
- name: Query information about a single organization named YourOrg
meraki_organization:
auth_key: abc12345
org_name: YourOrg
state: query
delegate_to: localhost
- name: Rename an organization to RenamedOrg
meraki_organization:
auth_key: abc12345
org_id: 987654321
org_name: RenamedOrg
state: present
delegate_to: localhost
- name: Clone an organization named Org to a new one called ClonedOrg
meraki_organization:
auth_key: abc12345
clone: Org
org_name: ClonedOrg
state: present
delegate_to: localhost
'''
RETURN = r'''
response:
description: Data returned from Meraki dashboard.
type: dict
returned: info
'''
import os
from ansible.module_utils.basic import AnsibleModule, json, env_fallback
from ansible.module_utils.urls import fetch_url
from ansible.module_utils._text import to_native
from ansible.module_utils.network.meraki.meraki import MerakiModule, meraki_argument_spec
def main():
# define the available arguments/parameters that a user can pass to
# the module
argument_spec = meraki_argument_spec()
argument_spec.update(clone=dict(type='str'),
state=dict(type='str', choices=['present', 'query'], default='present'),
org_name=dict(type='str', aliases=['name', 'organization']),
org_id=dict(type='int', aliases=['id']),
)
# seed the result dict in the object
# we primarily care about changed and state
# change is if this module effectively modified the target
# state will include any data that you want your module to pass back
# for consumption, for example, in a subsequent task
result = dict(
changed=False,
)
# the AnsibleModule object will be our abstraction working with Ansible
# this includes instantiation, a couple of common attr would be the
# args/params passed to the execution, as well as if the module
# supports check mode
module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True,
)
meraki = MerakiModule(module, function='organizations')
meraki.params['follow_redirects'] = 'all'
create_urls = {'organizations': '/organizations',
}
update_urls = {'organizations': '/organizations/{org_id}',
}
clone_urls = {'organizations': '/organizations/{org_id}/clone',
}
meraki.url_catalog['create'] = create_urls
meraki.url_catalog['update'] = update_urls
meraki.url_catalog['clone'] = clone_urls
payload = None
# if the user is working with this module in only check mode we do not
# want to make any changes to the environment, just return the current
# state with no modifications
# FIXME: Work with Meraki so they can implement a check mode
if module.check_mode:
meraki.exit_json(**meraki.result)
# execute checks for argument completeness
# manipulate or modify the state as needed (this is going to be the
# part where your module will do what it needs to do)
orgs = meraki.get_orgs()
if meraki.params['state'] == 'query':
if meraki.params['org_name']: # Query by organization name
module.warn('All matching organizations will be returned, even if there are duplicate named organizations')
for o in orgs:
if o['name'] == meraki.params['org_name']:
meraki.result['data'] = o
elif meraki.params['org_id']:
for o in orgs:
if o['id'] == meraki.params['org_id']:
meraki.result['data'] = o
else: # Query all organizations, no matter what
orgs = meraki.get_orgs()
meraki.result['data'] = orgs
elif meraki.params['state'] == 'present':
if meraki.params['clone']: # Cloning
payload = {'name': meraki.params['org_name']}
meraki.result['data'] = json.loads(
meraki.request(
meraki.construct_path(
'clone',
org_name=meraki.params['clone']
),
payload=json.dumps(payload),
method='POST'))
elif not meraki.params['org_id'] and meraki.params['org_name']: # Create new organization
payload = {'name': meraki.params['org_name']}
meraki.result['data'] = json.loads(
meraki.request(
meraki.construct_path('create'),
method='POST',
payload=json.dumps(payload)))
elif meraki.params['org_id'] and meraki.params['org_name']: # Update an existing organization
payload = {'name': meraki.params['org_name'],
'id': meraki.params['org_id'],
}
meraki.result['data'] = json.loads(
meraki.request(
meraki.construct_path(
'update',
org_id=meraki.params['org_id']
),
method='PUT',
payload=json.dumps(payload)))
# in the event of a successful module execution, you will want to
# simple AnsibleModule.exit_json(), passing the key/value results
meraki.exit_json(**meraki.result)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2018, Kevin Breit (@kbreit) <kevin.breit@kevinbreit.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
class ModuleDocFragment(object):
# Standard files for documentation fragment
DOCUMENTATION = '''
notes:
- More information about the Meraki API can be found at U(https://dashboard.meraki.com/api_docs).
- Some of the options are likely only used for developers within Meraki
options:
auth_key:
description:
- Authentication key provided by the dashboard. Required if environmental variable MERAKI_KEY is not set.
host:
description:
- Hostname for Meraki dashboard
- Only useful for internal Meraki developers
type: string
default: 'api.meraki.com'
use_proxy:
description:
- If C(no), it will not use a proxy, even if one is defined in an environment variable on the target hosts.
type: bool
use_https:
description:
- If C(no), it will use HTTP. Otherwise it will use HTTPS.
- Only useful for internal Meraki developers
type: bool
default: 'yes'
output_level:
description:
- Set amount of debug output during module execution
choices: ['normal', 'debug']
default: 'normal'
timeout:
description:
- Time to timeout for HTTP requests.
type: int
default: 30
validate_certs:
description:
- Whether to validate HTTP certificates.
type: bool
default: 'yes'
org_name:
description:
- Name of organization.
aliases: [ organization ]
org_id:
description:
- ID of organization.
'''

View file

@ -16,6 +16,7 @@ ios
iosxr
ironware
junos
meraki
net
netconf
nxos

View file

@ -0,0 +1 @@
unsupported

View file

@ -0,0 +1,60 @@
# Test code for the Meraki Organization module
# Copyright: (c) 2018, Kevin Breit (@kbreit)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
---
- name: Test an API key is provided
fail:
msg: Please define an API key
when: auth_key is not defined
- name: Create a new organization named IntTestOrg
meraki_organization:
auth_key: '{{ auth_key }}'
org_name: IntTestOrg
state: present
output_level: debug
delegate_to: localhost
register: new_org
- name: List all organizations
meraki_organization:
auth_key: '{{ auth_key }}'
state: query
delegate_to: localhost
register: query_all
- name: Query information about a single organization named IntTestOrg
meraki_organization:
auth_key: '{{ auth_key }}'
org_name: IntTestOrg
state: query
delegate_to: localhost
register: query_org
- name: Query information about IntTestOrg by organization ID
meraki_organization:
auth_key: '{{ auth_key }}'
org_id: '{{ query_org.data.id }}'
state: query
delegate_to: localhost
register: query_org_id
- name: Clone IntTestOrg
meraki_organization:
auth_key: '{{ auth_key }}'
clone: IntTestOrg
org_name: IntTestOrgCloned
state: present
delegate_to: localhost
register: cloned_org
- name: Present assertions
assert:
that:
- new_org.data.id is defined
- '{{ query_all | length}} > 0'
- query_org.data.id is defined
- 'query_org.data.name == "IntTestOrg"'
- cloned_org.data.id is defined
- 'query_org_id.data.id == query_org.data.id'