├── .bumpversion.cfg ├── .editorconfig ├── .gitignore ├── .travis.yml ├── CONTRIBUTING.rst ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.rst ├── appd ├── __init__.py ├── cmdline.py ├── model │ ├── __init__.py │ ├── account.py │ ├── action_suppressions.py │ ├── application.py │ ├── audit_history.py │ ├── backend.py │ ├── business_transaction.py │ ├── config_variable.py │ ├── entity_def.py │ ├── event.py │ ├── hourly_license_usage.py │ ├── license_module.py │ ├── license_usage.py │ ├── metric_data.py │ ├── metric_treenode.py │ ├── metric_value.py │ ├── node.py │ ├── policy_violation.py │ ├── set_controller_url.py │ ├── snapshot.py │ └── tier.py ├── request.py └── time.py ├── data ├── backend_metrics.xml ├── bt_list.xml ├── business_transactions.json ├── licenses_by_host.csv ├── licenses_by_host.xlsx ├── metric_tree_0.json ├── metric_tree_1.json ├── metric_tree_2.json ├── nodes.json └── tiers.json ├── docs ├── Makefile ├── api │ └── cmdline.rst ├── conf.py ├── contributing.rst ├── faq.rst ├── history.rst ├── index.rst ├── installation.rst ├── make.bat ├── readme.rst ├── ref.rst └── usage.rst ├── examples ├── audit.py ├── backend_metrics.py ├── bt_metrics.py ├── dependencies.py ├── healthrule_violations.py ├── license_count.py ├── license_count_by_app.py ├── license_count_by_appgroup.py ├── license_count_by_env.py ├── license_list_hosts.py ├── license_report_to_csv.py ├── nodelist.py ├── sample.py ├── usage_chart.py ├── usage_chart_java.py └── xsd │ ├── backend_metrics.xsd │ └── bt_metrics.xsd ├── requirements.txt ├── setup.cfg ├── setup.py ├── templates └── audit.jinja.html └── test ├── __init__.py ├── test.py ├── test_action_suppressions.py ├── test_create_event.py ├── test_create_user.py ├── test_exclude_bts.py ├── test_export_actions.py ├── test_export_analytics_dynamic_service_configs.py ├── test_export_custom_dashboard.py ├── test_export_email_action_templates.py ├── test_export_entry_point_type.py ├── test_export_entry_points.py ├── test_export_health_rules.py ├── test_export_httprequest_action_templates.py ├── test_export_import_action.py ├── test_export_policies.py ├── test_get_audit_history.py ├── test_get_backends.py ├── test_get_events.py ├── test_get_healthrule_violations.py ├── test_info_point_hr.py ├── test_mark_nodes_historical.py ├── test_set_config_value.py ├── test_set_controller_url.py ├── test_set_metric_retention.py ├── test_v1_applications.py ├── test_v2_accounts.py ├── test_v2_license_modules.py └── test_v2_license_usage.py /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | commit = True 3 | tag = True 4 | current_version = 0.4.18 5 | parse = (?P\d+)\.(?P\d+)\.(?P\d+) 6 | serialize = 7 | {major}.{minor}.{patch} 8 | 9 | [bumpversion:file:appd/__init__.py] 10 | 11 | [bumpversion:file:README.rst] 12 | 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | charset = utf-8 11 | end_of_line = lf 12 | 13 | [*.bat] 14 | indent_style = tab 15 | end_of_line = crlf 16 | 17 | [LICENSE] 18 | insert_final_newline = false 19 | 20 | [Makefile] 21 | indent_style = tab 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | dist/ 3 | tmp/ 4 | *.pyc 5 | MANIFEST 6 | .DS_Store 7 | *.egg-info 8 | .idea/ 9 | *.iml 10 | temp.* 11 | .eggs/ 12 | docs/_build/ 13 | .coverage 14 | .tox/ 15 | htmlcov/ 16 | *.old 17 | creds/ 18 | *.svg 19 | bin/ 20 | .ropeproject 21 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - '2.6' 4 | - '2.7' 5 | - '3.3' 6 | - '3.4' 7 | 8 | install: 9 | - pip install -r requirements.txt 10 | - pip install . 11 | 12 | script: nosetests 13 | 14 | deploy: 15 | provider: pypi 16 | user: tradel 17 | password: 18 | secure: "YUWD7Lsggc6lQoaC0mFWElu8Ea80Ej78mC6kTCdzR6O9Hmk+fh02YR2+nq6I4tMCAMILLjj6vHVaeSp6/Zx4GTe0O9XJxl+afuA6LarU6zN18Y6FOvK+9G8JRelESDice56IGZyYCwLXlSgihZ6tHTYXRfVT04i2dR7O5KM0uOE=" 19 | distributions: sdist bdist_wheel 20 | on: 21 | tags: true 22 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | Contributing to AppDynamics Extensions 2 | ====================================== 3 | 4 | If you would like to contribute code you can do so through GitHub by 5 | forking the repository and sending a pull request (on a branch other 6 | than ``master`` or ``gh-pages``). 7 | 8 | When submitting code, please make every effort to follow existing 9 | conventions and style in order to keep the code as consistent as 10 | possible. 11 | 12 | License 13 | ------- 14 | 15 | By contributing your code, you agree to license your contribution under 16 | the terms of the APLv2: 17 | https://github.com/extensions-commons/blob/master/LICENSE 18 | 19 | All files are released with the Apache 2.0 license. 20 | 21 | If you are adding a new file it should include a copyright header:: 22 | 23 | /** 24 | * Copyright 2013 AppDynamics, Inc. 25 | * 26 | * Licensed under the Apache License, Version 2.0 (the "License"); 27 | * you may not use this file except in compliance with the License. 28 | * You may obtain a copy of the License at 29 | * 30 | * http://www.apache.org/licenses/LICENSE-2.0 31 | * 32 | * Unless required by applicable law or agreed to in writing, software 33 | * distributed under the License is distributed on an "AS IS" BASIS, 34 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 35 | * See the License for the specific language governing permissions and 36 | * limitations under the License. 37 | */ 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | 191 | Copyright © 2013 AppDynamics, Inc. 192 | 193 | Licensed under the Apache License, Version 2.0 (the "License"); 194 | you may not use this file except in compliance with the License. 195 | You may obtain a copy of the License at 196 | 197 | http://www.apache.org/licenses/LICENSE-2.0 198 | 199 | Unless required by applicable law or agreed to in writing, software 200 | distributed under the License is distributed on an "AS IS" BASIS, 201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 202 | See the License for the specific language governing permissions and 203 | limitations under the License. 204 | 205 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include AUTHORS.rst 2 | include CONTRIBUTING.rst 3 | include HISTORY.rst 4 | include LICENSE 5 | include README.rst 6 | 7 | recursive-include examples * 8 | recursive-include data * 9 | recursive-include templates * 10 | recursive-include tests * 11 | 12 | recursive-exclude * __pycache__ 13 | recursive-exclude * *.py[co] 14 | 15 | recursive-include docs *.rst conf.py Makefile make.bat 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean-pyc clean-build docs clean 2 | 3 | help: 4 | @echo "clean - remove all build, test, coverage and Python artifacts" 5 | @echo "clean-build - remove build artifacts" 6 | @echo "clean-pyc - remove Python file artifacts" 7 | @echo "clean-test - remove test and coverage artifacts" 8 | @echo "lint - check style with flake8" 9 | @echo "test - run tests quickly with the default Python" 10 | @echo "test-all - run tests on every Python version with tox" 11 | @echo "coverage - check code coverage quickly with the default Python" 12 | @echo "docs - generate Sphinx HTML documentation, including API docs" 13 | @echo "release - package and upload a release" 14 | @echo "dist - package" 15 | @echo "install - install the package to the active Python's site-packages" 16 | 17 | clean: clean-build clean-pyc clean-test 18 | 19 | clean-build: 20 | rm -fr build/ 21 | rm -fr dist/ 22 | rm -fr .eggs/ 23 | find . -name '*.egg-info' -exec rm -fr {} + 24 | find . -name '*.egg' -exec rm -f {} + 25 | 26 | clean-pyc: 27 | find . -name '*.pyc' -exec rm -f {} + 28 | find . -name '*.pyo' -exec rm -f {} + 29 | find . -name '*~' -exec rm -f {} + 30 | find . -name '__pycache__' -exec rm -fr {} + 31 | 32 | clean-test: 33 | rm -fr .tox/ 34 | rm -f .coverage 35 | rm -fr htmlcov/ 36 | 37 | lint: 38 | flake8 AppDynamicsREST tests 39 | 40 | test: 41 | python setup.py test 42 | 43 | test-all: 44 | tox 45 | 46 | coverage: 47 | coverage run --source AppDynamicsREST setup.py test 48 | coverage report -m 49 | coverage html 50 | open htmlcov/index.html 51 | 52 | docs: 53 | rm -f docs/AppDynamicsREST.rst 54 | rm -f docs/modules.rst 55 | sphinx-apidoc -o docs/ AppDynamicsREST 56 | $(MAKE) -C docs clean 57 | $(MAKE) -C docs html 58 | open docs/_build/html/index.html 59 | 60 | release: clean 61 | python setup.py sdist upload 62 | python setup.py bdist_wheel upload 63 | 64 | dist: clean 65 | python setup.py sdist 66 | python setup.py bdist_wheel 67 | ls -l dist 68 | 69 | install: clean 70 | python setup.py install 71 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ============================ 2 | AppDynamics REST API Library 3 | ============================ 4 | 5 | Current version: 0.4.18 6 | 7 | 8 | Introduction 9 | ------------ 10 | 11 | AppDynamicsREST is a library that provides a clean Python interface to the 12 | REST API of an AppDynamics controller. 13 | 14 | AppDynamicsREST is developed using Python 2.7.6 on Mac OSX. It is known to 15 | work on most Linux distributions and on Windows, with your choice of Python 2.6, 2.7, 16 | 3.3, or 3.4. 17 | 18 | 19 | Installation 20 | ------------ 21 | 22 | Install via ``pip``:: 23 | 24 | $ pip install AppDynamicsREST 25 | 26 | Install from source:: 27 | 28 | $ git clone git://github.com/appdynamics/AppDynamicsREST.git 29 | $ cd AppDynamicsREST 30 | $ python setup.py install 31 | 32 | 33 | Prerequisites 34 | ------------- 35 | 36 | * `requests `_ 37 | * `argparse `_ 38 | * `nose `_ (for running unit tests) 39 | * `tzlocal `_ and 40 | `lxml `_ 41 | (used by some of the example scripts) 42 | * `jinja2 `_ (used by the audit report example) 43 | 44 | 45 | Documentation 46 | ------------- 47 | 48 | The documentation is hosted online at readthedocs.org_. 49 | 50 | 51 | A Quick Example 52 | --------------- 53 | 54 | Here's a simple example that retrieves a list of business applications 55 | from a controller on localhost, and prints them out: 56 | 57 | .. code:: python 58 | 59 | from appd.request import AppDynamicsClient 60 | 61 | c = AppDynamicsClient('http://localhost:8090', 'user1', 'password', 'customer1', verbose=True) 62 | for app in c.get_applications(): 63 | print app.name, app.id 64 | 65 | 66 | Testing 67 | ------- 68 | 69 | If you have cloned the repo, you can run the unit tests from ``setup.py``:: 70 | 71 | python setup.py test 72 | 73 | Or, if you have ``nose`` installed, you can use that:: 74 | 75 | nosetests 76 | 77 | 78 | For More Information 79 | -------------------- 80 | 81 | The main source repo is on Github_. 82 | 83 | To ask a question or join the discussion, visit the AppDynamics `Community page`_. 84 | 85 | 86 | 87 | .. _AppDynamics: http://www.appdynamics.com/ 88 | .. _Github: https://github.com/appdynamics/AppDynamicsREST 89 | .. _Community page: http://community.appdynamics.com/t5/eXchange-Community-AppDynamics/Python-SDK-for-Controller-REST-API/idi-p/917 90 | .. _readthedocs.org: http://appdynamicsrest.readthedocs.org/en/latest/ 91 | -------------------------------------------------------------------------------- /appd/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python SDK for AppDynamics REST API 3 | 4 | .. moduleauthor:: Todd Radel 5 | """ 6 | 7 | __version__ = '0.4.18' 8 | __author__ = 'Todd Radel ' 9 | -------------------------------------------------------------------------------- /appd/cmdline.py: -------------------------------------------------------------------------------- 1 | """ 2 | Command line parsing for REST API scripts. 3 | """ 4 | 5 | import argparse 6 | 7 | 8 | def parse_argv(app_desc=None): 9 | """ 10 | Provides a parser for a standard set of command line options. 11 | 12 | Use of this function is optional but highly encouraged, as it allows all scripts based on the SDK to be 13 | invoked in a familiar way. 14 | 15 | :param str app_desc: Short description of your application, to be printed when help is requested. 16 | :returns: a Namespace full of parsed arguments 17 | :rtype: argparse.Namespace 18 | """ 19 | 20 | parser = argparse.ArgumentParser(description=app_desc) 21 | parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', 22 | help='Enable verbose output') 23 | parser.add_argument('-c', '--url', dest='url', required=True, 24 | help='set the controller URL') 25 | parser.add_argument('-u', '--username', dest='username', required=True, 26 | help='set the username for authentication') 27 | parser.add_argument('-p', '--password', dest='password', required=True, 28 | help='set the password for authentication') 29 | parser.add_argument('-a', '--account', default='customer1', dest='account', 30 | help='set the controller account (default: customer1)') 31 | return parser.parse_args() 32 | 33 | -------------------------------------------------------------------------------- /appd/model/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Model classes for AppDynamics REST API 3 | 4 | .. moduleauthor:: Todd Radel 5 | """ 6 | 7 | 8 | from future.moves.collections import UserList 9 | 10 | 11 | def _filter_func(obj, pred): 12 | def func(): 13 | return obj.__class__([item for item in obj.data]) 14 | 15 | return func 16 | 17 | 18 | class JsonObject(object): 19 | 20 | FIELDS = {} 21 | 22 | @classmethod 23 | def _set_fields_from_json_dict(cls, obj, json_dict): 24 | for k, v in list(obj.FIELDS.items()): 25 | obj.__setattr__(k, json_dict[v or k]) 26 | 27 | @classmethod 28 | def from_json(cls, json_dict): 29 | obj = cls() 30 | cls._set_fields_from_json_dict(obj, json_dict) 31 | return obj 32 | 33 | def __str__(self): 34 | rep = ', '.join([x + '=' + repr(y) for x, y in list(self.__dict__.items())]) 35 | return '<{0}: {1}>'.format(self.__class__.__name__, rep) 36 | 37 | __repr__ = __str__ 38 | 39 | def _list_setter(self, attr_name, new_val, allowed_vals): 40 | if new_val and (new_val not in allowed_vals): 41 | raise ValueError('{0} must be one of [{1}] but got {2}'.format( 42 | attr_name, ', '.join(allowed_vals), new_val)) 43 | self.__setattr__(attr_name, new_val) 44 | 45 | 46 | class JsonList(UserList): 47 | 48 | OBJECT_TYPE = None 49 | 50 | def __init__(self, cls, initial_list=None): 51 | UserList.__init__(self) 52 | if initial_list: 53 | for item in initial_list: 54 | if isinstance(item, cls): 55 | self.data.append(item) 56 | elif isinstance(item, dict): 57 | self.data.append(cls.from_json(item)) 58 | 59 | @classmethod 60 | def from_json(cls, json_list): 61 | return cls(json_list) 62 | 63 | def __str__(self): 64 | rep = ', '.join([str(x) for x in self.data]) 65 | return '<{0}[{1}]: {2}>'.format(self.__class__.__name__, len(self.data), rep) 66 | -------------------------------------------------------------------------------- /appd/model/account.py: -------------------------------------------------------------------------------- 1 | """ 2 | Model classes for AppDynamics REST API 3 | 4 | .. moduleauthor:: Todd Radel 5 | """ 6 | 7 | from . import JsonObject, JsonList 8 | 9 | 10 | class Account(JsonObject): 11 | """ 12 | Represents a tenant account on the controller. The following attributes are defined: 13 | 14 | .. data:: id 15 | 16 | Numeric ID of the account. 17 | 18 | .. data:: name 19 | 20 | Account name. 21 | """ 22 | 23 | FIELDS = {'id': '', 'name': ''} 24 | 25 | def __init__(self, acct_id='0', name=None): 26 | self.id, self.name = acct_id, name 27 | 28 | 29 | class Accounts(JsonList): 30 | """ 31 | Represents a collection of :class:Account objects. Extends :class:UserList, so it supports the 32 | standard array index and :keyword:`for` semantics. 33 | """ 34 | def __init__(self, initial_list=None): 35 | super(Accounts, self).__init__(Account, initial_list) 36 | 37 | def __getitem__(self, i): 38 | """ 39 | :rtype: Account 40 | """ 41 | return self.data[i] 42 | 43 | def by_name(self, name): 44 | """ 45 | Finds an account by name. 46 | 47 | :returns: First account with the correct name 48 | :rtype: Account 49 | """ 50 | found = [x for x in self.data if x.name == name] 51 | try: 52 | return found[0] 53 | except IndexError: 54 | raise KeyError(name) 55 | -------------------------------------------------------------------------------- /appd/model/action_suppressions.py: -------------------------------------------------------------------------------- 1 | """ 2 | Model classes for AppDynamics REST API 3 | 4 | .. moduleauthor:: Kyle Furlong 5 | """ 6 | 7 | from . import JsonObject, JsonList 8 | 9 | 10 | class ActionSuppression(JsonObject): 11 | 12 | FIELDS = {'id': '', 13 | 'timeRange': '', 14 | 'name': '', 15 | 'affects': ''} 16 | 17 | def __init__(self, id=0, timeRange=None, name='', affects=None): 18 | (self.id, self.timeRange, self.name, self.affects) = (id, timeRange, name, affects) 19 | 20 | 21 | class ActionSuppressions(JsonList): 22 | 23 | def __init__(self, initial_list=None): 24 | super(ActionSuppressions, self).__init__(ActionSuppression, initial_list) 25 | 26 | def __getitem__(self, i): 27 | """ 28 | :rtype: ActionSuppression 29 | """ 30 | return self.data[i] 31 | 32 | 33 | class ActionSuppressionsResponse(JsonObject): 34 | 35 | FIELDS = {} 36 | 37 | def __init__(self): 38 | self.actionSuppressions = ActionSuppressions() 39 | 40 | @classmethod 41 | def from_json(cls, json_dict): 42 | print(json_dict) 43 | obj = super(ActionSuppressionsResponse, cls).from_json(json_dict) 44 | if 'actionSuppressions' in json_dict: 45 | obj.actionSuppressions = ActionSuppressions.from_json(json_dict['actionSuppressions']) 46 | return obj 47 | -------------------------------------------------------------------------------- /appd/model/application.py: -------------------------------------------------------------------------------- 1 | """ 2 | Model classes for AppDynamics REST API 3 | 4 | .. moduleauthor:: Todd Radel 5 | """ 6 | 7 | from . import JsonObject, JsonList 8 | 9 | 10 | class Application(JsonObject): 11 | """ 12 | Represents a business application. The following attributes are defined: 13 | 14 | .. data:: id 15 | 16 | Numeric ID of the application. 17 | 18 | .. data:: name 19 | 20 | Application name. 21 | 22 | .. data:: description 23 | 24 | Optional description of the application. 25 | """ 26 | 27 | FIELDS = {'id': '', 'name': '', 'description': ''} 28 | 29 | def __init__(self, app_id=0, name=None, description=None): 30 | self.id, self.name, self.description = app_id, name, description 31 | 32 | 33 | class Applications(JsonList): 34 | """ 35 | Represents a collection of :class:`Application` objects. Extends :class:`UserList`, so it supports the 36 | standard array index and :keyword:`for` semantics. 37 | """ 38 | def __init__(self, initial_list=None): 39 | super(Applications, self).__init__(Application, initial_list) 40 | 41 | def __getitem__(self, i): 42 | """ 43 | :rtype: Application 44 | """ 45 | return self.data[i] 46 | 47 | def by_name(self, name): 48 | """ 49 | Finds an application by name. 50 | 51 | :returns: First application with the correct name 52 | :rtype: :class:`Application` 53 | """ 54 | found = [x for x in self.data if x.name == name] 55 | try: 56 | return found[0] 57 | except IndexError: 58 | raise KeyError(name) 59 | -------------------------------------------------------------------------------- /appd/model/audit_history.py: -------------------------------------------------------------------------------- 1 | """ 2 | Model classes for AppDynamics REST API 3 | 4 | .. moduleauthor:: Kyle Furlong 5 | """ 6 | 7 | from . import JsonObject, JsonList 8 | 9 | 10 | class Audit(JsonObject): 11 | 12 | FIELDS = {'timestamp': 'timeStamp', 'account_name': 'accountName', 13 | 'security_provider_type': 'securityProviderType', 14 | 'user_name': 'userName', 'action': 'action'} 15 | 16 | def __init__(self, timestamp=0, account_name=None, security_provider_type=None, user_name=None, action=None): 17 | (self.timestamp, self.account_name, self.security_provider_type, 18 | self.user_name, self.action) = (timestamp, account_name, security_provider_type, user_name, action) 19 | 20 | 21 | class AuditHistory(JsonList): 22 | 23 | def __init__(self, initial_list=None): 24 | super(AuditHistory, self).__init__(Audit, initial_list) 25 | 26 | def __getitem__(self, i): 27 | """ 28 | :rtype: Node 29 | """ 30 | return self.data[i] 31 | 32 | def by_action(self, action): 33 | """ 34 | Filters an AuditHistory collection to return only the Audits matching the given action. 35 | :param str name: Action to match against. 36 | :returns: a AuditHistory collection filtered by action. 37 | :rtype: AuditHistory 38 | """ 39 | return AuditHistory([x for x in self.data if x.action == action]) 40 | 41 | def by_user_name(self, user_name): 42 | """ 43 | Filters an AuditHistory collection to return only the Audits matching the given user name. 44 | :param str name: User name to match against. 45 | :returns: a AuditHistory collection filtered by user name. 46 | :rtype: AuditHistory 47 | """ 48 | return AuditHistory([x for x in self.data if x.user_name == user_name]) 49 | 50 | def by_account_name(self, account_name): 51 | """ 52 | Filters an AuditHistory collection to return only the Audits matching the given account. 53 | :param str name: Account to match against. 54 | :returns: a AuditHistory collection filtered by account. 55 | :rtype: AuditHistory 56 | """ 57 | return AuditHistory([x for x in self.data if x.account_name == account_name]) 58 | -------------------------------------------------------------------------------- /appd/model/backend.py: -------------------------------------------------------------------------------- 1 | """ 2 | Model classes for AppDynamics REST API 3 | 4 | .. moduleauthor:: Kyle Furlong 5 | """ 6 | 7 | from . import JsonObject, JsonList 8 | 9 | 10 | class Backend(JsonObject): 11 | 12 | FIELDS = {'id': '', 'name': '', 'exit_point_type': 'exitPointType', 'properties': ''} 13 | EXIT_POINT_TYPES = ('HTTP', 'CACHE', 'DB', 'WEB_SERVICE', 'MODULE') 14 | 15 | def __init(self, backend_id=0, name='', exit_point_type='', properties=''): 16 | self._exit_point_type = None 17 | self.id, self.name, self.exit_point_type, self.properties, = \ 18 | backend_id, name, exit_point_type, properties 19 | 20 | @property 21 | def exit_point_type(self): 22 | """ 23 | :rtype: str 24 | """ 25 | return self._exit_point_type 26 | 27 | @exit_point_type.setter 28 | def exit_point_type(self, exit_point_type): 29 | self._list_setter('_exit_point_type', exit_point_type, Backend.EXIT_POINT_TYPES) 30 | 31 | 32 | class Backends(JsonList): 33 | 34 | def __init__(self, initial_list=None): 35 | super(Backends, self).__init__(Backend, initial_list) 36 | 37 | def __getitem__(self, i): 38 | """ 39 | :rtype: Backend 40 | """ 41 | return self.data[i] 42 | 43 | def by_exit_point_type(self, exit_point_type): 44 | """ 45 | Searches for backends of a particular type (which should be one of Backend.EXIT_POINT_TYPES). 46 | For example, to find all the HTTP backends: 47 | 48 | >>> from appd.request import AppDynamicsClient 49 | >>> client = AppDynamicsClient(...) 50 | >>> all_backends = client.get_backends() 51 | >>> http_backends = all_backends.by_exit_point_type('HTTP') 52 | 53 | :returns: a Tiers object containing any tiers matching the criteria 54 | :rtype: Tiers 55 | """ 56 | return Backends([x for x in self.data if x.exit_point_type == exit_point_type]) 57 | -------------------------------------------------------------------------------- /appd/model/business_transaction.py: -------------------------------------------------------------------------------- 1 | """ 2 | Model classes for AppDynamics REST API 3 | 4 | .. moduleauthor:: Todd Radel 5 | """ 6 | 7 | from . import JsonObject, JsonList 8 | 9 | 10 | class BusinessTransaction(JsonObject): 11 | """ 12 | Represents a business transaction definition. 13 | 14 | .. data:: id 15 | 16 | Numeric ID of the BT definition. 17 | 18 | .. data:: name 19 | 20 | """ 21 | FIELDS = {'id': '', 'name': '', 'type': 'entryPointType', 'internal_name': 'internalName', 22 | 'is_background': 'background', 'tier_id': 'tierId', 'tier_name': 'tierName'} 23 | 24 | def __init__(self, bt_id=0, name='', internal_name='', tier_id=0, tier_name='', 25 | bt_type='POJO', is_background=False): 26 | (self.id, self.name, self.internal_name, self.tier_id, self.tier_name, self.type, self.is_background) = \ 27 | (bt_id, name, internal_name, tier_id, tier_name, bt_type, is_background) 28 | 29 | 30 | class BusinessTransactions(JsonList): 31 | 32 | def __init__(self, initial_list=None): 33 | super(BusinessTransactions, self).__init__(BusinessTransaction, initial_list) 34 | 35 | def __getitem__(self, i): 36 | """ 37 | :rtype: BusinessTransaction 38 | """ 39 | return self.data[i] 40 | 41 | def by_name(self, bt_name): 42 | """ 43 | Searches for business transactions that match a particular name. Note that there may be more than one 44 | exact match, as different tiers can have transactions with the exact same name. 45 | 46 | :returns: a BusinessTransactions object containing the matching business transactions. 47 | :rtype: BusinessTransactions 48 | """ 49 | return BusinessTransactions([x for x in self.data if x.name == bt_name]) 50 | 51 | def by_tier_and_name(self, bt_name, tier_name): 52 | """ 53 | Searches for business transactions that match a particular BT name and tier name. 54 | 55 | :returns: a BusinessTransactions object containing the matching business transactions. 56 | :rtype: BusinessTransactions 57 | """ 58 | return BusinessTransactions([x for x in self.data if x.name == bt_name and x.tier_name == tier_name]) 59 | 60 | 61 | -------------------------------------------------------------------------------- /appd/model/config_variable.py: -------------------------------------------------------------------------------- 1 | """ 2 | Model classes for AppDynamics REST API 3 | 4 | .. moduleauthor:: Todd Radel 5 | """ 6 | 7 | from . import JsonObject, JsonList 8 | 9 | 10 | class ConfigVariable(JsonObject): 11 | """ 12 | Represents a controller configuration variable. The following attributes are defined: 13 | 14 | .. data:: name 15 | 16 | Variable name. 17 | 18 | .. data:: value 19 | 20 | Current value. 21 | 22 | .. data:: description 23 | 24 | Optional description of the variable. 25 | 26 | .. data:: updateable 27 | 28 | If :const:`True`, value can be changed. 29 | 30 | .. data:: scope 31 | 32 | Scope of the variable. The scope can be ``'cluster'`` or ``'local'``. Variables with cluster scope are 33 | replicated across HA controllers; local variables are not. 34 | """ 35 | FIELDS = { 36 | 'name': '', 37 | 'description': '', 38 | 'scope': '', 39 | 'updateable': '', 40 | 'value': '' 41 | } 42 | 43 | def __init__(self, name='', description='', scope='cluster', updateable=True, value=None): 44 | (self.name, self.description, self.scope, self.updateable, self.value) = (name, description, scope, 45 | updateable, value) 46 | 47 | 48 | class ConfigVariables(JsonList): 49 | """ 50 | Represents a collection of :class:`ConfigVariable` objects. Extends :class:`UserList`, so it supports the 51 | standard array index and :keyword:`for` semantics. 52 | """ 53 | def __init__(self, initial_list=None): 54 | super(ConfigVariables, self).__init__(ConfigVariable, initial_list) 55 | 56 | def __getitem__(self, i): 57 | """ 58 | :rtype: ConfigVariable 59 | """ 60 | return self.data[i] 61 | 62 | def by_name(self, name): 63 | """ 64 | Finds a config variable with the matching name. 65 | 66 | :param str name: Variable name to find. 67 | :return: The matching config variable. 68 | :rtype: appd.model.ConfigVariable 69 | """ 70 | found = [x for x in self.data if x.name == name] 71 | try: 72 | return found[0] 73 | except IndexError: 74 | raise KeyError(name) 75 | -------------------------------------------------------------------------------- /appd/model/entity_def.py: -------------------------------------------------------------------------------- 1 | """ 2 | Model classes for AppDynamics REST API 3 | 4 | .. moduleauthor:: Todd Radel 5 | """ 6 | 7 | from . import JsonObject, JsonList 8 | 9 | 10 | class EntityDefinition(JsonObject): 11 | FIELDS = {'entity_id': 'entityId', 12 | 'type': 'entityType'} 13 | 14 | ENTITY_TYPES = ('ACCOUNT', 'AGENT_CONFIGURATION', 'ALL', 'APPLICATION', 'APPLICATION_COMPONENT', 15 | 'APPLICATION_COMPONENT_NODE', 'APPLICATION_DIAGNOSTIC_DATA', 'AUTOMATIC_LEAK_DETECTION', 16 | 'BACKEND', 'BACKEND_DISCOVERY_CONFIG', 'BUSINESS_TRANSACTION', 'BUSINESS_TRANSACTION_GROUP', 17 | 'CALL_GRAPH_CONFIGURATION', 'CUSTOM_EXIT_POINT_DEFINITION', 'CUSTOM_MATCH_POINT_DEFINITION', 18 | 'CUSTOM_MEMORY_STRUCTURE', 'DASHBOARD', 'ERROR', 'ERROR_CONFIGURATION', 19 | 'DOT_NET_ERROR_CONFIGURATION', 'PHP_ERROR_CONFIGURATION', 'EUM_CONFIGURATION', 'EVENT', 20 | 'GLOBAL_CONFIGURATION', 'INCIDENT', 'JMX_CONFIG', 'MACHINE_INSTANCE', 'MEMORY_CONFIGURATION', 21 | 'NOTIFICATION_CONFIG', 'OBJECT_INSTANCE_TRACKING', 'POLICY', 'RULE', 'SQL_DATA_GATHERER_CONFIG', 22 | 'STACK_TRACE', 'THREAD_TASK', 'TRANSACTION_MATCH_POINT_CONFIG', 'USER', 'GROUP', 'ACCOUNT_ROLE', 23 | 'WORKFLOW', 'WORKFLOW_EXCUTION', 'POJO_DATA_GATHERER_CONFIG', 'HTTP_REQUEST_DATA_GATHERER_CONFIG', 24 | 'BASE_PAGE', 'IFRAME', 'AJAX_REQUEST', 'INFO_POINT') 25 | 26 | def __init__(self, entity_id=0, entity_type=''): 27 | self._type = None 28 | (self.id, self.type) = (entity_id, entity_type) 29 | 30 | @property 31 | def type(self): 32 | return self._type 33 | 34 | @type.setter 35 | def type(self, new_type): 36 | self._list_setter('_type', new_type, EntityDefinition.ENTITY_TYPES) 37 | -------------------------------------------------------------------------------- /appd/model/event.py: -------------------------------------------------------------------------------- 1 | """ 2 | Model classes for AppDynamics REST API 3 | 4 | .. moduleauthor:: Todd Radel 5 | """ 6 | 7 | from . import JsonObject, JsonList 8 | from .entity_def import EntityDefinition 9 | 10 | 11 | class Event(JsonObject): 12 | 13 | FIELDS = {'id': '', 14 | 'summary': '', 15 | 'type': '', 16 | 'archived': '', 17 | 'deep_link_url': 'deepLinkUrl', 18 | 'event_time_ms': 'eventTime', 19 | 'is_read': 'markedAsRead', 20 | 'is_resolved': 'markedAsResolved', 21 | 'severity': '', 22 | 'sub_type': 'subType'} 23 | 24 | """ 25 | https://docs.appdynamics.com/display/PRO42/Events+Reference 26 | """ 27 | EVENT_TYPES = ( 28 | 'ACTIVITY_TRACE', 29 | 'ADJUDICATION_CANCELLED', 30 | 'AGENT_ADD_BLACKLIST_REG_LIMIT_REACHED', 31 | 'AGENT_ASYNC_ADD_REG_LIMIT_REACHED', 32 | 'AGENT_CONFIGURATION_ERROR', 33 | 'AGENT_DIAGNOSTICS', 34 | 'AGENT_ERROR_ADD_REG_LIMIT_REACHED', 35 | 'AGENT_EVENT', 36 | 'AGENT_METRIC_BLACKLIST_REG_LIMIT_REACHED', 37 | 'AGENT_METRIC_REG_LIMIT_REACHED', 38 | 'AGENT_STATUS', 39 | 'ALREADY_ADJUDICATED', 40 | 'APPDYNAMICS_CONFIGURATION_WARNINGS', 41 | 'APPDYNAMICS_DATA', 42 | 'APPLICATION_CONFIG_CHANGE', 43 | 'APPLICATION_DEPLOYMENT', 44 | 'APPLICATION_ERROR', 45 | 'APP_SERVER_RESTART', 46 | 'AZURE_AUTO_SCALING', 47 | 'BACKEND_DISCOVERED', 48 | 'BT_DISCOVERED', 49 | 'CONTROLLER_AGENT_VERSION_INCOMPATIBILITY', 50 | 'CONTROLLER_ASYNC_ADD_REG_LIMIT_REACHED', 51 | 'CONTROLLER_ERROR_ADD_REG_LIMIT_REACHED', 52 | 'CONTROLLER_EVENT_UPLOAD_LIMIT_REACHED', 53 | 'CONTROLLER_METRIC_REG_LIMIT_REACHED', 54 | 'CONTROLLER_RSD_UPLOAD_LIMIT_REACHED', 55 | 'CUSTOM', 56 | 'CUSTOM_ACTION_END', 57 | 'CUSTOM_ACTION_FAILED', 58 | 'CUSTOM_ACTION_STARTED', 59 | 'DEADLOCK', 60 | 'DIAGNOSTIC_SESSION', 61 | 'DISK_SPACE', 62 | 'EMAIL_SENT', 63 | 'EUM_CLOUD_BROWSER_EVENT', 64 | 'HIGH_END_TO_END_LATENCY', 65 | 'INFO_INSTRUMENTATION_VISIBILITY', 66 | 'INTERNAL_UI_EVENT', 67 | 'LICENSE', 68 | 'LOW_HEAP_MEMORY', 69 | 'MACHINE_DISCOVERED', 70 | 'MEMORY', 71 | 'MEMORY_LEAK', 72 | 'MEMORY_LEAK_DIAGNOSTICS', 73 | 'MOBILE_CRASH_IOS_EVENT', 74 | 'MOBILE_CRASH_ANDROID_EVENT', 75 | 'NODE_DISCOVERED', 76 | 'NORMAL', 77 | 'OBJECT_CONTENT_SUMMARY', 78 | 'POLICY_CANCELED', 79 | 'POLICY_CANCELED_CRITICAL', 80 | 'POLICY_CANCELED_WARNING', 81 | 'POLICY_CLOSE', 82 | 'POLICY_CLOSE_CRITICAL', 83 | 'POLICY_CLOSE_WARNING', 84 | 'POLICY_CONTINUES', 85 | 'POLICY_CONTINUES_CRITICAL', 86 | 'POLICY_CONTINUES_WARNING', 87 | 'POLICY_DOWNGRADED', 88 | 'POLICY_OPEN', 89 | 'POLICY_OPEN_CRITICAL', 90 | 'POLICY_OPEN_WARNING', 91 | 'POLICY_UPGRADED', 92 | 'RESOURCE_POOL_LIMIT', 93 | 'RUNBOOK_DIAGNOSTIC_SESSION_END', 94 | 'RUNBOOK_DIAGNOSTIC_SESSION_FAILED', 95 | 'RUNBOOK_DIAGNOSTIC_SESSION_STARTED', 96 | 'RUN_LOCAL_SCRIPT_ACTION_END', 97 | 'RUN_LOCAL_SCRIPT_ACTION_FAILED', 98 | 'RUN_LOCAL_SCRIPT_ACTION_STARTED', 99 | 'SERVICE_ENDPOINT_DISCOVERED', 100 | 'SLOW', 101 | 'SMS_SENT', 102 | 'STALL', 103 | 'STALLED', 104 | 'SYSTEM_LOG' 105 | 'THREAD_DUMP_ACTION_END', 106 | 'THREAD_DUMP_ACTION_FAILED', 107 | 'THREAD_DUMP_ACTION_STARTED', 108 | 'TIER_DISCOVERED', 109 | 'VERY_SLOW', 110 | 'WORKFLOW_ACTION_END', 111 | 'WORKFLOW_ACTION_FAILED', 112 | 'WORKFLOW_ACTION_STARTED') 113 | 114 | def __init__(self, event_id=0, event_type='CUSTOM', sub_type='', summary='', archived=False, event_time_ms=0, 115 | is_read=False, is_resolved=False, severity='INFO', deep_link_url='', 116 | triggered_entity=None, affected_entities=None): 117 | self._event_type = None 118 | (self.id, self.type, self.sub_type, self.summary, self.archived, self.event_time_ms, self.is_read, 119 | self.is_resolved, self.severity, self.deep_link_url) = (event_id, event_type, sub_type, summary, archived, 120 | event_time_ms, is_read, is_resolved, severity, 121 | deep_link_url) 122 | self.triggered_entity = triggered_entity or EntityDefinition() 123 | self.affected_entities = affected_entities or [] 124 | 125 | @property 126 | def event_type(self): 127 | """ 128 | :return: 129 | """ 130 | return self._event_type 131 | 132 | @event_type.setter 133 | def event_type(self, new_type): 134 | self._list_setter('_event_type', new_type, Event.EVENT_TYPES) 135 | 136 | 137 | class Events(JsonList): 138 | 139 | def __init__(self, initial_list=None): 140 | super(Events, self).__init__(Event, initial_list) 141 | 142 | def __getitem__(self, i): 143 | """ 144 | :rtype: Event 145 | """ 146 | return self.data[i] 147 | -------------------------------------------------------------------------------- /appd/model/hourly_license_usage.py: -------------------------------------------------------------------------------- 1 | """ 2 | Model classes for AppDynamics REST API 3 | """ 4 | 5 | from . import JsonObject, JsonList 6 | from appd.time import from_ts 7 | 8 | class HourlyLicenseUsage(JsonObject): 9 | 10 | FIELDS = { 11 | 'id': '', 12 | 'account_id': 'accountId', 13 | 'max_units_used': 'maxUnitsUsed', 14 | 'min_units_used': 'minUnitsUsed', 15 | 'avg_units_used': 'avgUnitsUsed', 16 | 'total_units_used': 'totalUnitsUsed', 17 | 'sample_count': 'sampleCount', 18 | 'avg_units_allowed': 'avgUnitsAllowed', 19 | 'avg_units_provisioned': 'avgUnitsProvisioned', 20 | 'license_module': 'agentType', 21 | 'created_on_ms': 'createdOn', 22 | } 23 | 24 | def __init__(self, id=0, account_id=0, max_units_used=0, min_units_used=0, avg_units_used=0, total_units_used=0, 25 | sample_count=0, avg_units_allowed=0, avg_units_provisioned=None, license_module=None, 26 | created_on_ms=0): 27 | (self.id, self.account_id, self.max_units_used, self.min_units_used, self.avg_units_used, self.total_units_used, 28 | self.sample_count, self.avg_units_allowed, self.avg_units_provisioned, self.license_module, 29 | self.created_on_ms) = (id, account_id, max_units_used, min_units_used, avg_units_used, total_units_used, 30 | sample_count, avg_units_allowed, avg_units_provisioned, license_module, 31 | created_on_ms) 32 | 33 | @property 34 | def created_on(self): 35 | """ 36 | :rtype: datetime.datetime 37 | """ 38 | return from_ts(self.created_on_ms) 39 | 40 | 41 | class HourlyLicenseUsageList(JsonList): 42 | 43 | def __init__(self, initial_list=None): 44 | super(HourlyLicenseUsageList, self).__init__(HourlyLicenseUsage, initial_list) 45 | 46 | def __getitem__(self, i): 47 | """ 48 | :rtype: HourlyLicenseUsage 49 | """ 50 | return self.data[i] 51 | 52 | def by_account_id(self, account_id): 53 | """ 54 | :rtype: HourlyLicenseUsageList 55 | """ 56 | return HourlyLicenseUsageList([x for x in self.data if x.account_id == account_id]) 57 | 58 | def by_license_module(self, license_module): 59 | """ 60 | :rtype: HourlyLicenseUsageList 61 | """ 62 | return HourlyLicenseUsageList([x for x in self.data if x.license_module == license_module]) 63 | 64 | 65 | class HourlyLicenseUsages(JsonObject): 66 | 67 | FIELDS = {} 68 | 69 | def __init__(self): 70 | self.usages = HourlyLicenseUsageList() 71 | 72 | @classmethod 73 | def from_json(cls, json_dict): 74 | obj = super(HourlyLicenseUsages, cls).from_json(json_dict) 75 | obj.usages = HourlyLicenseUsageList.from_json(json_dict['usages']) 76 | return obj 77 | 78 | -------------------------------------------------------------------------------- /appd/model/license_module.py: -------------------------------------------------------------------------------- 1 | """ 2 | Model classes for AppDynamics REST API 3 | """ 4 | 5 | from . import JsonObject, JsonList 6 | 7 | 8 | class LicenseModule(JsonObject): 9 | 10 | FIELDS = {'name': ''} 11 | 12 | def __init__(self, name=None): 13 | self.name = name 14 | 15 | 16 | class LicenseModuleList(JsonList): 17 | """ 18 | Represents a collection of :class:Account objects. Extends :class:UserList, so it supports the 19 | standard array index and :keyword:`for` semantics. 20 | """ 21 | def __init__(self, initial_list=None): 22 | super(LicenseModuleList, self).__init__(LicenseModule, initial_list) 23 | 24 | def __getitem__(self, i): 25 | """ 26 | :rtype: LicenseModule 27 | """ 28 | return self.data[i] 29 | 30 | def by_name(self, name): 31 | """ 32 | Finds an account by name. 33 | 34 | :returns: First account with the correct name 35 | :rtype: LicenseModule 36 | """ 37 | found = [x for x in self.data if x.name == name] 38 | try: 39 | return found[0] 40 | except IndexError: 41 | raise KeyError(name) 42 | 43 | def __contains__(self, item): 44 | found = [x for x in self.data if x.name == item] 45 | return True if found else False 46 | 47 | 48 | class LicenseModules(JsonObject): 49 | 50 | FIELDS = {} 51 | 52 | def __init__(self): 53 | self.modules = LicenseModuleList() 54 | 55 | @classmethod 56 | def from_json(cls, json_dict): 57 | obj = super(LicenseModules, cls).from_json(json_dict) 58 | obj.modules = LicenseModuleList.from_json(json_dict['modules']) 59 | return obj 60 | 61 | -------------------------------------------------------------------------------- /appd/model/license_usage.py: -------------------------------------------------------------------------------- 1 | """ 2 | Model classes for AppDynamics REST API 3 | """ 4 | 5 | from . import JsonObject, JsonList 6 | from appd.time import from_ts 7 | 8 | class LicenseUsage(JsonObject): 9 | 10 | FIELDS = { 11 | 'id': '', 12 | 'account_id': 'accountId', 13 | 'units_used': 'unitsUsed', 14 | 'units_allowed': 'unitsAllowed', 15 | 'units_provisioned': 'unitsProvisioned', 16 | 'license_module': 'agentType', 17 | 'created_on_ms': 'createdOn', 18 | } 19 | 20 | def __init__(self, id=0, account_id=0, license_module=None, units_used=0, 21 | units_allowed=0, units_provisioned=None, created_on_ms=0): 22 | (self.id, self.account_id, self.license_module, self.units_used, 23 | self.units_allowed, self.units_provisioned, self.created_on_ms) = (id, account_id, license_module, 24 | units_used, units_allowed, 25 | units_provisioned, created_on_ms) 26 | 27 | @property 28 | def created_on(self): 29 | """ 30 | :rtype: datetime.datetime 31 | """ 32 | return from_ts(self.created_on_ms) 33 | 34 | 35 | class LicenseUsageList(JsonList): 36 | 37 | def __init__(self, initial_list=None): 38 | super(LicenseUsageList, self).__init__(LicenseUsage, initial_list) 39 | 40 | def __getitem__(self, i): 41 | """ 42 | :rtype: LicenseUsage 43 | """ 44 | return self.data[i] 45 | 46 | def by_account_id(self, account_id): 47 | """ 48 | :rtype: LicenseUsageList 49 | """ 50 | return LicenseUsageList([x for x in self.data if x.account_id == account_id]) 51 | 52 | def by_license_module(self, license_module): 53 | """ 54 | :rtype: LicenseUsageList 55 | """ 56 | return LicenseUsageList([x for x in self.data if x.license_module == license_module]) 57 | 58 | 59 | class LicenseUsages(JsonObject): 60 | 61 | FIELDS = {} 62 | 63 | def __init__(self): 64 | self.usages = LicenseUsageList() 65 | 66 | @classmethod 67 | def from_json(cls, json_dict): 68 | obj = super(LicenseUsages, cls).from_json(json_dict) 69 | obj.usages = LicenseUsageList.from_json(json_dict['usages']) 70 | return obj 71 | 72 | -------------------------------------------------------------------------------- /appd/model/metric_data.py: -------------------------------------------------------------------------------- 1 | """ 2 | Model classes for AppDynamics REST API 3 | 4 | .. moduleauthor:: Todd Radel 5 | """ 6 | 7 | from . import JsonObject, JsonList 8 | from .metric_value import MetricValues 9 | 10 | class MetricDataSingle(JsonObject): 11 | 12 | FIELDS = { 13 | 'frequency': '', 14 | 'path': 'metricPath' 15 | } 16 | 17 | FREQUENCIES = ('ONE_MIN', 'TEN_MIN', 'SIXTY_MIN') 18 | 19 | def __init__(self, path='', frequency='ONE_MIN', values=MetricValues()): 20 | self._frequency = None 21 | self.path, self.frequency, self.values = path, frequency, values 22 | 23 | @classmethod 24 | def _set_fields_from_json_dict(cls, obj, json_dict): 25 | JsonObject._set_fields_from_json_dict(obj, json_dict) 26 | obj.values = MetricValues.from_json(json_dict['metricValues']) 27 | 28 | @property 29 | def frequency(self): 30 | return self._frequency 31 | 32 | @frequency.setter 33 | def frequency(self, new_freq): 34 | self._list_setter('_frequency', new_freq, MetricDataSingle.FREQUENCIES) 35 | 36 | 37 | class MetricData(JsonList): 38 | def __init__(self, initial_list=None): 39 | super(MetricData, self).__init__(MetricDataSingle, initial_list) 40 | 41 | def __getitem__(self, i): 42 | """ 43 | :rtype: MetricDataSingle 44 | """ 45 | return self.data[i] 46 | 47 | def by_partial_name(self, name): 48 | return MetricData([x for x in self if name in x.path]) 49 | 50 | def by_leaf_name(self, name): 51 | return MetricData([x for x in self if x.path.split('|')[-1] == name]) 52 | 53 | def by_path(self, path): 54 | return MetricData([x for x in self if x.path == path]) 55 | 56 | def first_value(self): 57 | return self[0].values[0].value 58 | -------------------------------------------------------------------------------- /appd/model/metric_treenode.py: -------------------------------------------------------------------------------- 1 | """ 2 | Model classes for AppDynamics REST API 3 | 4 | .. moduleauthor:: Todd Radel 5 | """ 6 | 7 | from . import JsonObject, JsonList 8 | 9 | 10 | class MetricTreeNode(JsonObject): 11 | 12 | FIELDS = {'name': '', 'type': ''} 13 | NODE_TYPES = ('leaf', 'folder') 14 | 15 | def __init__(self, parent=None, node_name='', node_type=''): 16 | self.parent, self.name, self.type = parent, node_name, node_type 17 | self._children = MetricTreeNodes() 18 | if parent: 19 | parent._children.append(self) 20 | 21 | @property 22 | def path(self): 23 | n = self 24 | stack = [] 25 | while n: 26 | stack.insert(0, n.name) 27 | n = n.parent 28 | return '|'.join(stack) 29 | 30 | 31 | class MetricTreeNodes(JsonList): 32 | 33 | def __init__(self, initial_list=None, parent=None): 34 | super(MetricTreeNodes, self).__init__(MetricTreeNode, initial_list) 35 | 36 | self.parent = parent 37 | if parent: 38 | if not isinstance(parent, MetricTreeNode): 39 | raise TypeError('was expecting a MetricTreeNode') 40 | for x in self.data: 41 | x.parent = parent 42 | 43 | def __getitem__(self, i): 44 | """ 45 | :rtype: MetricTreeNode 46 | """ 47 | return self.data[i] 48 | 49 | @classmethod 50 | def from_json(cls, json_list, parent=None): 51 | return cls(json_list, parent) 52 | 53 | def by_name(self, name): 54 | """ 55 | Finds a tree node with the matching name. 56 | 57 | :param str name: Variable name to find. 58 | :return: Metric tree node matching the name. 59 | :rtype: appd.model.MetricTreeNode 60 | """ 61 | found = [x for x in self.data if x.name == name] 62 | try: 63 | return found[0] 64 | except IndexError: 65 | raise KeyError(name) 66 | -------------------------------------------------------------------------------- /appd/model/metric_value.py: -------------------------------------------------------------------------------- 1 | """ 2 | Model classes for AppDynamics REST API 3 | 4 | .. moduleauthor:: Todd Radel 5 | """ 6 | 7 | from . import JsonObject, JsonList 8 | from appd.time import from_ts 9 | 10 | class MetricValue(JsonObject): 11 | 12 | FIELDS = { 13 | 'current': '', 14 | 'min': '', 15 | 'max': '', 16 | 'value': '', 17 | 'count': '', 18 | 'sum': '', 19 | 'start_time_ms': 'startTimeInMillis' 20 | } 21 | 22 | def __init__(self, current=0, value=0, min_value=0, max_value=0, count=0, sum=0, start_time_ms=0): 23 | (self.current, self.value, self.min, self.max, self.count, self.sum, self.start_time_ms) = (current, value, min_value, max_value, count, sum, start_time_ms) 24 | 25 | @property 26 | def start_time(self): 27 | """ 28 | Gets the timestamp of the metric data, converting it from an AppDynamics timestamp to standard 29 | Python :class:`datetime`. 30 | 31 | :return: Time the violation was resolved 32 | :rtype: :class:`datetime.datetime` 33 | """ 34 | return from_ts(self.start_time_ms) 35 | 36 | 37 | class MetricValues(JsonList): 38 | 39 | def __init__(self, initial_list=None): 40 | super(MetricValues, self).__init__(MetricValue, initial_list) 41 | 42 | def __getitem__(self, i): 43 | """ 44 | :rtype: MetricValue 45 | """ 46 | return self.data[i] 47 | -------------------------------------------------------------------------------- /appd/model/node.py: -------------------------------------------------------------------------------- 1 | """ 2 | Model classes for AppDynamics REST API 3 | 4 | .. moduleauthor:: Todd Radel 5 | """ 6 | 7 | from . import JsonObject, JsonList 8 | 9 | 10 | class Node(JsonObject): 11 | 12 | FIELDS = {'id': '', 'name': '', 'type': '', 'machine_id': 'machineId', 'machine_name': 'machineName', 13 | 'tier_id': 'tierId', 'tier_name': 'tierName', 'unique_id': 'nodeUniqueLocalId', 14 | 'os_type': 'machineOSType', 'has_app_agent': 'appAgentPresent', 'app_agent_version': 'appAgentVersion', 15 | 'has_machine_agent': 'machineAgentPresent', 'machine_agent_version': 'machineAgentVersion'} 16 | 17 | def __init__(self, node_id=0, name='', node_type='', machine_id=0, machine_name='', os_type='', 18 | unique_local_id='', tier_id=0, tier_name='', has_app_agent=False, app_agent_version='', 19 | has_machine_agent=False, machine_agent_version=''): 20 | (self.id, self.name, self.type, self.machine_id, self.machine_name, self.os_type, self.unique_local_id, 21 | self.tier_id, self.tier_name, self.has_app_agent, self.app_agent_version, self.has_machine_agent, 22 | self.machine_agent_version) = (node_id, name, node_type, machine_id, machine_name, os_type, unique_local_id, 23 | tier_id, tier_name, has_app_agent, app_agent_version, 24 | has_machine_agent, machine_agent_version) 25 | 26 | 27 | class Nodes(JsonList): 28 | 29 | def __init__(self, initial_list=None): 30 | super(Nodes, self).__init__(Node, initial_list) 31 | 32 | def __getitem__(self, i): 33 | """ 34 | :rtype: Node 35 | """ 36 | return self.data[i] 37 | 38 | def by_machine_name(self, name): 39 | """ 40 | Filters a Nodes collection to return only the nodes matching the given hostname. 41 | :param str name: Hostname to match against. 42 | :returns: a Nodes collection filtered by hostname. 43 | :rtype: Nodes 44 | """ 45 | return Nodes([x for x in self.data if x.machineName == name]) 46 | 47 | def by_machine_id(self, machine_id): 48 | """ 49 | Filters a Nodes collection to return only the nodes matching the given machine instance ID. 50 | :param int machine_id: Machine ID to match against. 51 | :returns: a Nodes collection filtered by machine ID. 52 | :rtype: Nodes 53 | """ 54 | return Nodes([x for x in self.data if x.machineId == machine_id]) 55 | 56 | def by_tier_name(self, name): 57 | """ 58 | Filters a Nodes collection to return only the nodes belonging to the given tier. 59 | :param str name: Tier name to match against. 60 | :returns: a Nodes collection filtered by tier. 61 | :rtype: Nodes 62 | """ 63 | return Nodes([x for x in self.data if x.tier_name == name]) 64 | 65 | def by_tier_id(self, tier_id): 66 | """ 67 | Filters a Nodes collection to return only the nodes belonging to the given tier ID. 68 | :param int tier_id: Tier ID to match against. 69 | :returns: a Nodes collection filtered by tier. 70 | :rtype: Nodes 71 | """ 72 | return Nodes([x for x in self.data if x.tier_id == tier_id]) 73 | -------------------------------------------------------------------------------- /appd/model/policy_violation.py: -------------------------------------------------------------------------------- 1 | """ 2 | Model classes for AppDynamics REST API 3 | 4 | .. moduleauthor:: Todd Radel 5 | """ 6 | 7 | from . import JsonObject, JsonList 8 | from .entity_def import EntityDefinition 9 | from appd.time import from_ts 10 | 11 | 12 | class PolicyViolation(JsonObject): 13 | 14 | FIELDS = {'id': '', 15 | 'name': '', 16 | 'description': '', 17 | 'status': 'incidentStatus', 18 | 'severity': '', 19 | 'start_time_ms': 'startTimeInMillis', 20 | 'end_time_ms': 'endTimeInMillis', 21 | 'detected_time_ms': 'detectedTimeInMillis', 22 | 'deep_link_url': 'deepLinkUrl'} 23 | 24 | STATUSES = ('NOT_APPLICABLE', 'OPEN', 'RESOLVED', 'CANCELLED') 25 | 26 | SEVERITIES = ('INFO', 'WARNING', 'CRITICAL') 27 | 28 | def __init__(self, pv_id=0, pv_name='', description='', status='OPEN', severity='INFO', 29 | start_time_ms=0, end_time_ms=0, detected_time_ms=0, deep_link_url='', 30 | affected_entity=None, triggered_entity=None): 31 | self._severity = None 32 | self._status = None 33 | (self.id, self.name, self.description, self.status, self.severity, self.start_time_ms, self.end_time_ms, 34 | self.detected_time_ms, self.deep_link_url, self.affected_entity, self.triggered_entity) = \ 35 | (pv_id, pv_name, description, status, severity, start_time_ms, end_time_ms, detected_time_ms, 36 | deep_link_url, affected_entity, triggered_entity) 37 | 38 | @classmethod 39 | def _set_fields_from_json_dict(cls, obj, json_dict): 40 | JsonObject._set_fields_from_json_dict(obj, json_dict) 41 | obj.affected_entity = EntityDefinition.from_json(json_dict['affectedEntityDefinition']) 42 | obj.triggered_entity = EntityDefinition.from_json(json_dict['triggeredEntityDefinition']) 43 | 44 | @property 45 | def status(self): 46 | return self._status 47 | 48 | @status.setter 49 | def status(self, new_status): 50 | self._list_setter('_status', new_status, PolicyViolation.STATUSES) 51 | 52 | @property 53 | def severity(self): 54 | return self._severity 55 | 56 | @severity.setter 57 | def severity(self, new_sev): 58 | self._list_setter('_severity', new_sev, PolicyViolation.SEVERITIES) 59 | 60 | @property 61 | def start_time(self): 62 | return from_ts(self.start_time_ms) 63 | 64 | @property 65 | def end_time(self): 66 | """ 67 | Gets the end time of the violation, converting it from an AppDynamics timestamp to standard 68 | Python :class:datetime. 69 | 70 | :return: Time the violation was resolved 71 | :rtype: datetime 72 | """ 73 | return from_ts(self.end_time_ms) 74 | 75 | @property 76 | def detected_time(self): 77 | return from_ts(self.detected_time_ms) 78 | 79 | 80 | class PolicyViolations(JsonList): 81 | 82 | def __init__(self, initial_list=None): 83 | super(PolicyViolations, self).__init__(PolicyViolation, initial_list) 84 | 85 | def __getitem__(self, i): 86 | """ 87 | :rtype: PolicyViolation 88 | """ 89 | return self.data[i] 90 | -------------------------------------------------------------------------------- /appd/model/set_controller_url.py: -------------------------------------------------------------------------------- 1 | """ 2 | Model classes for AppDynamics REST API 3 | 4 | .. moduleauthor:: Kyle Furlong 5 | """ 6 | 7 | from . import JsonObject, JsonList 8 | 9 | 10 | class SetControllerUrl(JsonObject): 11 | 12 | FIELDS = {'controllerURL': ''} 13 | 14 | def __init__(self, controllerURL=''): 15 | (self.controllerURL) = (controllerURL) 16 | 17 | 18 | class SetControllerUrlResponse(JsonList): 19 | 20 | def __init__(self, initial_list=None): 21 | super(SetControllerUrlResponse, self).__init__(SetControllerUrl, initial_list) 22 | 23 | def __getitem__(self, i): 24 | """ 25 | :rtype: Node 26 | """ 27 | return self.data[i] 28 | -------------------------------------------------------------------------------- /appd/model/snapshot.py: -------------------------------------------------------------------------------- 1 | """ 2 | Model classes for AppDynamics REST API 3 | 4 | .. moduleauthor:: Todd Radel 5 | """ 6 | 7 | from . import JsonObject, JsonList 8 | from appd.time import from_ts 9 | 10 | 11 | class Snapshot(JsonObject): 12 | 13 | FIELDS = {'id': '', 'local_id': 'localID', 'request_guid': 'requestGUID', 'summary': '', 14 | 'bt_id': 'businessTransactionId', 'app_id': 'applicationId', 15 | 'url': 'URL', 'archived': '', 'async': '', 'stall_dump': 'stallDump', 16 | 'call_chain': 'callChain', 'is_first_in_chain': 'firstInChain', 17 | 'diag_session_guid': 'diagnosticSessionGUID', 'exit_sequence': 'snapshotExitSequence', 18 | 'exit_calls_truncated': 'exitCallsDataTruncated', 19 | 'exit_calls_truncated_msg': 'exitCallsDataTruncationMessage', 20 | 'app_component_id': 'applicationComponentId', 'app_component_node_id': 'applicationComponentNodeId', 21 | 'local_start_time_ms': 'localStartTime', 'server_start_time_ms': 'serverStartTime', 22 | 'thread_id': 'threadID', 'thread_name': 'threadName', 23 | 'http_headers': 'httpHeaders', 'response_headers': 'responseHeaders', 24 | 'http_params': 'httpParameters', 'cookies': '', 25 | 'http_session_id': 'httpSessionID', 'session_keys': 'sessionKeys', 'business_data': 'businessData', 26 | 'error_ids': 'errorIDs', 'error_occurred': 'errorOccured', 'error_summary': 'errorSummary', 27 | 'error_details': 'errorDetails', 'log_messages': 'logMessages', 28 | 'bt_events': 'transactionEvents', 'bt_properties': 'transactionProperties', 29 | 'dotnet_properties': 'dotnetProperty', 30 | 'has_deep_dive_data': 'hasDeepDiveData', 'deep_dive_policy': 'deepDivePolicy', 31 | 'is_delayed_deep_dive': 'delayedDeepDive', 'delayed_deep_dive_offset': 'delayedDeepDiveOffSet', 32 | 'has_unresolved_calls': 'unresolvedCallInCallChain', 33 | 'time_taken_ms': 'timeTakenInMilliSecs', 'cpu_time_taken_ms': 'cpuTimeTakenInMilliSecs', 34 | 'warning_threshold': 'warningThreshold', 'critical_threshold': 'criticalThreshold', 35 | 'user_experience': 'userExperience'} 36 | 37 | def __init__(self, snap_id=0, **kwargs): 38 | self.id = snap_id 39 | self.local_start_time_ms, self.server_start_time_ms = 0, 0 40 | for k, v in list(Snapshot.FIELDS.items()): 41 | self.__setattr__(k, kwargs.get(k, None)) 42 | 43 | @property 44 | def local_start_time(self): 45 | return from_ts(self.local_start_time_ms) 46 | 47 | @property 48 | def server_start_time(self): 49 | return from_ts(self.server_start_time_ms) 50 | 51 | 52 | class Snapshots(JsonList): 53 | 54 | def __init__(self, initial_list=None): 55 | super(Snapshots, self).__init__(Snapshot, initial_list) 56 | 57 | def __getitem__(self, i): 58 | """ 59 | :rtype: Snapshot 60 | """ 61 | return self.data[i] 62 | -------------------------------------------------------------------------------- /appd/model/tier.py: -------------------------------------------------------------------------------- 1 | """ 2 | Model classes for AppDynamics REST API 3 | 4 | .. moduleauthor:: Todd Radel 5 | """ 6 | 7 | from . import JsonObject, JsonList 8 | 9 | 10 | class Tier(JsonObject): 11 | 12 | FIELDS = {'id': '', 'name': '', 'description': '', 'type': '', 13 | 'node_count': 'numberOfNodes', 'agent_type': 'agentType'} 14 | AGENT_TYPES = ('APP_AGENT', 'MACHINE_AGENT', 15 | 'DOT_NET_APP_AGENT', 'DOT_NET_MACHINE_AGENT', 16 | 'PHP_APP_AGENT', 'PHP_MACHINE_AGENT', 17 | 'NODEJS_APP_AGENT','NODEJS_MACHINE_AGENT', 18 | 'PYTHON_APP_AGENT', 19 | 'NATIVE_APP_AGENT','NATIVE_SDK', 'NATIVE_DYNAMIC','NATIVE_WEB_SERVER', 20 | 'DB_AGENT','DB_COLLECTOR', 21 | 'RUBY_APP_AGENT', 22 | 'SIM_MACHINE_AGENT','APM_MACHINE_AGENT','SERVICE_AVAIL_MACHINE_AGENT', 23 | 'APM_APP_AGENT', 24 | 'ANALYTICS_AGENT', 25 | 'GOLANG_SDK', 26 | 'WMB_AGENT') 27 | 28 | def __init(self, tier_id=0, name='', description='', agent_type='JAVA_AGENT', node_count=0, 29 | tier_type='Java Application Server'): 30 | self._agent_type = None 31 | self.id, self.name, self.description, self.agent_type, self.node_count, self.type = \ 32 | tier_id, name, description, agent_type, node_count, tier_type 33 | 34 | @property 35 | def agent_type(self): 36 | """ 37 | :rtype: str 38 | """ 39 | return self._agent_type 40 | 41 | @agent_type.setter 42 | def agent_type(self, agent_type): 43 | self._list_setter('_agent_type', agent_type, Tier.AGENT_TYPES) 44 | 45 | 46 | class Tiers(JsonList): 47 | 48 | def __init__(self, initial_list=None): 49 | super(Tiers, self).__init__(Tier, initial_list) 50 | 51 | def __getitem__(self, i): 52 | """ 53 | :rtype: Tier 54 | """ 55 | return self.data[i] 56 | 57 | def by_agent_type(self, agent_type): 58 | """ 59 | Searches for tiers of a particular type (which should be one of Tier.AGENT_TYPES). For example, to find 60 | all the Java app server tiers: 61 | 62 | >>> from appd.request import AppDynamicsClient 63 | >>> client = AppDynamicsClient(...) 64 | >>> all_tiers = client.get_tiers() 65 | >>> java_tiers = all_tiers.by_agent_type('APP_AGENT') 66 | 67 | :returns: a Tiers object containing any tiers matching the criteria 68 | :rtype: Tiers 69 | """ 70 | return Tiers([x for x in self.data if x.agentType == agent_type]) 71 | -------------------------------------------------------------------------------- /appd/time.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module contains the main classes for handling requests to the AppDynamics REST API. 3 | """ 4 | 5 | from __future__ import absolute_import 6 | 7 | from datetime import datetime 8 | from time import mktime 9 | 10 | 11 | def from_ts(ms): 12 | """ 13 | Converts a timestamp from AppDynamics internal format to a Python :class:`datetime ` object. 14 | 15 | :param long ms: Timestamp expressed as milliseconds since epoch. 16 | :returns: Converted value. 17 | :rtype: datetime 18 | """ 19 | 20 | return datetime.fromtimestamp(ms / 1000) 21 | 22 | 23 | def to_ts(dt): 24 | """ 25 | Converts a timestamp from Python :class:`datetime ` to AppDynamics format. 26 | 27 | :param datetime dt: Timestamp to convert. 28 | :returns: Converted value. 29 | :rtype: long 30 | """ 31 | 32 | return int(mktime(dt.timetuple())) * 1000 33 | 34 | 35 | -------------------------------------------------------------------------------- /data/backend_metrics.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ACME Book Store Application 5 | Discovered backend call - APPDY-MySQL DB-LOCALHOST 6 | 0 7 | 0 8 | 0 9 | 0 10 | 11 | 0 12 | 0 13 | 0 14 | 15 | 16 | 17 | ACME Book Store Application 18 | Discovered backend call - Active MQ-OrderQueue 19 | 0 20 | 0 21 | 0 22 | 0 23 | 24 | 0 25 | 0 26 | 0 27 | 28 | 29 | 30 | ACME Book Store Application 31 | Discovered backend call - INVENTORY-MySQL DB-LOCALHOST 32 | 0 33 | 0 34 | 0 35 | 0 36 | 37 | 0 38 | 0 39 | 0 40 | 41 | 42 | 43 | ACME Book Store Application 44 | Discovered backend call - Oracle - 10.0.0 45 | 0 46 | 0 47 | 0 48 | 0 49 | 50 | 0 51 | 0 52 | 0 53 | 54 | 55 | 56 | ACME Book Store Application 57 | Inventory Server 58 | 0 59 | 0 60 | 0 61 | 0 62 | 63 | 0 64 | 0 65 | 0 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /data/bt_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ACME Book Store Application 5 | /appdynamicspilot/ 6 | ECommerce Server 7 | 23 8 | 50 9 | 750 10 | 0 11 | 0 12 | 0.0 13 | 0 14 | 0 15 | 0 16 | 17 | 18 | ACME Book Store Application 19 | UserLogOut.memberLogOut 20 | ECommerce Server 21 | 8 22 | 74 23 | 1110 24 | 0 25 | 0 26 | 0.0 27 | 0 28 | 0 29 | 0 30 | 31 | 32 | ACME Book Store Application 33 | UserLogin.memberLogin 34 | ECommerce Server 35 | 14 36 | 50 37 | 750 38 | 0 39 | 0 40 | 0.0 41 | 0 42 | 0 43 | 0 44 | 45 | 46 | ACME Book Store Application 47 | ViewCart.addToCart 48 | ECommerce Server 49 | 23 50 | 50 51 | 750 52 | 0 53 | 0 54 | 0.0 55 | 0 56 | 0 57 | 0 58 | 59 | 60 | ACME Book Store Application 61 | ViewCart.sendItems 62 | ECommerce Server 63 | 487 64 | 74 65 | 1110 66 | 30 67 | 2 68 | 2.7 69 | 0 70 | 1 71 | 0 72 | 73 | 74 | ACME Book Store Application 75 | ViewItems.getAllItems 76 | ECommerce Server 77 | 23 78 | 50 79 | 750 80 | 0 81 | 0 82 | 0.0 83 | 0 84 | 0 85 | 0 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /data/business_transactions.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "background": false, 4 | "entryPointType": "ASP_DOTNET", 5 | "id": 234, 6 | "internalName": "/consumer/ProductFetch.aspx", 7 | "name": "Fetch Product", 8 | "tierId": 18, 9 | "tierName": "Consumer Web" 10 | }, 11 | { 12 | "background": false, 13 | "entryPointType": "ASP_DOTNET", 14 | "id": 235, 15 | "internalName": "/consumer/UpdateOrders.aspx", 16 | "name": "Update Orders", 17 | "tierId": 18, 18 | "tierName": "Consumer Web" 19 | }, 20 | { 21 | "background": false, 22 | "entryPointType": "ASP_DOTNET", 23 | "id": 238, 24 | "internalName": "/consumer/login.aspx", 25 | "name": "Login", 26 | "tierId": 18, 27 | "tierName": "Consumer Web" 28 | }, 29 | { 30 | "background": false, 31 | "entryPointType": "ASP_DOTNET", 32 | "id": 239, 33 | "internalName": "/consumer/logout.aspx", 34 | "name": "Logout", 35 | "tierId": 18, 36 | "tierName": "Consumer Web" 37 | }, 38 | { 39 | "background": false, 40 | "entryPointType": "ASP_DOTNET", 41 | "id": 241, 42 | "internalName": "/consumer/WebResource.axd", 43 | "name": "/consumer/WebResource.axd", 44 | "tierId": 18, 45 | "tierName": "Consumer Web" 46 | }, 47 | { 48 | "background": false, 49 | "entryPointType": "WCF", 50 | "id": 245, 51 | "internalName": "OrderService.SaveOrder", 52 | "name": "OrderService.SaveOrder", 53 | "tierId": 20, 54 | "tierName": "Order Service" 55 | }, 56 | { 57 | "background": false, 58 | "entryPointType": "WCF", 59 | "id": 246, 60 | "internalName": "OrderService.GetOrderById", 61 | "name": "OrderService.GetOrderById", 62 | "tierId": 20, 63 | "tierName": "Order Service" 64 | }, 65 | { 66 | "background": false, 67 | "entryPointType": "ASP_DOTNET_WEB_SERVICE", 68 | "id": 248, 69 | "internalName": "DocumentationServerProtocol.Documentation", 70 | "name": "DocumentationServerProtocol.Documentation", 71 | "tierId": 19, 72 | "tierName": "Product" 73 | }, 74 | { 75 | "background": false, 76 | "entryPointType": "ASP_DOTNET", 77 | "id": 249, 78 | "internalName": "/ProductService.asmx", 79 | "name": "/ProductService.asmx", 80 | "tierId": 19, 81 | "tierName": "Product" 82 | }, 83 | { 84 | "background": false, 85 | "entryPointType": "ASP_DOTNET", 86 | "id": 250, 87 | "internalName": "/consumer/default.aspx", 88 | "name": "/consumer/default.aspx", 89 | "tierId": 18, 90 | "tierName": "Consumer Web" 91 | }, 92 | { 93 | "background": false, 94 | "entryPointType": "WCF", 95 | "id": 254, 96 | "internalName": "QueueService.SendOrderIdtoQueue", 97 | "name": "QueueService.SendOrderIdtoQueue", 98 | "tierId": 22, 99 | "tierName": "Queue Service" 100 | }, 101 | { 102 | "background": false, 103 | "entryPointType": "WCF", 104 | "id": 255, 105 | "internalName": "Fedex_Service.GetMultiply", 106 | "name": "Fedex_Service.GetMultiply", 107 | "tierId": 21, 108 | "tierName": "Fedex Service" 109 | }, 110 | { 111 | "background": false, 112 | "entryPointType": "WCF", 113 | "id": 256, 114 | "internalName": "Fedex_Service.GetShippingPrice", 115 | "name": "Fedex_Service.GetShippingPrice", 116 | "tierId": 21, 117 | "tierName": "Fedex Service" 118 | }, 119 | { 120 | "background": false, 121 | "entryPointType": "ASP_DOTNET_WEB_SERVICE", 122 | "id": 260, 123 | "internalName": "ProductService.GetAllProducts", 124 | "name": "ProductService.GetAllProducts", 125 | "tierId": 19, 126 | "tierName": "Product" 127 | }, 128 | { 129 | "background": false, 130 | "entryPointType": "ASP_DOTNET", 131 | "id": 261, 132 | "internalName": "/image/category.html", 133 | "name": "/image/category.html", 134 | "tierId": 19, 135 | "tierName": "Product" 136 | }, 137 | { 138 | "background": false, 139 | "entryPointType": "ASP_DOTNET_WEB_SERVICE", 140 | "id": 262, 141 | "internalName": "ProductService.GetAllSuppliers", 142 | "name": "ProductService.GetAllSuppliers", 143 | "tierId": 19, 144 | "tierName": "Product" 145 | }, 146 | { 147 | "background": false, 148 | "entryPointType": "ASP_DOTNET", 149 | "id": 265, 150 | "internalName": "/consumer/home.aspx", 151 | "name": "/consumer/home.aspx", 152 | "tierId": 18, 153 | "tierName": "Consumer Web" 154 | }, 155 | { 156 | "background": false, 157 | "entryPointType": "ASP_DOTNET", 158 | "id": 266, 159 | "internalName": "/consumer/products.aspx", 160 | "name": "/consumer/products.aspx", 161 | "tierId": 18, 162 | "tierName": "Consumer Web" 163 | }, 164 | { 165 | "background": false, 166 | "entryPointType": "ASP_DOTNET", 167 | "id": 267, 168 | "internalName": "/consumer/cart.aspx", 169 | "name": "/consumer/cart.aspx", 170 | "tierId": 18, 171 | "tierName": "Consumer Web" 172 | }, 173 | { 174 | "background": false, 175 | "entryPointType": "ASP_DOTNET", 176 | "id": 268, 177 | "internalName": "/consumer/order-detail.aspx", 178 | "name": "/consumer/order-detail.aspx", 179 | "tierId": 18, 180 | "tierName": "Consumer Web" 181 | }, 182 | { 183 | "background": false, 184 | "entryPointType": "ASP_DOTNET", 185 | "id": 269, 186 | "internalName": "/consumer/thank-you.aspx", 187 | "name": "/consumer/thank-you.aspx", 188 | "tierId": 18, 189 | "tierName": "Consumer Web" 190 | }, 191 | { 192 | "background": false, 193 | "entryPointType": "ASP_DOTNET", 194 | "id": 270, 195 | "internalName": "/consumer/order-confirm.aspx", 196 | "name": "/consumer/order-confirm.aspx", 197 | "tierId": 18, 198 | "tierName": "Consumer Web" 199 | }, 200 | { 201 | "background": false, 202 | "entryPointType": "ASP_DOTNET_WEB_SERVICE", 203 | "id": 337, 204 | "internalName": "ProductService.GetAllCategories", 205 | "name": "ProductService.GetAllCategories", 206 | "tierId": 19, 207 | "tierName": "Product" 208 | }, 209 | { 210 | "background": false, 211 | "entryPointType": "ASP_DOTNET", 212 | "id": 346, 213 | "internalName": "/consumer/misc.aspx", 214 | "name": "Checkout", 215 | "tierId": 18, 216 | "tierName": "Consumer Web" 217 | }, 218 | { 219 | "background": false, 220 | "entryPointType": "ASP_DOTNET", 221 | "id": 369, 222 | "internalName": "/consumer/SaveOrder.aspx", 223 | "name": "Save Order", 224 | "tierId": 18, 225 | "tierName": "Consumer Web" 226 | }, 227 | { 228 | "background": false, 229 | "entryPointType": "ASP_DOTNET", 230 | "id": 378, 231 | "internalName": "/consumer/SaveOrder.aspx", 232 | "name": "/consumer/SaveOrder.aspx", 233 | "tierId": 19, 234 | "tierName": "Product" 235 | }, 236 | { 237 | "background": false, 238 | "entryPointType": "ASP_DOTNET", 239 | "id": 380, 240 | "internalName": "/consumer/ProductFetch.aspx", 241 | "name": "/consumer/ProductFetch.aspx", 242 | "tierId": 19, 243 | "tierName": "Product" 244 | }, 245 | { 246 | "background": false, 247 | "entryPointType": "ASP_DOTNET", 248 | "id": 381, 249 | "internalName": "/consumer/misc.aspx", 250 | "name": "/consumer/misc.aspx", 251 | "tierId": 19, 252 | "tierName": "Product" 253 | }, 254 | { 255 | "background": false, 256 | "entryPointType": "ASP_DOTNET", 257 | "id": 382, 258 | "internalName": "/consumer/UpdateOrders.aspx", 259 | "name": "/consumer/UpdateOrders.aspx", 260 | "tierId": 19, 261 | "tierName": "Product" 262 | }, 263 | { 264 | "background": false, 265 | "entryPointType": "ASP_DOTNET", 266 | "id": 384, 267 | "internalName": "/consumer/login.aspx", 268 | "name": "/consumer/login.aspx", 269 | "tierId": 19, 270 | "tierName": "Product" 271 | }, 272 | { 273 | "background": false, 274 | "entryPointType": "ASP_DOTNET", 275 | "id": 385, 276 | "internalName": "/consumer/logout.aspx", 277 | "name": "/consumer/logout.aspx", 278 | "tierId": 19, 279 | "tierName": "Product" 280 | } 281 | ] -------------------------------------------------------------------------------- /data/licenses_by_host.csv: -------------------------------------------------------------------------------- 1 | "App Name","Host Name","Host Type","License Count" 2 | "NWTraders","WIN-DEMO2",".NET App Agent",1 3 | "Acme Online Book Store EUM2","cart-machine","Java App Agent",4 4 | "Magento","ip-10-245-62-247","Java App Agent",1 5 | -------------------------------------------------------------------------------- /data/licenses_by_host.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Appdynamics/AppDynamicsREST/b6a5ebebc511691836584816b10d6e28f2950599/data/licenses_by_host.xlsx -------------------------------------------------------------------------------- /data/metric_tree_0.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Business Transaction Performance", 4 | "type": "folder" 5 | }, 6 | { 7 | "name": "Errors", 8 | "type": "folder" 9 | }, 10 | { 11 | "name": "Information Points", 12 | "type": "folder" 13 | }, 14 | { 15 | "name": "Backends", 16 | "type": "folder" 17 | }, 18 | { 19 | "name": "Application Infrastructure Performance", 20 | "type": "folder" 21 | }, 22 | { 23 | "name": "Overall Application Performance", 24 | "type": "folder" 25 | }, 26 | { 27 | "name": "End User Monitoring", 28 | "type": "folder" 29 | } 30 | ] -------------------------------------------------------------------------------- /data/metric_tree_1.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Average End to End Latency (ms)", 4 | "type": "leaf" 5 | }, 6 | { 7 | "name": "Average Response Time (ms)", 8 | "type": "leaf" 9 | }, 10 | { 11 | "name": "Calls per Minute", 12 | "type": "leaf" 13 | }, 14 | { 15 | "name": "End to End Messages per Minute", 16 | "type": "leaf" 17 | }, 18 | { 19 | "name": "Error Page Redirects per Minute", 20 | "type": "leaf" 21 | }, 22 | { 23 | "name": "Errors per Minute", 24 | "type": "leaf" 25 | }, 26 | { 27 | "name": "Exceptions per Minute", 28 | "type": "leaf" 29 | }, 30 | { 31 | "name": "HTTP Error Codes per Minute", 32 | "type": "leaf" 33 | }, 34 | { 35 | "name": "Infrastructure Errors per Minute", 36 | "type": "leaf" 37 | }, 38 | { 39 | "name": "Normal Average Response Time (ms)", 40 | "type": "leaf" 41 | }, 42 | { 43 | "name": "Number of Slow Calls", 44 | "type": "leaf" 45 | }, 46 | { 47 | "name": "Number of Slow End to End Messages", 48 | "type": "leaf" 49 | }, 50 | { 51 | "name": "Number of Very Slow Calls", 52 | "type": "leaf" 53 | }, 54 | { 55 | "name": "Stall Count", 56 | "type": "leaf" 57 | }, 58 | { 59 | "name": "Fedex Service", 60 | "type": "folder" 61 | }, 62 | { 63 | "name": "Order Service", 64 | "type": "folder" 65 | }, 66 | { 67 | "name": "Product", 68 | "type": "folder" 69 | }, 70 | { 71 | "name": "Queue Service", 72 | "type": "folder" 73 | }, 74 | { 75 | "name": "Consumer Web", 76 | "type": "folder" 77 | } 78 | ] -------------------------------------------------------------------------------- /data/metric_tree_2.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Average Response Time (ms)", 4 | "type": "leaf" 5 | }, 6 | { 7 | "name": "Calls per Minute", 8 | "type": "leaf" 9 | }, 10 | { 11 | "name": "Error Page Redirects per Minute", 12 | "type": "leaf" 13 | }, 14 | { 15 | "name": "Errors per Minute", 16 | "type": "leaf" 17 | }, 18 | { 19 | "name": "Exceptions per Minute", 20 | "type": "leaf" 21 | }, 22 | { 23 | "name": "HTTP Error Codes per Minute", 24 | "type": "leaf" 25 | }, 26 | { 27 | "name": "Infrastructure Errors per Minute", 28 | "type": "leaf" 29 | }, 30 | { 31 | "name": "Number of Slow Calls", 32 | "type": "leaf" 33 | }, 34 | { 35 | "name": "Number of Very Slow Calls", 36 | "type": "leaf" 37 | }, 38 | { 39 | "name": "Stall Count", 40 | "type": "leaf" 41 | }, 42 | { 43 | "name": "External Calls", 44 | "type": "folder" 45 | }, 46 | { 47 | "name": "Thread Tasks", 48 | "type": "folder" 49 | }, 50 | { 51 | "name": "Individual Nodes", 52 | "type": "folder" 53 | } 54 | ] -------------------------------------------------------------------------------- /data/nodes.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "appAgentPresent": true, 4 | "appAgentVersion": "3.7.0.231", 5 | "id": 20, 6 | "ipAddresses": null, 7 | "machineAgentPresent": true, 8 | "machineAgentVersion": "3.7.0.231", 9 | "machineId": 8, 10 | "machineName": "WIN-DEMO2", 11 | "machineOSType": "Other", 12 | "name": "Fedex Service-WIN-DEMO2-NwTraders-0-1-0", 13 | "nodeUniqueLocalId": "", 14 | "tierId": 10, 15 | "tierName": "Fedex Service", 16 | "type": "IIS 7" 17 | }, 18 | { 19 | "appAgentPresent": true, 20 | "appAgentVersion": "3.7.0.231", 21 | "id": 21, 22 | "ipAddresses": null, 23 | "machineAgentPresent": true, 24 | "machineAgentVersion": "3.7.0.231", 25 | "machineId": 8, 26 | "machineName": "WIN-DEMO2", 27 | "machineOSType": "Other", 28 | "name": "Order Service-WIN-DEMO2-NwTraders-0-1-2", 29 | "nodeUniqueLocalId": "", 30 | "tierId": 9, 31 | "tierName": "Order Service", 32 | "type": "IIS 7" 33 | }, 34 | { 35 | "appAgentPresent": true, 36 | "appAgentVersion": "3.7.0.231", 37 | "id": 23, 38 | "ipAddresses": null, 39 | "machineAgentPresent": true, 40 | "machineAgentVersion": "3.7.0.231", 41 | "machineId": 8, 42 | "machineName": "WIN-DEMO2", 43 | "machineOSType": "Other", 44 | "name": "Consumer Web-WIN-DEMO2-NwTraders-0-1-3", 45 | "nodeUniqueLocalId": "", 46 | "tierId": 7, 47 | "tierName": "Consumer Web", 48 | "type": "IIS 7" 49 | }, 50 | { 51 | "appAgentPresent": true, 52 | "appAgentVersion": "3.7.0.231", 53 | "id": 31, 54 | "ipAddresses": null, 55 | "machineAgentPresent": true, 56 | "machineAgentVersion": "3.7.0.231", 57 | "machineId": 8, 58 | "machineName": "WIN-DEMO2", 59 | "machineOSType": "Other", 60 | "name": "Order Service-WIN-DEMO2-NwTraders-1-1-0", 61 | "nodeUniqueLocalId": "", 62 | "tierId": 9, 63 | "tierName": "Order Service", 64 | "type": "IIS 7" 65 | }, 66 | { 67 | "appAgentPresent": true, 68 | "appAgentVersion": "3.7.0.231", 69 | "id": 32, 70 | "ipAddresses": null, 71 | "machineAgentPresent": true, 72 | "machineAgentVersion": "3.7.0.231", 73 | "machineId": 8, 74 | "machineName": "WIN-DEMO2", 75 | "machineOSType": "Other", 76 | "name": "Consumer Web-WIN-DEMO2-NwTraders-1-1-1", 77 | "nodeUniqueLocalId": "", 78 | "tierId": 7, 79 | "tierName": "Consumer Web", 80 | "type": "IIS 7" 81 | }, 82 | { 83 | "appAgentPresent": true, 84 | "appAgentVersion": "3.7.0.231", 85 | "id": 33, 86 | "ipAddresses": null, 87 | "machineAgentPresent": true, 88 | "machineAgentVersion": "3.7.0.231", 89 | "machineId": 8, 90 | "machineName": "WIN-DEMO2", 91 | "machineOSType": "Other", 92 | "name": "Product-WIN-DEMO2-NwTraders-1-1-2", 93 | "nodeUniqueLocalId": "", 94 | "tierId": 8, 95 | "tierName": "Product", 96 | "type": "IIS 7" 97 | }, 98 | { 99 | "appAgentPresent": true, 100 | "appAgentVersion": "3.7.0.231", 101 | "id": 35, 102 | "ipAddresses": null, 103 | "machineAgentPresent": true, 104 | "machineAgentVersion": "3.7.0.231", 105 | "machineId": 8, 106 | "machineName": "WIN-DEMO2", 107 | "machineOSType": "Other", 108 | "name": "Queue Service-WIN-DEMO2-NwTraders-1-1-4", 109 | "nodeUniqueLocalId": "", 110 | "tierId": 11, 111 | "tierName": "Queue Service", 112 | "type": "IIS 7" 113 | }, 114 | { 115 | "appAgentPresent": true, 116 | "appAgentVersion": "3.7.0.231", 117 | "id": 36, 118 | "ipAddresses": null, 119 | "machineAgentPresent": true, 120 | "machineAgentVersion": "3.7.0.231", 121 | "machineId": 8, 122 | "machineName": "WIN-DEMO2", 123 | "machineOSType": "Other", 124 | "name": "Consumer Web-WIN-DEMO2-NwTraders-0-1-0", 125 | "nodeUniqueLocalId": "", 126 | "tierId": 7, 127 | "tierName": "Consumer Web", 128 | "type": "IIS 7" 129 | }, 130 | { 131 | "appAgentPresent": true, 132 | "appAgentVersion": "3.7.0.231", 133 | "id": 37, 134 | "ipAddresses": null, 135 | "machineAgentPresent": true, 136 | "machineAgentVersion": "3.7.0.231", 137 | "machineId": 8, 138 | "machineName": "WIN-DEMO2", 139 | "machineOSType": "Other", 140 | "name": "Product-WIN-DEMO2-NwTraders-0-1-1", 141 | "nodeUniqueLocalId": "", 142 | "tierId": 8, 143 | "tierName": "Product", 144 | "type": "IIS 7" 145 | }, 146 | { 147 | "appAgentPresent": true, 148 | "appAgentVersion": "3.7.0.231", 149 | "id": 40, 150 | "ipAddresses": null, 151 | "machineAgentPresent": true, 152 | "machineAgentVersion": "3.7.0.231", 153 | "machineId": 8, 154 | "machineName": "WIN-DEMO2", 155 | "machineOSType": "Other", 156 | "name": "Queue Service-WIN-DEMO2-NwTraders-0-1-4", 157 | "nodeUniqueLocalId": "", 158 | "tierId": 11, 159 | "tierName": "Queue Service", 160 | "type": "IIS 7" 161 | }, 162 | { 163 | "appAgentPresent": true, 164 | "appAgentVersion": "3.7.0.231", 165 | "id": 41, 166 | "ipAddresses": null, 167 | "machineAgentPresent": true, 168 | "machineAgentVersion": "3.7.0.231", 169 | "machineId": 8, 170 | "machineName": "WIN-DEMO2", 171 | "machineOSType": "Other", 172 | "name": "Fedex Service-WIN-DEMO2-NwTraders-0-1-3", 173 | "nodeUniqueLocalId": "", 174 | "tierId": 10, 175 | "tierName": "Fedex Service", 176 | "type": "IIS 7" 177 | }, 178 | { 179 | "appAgentPresent": true, 180 | "appAgentVersion": "3.7.0.231", 181 | "id": 44, 182 | "ipAddresses": null, 183 | "machineAgentPresent": true, 184 | "machineAgentVersion": "3.7.0.231", 185 | "machineId": 8, 186 | "machineName": "WIN-DEMO2", 187 | "machineOSType": "Other", 188 | "name": "Order Service-WIN-DEMO2-NwTraders-1-1-3", 189 | "nodeUniqueLocalId": "", 190 | "tierId": 9, 191 | "tierName": "Order Service", 192 | "type": "IIS 7" 193 | }, 194 | { 195 | "appAgentPresent": true, 196 | "appAgentVersion": "3.7.0.231", 197 | "id": 45, 198 | "ipAddresses": null, 199 | "machineAgentPresent": true, 200 | "machineAgentVersion": "3.7.0.231", 201 | "machineId": 8, 202 | "machineName": "WIN-DEMO2", 203 | "machineOSType": "Other", 204 | "name": "Fedex Service-WIN-DEMO2-NwTraders-1-1-0", 205 | "nodeUniqueLocalId": "", 206 | "tierId": 10, 207 | "tierName": "Fedex Service", 208 | "type": "IIS 7" 209 | }, 210 | { 211 | "appAgentPresent": true, 212 | "appAgentVersion": "3.7.0.231", 213 | "id": 46, 214 | "ipAddresses": null, 215 | "machineAgentPresent": true, 216 | "machineAgentVersion": "3.7.0.231", 217 | "machineId": 8, 218 | "machineName": "WIN-DEMO2", 219 | "machineOSType": "Other", 220 | "name": "Queue Service-WIN-DEMO2-NwTraders-1-1-3", 221 | "nodeUniqueLocalId": "", 222 | "tierId": 11, 223 | "tierName": "Queue Service", 224 | "type": "IIS 7" 225 | }, 226 | { 227 | "appAgentPresent": true, 228 | "appAgentVersion": "3.7.0.231", 229 | "id": 47, 230 | "ipAddresses": null, 231 | "machineAgentPresent": true, 232 | "machineAgentVersion": "3.7.0.231", 233 | "machineId": 8, 234 | "machineName": "WIN-DEMO2", 235 | "machineOSType": "Other", 236 | "name": "Order Service-WIN-DEMO2-NwTraders-1-1-4", 237 | "nodeUniqueLocalId": "", 238 | "tierId": 9, 239 | "tierName": "Order Service", 240 | "type": "IIS 7" 241 | }, 242 | { 243 | "appAgentPresent": true, 244 | "appAgentVersion": "3.7.0.231", 245 | "id": 53, 246 | "ipAddresses": null, 247 | "machineAgentPresent": true, 248 | "machineAgentVersion": "3.7.0.231", 249 | "machineId": 8, 250 | "machineName": "WIN-DEMO2", 251 | "machineOSType": "Other", 252 | "name": "Queue Service-WIN-DEMO2-NwTraders-1-1-0", 253 | "nodeUniqueLocalId": "", 254 | "tierId": 11, 255 | "tierName": "Queue Service", 256 | "type": "IIS 7" 257 | }, 258 | { 259 | "appAgentPresent": true, 260 | "appAgentVersion": "3.7.0.231", 261 | "id": 54, 262 | "ipAddresses": null, 263 | "machineAgentPresent": true, 264 | "machineAgentVersion": "3.7.0.231", 265 | "machineId": 8, 266 | "machineName": "WIN-DEMO2", 267 | "machineOSType": "Other", 268 | "name": "Fedex Service-WIN-DEMO2-NwTraders-1-1-4", 269 | "nodeUniqueLocalId": "", 270 | "tierId": 10, 271 | "tierName": "Fedex Service", 272 | "type": "IIS 7" 273 | }, 274 | { 275 | "appAgentPresent": true, 276 | "appAgentVersion": "3.7.0.231", 277 | "id": 55, 278 | "ipAddresses": null, 279 | "machineAgentPresent": true, 280 | "machineAgentVersion": "3.7.0.231", 281 | "machineId": 8, 282 | "machineName": "WIN-DEMO2", 283 | "machineOSType": "Other", 284 | "name": "Order Service-WIN-DEMO2-NwTraders-1-1-1", 285 | "nodeUniqueLocalId": "", 286 | "tierId": 9, 287 | "tierName": "Order Service", 288 | "type": "IIS 7" 289 | }, 290 | { 291 | "appAgentPresent": true, 292 | "appAgentVersion": "3.7.0.231", 293 | "id": 56, 294 | "ipAddresses": null, 295 | "machineAgentPresent": true, 296 | "machineAgentVersion": "3.7.0.231", 297 | "machineId": 8, 298 | "machineName": "WIN-DEMO2", 299 | "machineOSType": "Other", 300 | "name": "Consumer Web-WIN-DEMO2-NwTraders-1-1-2", 301 | "nodeUniqueLocalId": "", 302 | "tierId": 7, 303 | "tierName": "Consumer Web", 304 | "type": "IIS 7" 305 | }, 306 | { 307 | "appAgentPresent": true, 308 | "appAgentVersion": "3.7.0.231", 309 | "id": 57, 310 | "ipAddresses": null, 311 | "machineAgentPresent": true, 312 | "machineAgentVersion": "3.7.0.231", 313 | "machineId": 8, 314 | "machineName": "WIN-DEMO2", 315 | "machineOSType": "Other", 316 | "name": "Product-WIN-DEMO2-NwTraders-1-1-3", 317 | "nodeUniqueLocalId": "", 318 | "tierId": 8, 319 | "tierName": "Product", 320 | "type": "IIS 7" 321 | }, 322 | { 323 | "appAgentPresent": true, 324 | "appAgentVersion": "3.7.0.231", 325 | "id": 58, 326 | "ipAddresses": null, 327 | "machineAgentPresent": true, 328 | "machineAgentVersion": "3.7.0.231", 329 | "machineId": 8, 330 | "machineName": "WIN-DEMO2", 331 | "machineOSType": "Other", 332 | "name": "Order Service-WIN-DEMO2-NwTraders-1-1-2", 333 | "nodeUniqueLocalId": "", 334 | "tierId": 9, 335 | "tierName": "Order Service", 336 | "type": "IIS 7" 337 | }, 338 | { 339 | "appAgentPresent": true, 340 | "appAgentVersion": "3.7.0.231", 341 | "id": 59, 342 | "ipAddresses": null, 343 | "machineAgentPresent": true, 344 | "machineAgentVersion": "3.7.0.231", 345 | "machineId": 8, 346 | "machineName": "WIN-DEMO2", 347 | "machineOSType": "Other", 348 | "name": "Queue Service-WIN-DEMO2-NwTraders-1-1-1", 349 | "nodeUniqueLocalId": "", 350 | "tierId": 11, 351 | "tierName": "Queue Service", 352 | "type": "IIS 7" 353 | }, 354 | { 355 | "appAgentPresent": true, 356 | "appAgentVersion": "3.7.0.231", 357 | "id": 60, 358 | "ipAddresses": null, 359 | "machineAgentPresent": true, 360 | "machineAgentVersion": "3.7.0.231", 361 | "machineId": 8, 362 | "machineName": "WIN-DEMO2", 363 | "machineOSType": "Other", 364 | "name": "Fedex Service-WIN-DEMO2-NwTraders-1-1-2", 365 | "nodeUniqueLocalId": "", 366 | "tierId": 10, 367 | "tierName": "Fedex Service", 368 | "type": "IIS 7" 369 | }, 370 | { 371 | "appAgentPresent": true, 372 | "appAgentVersion": "3.7.0.231", 373 | "id": 61, 374 | "ipAddresses": null, 375 | "machineAgentPresent": true, 376 | "machineAgentVersion": "3.7.0.231", 377 | "machineId": 8, 378 | "machineName": "WIN-DEMO2", 379 | "machineOSType": "Other", 380 | "name": "Consumer Web-WIN-DEMO2-NwTraders-1-1-3", 381 | "nodeUniqueLocalId": "", 382 | "tierId": 7, 383 | "tierName": "Consumer Web", 384 | "type": "IIS 7" 385 | }, 386 | { 387 | "appAgentPresent": true, 388 | "appAgentVersion": "3.7.0.231", 389 | "id": 62, 390 | "ipAddresses": null, 391 | "machineAgentPresent": true, 392 | "machineAgentVersion": "3.7.0.231", 393 | "machineId": 8, 394 | "machineName": "WIN-DEMO2", 395 | "machineOSType": "Other", 396 | "name": "Product-WIN-DEMO2-NwTraders-1-1-4", 397 | "nodeUniqueLocalId": "", 398 | "tierId": 8, 399 | "tierName": "Product", 400 | "type": "IIS 7" 401 | }, 402 | { 403 | "appAgentPresent": true, 404 | "appAgentVersion": "3.7.0.231", 405 | "id": 63, 406 | "ipAddresses": null, 407 | "machineAgentPresent": true, 408 | "machineAgentVersion": "3.7.0.231", 409 | "machineId": 8, 410 | "machineName": "WIN-DEMO2", 411 | "machineOSType": "Other", 412 | "name": "Queue Service-WIN-DEMO2-NwTraders-0-1-1", 413 | "nodeUniqueLocalId": "", 414 | "tierId": 11, 415 | "tierName": "Queue Service", 416 | "type": "IIS 7" 417 | }, 418 | { 419 | "appAgentPresent": true, 420 | "appAgentVersion": "3.7.0.231", 421 | "id": 64, 422 | "ipAddresses": null, 423 | "machineAgentPresent": true, 424 | "machineAgentVersion": "3.7.0.231", 425 | "machineId": 8, 426 | "machineName": "WIN-DEMO2", 427 | "machineOSType": "Other", 428 | "name": "Product-WIN-DEMO2-NwTraders-0-1-4", 429 | "nodeUniqueLocalId": "", 430 | "tierId": 8, 431 | "tierName": "Product", 432 | "type": "IIS 7" 433 | } 434 | ] -------------------------------------------------------------------------------- /data/tiers.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "agentType": "APP_AGENT", 4 | "description": "", 5 | "id": 12, 6 | "name": "E-Commerce", 7 | "numberOfNodes": 2, 8 | "type": "Application Server" 9 | }, 10 | { 11 | "agentType": "APP_AGENT", 12 | "description": "", 13 | "id": 13, 14 | "name": "Inventory", 15 | "numberOfNodes": 1, 16 | "type": "Application Server" 17 | }, 18 | { 19 | "agentType": "APP_AGENT", 20 | "description": "", 21 | "id": 14, 22 | "name": "Order Processing", 23 | "numberOfNodes": 1, 24 | "type": "Application Server" 25 | } 26 | ] -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | 49 | clean: 50 | rm -rf $(BUILDDIR)/* 51 | 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | dirhtml: 58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 61 | 62 | singlehtml: 63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 64 | @echo 65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 66 | 67 | pickle: 68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 69 | @echo 70 | @echo "Build finished; now you can process the pickle files." 71 | 72 | json: 73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 74 | @echo 75 | @echo "Build finished; now you can process the JSON files." 76 | 77 | htmlhelp: 78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 79 | @echo 80 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 81 | ".hhp project file in $(BUILDDIR)/htmlhelp." 82 | 83 | qthelp: 84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 85 | @echo 86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/AppDynamicsREST.qhcp" 89 | @echo "To view the help file:" 90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/AppDynamicsREST.qhc" 91 | 92 | devhelp: 93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 94 | @echo 95 | @echo "Build finished." 96 | @echo "To view the help file:" 97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/AppDynamicsREST" 98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/AppDynamicsREST" 99 | @echo "# devhelp" 100 | 101 | epub: 102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 103 | @echo 104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 105 | 106 | latex: 107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 108 | @echo 109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 111 | "(use \`make latexpdf' here to do that automatically)." 112 | 113 | latexpdf: 114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 115 | @echo "Running LaTeX files through pdflatex..." 116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 118 | 119 | latexpdfja: 120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 121 | @echo "Running LaTeX files through platex and dvipdfmx..." 122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 124 | 125 | text: 126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 127 | @echo 128 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 129 | 130 | man: 131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 132 | @echo 133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 134 | 135 | texinfo: 136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 137 | @echo 138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 139 | @echo "Run \`make' in that directory to run these through makeinfo" \ 140 | "(use \`make info' here to do that automatically)." 141 | 142 | info: 143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 144 | @echo "Running Texinfo files through makeinfo..." 145 | make -C $(BUILDDIR)/texinfo info 146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 147 | 148 | gettext: 149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 150 | @echo 151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 152 | 153 | changes: 154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 155 | @echo 156 | @echo "The overview file is in $(BUILDDIR)/changes." 157 | 158 | linkcheck: 159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 160 | @echo 161 | @echo "Link check complete; look for any errors in the above output " \ 162 | "or in $(BUILDDIR)/linkcheck/output.txt." 163 | 164 | doctest: 165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 166 | @echo "Testing of doctests in the sources finished, look at the " \ 167 | "results in $(BUILDDIR)/doctest/output.txt." 168 | 169 | xml: 170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 171 | @echo 172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 173 | 174 | pseudoxml: 175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 176 | @echo 177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 178 | -------------------------------------------------------------------------------- /docs/api/cmdline.rst: -------------------------------------------------------------------------------- 1 | ## Command Line Options 2 | 3 | This package includes a module called `appd.cmdline` that provides a simple command-line parser for use 4 | in your scripts. You're not required to use it, but it allows you to point your script at different controllers 5 | without making any code changes, and if you use it consistently, your scripts will all have a common 6 | command-line syntax, which is nice. It supports the following options: 7 | 8 | - **-c** or **--url** for the controller URL. Required. 9 | - **-a** or **--account** for the account name. Optional and defaults to "customer1", which is the account 10 | name on single-tenant controllers. 11 | - **-u** or **--username** for the user name. Required. 12 | - **-p** or **--password** for the password. Required. 13 | - **-v** or **--verbose** will print out the URLs before they are retrieved. 14 | - **-h** or **--help** will display a summary of the command line options. 15 | 16 | The example scripts all use the parser, so you can look at their source to see how to use it. 17 | 18 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # AppDynamicsREST documentation build configuration file, created by 5 | # sphinx-quickstart on Tue Jul 9 22:26:36 2013. 6 | # 7 | # This file is execfile()d with the current directory set to its 8 | # containing dir. 9 | # 10 | # Note that not all possible configuration values are present in this 11 | # autogenerated file. 12 | # 13 | # All configuration values have a default; values that are commented out 14 | # serve to show the default. 15 | 16 | import sys 17 | import os 18 | 19 | # If extensions (or modules to document with autodoc) are in another 20 | # directory, add these directories to sys.path here. If the directory is 21 | # relative to the documentation root, use os.path.abspath to make it 22 | # absolute, like shown here. 23 | #sys.path.insert(0, os.path.abspath('.')) 24 | 25 | # Get the project root dir, which is the parent dir of this 26 | cwd = os.getcwd() 27 | project_root = os.path.dirname(cwd) 28 | 29 | # Insert the project root dir as the first element in the PYTHONPATH. 30 | # This lets us ensure that the source package is imported, and that its 31 | # version is used. 32 | sys.path.insert(0, project_root) 33 | 34 | import appd 35 | 36 | # -- General configuration --------------------------------------------- 37 | 38 | # If your documentation needs a minimal Sphinx version, state it here. 39 | #needs_sphinx = '1.0' 40 | 41 | # Add any Sphinx extension module names here, as strings. They can be 42 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 43 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode'] 44 | 45 | # Add any paths that contain templates here, relative to this directory. 46 | templates_path = ['_templates'] 47 | 48 | # The suffix of source filenames. 49 | source_suffix = '.rst' 50 | 51 | # The encoding of source files. 52 | #source_encoding = 'utf-8-sig' 53 | 54 | # The master toctree document. 55 | master_doc = 'index' 56 | 57 | # General information about the project. 58 | project = u'AppDynamics REST API Library' 59 | copyright = u'2013-2015 AppDynamics' 60 | 61 | # The version info for the project you're documenting, acts as replacement 62 | # for |version| and |release|, also used in various other places throughout 63 | # the built documents. 64 | # 65 | # The short X.Y version. 66 | version = appd.__version__ 67 | # The full version, including alpha/beta/rc tags. 68 | release = appd.__version__ 69 | 70 | # The language for content autogenerated by Sphinx. Refer to documentation 71 | # for a list of supported languages. 72 | #language = None 73 | 74 | # There are two options for replacing |today|: either, you set today to 75 | # some non-false value, then it is used: 76 | #today = '' 77 | # Else, today_fmt is used as the format for a strftime call. 78 | #today_fmt = '%B %d, %Y' 79 | 80 | # List of patterns, relative to source directory, that match files and 81 | # directories to ignore when looking for source files. 82 | exclude_patterns = ['_build'] 83 | 84 | # The reST default role (used for this markup: `text`) to use for all 85 | # documents. 86 | #default_role = None 87 | 88 | # If true, '()' will be appended to :func: etc. cross-reference text. 89 | #add_function_parentheses = True 90 | 91 | # If true, the current module name will be prepended to all description 92 | # unit titles (such as .. function::). 93 | #add_module_names = True 94 | 95 | # If true, sectionauthor and moduleauthor directives will be shown in the 96 | # output. They are ignored by default. 97 | #show_authors = False 98 | 99 | # The name of the Pygments (syntax highlighting) style to use. 100 | pygments_style = 'sphinx' 101 | 102 | # A list of ignored prefixes for module index sorting. 103 | #modindex_common_prefix = [] 104 | 105 | # If true, keep warnings as "system message" paragraphs in the built 106 | # documents. 107 | #keep_warnings = False 108 | 109 | 110 | # -- Options for HTML output ------------------------------------------- 111 | 112 | # The theme to use for HTML and HTML Help pages. See the documentation for 113 | # a list of builtin themes. 114 | html_theme = 'sphnix_rtd_theme' 115 | 116 | # Theme options are theme-specific and customize the look and feel of a 117 | # theme further. For a list of options available for each theme, see the 118 | # documentation. 119 | #html_theme_options = {} 120 | 121 | # Add any paths that contain custom themes here, relative to this directory. 122 | #html_theme_path = [] 123 | 124 | # The name for this set of Sphinx documents. If None, it defaults to 125 | # " v documentation". 126 | #html_title = None 127 | 128 | # A shorter title for the navigation bar. Default is the same as 129 | # html_title. 130 | #html_short_title = None 131 | 132 | # The name of an image file (relative to this directory) to place at the 133 | # top of the sidebar. 134 | #html_logo = None 135 | 136 | # The name of an image file (within the static path) to use as favicon 137 | # of the docs. This file should be a Windows icon file (.ico) being 138 | # 16x16 or 32x32 pixels large. 139 | #html_favicon = None 140 | 141 | # Add any paths that contain custom static files (such as style sheets) 142 | # here, relative to this directory. They are copied after the builtin 143 | # static files, so a file named "default.css" will overwrite the builtin 144 | # "default.css". 145 | html_static_path = ['_static'] 146 | 147 | # If not '', a 'Last updated on:' timestamp is inserted at every page 148 | # bottom, using the given strftime format. 149 | #html_last_updated_fmt = '%b %d, %Y' 150 | 151 | # If true, SmartyPants will be used to convert quotes and dashes to 152 | # typographically correct entities. 153 | #html_use_smartypants = True 154 | 155 | # Custom sidebar templates, maps document names to template names. 156 | #html_sidebars = {} 157 | 158 | # Additional templates that should be rendered to pages, maps page names 159 | # to template names. 160 | #html_additional_pages = {} 161 | 162 | # If false, no module index is generated. 163 | #html_domain_indices = True 164 | 165 | # If false, no index is generated. 166 | #html_use_index = True 167 | 168 | # If true, the index is split into individual pages for each letter. 169 | #html_split_index = False 170 | 171 | # If true, links to the reST sources are added to the pages. 172 | #html_show_sourcelink = True 173 | 174 | # If true, "Created using Sphinx" is shown in the HTML footer. 175 | # Default is True. 176 | #html_show_sphinx = True 177 | 178 | # If true, "(C) Copyright ..." is shown in the HTML footer. 179 | # Default is True. 180 | #html_show_copyright = True 181 | 182 | # If true, an OpenSearch description file will be output, and all pages 183 | # will contain a tag referring to it. The value of this option 184 | # must be the base URL from which the finished HTML is served. 185 | #html_use_opensearch = '' 186 | 187 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 188 | #html_file_suffix = None 189 | 190 | # Output file base name for HTML help builder. 191 | htmlhelp_basename = 'appd_doc' 192 | 193 | 194 | # -- Options for LaTeX output ------------------------------------------ 195 | 196 | latex_elements = { 197 | # The paper size ('letterpaper' or 'a4paper'). 198 | #'papersize': 'letterpaper', 199 | 200 | # The font size ('10pt', '11pt' or '12pt'). 201 | #'pointsize': '10pt', 202 | 203 | # Additional stuff for the LaTeX preamble. 204 | #'preamble': '', 205 | } 206 | 207 | # Grouping the document tree into LaTeX files. List of tuples 208 | # (source start file, target name, title, author, documentclass 209 | # [howto/manual]). 210 | latex_documents = [ 211 | ('index', 'AppDynamicsREST.tex', 212 | u'AppDynamics REST API Library Documentation', 213 | u'Todd Radel', 'manual'), 214 | ] 215 | 216 | # The name of an image file (relative to this directory) to place at 217 | # the top of the title page. 218 | #latex_logo = None 219 | 220 | # For "manual" documents, if this is true, then toplevel headings 221 | # are parts, not chapters. 222 | #latex_use_parts = False 223 | 224 | # If true, show page references after internal links. 225 | #latex_show_pagerefs = False 226 | 227 | # If true, show URL addresses after external links. 228 | #latex_show_urls = False 229 | 230 | # Documents to append as an appendix to all manuals. 231 | #latex_appendices = [] 232 | 233 | # If false, no module index is generated. 234 | #latex_domain_indices = True 235 | 236 | 237 | # -- Options for manual page output ------------------------------------ 238 | 239 | # One entry per manual page. List of tuples 240 | # (source start file, name, description, authors, manual section). 241 | man_pages = [ 242 | ('index', 'AppDynamicsREST', 243 | u'AppDynamics REST API Library Documentation', 244 | [u'Todd Radel'], 1) 245 | ] 246 | 247 | # If true, show URL addresses after external links. 248 | #man_show_urls = False 249 | 250 | 251 | # -- Options for Texinfo output ---------------------------------------- 252 | 253 | # Grouping the document tree into Texinfo files. List of tuples 254 | # (source start file, target name, title, author, 255 | # dir menu entry, description, category) 256 | texinfo_documents = [ 257 | ('index', 'AppDynamicsREST', 258 | u'AppDynamics REST API Library Documentation', 259 | u'Todd Radel', 260 | 'AppDynamicsREST', 261 | 'One line description of project.', 262 | 'Miscellaneous'), 263 | ] 264 | 265 | # Documents to append as an appendix to all manuals. 266 | #texinfo_appendices = [] 267 | 268 | # If false, no module index is generated. 269 | #texinfo_domain_indices = True 270 | 271 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 272 | #texinfo_show_urls = 'footnote' 273 | 274 | # If true, do not generate a @detailmenu in the "Top" node's menu. 275 | #texinfo_no_detailmenu = False 276 | -------------------------------------------------------------------------------- /docs/contributing.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../CONTRIBUTING.rst 2 | -------------------------------------------------------------------------------- /docs/faq.rst: -------------------------------------------------------------------------------- 1 | ## FAQ 2 | 3 | **I get errors like `ImportError: No module named appd.cmdline` when I try to run the examples scripts.** 4 | 5 | You'll see this if you try to run the example scripts before installing the package into your Python `site-packages` 6 | folder. Either follow the installation instructions above, or set the `PYTHONPATH` environment variable before 7 | running the script, like this: 8 | 9 | ``` bash 10 | PYTHONPATH=. python examples/bt_metrics.py 11 | ``` 12 | 13 | **I can't seem to get the authentication right. I keep getting `HTTPError: 401 Client Error: Unauthorized`.** 14 | 15 | Use the same username, password, and account you use when you log into your controller. If your login screen 16 | only has two fields in it (username and password), then you can omit the account. 17 | -------------------------------------------------------------------------------- /docs/history.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../HISTORY.rst 2 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. AppDynamicsREST documentation master file, created by 2 | sphinx-quickstart on Tue Jul 9 22:26:36 2013. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to AppDynamics REST Library's documentation! 7 | ====================================== 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | readme 15 | installation 16 | usage 17 | contributing 18 | authors 19 | history 20 | 21 | Indices and tables 22 | ================== 23 | 24 | * :ref:`genindex` 25 | * :ref:`modindex` 26 | * :ref:`search` 27 | 28 | -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Installation 3 | ============ 4 | 5 | At the command line:: 6 | 7 | $ easy_install AppDynamicsREST 8 | 9 | Or, if you have virtualenvwrapper installed:: 10 | 11 | $ mkvirtualenv AppDynamicsREST 12 | $ pip install AppDynamicsREST 13 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | goto end 41 | ) 42 | 43 | if "%1" == "clean" ( 44 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 45 | del /q /s %BUILDDIR%\* 46 | goto end 47 | ) 48 | 49 | 50 | %SPHINXBUILD% 2> nul 51 | if errorlevel 9009 ( 52 | echo. 53 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 54 | echo.installed, then set the SPHINXBUILD environment variable to point 55 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 56 | echo.may add the Sphinx directory to PATH. 57 | echo. 58 | echo.If you don't have Sphinx installed, grab it from 59 | echo.http://sphinx-doc.org/ 60 | exit /b 1 61 | ) 62 | 63 | if "%1" == "html" ( 64 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 65 | if errorlevel 1 exit /b 1 66 | echo. 67 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 68 | goto end 69 | ) 70 | 71 | if "%1" == "dirhtml" ( 72 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 73 | if errorlevel 1 exit /b 1 74 | echo. 75 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 76 | goto end 77 | ) 78 | 79 | if "%1" == "singlehtml" ( 80 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 81 | if errorlevel 1 exit /b 1 82 | echo. 83 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 84 | goto end 85 | ) 86 | 87 | if "%1" == "pickle" ( 88 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 89 | if errorlevel 1 exit /b 1 90 | echo. 91 | echo.Build finished; now you can process the pickle files. 92 | goto end 93 | ) 94 | 95 | if "%1" == "json" ( 96 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 97 | if errorlevel 1 exit /b 1 98 | echo. 99 | echo.Build finished; now you can process the JSON files. 100 | goto end 101 | ) 102 | 103 | if "%1" == "htmlhelp" ( 104 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 105 | if errorlevel 1 exit /b 1 106 | echo. 107 | echo.Build finished; now you can run HTML Help Workshop with the ^ 108 | .hhp project file in %BUILDDIR%/htmlhelp. 109 | goto end 110 | ) 111 | 112 | if "%1" == "qthelp" ( 113 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 114 | if errorlevel 1 exit /b 1 115 | echo. 116 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 117 | .qhcp project file in %BUILDDIR%/qthelp, like this: 118 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\AppDynamicsREST.qhcp 119 | echo.To view the help file: 120 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\AppDynamicsREST.ghc 121 | goto end 122 | ) 123 | 124 | if "%1" == "devhelp" ( 125 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished. 129 | goto end 130 | ) 131 | 132 | if "%1" == "epub" ( 133 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 134 | if errorlevel 1 exit /b 1 135 | echo. 136 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 137 | goto end 138 | ) 139 | 140 | if "%1" == "latex" ( 141 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 142 | if errorlevel 1 exit /b 1 143 | echo. 144 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 145 | goto end 146 | ) 147 | 148 | if "%1" == "latexpdf" ( 149 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 150 | cd %BUILDDIR%/latex 151 | make all-pdf 152 | cd %BUILDDIR%/.. 153 | echo. 154 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 155 | goto end 156 | ) 157 | 158 | if "%1" == "latexpdfja" ( 159 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 160 | cd %BUILDDIR%/latex 161 | make all-pdf-ja 162 | cd %BUILDDIR%/.. 163 | echo. 164 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 165 | goto end 166 | ) 167 | 168 | if "%1" == "text" ( 169 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 170 | if errorlevel 1 exit /b 1 171 | echo. 172 | echo.Build finished. The text files are in %BUILDDIR%/text. 173 | goto end 174 | ) 175 | 176 | if "%1" == "man" ( 177 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 178 | if errorlevel 1 exit /b 1 179 | echo. 180 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 181 | goto end 182 | ) 183 | 184 | if "%1" == "texinfo" ( 185 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 186 | if errorlevel 1 exit /b 1 187 | echo. 188 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 189 | goto end 190 | ) 191 | 192 | if "%1" == "gettext" ( 193 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 194 | if errorlevel 1 exit /b 1 195 | echo. 196 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 197 | goto end 198 | ) 199 | 200 | if "%1" == "changes" ( 201 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 202 | if errorlevel 1 exit /b 1 203 | echo. 204 | echo.The overview file is in %BUILDDIR%/changes. 205 | goto end 206 | ) 207 | 208 | if "%1" == "linkcheck" ( 209 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 210 | if errorlevel 1 exit /b 1 211 | echo. 212 | echo.Link check complete; look for any errors in the above output ^ 213 | or in %BUILDDIR%/linkcheck/output.txt. 214 | goto end 215 | ) 216 | 217 | if "%1" == "doctest" ( 218 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 219 | if errorlevel 1 exit /b 1 220 | echo. 221 | echo.Testing of doctests in the sources finished, look at the ^ 222 | results in %BUILDDIR%/doctest/output.txt. 223 | goto end 224 | ) 225 | 226 | if "%1" == "xml" ( 227 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 228 | if errorlevel 1 exit /b 1 229 | echo. 230 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 231 | goto end 232 | ) 233 | 234 | if "%1" == "pseudoxml" ( 235 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 236 | if errorlevel 1 exit /b 1 237 | echo. 238 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 239 | goto end 240 | ) 241 | 242 | :end 243 | -------------------------------------------------------------------------------- /docs/readme.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../README.rst 2 | -------------------------------------------------------------------------------- /docs/ref.rst: -------------------------------------------------------------------------------- 1 | .. ref 2 | 3 | ============= 4 | API Reference 5 | ============= 6 | 7 | appd.model 8 | ---------- 9 | 10 | .. automodule:: appd.model 11 | :members: 12 | 13 | appd.request 14 | ------------ 15 | 16 | .. automodule:: appd.request 17 | :members: 18 | 19 | 20 | appd.time 21 | --------- 22 | 23 | .. automodule:: appd.time 24 | :members: 25 | 26 | appd.cmdline 27 | ------------ 28 | 29 | .. automodule:: appd.cmdline 30 | :members: 31 | -------------------------------------------------------------------------------- /docs/usage.rst: -------------------------------------------------------------------------------- 1 | ======== 2 | Usage 3 | ======== 4 | 5 | To use AppDynamics REST Library in a project:: 6 | 7 | import AppDynamicsREST 8 | -------------------------------------------------------------------------------- /examples/backend_metrics.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | This script generates a report of response times, calls, and errors for all the backends registered 6 | on a controller. The output is generated as XML and can be sent to a file with this syntax:: 7 | 8 | python BackendMetrics.py > metric_output.xml 9 | 10 | By default this will connect to a controller on localhost:8090. The script accepts command-line 11 | arguments to change the connection parameters: 12 | 13 | --controller= 14 | --account= 15 | --username= 16 | --password= 17 | 18 | Example:: 19 | python BackendMetrics.py --controller=http://10.1.2.3:8090 --account=customer1 --username=demo --password=abc123 20 | """ 21 | 22 | from __future__ import print_function 23 | 24 | from collections import defaultdict 25 | from datetime import datetime 26 | from time import mktime 27 | from lxml.builder import ElementMaker 28 | from lxml import etree 29 | import tzlocal 30 | 31 | from appd.cmdline import parse_argv 32 | from appd.request import AppDynamicsClient 33 | 34 | 35 | __author__ = 'Todd Radel' 36 | __copyright__ = 'Copyright (c) 2013-2015 AppDynamics Inc.' 37 | 38 | 39 | # The report will generate data for the 24-hour period before midnight of the current day. To change the 40 | # reporting period, adjust these variables. 41 | 42 | time_in_mins = 24 * 60 43 | end_time = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) 44 | end_epoch = int(mktime(end_time.timetuple())) * 1000 45 | 46 | 47 | # Metric mapper 48 | # 49 | # Uses a hash table to call the function appropriate for specific metric types, e.g.: calls map_art for 50 | # Average Response Time, map_cpm for Calls per Minute, etc. 51 | 52 | def map_art(d, metric_total, metric_average, metric_sum, max_point): 53 | d['art'] = metric_average 54 | 55 | 56 | def map_cpm(d, metric_total, metric_average, metric_sum, max_point): 57 | d['total_calls'] = metric_total 58 | d['cpm_average'] = metric_average 59 | d['cpm_max'] = max_point[0] 60 | d['cpm_max_time'] = max_point[1] 61 | 62 | 63 | def map_err(d, metric_total, metric_average, metric_sum, max_point): 64 | d['total_errors'] = metric_total 65 | d['epm_avg'] = metric_average 66 | d['epm_max'] = max_point[0] 67 | d['epm_max_time'] = max_point[1] 68 | 69 | 70 | def no_such_metric(d, metric_total, metric_average, metric_sum, max_point): 71 | raise ValueError("no such metric") 72 | 73 | 74 | METRIC_DISPATCHER = { 75 | 'Average Response Time (ms)': map_art, 76 | 'Calls per Minute': map_cpm, 77 | 'Errors per Minute': map_err, 78 | 'DEFAULT': no_such_metric 79 | } 80 | 81 | 82 | # Helper functions 83 | 84 | def now_rfc3339(): 85 | return datetime.now(tzlocal.get_localzone()).isoformat('T') 86 | 87 | 88 | def freq_to_mins(md): 89 | FREQ_MAP = {'ONE_MIN': 1, 'TEN_MIN': 10, 'SIXTY_MIN': 60} 90 | return FREQ_MAP[md.frequency] 91 | 92 | 93 | # Parse command line arguments and create AD client: 94 | 95 | args = parse_argv() 96 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 97 | 98 | 99 | # Get the list of configured apps, and get backend metrics for each one: 100 | 101 | rows = defaultdict(dict) 102 | for app in c.get_applications(): 103 | for md in c.get_metrics('Backends|*|*', app.id, time_range_type='BEFORE_TIME', end_time=end_epoch, 104 | duration_in_mins=time_in_mins, rollup=False): 105 | 106 | # Get the last two components of the metric path. This should be 'backend_name|metric_name'. 107 | backend_name, metric_name = md.path.split('|')[-2:] 108 | 109 | if 'Discovered backend call' in backend_name: 110 | backend_name = backend_name[26:] 111 | metric_sum = sum([x.value for x in md.values]) 112 | metric_total = metric_sum * freq_to_mins(md) 113 | metric_average = 0 114 | max_point = (0, None) 115 | if len(md.values) > 0: 116 | metric_average = metric_sum / len(md.values) 117 | max_point = max([(max(x.value, x.current, x.max), x.start_time) for x in md.values]) 118 | 119 | func = METRIC_DISPATCHER.get(metric_name, None) 120 | if func: 121 | func(rows[backend_name], metric_total, metric_average, metric_sum, max_point) 122 | rows[backend_name]['app'] = app.name 123 | 124 | # Generate the report. 125 | 126 | XSI = 'http://www.w3.org/2001/XMLSchema-instance' 127 | E = ElementMaker(nsmap={'xsi': XSI}) 128 | 129 | root = E.BackendResponseTimes(Controller=c.base_url, GenerationTime=now_rfc3339()) 130 | root.set('{%s}noNamespaceSchemaLocation' % XSI, 'backend_metrics.xsd') 131 | 132 | for k, v in sorted(rows.items()): 133 | v.setdefault('cpm_max_time', '') 134 | v.setdefault('epm_max_time', '') 135 | root.append(E.Backend( 136 | E.ApplicationName(v['app']), 137 | E.BackendName(k), 138 | E.AverageResponseTime(str(v.get('art', 0))), 139 | E.CallsPerMinute(str(v.get('cpm_average', 0))), 140 | E.TotalCalls(str(v.get('total_calls', 0))), 141 | E.MaximumCallsPerMinute(str(v.get('cpm_max', 0))), 142 | E.MaximumCallTime(v['cpm_max_time'].isoformat('T') if v['cpm_max_time'] else ''), 143 | E.ErrorsPerMinute(str(v.get('epm_avg', 0))), 144 | E.TotalErrors(str(v.get('total_errors', 0))), 145 | E.MaximumErrorsPerMinute(str(v.get('epm_max', 0))), 146 | E.MaximumErrorTime(v['epm_max_time'].isoformat('T') if v['epm_max_time'] else ''), 147 | )) 148 | 149 | # Print the report to stdout. 150 | 151 | print(etree.ProcessingInstruction('xml', 'version="1.0" encoding="UTF-8"')) 152 | print(etree.tostring(root, pretty_print=True, encoding='UTF-8')) 153 | -------------------------------------------------------------------------------- /examples/bt_metrics.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | This script generates a report of response times, calls, and errors for all the BT's registered 6 | on a controller. The output is generated as XML and can be sent to a file with this syntax:: 7 | 8 | python BusinessTransactionsMetrics.py > metric_output.xml 9 | 10 | By default this will connect to a controller on localhost:8090. The script accepts command-line 11 | arguments to change the connection parameters: 12 | 13 | --controller= 14 | --account= 15 | --username= 16 | --password= 17 | 18 | Example:: 19 | python BTMetrics.py --controller=http://10.1.2.3:8090 --account=customer1 --username=demo --password=abc123 20 | """ 21 | 22 | from __future__ import print_function 23 | 24 | from collections import defaultdict 25 | from datetime import datetime 26 | from time import mktime 27 | from lxml.builder import ElementMaker 28 | from lxml import etree 29 | import tzlocal 30 | 31 | from appd.cmdline import parse_argv 32 | from appd.request import AppDynamicsClient 33 | 34 | 35 | __author__ = 'Todd Radel' 36 | __copyright__ = 'Copyright (c) 2013-2015 AppDynamics Inc.' 37 | 38 | 39 | # The report will generate data for the 24-hour period before midnight of the current day. To change the 40 | # reporting period, adjust these variables. 41 | 42 | time_in_mins = 1440 43 | end_time = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) 44 | end_epoch = int(mktime(end_time.timetuple())) * 1000 45 | 46 | 47 | # Helper functions 48 | 49 | def now_rfc3339(): 50 | return datetime.now(tzlocal.get_localzone()).isoformat('T') 51 | 52 | 53 | # Parse command line arguments and create AD client: 54 | 55 | args = parse_argv() 56 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 57 | 58 | 59 | # Get the list of configured apps, and get backend metrics for each one: 60 | 61 | METRIC_MAP = {'Average Block Time (ms)': 'abt', 62 | 'Average CPU Used (ms)': 'cpu', 63 | 'Average Request Size': 'req_size', 64 | 'Average Response Time (ms)': 'art', 65 | 'Average Wait Time (ms)': 'wait_time', 66 | 'Calls per Minute': 'cpm', 67 | 'End User Average Response Time (ms)': 'eum_art', 68 | 'End User Network Average Response Time (ms)': 'eum_net', 69 | 'End User Page Render Average Response Time (ms)': 'eum_render', 70 | 'Errors per Minute': 'epm', 71 | 'Normal Average Response Time (ms)': 'norm_art', 72 | 'Number of Slow Calls': 'slow', 73 | 'Number of Very Slow Calls': 'veryslow', 74 | 'Stall Count': 'stalls'} 75 | 76 | 77 | empty_row = dict([(x, 0) for x in list(METRIC_MAP.values())]) 78 | rows = defaultdict(dict) 79 | 80 | for app in c.get_applications(): 81 | 82 | bt_list = c.get_bt_list(app.id) 83 | 84 | for md in c.get_metrics('Business Transaction Performance|Business Transactions|*|*|*', 85 | app.id, time_range_type='BEFORE_TIME', end_time=end_epoch, 86 | duration_in_mins=time_in_mins, rollup=True): 87 | 88 | # Get the last 3 components of the metric path. This should be 'tier_name|bt_name|metric_name'. 89 | tier_name, bt_name, metric_name = md.path.split('|')[-3:] 90 | tier_bts = bt_list.by_tier_and_name(bt_name, tier_name) 91 | if tier_bts: 92 | bt = tier_bts[0] 93 | if len(md.values) > 0 and metric_name in METRIC_MAP: 94 | key = (tier_name, bt_name) 95 | rows.setdefault(key, empty_row.copy()).update({'app_id': app.id, 96 | 'app_name': app.name, 97 | 'bt_id': bt.id, 98 | 'bt_name': bt.name, 99 | 'tier_name': bt.tier_name, 100 | 'type': bt.type, 101 | METRIC_MAP[metric_name]: md.values[0].value}) 102 | 103 | 104 | # Generate the report. 105 | 106 | XSI = 'http://www.w3.org/2001/XMLSchema-instance' 107 | E = ElementMaker(nsmap={'xsi': XSI}) 108 | 109 | root = E.BusinessTransactions(Controller=c.base_url, GenerationTime=now_rfc3339()) 110 | root.set('{%s}noNamespaceSchemaLocation' % XSI, 'bt_metrics.xsd') 111 | 112 | for k, v in sorted(rows.items()): 113 | v['calls'] = v['cpm'] * time_in_mins 114 | v['errors'] = v['epm'] * time_in_mins 115 | v['error_pct'] = round(float(v['errors']) / float(v['calls']) * 100.0, 1) if v['calls'] > 0 else 0 116 | 117 | root.append(E.BusinessTransaction( 118 | E.ApplicationName(v['app_name']), 119 | E.BusinessTransactionName(v['bt_name']), 120 | E.TierName(v['tier_name']), 121 | E.AverageResponseTime(str(v['art'])), 122 | E.CallsPerMinute(str(v['cpm'])), 123 | E.TotalCalls(str(v['calls'])), 124 | E.TotalErrors(str(v['errors'])), 125 | E.ErrorsPerMinute(str(v['epm'])), 126 | E.ErrorPercentage(str(v['error_pct'])), 127 | E.SlowCalls(str(v['slow'])), 128 | E.VerySlowCalls(str(v['veryslow'])), 129 | E.Stalls(str(v['stalls'])), 130 | )) 131 | 132 | 133 | # Print the report to stdout. 134 | 135 | print(etree.ProcessingInstruction('xml', 'version="1.0" encoding="UTF-8"')) 136 | print(etree.tostring(root, pretty_print=True, encoding='UTF-8')) 137 | -------------------------------------------------------------------------------- /examples/dependencies.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to print a list of all external dependencies for an application. 6 | This would include databases, message queues, web sites, 7 | Output format: ``app_name,tier_name,node_name,host_name`` 8 | """ 9 | 10 | from __future__ import print_function 11 | 12 | from appd.cmdline import parse_argv 13 | from appd.request import AppDynamicsClient 14 | 15 | 16 | __author__ = 'Todd Radel' 17 | __copyright__ = 'Copyright (c) 2013-2015 AppDynamics Inc.' 18 | 19 | 20 | args = parse_argv() 21 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 22 | 23 | app_name = 'E-Commerce_Demo' 24 | tier_name = 'ECommerce-Server' 25 | metric_path = 'Overall Application Performance|' + tier_name + '|External Calls' 26 | 27 | app_id = -1 28 | for app in c.get_applications(): 29 | if app.name == app_name: 30 | app_id = app.id 31 | 32 | deps = c.get_metric_tree(app_id, metric_path) 33 | for dep in deps: 34 | if dep.type == 'folder': 35 | print(dep.name) 36 | -------------------------------------------------------------------------------- /examples/healthrule_violations.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to generate a list of all health rule violations that occurred yesterday. 6 | """ 7 | 8 | from __future__ import print_function 9 | 10 | from datetime import datetime 11 | 12 | from appd.cmdline import parse_argv 13 | from appd.request import AppDynamicsClient 14 | from appd.time import from_ts, to_ts 15 | 16 | 17 | __author__ = 'Todd Radel' 18 | __copyright__ = 'Copyright (c) 2013-2015 AppDynamics Inc.' 19 | 20 | 21 | # Parse the command line arguments and initialize the client 22 | # 23 | args = parse_argv() 24 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 25 | 26 | # Get the application list and find "MyApp" 27 | # 28 | apps = c.get_applications() 29 | gn_prod = [x for x in apps if x.name == "MyApp"][0] 30 | 31 | # Calculate start and end times for the report: 24h period ending with midnight last night 32 | # 33 | today = to_ts(datetime.now().date()) 34 | yesterday = today - (86400 * 1000) 35 | 36 | # Get the list of nodes so we can look up by node_id 37 | # 38 | all_nodes = c.get_nodes(gn_prod.id) 39 | nodes_by_id = dict(list(zip([x.id for x in all_nodes], all_nodes))) 40 | 41 | # Get all health rule violations and print them 42 | # 43 | violations = c.get_healthrule_violations(gn_prod.id, 'BETWEEN_TIMES', start_time=yesterday, end_time=today) 44 | for v in violations: 45 | print("-" * 70) 46 | print("Start time: ", from_ts(v.start_time_ms)) 47 | print("End time: ", from_ts(v.end_time_ms)) 48 | print("Description: ", v.description) 49 | print("Details: ", v.deep_link_url) 50 | if v.affected_entity.type == "APPLICATION_COMPONENT_NODE": 51 | try: 52 | print("Affected Node: ", nodes_by_id[v.affected_entity.entity_id].name) 53 | except KeyError: 54 | print("Affected Node: UNKNOWN") 55 | -------------------------------------------------------------------------------- /examples/license_count.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from __future__ import print_function 5 | 6 | from datetime import datetime 7 | import itertools 8 | 9 | from appd.cmdline import parse_argv 10 | from appd.request import AppDynamicsClient 11 | 12 | 13 | __author__ = 'Todd Radel' 14 | __copyright__ = 'Copyright (c) 2013-2015 AppDynamics Inc.' 15 | 16 | 17 | def incr(d, name, amt=1): 18 | d[name] = d.get(name, 0) + amt 19 | 20 | 21 | args = parse_argv() 22 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 23 | 24 | nodes = [] 25 | for app in c.get_applications(): 26 | for node in c.get_nodes(app.id): 27 | # node_type = node.type 28 | # print node.id, node.machine_id, node.machine_name, node.type 29 | # print node.type, node.os_type, node.app_agent_version 30 | if node.has_machine_agent or node.has_app_agent: 31 | if node.has_app_agent: 32 | if 'PHP' in node.type: 33 | node.group_type = 'PHP App Agent' 34 | if 'IIS' in node.type: 35 | node.group_type = '.NET App Agent' 36 | else: 37 | node.group_type = 'Java App Agent' 38 | else: 39 | node.group_type = 'Machine Agent only' 40 | nodes.append(node) 41 | 42 | 43 | # Sort and group the nodes by machine_id. 44 | 45 | group_func = lambda x: x.machine_id 46 | nodes.sort(key=group_func) 47 | 48 | host_counts = dict() 49 | node_counts = dict() 50 | lic_counts = dict() 51 | for machine_id, nodes_on_machine_iter in itertools.groupby(nodes, key=group_func): 52 | 53 | nodes_on_machine = list(nodes_on_machine_iter) 54 | first_node = nodes_on_machine[0] 55 | agent_type = first_node.group_type 56 | # types = [x.group_type for x in nodes_on_machine] 57 | # all_same = all(x.group_type == agent_type for x in nodes_on_machine) 58 | # print all_same, types 59 | # assert all_same, first_node 60 | 61 | license_count = 1 62 | if 'Java' in agent_type: 63 | license_count = len(nodes_on_machine) 64 | 65 | incr(lic_counts, agent_type, license_count) 66 | incr(host_counts, agent_type, 1) 67 | incr(node_counts, agent_type, len(nodes_on_machine)) 68 | 69 | # if '.NET' in agent_type: 70 | # node_names = [x.name for x in nodes_on_machine] 71 | # print 'Host:', first_node.machine_name, '\n\t', '\n\t'.join(node_names) 72 | 73 | 74 | # Print the results. 75 | tot_nodes, tot_hosts, tot_licenses = (0, 0, 0) 76 | header_fmt = '%-30s %-15s %-15s %s' 77 | data_fmt = '%-30s %15d %15d %15d' 78 | 79 | print() 80 | print('License usage report for ' + args.url) 81 | print('Generated at: ' + str(datetime.now())) 82 | print() 83 | print(header_fmt % ('Node Type', 'Node Count', 'Host Count', 'License Count')) 84 | print(header_fmt % ('=' * 30, '=' * 15, '=' * 15, '=' * 15)) 85 | 86 | for node_type in ('Java App Agent', '.NET App Agent', 'PHP App Agent', 'Machine Agent only'): 87 | node_count = node_counts.get(node_type, 0) 88 | host_count = host_counts.get(node_type, 0) 89 | lic_count = lic_counts.get(node_type, 0) 90 | tot_nodes += node_count 91 | tot_hosts += host_count 92 | tot_licenses += lic_count 93 | print(data_fmt % (node_type, node_count, host_count, lic_count)) 94 | 95 | print(header_fmt % ('=' * 30, '=' * 15, '=' * 15, '=' * 15)) 96 | print(data_fmt % ('TOTAL', tot_nodes, tot_hosts, tot_licenses)) 97 | -------------------------------------------------------------------------------- /examples/license_count_by_app.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from __future__ import print_function 5 | 6 | from datetime import datetime 7 | import itertools 8 | 9 | from appd.cmdline import parse_argv 10 | from appd.request import AppDynamicsClient 11 | 12 | 13 | __author__ = 'Todd Radel' 14 | __copyright__ = 'Copyright (c) 2013-2015 AppDynamics Inc.' 15 | 16 | 17 | def incr(d, name, amt=1): 18 | d[name] = d.get(name, 0) + amt 19 | 20 | 21 | args = parse_argv() 22 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 23 | 24 | nodes = [] 25 | for app in c.get_applications(): 26 | for node in c.get_nodes(app.id): 27 | if node.has_machine_agent or node.has_app_agent: 28 | if node.has_app_agent: 29 | if 'PHP' in node.type: 30 | node.group_type = 'PHP App Agent' 31 | if 'IIS' in node.type: 32 | node.group_type = '.NET App Agent' 33 | else: 34 | node.group_type = 'Java App Agent' 35 | else: 36 | node.group_type = 'Machine Agent only' 37 | node.app = app 38 | nodes.append(node) 39 | 40 | 41 | # Sort and group the nodes by machine_id. 42 | 43 | group_func = lambda x: x.machine_id 44 | nodes.sort(key=group_func) 45 | 46 | host_counts = dict() 47 | node_counts = dict() 48 | lic_counts = dict() 49 | for machine_id, nodes_on_machine_iter in itertools.groupby(nodes, key=group_func): 50 | 51 | nodes_on_machine = list(nodes_on_machine_iter) 52 | first_node = nodes_on_machine[0] 53 | agent_type = first_node.group_type 54 | app_name = first_node.app.name 55 | 56 | all_same = all(x.group_type == agent_type for x in nodes_on_machine) 57 | # assert all_same, first_node 58 | 59 | all_same = all(x.app.name == app_name for x in nodes_on_machine) 60 | # assert all_same, first_node 61 | 62 | license_count = 1 63 | if 'Java' in agent_type: 64 | license_count = len(nodes_on_machine) 65 | 66 | incr(lic_counts, app_name, license_count) 67 | incr(host_counts, app_name, 1) 68 | incr(node_counts, app_name, len(nodes_on_machine)) 69 | 70 | 71 | # Print the results. 72 | tot_nodes, tot_hosts, tot_licenses = (0, 0, 0) 73 | header_fmt = '%-30s %-15s %-15s %s' 74 | data_fmt = '%-30s %15d %15d %15d' 75 | 76 | print() 77 | print('License usage report for ', args.url) 78 | print('Generated at: ', datetime.now()) 79 | print() 80 | print(header_fmt % ('App Name', 'Node Count', 'Host Count', 'License Count')) 81 | print(header_fmt % ('=' * 30, '=' * 15, '=' * 15, '=' * 15)) 82 | 83 | for node_type in sorted(node_counts.keys()): 84 | node_count = node_counts.get(node_type, 0) 85 | host_count = host_counts.get(node_type, 0) 86 | lic_count = lic_counts.get(node_type, 0) 87 | tot_nodes += node_count 88 | tot_hosts += host_count 89 | tot_licenses += lic_count 90 | print(data_fmt % (node_type, node_count, host_count, lic_count)) 91 | 92 | print(header_fmt % ('=' * 30, '=' * 15, '=' * 15, '=' * 15)) 93 | print(data_fmt % ('TOTAL', tot_nodes, tot_hosts, tot_licenses)) 94 | -------------------------------------------------------------------------------- /examples/license_count_by_appgroup.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to generate a count of licenses used by app name, regardless of environment type 6 | (Prod, Devl, Qual, Cert, etc.) 7 | """ 8 | 9 | from __future__ import print_function 10 | 11 | from datetime import datetime 12 | import itertools 13 | 14 | from appd.cmdline import parse_argv 15 | from appd.request import AppDynamicsClient 16 | 17 | 18 | __author__ = 'Todd Radel' 19 | __copyright__ = 'Copyright (c) 2013-2015 AppDynamics Inc.' 20 | 21 | 22 | def incr(d, name, amt=1): 23 | d[name] = d.get(name, 0) + amt 24 | 25 | 26 | args = parse_argv() 27 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 28 | 29 | nodes = [] 30 | for app in c.get_applications(): 31 | for node in c.get_nodes(app.id): 32 | if node.has_machine_agent or node.has_app_agent: 33 | if node.has_app_agent: 34 | if 'PHP' in node.type: 35 | node.group_type = 'PHP App Agent' 36 | if 'IIS' in node.type: 37 | node.group_type = '.NET App Agent' 38 | else: 39 | node.group_type = 'Java App Agent' 40 | else: 41 | node.group_type = 'Machine Agent only' 42 | node.app = app 43 | nodes.append(node) 44 | 45 | 46 | # Sort and group the nodes by machine_id. 47 | 48 | group_func = lambda x: x.machine_id 49 | nodes.sort(key=group_func) 50 | 51 | host_counts = dict() 52 | node_counts = dict() 53 | lic_counts = dict() 54 | for machine_id, nodes_on_machine_iter in itertools.groupby(nodes, key=group_func): 55 | 56 | nodes_on_machine = list(nodes_on_machine_iter) 57 | first_node = nodes_on_machine[0] 58 | agent_type = first_node.group_type 59 | 60 | app_name = first_node.app.name 61 | app_name = app_name.replace('Prod', '').replace('Qual', '').replace('Cert', '').replace('Devl', '') 62 | 63 | all_same = all(x.group_type == agent_type for x in nodes_on_machine) 64 | # assert all_same, first_node 65 | 66 | all_same = all(x.app.name == app_name for x in nodes_on_machine) 67 | # assert all_same, first_node 68 | 69 | license_count = 1 70 | if 'Java' in agent_type: 71 | license_count = len(nodes_on_machine) 72 | 73 | incr(lic_counts, app_name, license_count) 74 | incr(host_counts, app_name, 1) 75 | incr(node_counts, app_name, len(nodes_on_machine)) 76 | 77 | 78 | # Print the results. 79 | tot_nodes, tot_hosts, tot_licenses = (0, 0, 0) 80 | header_fmt = '%-30s\t%-15s\t%-15s\t%s' 81 | data_fmt = '%-30s\t%15d\t%15d\t%15d' 82 | 83 | print() 84 | print('License usage report for ', args.url) 85 | print('Generated at: ', datetime.now()) 86 | print() 87 | print(header_fmt % ('App Name', 'Node Count', 'Host Count', 'License Count')) 88 | print(header_fmt % ('=' * 30, '=' * 15, '=' * 15, '=' * 15)) 89 | 90 | for app_name in sorted(node_counts.keys()): 91 | node_count = node_counts.get(app_name, 0) 92 | host_count = host_counts.get(app_name, 0) 93 | lic_count = lic_counts.get(app_name, 0) 94 | tot_nodes += node_count 95 | tot_hosts += host_count 96 | tot_licenses += lic_count 97 | print(data_fmt % (app_name, node_count, host_count, lic_count)) 98 | 99 | print(header_fmt % ('=' * 30, '=' * 15, '=' * 15, '=' * 15)) 100 | print(data_fmt % ('TOTAL', tot_nodes, tot_hosts, tot_licenses)) 101 | -------------------------------------------------------------------------------- /examples/license_count_by_env.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to generate a count of licenses used by environment type (Prod, Devl, Qual, Cert, etc.) 6 | """ 7 | 8 | from __future__ import print_function 9 | 10 | from datetime import datetime 11 | import itertools 12 | 13 | from appd.cmdline import parse_argv 14 | from appd.request import AppDynamicsClient 15 | 16 | 17 | __author__ = 'Todd Radel' 18 | __copyright__ = 'Copyright (c) 2013-2015 AppDynamics Inc.' 19 | 20 | 21 | def incr(d, name, amt=1): 22 | d[name] = d.get(name, 0) + amt 23 | 24 | 25 | args = parse_argv() 26 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 27 | 28 | nodes = [] 29 | for app in c.get_applications(): 30 | for node in c.get_nodes(app.id): 31 | if node.has_machine_agent or node.has_app_agent: 32 | if node.has_app_agent: 33 | if 'PHP' in node.type: 34 | node.group_type = 'PHP App Agent' 35 | if 'IIS' in node.type: 36 | node.group_type = '.NET App Agent' 37 | else: 38 | node.group_type = 'Java App Agent' 39 | else: 40 | node.group_type = 'Machine Agent only' 41 | node.app = app 42 | nodes.append(node) 43 | 44 | 45 | # Sort and group the nodes by machine_id. 46 | 47 | group_func = lambda x: x.machine_id 48 | nodes.sort(key=group_func) 49 | 50 | host_counts = dict() 51 | node_counts = dict() 52 | lic_counts = dict() 53 | for machine_id, nodes_on_machine_iter in itertools.groupby(nodes, key=group_func): 54 | 55 | nodes_on_machine = list(nodes_on_machine_iter) 56 | first_node = nodes_on_machine[0] 57 | agent_type = first_node.group_type 58 | 59 | app_name = first_node.app.name 60 | env = 'Production' 61 | if 'Devl' in app_name: 62 | env = 'Development' 63 | if 'Qual' in app_name: 64 | env = 'Qual' 65 | if 'Cert' in app_name: 66 | env = 'Cert' 67 | 68 | all_same = all(x.group_type == agent_type for x in nodes_on_machine) 69 | # assert all_same, first_node 70 | 71 | all_same = all(x.app.name == app_name for x in nodes_on_machine) 72 | # assert all_same, first_node 73 | 74 | license_count = 1 75 | if 'Java' in agent_type: 76 | license_count = len(nodes_on_machine) 77 | 78 | incr(lic_counts, env, license_count) 79 | incr(host_counts, env, 1) 80 | incr(node_counts, env, len(nodes_on_machine)) 81 | 82 | 83 | # Print the results. 84 | tot_nodes, tot_hosts, tot_licenses = (0, 0, 0) 85 | header_fmt = '%-30s %-15s %-15s %s' 86 | data_fmt = '%-30s %15d %15d %15d' 87 | 88 | print() 89 | print('License usage report for ', args.url) 90 | print('Generated at: ', datetime.now()) 91 | print() 92 | print(header_fmt % ('Environment', 'Node Count', 'Host Count', 'License Count')) 93 | print(header_fmt % ('=' * 30, '=' * 15, '=' * 15, '=' * 15)) 94 | 95 | for env in sorted(node_counts.keys()): 96 | node_count = node_counts.get(env, 0) 97 | host_count = host_counts.get(env, 0) 98 | lic_count = lic_counts.get(env, 0) 99 | tot_nodes += node_count 100 | tot_hosts += host_count 101 | tot_licenses += lic_count 102 | print(data_fmt % (env, node_count, host_count, lic_count)) 103 | 104 | print(header_fmt % ('=' * 30, '=' * 15, '=' * 15, '=' * 15)) 105 | print(data_fmt % ('TOTAL', tot_nodes, tot_hosts, tot_licenses)) 106 | -------------------------------------------------------------------------------- /examples/license_list_hosts.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from __future__ import print_function 5 | 6 | import itertools 7 | import csv 8 | 9 | from appd.cmdline import parse_argv 10 | from appd.request import AppDynamicsClient 11 | 12 | 13 | __author__ = 'Todd Radel' 14 | __copyright__ = 'Copyright (c) 2013-2015 AppDynamics Inc.' 15 | 16 | 17 | def incr(d, name, amt=1): 18 | d[name] = d.get(name, 0) + amt 19 | 20 | 21 | args = parse_argv() 22 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 23 | 24 | nodes = [] 25 | for app in c.get_applications(): 26 | for node in c.get_nodes(app.id): 27 | 28 | node.app_id = app.id 29 | node.app_name = app.name 30 | 31 | if node.has_machine_agent or node.has_app_agent: 32 | if node.has_app_agent: 33 | if 'PHP' in node.type: 34 | node.group_type = 'PHP App Agent' 35 | if 'IIS' in node.type: 36 | node.group_type = '.NET App Agent' 37 | else: 38 | node.group_type = 'Java App Agent' 39 | else: 40 | node.group_type = 'Machine Agent only' 41 | 42 | nodes.append(node) 43 | 44 | 45 | # Sort and group the nodes by machine_id. 46 | 47 | group_func = lambda x: x.machine_id 48 | nodes.sort(key=group_func) 49 | 50 | with open('data/licenses_by_host.csv', 'w') as f: 51 | w = csv.writer(f, quoting=csv.QUOTE_NONNUMERIC, dialect=csv.excel) 52 | 53 | hdr = ['App Name', 'Host Name', 'Host Type', 'License Count'] 54 | w.writerow(hdr) 55 | 56 | for machine_id, nodes_on_machine_iter in itertools.groupby(nodes, key=group_func): 57 | 58 | nodes_on_machine = list(nodes_on_machine_iter) 59 | first_node = nodes_on_machine[0] 60 | agent_type = first_node.group_type 61 | types = [x.group_type for x in nodes_on_machine] 62 | all_same = all(x.group_type == agent_type for x in nodes_on_machine) 63 | assert all_same, first_node 64 | 65 | license_count = 1 66 | if 'Java' in agent_type: 67 | license_count = len(nodes_on_machine) 68 | 69 | w.writerow([first_node.app_name, first_node.machine_name, agent_type, license_count]) 70 | -------------------------------------------------------------------------------- /examples/license_report_to_csv.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from __future__ import print_function 5 | 6 | import itertools 7 | import csv 8 | 9 | from appd.cmdline import parse_argv 10 | from appd.request import AppDynamicsClient 11 | 12 | 13 | __author__ = 'Todd Radel' 14 | __copyright__ = 'Copyright (c) 2013-2015 AppDynamics Inc.' 15 | 16 | 17 | args = parse_argv() 18 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 19 | 20 | nodes = [] 21 | for app in c.get_applications(): 22 | for node in c.get_nodes(app.id): 23 | node_type = node.type 24 | if node.has_machine_agent and not node.has_app_agent: 25 | node.type = 'Machine Agent' 26 | nodes.append(node) 27 | 28 | 29 | # Sort and group the nodes by machine_id. 30 | 31 | group_func = lambda x: (x.machine_id, x.type) 32 | nodes.sort(key=group_func) 33 | 34 | tier_names = set() 35 | tiers = dict() 36 | for machine_id, nodes_on_machine_iter in itertools.groupby(nodes, key=group_func): 37 | 38 | nodes_on_machine = list(nodes_on_machine_iter) 39 | agent_type = nodes_on_machine[0].type 40 | tier_name = nodes_on_machine[0].tier_name.split('.')[0] 41 | 42 | license_count = len(nodes_on_machine) 43 | if 'PHP' in agent_type: 44 | agent_type = 'PHP' 45 | if 'IIS' in agent_type: 46 | agent_type = 'DOT_NET' 47 | license_count = 1 48 | elif agent_type == 'Machine Agent': 49 | agent_type = 'MACHINE' 50 | license_count = 1 51 | assert len(nodes_on_machine) == 1 52 | else: 53 | agent_type = 'JAVA' 54 | 55 | def key(name): 56 | return '%s|%s|%s' % (tier_name, agent_type, name) 57 | 58 | def incr(name, amt=1): 59 | k = key(name) 60 | tiers[k] = tiers.get(k, 0) + amt 61 | 62 | incr('licenses', license_count) 63 | incr('agents', len(nodes_on_machine)) 64 | tiers.setdefault(key('host_set'), set()).add(machine_id[0]) 65 | tiers[key('hosts')] = len(tiers[key('host_set')]) 66 | tier_names.add(tier_name) 67 | 68 | 69 | AGENT_TYPES = ('JAVA', 'DOT_NET', 'PHP', 'MACHINE') 70 | 71 | with open('data/licenses.csv', 'w') as f: 72 | w = csv.writer(f, quoting=csv.QUOTE_NONNUMERIC, dialect=csv.excel) 73 | 74 | hdr = ['Tier Name', 'Java Licenses', 'Java Agents', 'Java Hosts', '.NET Licenses', '.NET Agents', '.NET Hosts', 75 | 'PHP Licenses', 'PHP Agents', 'PHP Hosts', 'Machine Agent Licenses', 'Machine Agents', 'Machine Agent Hosts'] 76 | w.writerow(hdr) 77 | 78 | for tier_name in sorted(tier_names): 79 | row = [tier_name] 80 | for agent_type in AGENT_TYPES: 81 | def get(name): 82 | return tiers.get('%s|%s|%s' % (tier_name, agent_type, name), 0) 83 | row.extend([get('licenses'), get('agents'), get('hosts')]) 84 | w.writerow(row) 85 | -------------------------------------------------------------------------------- /examples/nodelist.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to print a simple list of all the nodes registered with the controller. 6 | Output format: ``app_name,tier_name,node_name,host_name`` 7 | """ 8 | 9 | from __future__ import print_function 10 | 11 | from appd.cmdline import parse_argv 12 | from appd.request import AppDynamicsClient 13 | 14 | __author__ = 'Todd Radel' 15 | __copyright__ = 'Copyright (c) 2013-2015 AppDynamics Inc.' 16 | 17 | 18 | args = parse_argv() 19 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 20 | 21 | for app in c.get_applications(): 22 | for node in c.get_nodes(app.id): 23 | print(','.join([app.name, node.tier_name, node.name, node.machine_name])) 24 | -------------------------------------------------------------------------------- /examples/sample.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from __future__ import print_function 5 | 6 | from appd.cmdline import parse_argv 7 | from appd.request import AppDynamicsClient 8 | 9 | __author__ = 'Todd Radel' 10 | __copyright__ = 'Copyright (c) 2013-2015 AppDynamics Inc.' 11 | 12 | 13 | args = parse_argv() 14 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 15 | 16 | for app in c.get_applications(): 17 | metric_data = c.get_metrics('Overall Application Performance|*', app_id=app.id) 18 | art = metric_data.by_leaf_name(c.AVERAGE_RESPONSE_TIME).first_value() 19 | cpm = metric_data.by_leaf_name(c.CALLS_PER_MINUTE).first_value() 20 | epm = metric_data.by_leaf_name(c.ERRORS_PER_MINUTE).first_value() 21 | error_pct = round(float(epm) / float(cpm) * 100.0, 1) if cpm > 0 else 0 22 | print(app.name, art, cpm, epm, error_pct) 23 | -------------------------------------------------------------------------------- /examples/usage_chart.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from datetime import datetime, timedelta 5 | from collections import OrderedDict, defaultdict 6 | 7 | import pygal 8 | import tzlocal 9 | 10 | from appd.request import AppDynamicsClient 11 | import creds.demo2 as creds 12 | 13 | __author__ = 'Todd Radel' 14 | __copyright__ = 'Copyright (c) 2013-2015 AppDynamics Inc.' 15 | 16 | # Set up API client 17 | c = AppDynamicsClient(creds.url, creds.user, creds.password, creds.account) 18 | 19 | # Get my tenant account info 20 | my_acct = c.get_my_account() 21 | 22 | # Calculate start and end dates - we will start at midnight last night and go back 7 days 23 | days = 15 24 | mytz = tzlocal.get_localzone() 25 | end_dt = datetime.now(mytz).replace(hour=0, minute=0, second=0, microsecond=0) 26 | start_dt = end_dt - timedelta(days) 27 | 28 | # Get license usage for my account 29 | usage = c.get_license_usage(my_acct.id, None, start_dt, end_dt) 30 | 31 | 32 | def daterange(start_dt, end_dt): 33 | for n in range(int((end_dt - start_dt).days)): 34 | yield start_dt + timedelta(days=n) 35 | 36 | # Get the list of all licensed products: 37 | products = set(x.license_module for x in usage.usages) 38 | products = [x for x in products if 'eum' not in x and 'analytics' not in x] 39 | 40 | usage_by_product = defaultdict(OrderedDict) 41 | for product in products: 42 | for day in daterange(start_dt, end_dt): 43 | units = [x.max_units_used for x in usage.usages if 44 | x.created_on.date() == day.date() and x.license_module == product] 45 | usage_by_product[product][day] = max(units) 46 | 47 | # Make a simple graph and display it 48 | chart = pygal.StackedBar(x_label_rotation=45, width=1000) 49 | chart.title = 'License Usage By Product - ' + c.base_url 50 | chart.x_labels = [str(x.date()) for x in daterange(start_dt, end_dt)] 51 | for product in products: 52 | chart.add(product, usage_by_product[product].values()) 53 | chart.render_to_file('all_usage.svg') 54 | -------------------------------------------------------------------------------- /examples/usage_chart_java.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from datetime import datetime, timedelta 5 | from collections import OrderedDict 6 | 7 | import pygal 8 | import tzlocal 9 | 10 | from appd.request import AppDynamicsClient 11 | import creds.demo2 as creds 12 | 13 | __author__ = 'Todd Radel' 14 | __copyright__ = 'Copyright (c) 2013-2015 AppDynamics Inc.' 15 | 16 | # Set up API client 17 | c = AppDynamicsClient(creds.url, creds.user, creds.password, creds.account) 18 | 19 | # Get my tenant account info 20 | my_acct = c.get_my_account() 21 | 22 | # Calculate start and end dates - we will start at midnight last night and go back 7 days 23 | days = 15 24 | mytz = tzlocal.get_localzone() 25 | end_dt = datetime.now(mytz).replace(hour=0, minute=0, second=0, microsecond=0) 26 | start_dt = end_dt - timedelta(days) 27 | 28 | # Get license usage for my account 29 | usage = c.get_license_usage(my_acct.id, 'java', start_dt, end_dt) 30 | 31 | 32 | def daterange(start_dt, end_dt): 33 | for n in range(int((end_dt - start_dt).days)): 34 | yield start_dt + timedelta(days=n) 35 | 36 | 37 | usage_by_day = OrderedDict() 38 | for day in daterange(start_dt, end_dt): 39 | units = [x.max_units_used for x in usage.usages if x.created_on.date() == day.date()] 40 | usage_by_day[day] = max(units) 41 | 42 | # Make a simple graph and display it 43 | chart = pygal.StackedBar(x_label_rotation=45, width=1000) 44 | chart.title = 'Java License Usage - ' + c.base_url 45 | chart.x_labels = [str(x.date()) for x in daterange(start_dt, end_dt)] 46 | chart.add('java', usage_by_day.values()) 47 | chart.render_to_file('java_usage.svg') 48 | -------------------------------------------------------------------------------- /examples/xsd/backend_metrics.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /examples/xsd/bt_metrics.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | argparse 3 | future 4 | tzlocal 5 | pycurl 6 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.rst 3 | 4 | [wheel] 5 | universal = 1 6 | 7 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python2.7 2 | # -*- coding: utf-8 -*- 3 | 4 | # __author__ = 'Todd Radel ' 5 | 6 | from setuptools import setup 7 | import io 8 | import os 9 | import sys 10 | 11 | import appd 12 | 13 | here = os.path.abspath(os.path.dirname(__file__)) 14 | 15 | 16 | def read(*filenames, **kwargs): 17 | encoding = kwargs.get('encoding', 'utf-8') 18 | sep = kwargs.get('sep', '\n') 19 | buf = [] 20 | for filename in filenames: 21 | with io.open(filename, encoding=encoding) as f: 22 | buf.append(f.read()) 23 | return sep.join(buf) 24 | 25 | setup(name='AppDynamicsREST', 26 | version=appd.__version__, 27 | description='AppDynamics REST API Library', 28 | long_description=read('README.rst'), 29 | author='Todd Radel', 30 | author_email='tradel@appdynamics.com', 31 | url='https://github.com/tradel/AppDynamicsREST', 32 | packages=['appd','appd.model'], 33 | platforms='any', 34 | package_data={'': ['README.md', 'data/*', 'examples/*', 'templates/*']}, 35 | install_requires=['requests', 'argparse', 'future', 'pycurl'], 36 | extras_require={'examples': ['lxml', 'tzlocal', 'jinja2'], 'testing': ['nose']}, 37 | test_suite='nose.collector', 38 | tests_require=['nose'], 39 | license='Apache', 40 | classifiers=[ 41 | 'Programming Language :: Python', 42 | 'Intended Audience :: Developers', 43 | 'Development Status :: 4 - Beta', 44 | 'Natural Language :: English', 45 | 'Operating System :: OS Independent', 46 | 'Topic :: System :: Monitoring', 47 | 'Topic :: Software Development :: Libraries :: Python Modules', 48 | 'Programming Language :: Python :: 2', 49 | 'Programming Language :: Python :: 2.6', 50 | 'Programming Language :: Python :: 2.7', 51 | 'Programming Language :: Python :: 3', 52 | 'Programming Language :: Python :: 3.3', 53 | 'Programming Language :: Python :: 3.4'], 54 | ) 55 | -------------------------------------------------------------------------------- /templates/audit.jinja.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Controller Audit Report 5 | 6 | 7 | AppDynamics, Inc. 8 | 9 |
10 |

Controller Audit Report

11 |
12 |
13 |

14 | Generated for account {{ args.account }} on controller {{ args.host }}, 15 | {% if args.days == 1 %} 16 | for {{ start_time.strftime('%A, %d %b %Y') }}.

17 | {% else %} 18 | for {{ args.days }} days prior to {{ end_time.strftime('%A, %d %b %Y') }}. 19 | {% endif %} 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | {% for row in cur %} 34 | {% set action_name = ACTIONS.get(row['action'], row['action']) %} 35 | {% set table_data = AUDIT_TABLES.get(row['object_name'], dict()) %} 36 | {% set display_name = table_data.get('display_name', row['object_name'] or '') %} 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | {% endfor %} 48 | 49 |
Date/TimeAccountSecurity ProviderUsernameApplicationActionObject TypeObject Name
{{ from_ts(row['ts_ms']) }}{{ row['account_name'] }}{{ row['user_security_provider_type'] }}{{ row['user_name'] }}{{ row['application_name'] or '' }}{{ action_name }}{{ display_name }}{{ row['object_desc'] or '' }}
50 |

51 | If you have any questions, please contact our Ops team. 52 |

53 |
54 |
55 | 56 | 57 | -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Appdynamics/AppDynamicsREST/b6a5ebebc511691836584816b10d6e28f2950599/test/__init__.py -------------------------------------------------------------------------------- /test/test.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Unit tests for AppDynamics REST API 6 | """ 7 | 8 | import unittest 9 | import appd 10 | import creds.localhost as creds 11 | 12 | __author__ = 'Todd Radel' 13 | __copyright__ = 'Copyright (c) 2013-2015 AppDynamics Inc.' 14 | 15 | 16 | class ApplicationApiTest(unittest.TestCase): 17 | 18 | def setUp(self): 19 | self.c = appd.request.AppDynamicsClient(creds.url, creds.user, creds.password, creds.account) 20 | 21 | def assertIn(self, test_value, expected_set): 22 | """ 23 | Included for compatibility with Python 2.6 24 | """ 25 | msg = "%s did not occur in %s" % (test_value, expected_set) 26 | self.assert_(test_value in expected_set, msg) 27 | 28 | def assertNotIn(self, test_value, expected_set): 29 | """ 30 | Included for compatibility with Python 2.6 31 | """ 32 | msg = "%s was found in %s" % (test_value, expected_set) 33 | self.assert_(test_value not in expected_set, msg) 34 | 35 | 36 | if __name__ == '__main__': 37 | from test_v1_applications import * 38 | from test_v2_accounts import * 39 | from test_v2_license_modules import * 40 | suite = unittest.TestLoader()\ 41 | .loadTestsFromTestCase(V1_ApplicationTest)\ 42 | .loadTestsFromTestCase(V2_AccountTest)\ 43 | .loadTestsFromTestCase(V2_LicenseModuleTest) 44 | 45 | unittest.TextTestRunner(verbosity=2).run(suite) 46 | -------------------------------------------------------------------------------- /test/test_action_suppressions.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to print a list of events from the specified controller and app. 6 | """ 7 | 8 | from __future__ import print_function 9 | 10 | from appd.cmdline import parse_argv 11 | from appd.request import AppDynamicsClient 12 | 13 | __author__ = 'Kyle Furlong' 14 | __copyright__ = 'Copyright (c) 2013-2017 AppDynamics Inc.' 15 | 16 | 17 | args = parse_argv() 18 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 19 | 20 | account = c.get_my_account() 21 | 22 | action = {'name': 'suppress', 23 | 'timeRange': {'startTimeMillis': '2017-03-24T16:16:57+0000', 24 | 'endTimeMillis': '2017-10-25T04:16:57+0000'}, 25 | 'heathRuleIds': '1,2,3', 26 | 'affects': {'type': 'APP'}} 27 | resp = c.create_action_suppression(account.id, app_id=16, params=action) 28 | print(resp) 29 | 30 | resp = c.get_action_suppressions(account.id, app_id=16) 31 | print(resp) 32 | 33 | id = resp.actionSuppressions[0].id 34 | resp = c.get_action_suppression(account.id, app_id=16, action_suppression_id=id) 35 | print(resp) 36 | 37 | resp = c.delete_action_suppression(account.id, app_id=16, action_suppression_id=id) 38 | print(resp) 39 | -------------------------------------------------------------------------------- /test/test_create_event.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to print a list of events from the specified controller and app. 6 | """ 7 | 8 | from __future__ import print_function 9 | 10 | from appd.cmdline import parse_argv 11 | from appd.request import AppDynamicsClient 12 | 13 | __author__ = 'Kyle Furlong' 14 | __copyright__ = 'Copyright (c) 2013-2017 AppDynamics Inc.' 15 | 16 | args = parse_argv() 17 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 18 | 19 | apps = c.get_applications() 20 | 21 | if len(apps) > 0: 22 | tiers = c.get_tiers(apps[0].id) 23 | if len(tiers) > 0: 24 | bts = c.get_bt_list(apps[0].id) 25 | 26 | if len(bts) > 0: 27 | resp = c.create_event(app_id=apps[0].id, 28 | summary='Custom Event 1', 29 | comment='This is an event created by the Python SDK', 30 | severity='INFO', 31 | eventtype='CUSTOM', 32 | customeventtype='MYCUSTOMEVENT', 33 | # node=nodes[0].name, 34 | tier=bts[0].tier_name, 35 | bt=bts[0].name) 36 | print(resp) 37 | else: 38 | print('BT, not found!') 39 | else: 40 | print('Tier, not found!') 41 | 42 | resp = c.create_event(apps[0].id, 43 | summary='Event 1', 44 | comment='This is an event created by the Python SDK', 45 | severity='INFO', 46 | eventtype='APPLICATION_CONFIG_CHANGE') 47 | print(resp) 48 | 49 | else: 50 | print('Application, not found!') 51 | -------------------------------------------------------------------------------- /test/test_create_user.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to print a list of events from the specified controller and app. 6 | """ 7 | 8 | from __future__ import print_function 9 | 10 | from appd.cmdline import parse_argv 11 | from appd.request import AppDynamicsClient 12 | 13 | __author__ = 'Kyle Furlong' 14 | __copyright__ = 'Copyright (c) 2013-2017 AppDynamics Inc.' 15 | 16 | args = parse_argv() 17 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 18 | 19 | apps = c.get_applications() 20 | if len(apps) > 0: 21 | resp = c.create_user('John', 'Doe', 'john@nowhere.nowhen', 22 | user_password='johndoe', user_roles='Administrator,Universal Agent User') 23 | print(resp) 24 | else: 25 | print('Application, not found!') 26 | -------------------------------------------------------------------------------- /test/test_exclude_bts.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to print a list of events from the specified controller and app. 6 | """ 7 | 8 | from __future__ import print_function 9 | 10 | from appd.cmdline import parse_argv 11 | from appd.request import AppDynamicsClient 12 | 13 | __author__ = 'Kyle Furlong' 14 | __copyright__ = 'Copyright (c) 2013-2017 AppDynamics Inc.' 15 | 16 | 17 | args = parse_argv() 18 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 19 | 20 | resp = c.exclude_bt_list(5, [32]) 21 | print(resp) 22 | resp = c.exclude_bt_list(5, [32], False) 23 | print(resp) 24 | -------------------------------------------------------------------------------- /test/test_export_actions.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to print a list of events from the specified controller and app. 6 | """ 7 | 8 | from __future__ import print_function 9 | 10 | from appd.cmdline import parse_argv 11 | from appd.request import AppDynamicsClient 12 | 13 | __author__ = 'Kyle Furlong' 14 | __copyright__ = 'Copyright (c) 2013-2017 AppDynamics Inc.' 15 | 16 | 17 | args = parse_argv() 18 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 19 | 20 | resp = c.export_actions(5) 21 | print(resp) 22 | resp = c.import_actions(5, resp) 23 | print(resp) 24 | -------------------------------------------------------------------------------- /test/test_export_analytics_dynamic_service_configs.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to print a list of events from the specified controller and app. 6 | """ 7 | 8 | from __future__ import print_function 9 | 10 | from appd.cmdline import parse_argv 11 | from appd.request import AppDynamicsClient 12 | 13 | __author__ = 'Kyle Furlong' 14 | __copyright__ = 'Copyright (c) 2013-2017 AppDynamics Inc.' 15 | 16 | 17 | args = parse_argv() 18 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 19 | 20 | resp = c.export_analytics_dynamic_service_configs(5) 21 | print(resp) 22 | resp = c.import_analytics_dynamic_service_configs(5, resp) 23 | print(resp) 24 | -------------------------------------------------------------------------------- /test/test_export_custom_dashboard.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to print a list of events from the specified controller and app. 6 | """ 7 | 8 | from __future__ import print_function 9 | 10 | from appd.cmdline import parse_argv 11 | from appd.request import AppDynamicsClient 12 | 13 | __author__ = 'Kyle Furlong' 14 | __copyright__ = 'Copyright (c) 2013-2017 AppDynamics Inc.' 15 | 16 | 17 | args = parse_argv() 18 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 19 | 20 | resp = c.export_custom_dashboard(1) 21 | print(resp) 22 | resp = c.import_custom_dashboard(resp) 23 | print(resp) 24 | -------------------------------------------------------------------------------- /test/test_export_email_action_templates.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to print a list of events from the specified controller and app. 6 | """ 7 | 8 | from __future__ import print_function 9 | 10 | from appd.cmdline import parse_argv 11 | from appd.request import AppDynamicsClient 12 | 13 | __author__ = 'Kyle Furlong' 14 | __copyright__ = 'Copyright (c) 2013-2017 AppDynamics Inc.' 15 | 16 | 17 | args = parse_argv() 18 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 19 | 20 | resp = c.export_email_action_templates() 21 | print(resp) 22 | resp = c.import_email_action_templates(resp) 23 | print(resp) 24 | -------------------------------------------------------------------------------- /test/test_export_entry_point_type.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to print a list of events from the specified controller and app. 6 | """ 7 | 8 | from __future__ import print_function 9 | 10 | from appd.cmdline import parse_argv 11 | from appd.request import AppDynamicsClient 12 | 13 | __author__ = 'Kyle Furlong' 14 | __copyright__ = 'Copyright (c) 2013-2017 AppDynamics Inc.' 15 | 16 | 17 | args = parse_argv() 18 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 19 | 20 | resp = c.export_entry_point_type(5, 'servlet', 'custom', 'java', 'asdf') 21 | print(resp) 22 | resp = c.import_entry_point_type(5, 'servlet', 'custom', resp, 'java', 'asdf') 23 | print(resp) 24 | -------------------------------------------------------------------------------- /test/test_export_entry_points.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to print a list of events from the specified controller and app. 6 | """ 7 | 8 | from __future__ import print_function 9 | 10 | from appd.cmdline import parse_argv 11 | from appd.request import AppDynamicsClient 12 | 13 | __author__ = 'Kyle Furlong' 14 | __copyright__ = 'Copyright (c) 2013-2017 AppDynamics Inc.' 15 | 16 | 17 | args = parse_argv() 18 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 19 | 20 | resp = c.export_entry_points(5, 'auto') 21 | print(resp) 22 | resp = c.import_entry_points(5, 'auto', resp) 23 | print(resp) 24 | -------------------------------------------------------------------------------- /test/test_export_health_rules.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to print a list of events from the specified controller and app. 6 | """ 7 | 8 | from __future__ import print_function 9 | 10 | from appd.cmdline import parse_argv 11 | from appd.request import AppDynamicsClient 12 | 13 | __author__ = 'Kyle Furlong' 14 | __copyright__ = 'Copyright (c) 2013-2017 AppDynamics Inc.' 15 | 16 | 17 | args = parse_argv() 18 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 19 | 20 | resp = c.export_health_rules(5) 21 | print(resp) 22 | resp = c.import_health_rules(5, resp) 23 | print(resp) 24 | -------------------------------------------------------------------------------- /test/test_export_httprequest_action_templates.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to print a list of events from the specified controller and app. 6 | """ 7 | 8 | from __future__ import print_function 9 | 10 | from appd.cmdline import parse_argv 11 | from appd.request import AppDynamicsClient 12 | 13 | __author__ = 'Kyle Furlong' 14 | __copyright__ = 'Copyright (c) 2013-2017 AppDynamics Inc.' 15 | 16 | 17 | args = parse_argv() 18 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 19 | 20 | resp = c.export_httprequest_action_templates() 21 | print(resp) 22 | resp = c.import_httprequest_action_templates(resp) 23 | print(resp) 24 | -------------------------------------------------------------------------------- /test/test_export_import_action.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to print a list of events from the specified controller and app. 6 | """ 7 | 8 | from __future__ import print_function 9 | 10 | from appd.cmdline import parse_argv 11 | from appd.request import AppDynamicsClient 12 | 13 | __author__ = 'Kyle Furlong' 14 | __copyright__ = 'Copyright (c) 2013-2017 AppDynamics Inc.' 15 | 16 | 17 | args = parse_argv() 18 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 19 | 20 | apps = c.get_applications() 21 | if len(apps) > 0: 22 | resp = c.export_actions(apps[0].id) 23 | print(resp) 24 | resp = c.import_actions(apps[0].id, resp) 25 | print(resp) 26 | else: 27 | print('Application, not found!') 28 | -------------------------------------------------------------------------------- /test/test_export_policies.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to print a list of events from the specified controller and app. 6 | """ 7 | 8 | from __future__ import print_function 9 | 10 | from appd.cmdline import parse_argv 11 | from appd.request import AppDynamicsClient 12 | 13 | __author__ = 'Kyle Furlong' 14 | __copyright__ = 'Copyright (c) 2013-2017 AppDynamics Inc.' 15 | 16 | 17 | args = parse_argv() 18 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 19 | 20 | resp = c.export_policies(5) 21 | print(resp) 22 | resp = c.import_policies(5, resp) 23 | print(resp) 24 | -------------------------------------------------------------------------------- /test/test_get_audit_history.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to print a list of events from the specified controller and app. 6 | """ 7 | 8 | from __future__ import print_function 9 | 10 | from appd.cmdline import parse_argv 11 | from appd.request import AppDynamicsClient 12 | 13 | __author__ = 'Kyle Furlong' 14 | __copyright__ = 'Copyright (c) 2013-2017 AppDynamics Inc.' 15 | 16 | 17 | args = parse_argv() 18 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 19 | 20 | resp = c.get_audit_history('2017-04-27T08:00:00.000-0800', '2017-04-27T20:00:00.000-0800') 21 | 22 | for audit in resp.by_user_name("user1"): 23 | print(audit) 24 | for audit in resp.by_action("LOGIN"): 25 | print(audit) 26 | for audit in resp.by_account_name("customer1"): 27 | print(audit) 28 | -------------------------------------------------------------------------------- /test/test_get_backends.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to print a list of events from the specified controller and app. 6 | """ 7 | 8 | from __future__ import print_function 9 | 10 | from appd.cmdline import parse_argv 11 | from appd.request import AppDynamicsClient 12 | 13 | __author__ = 'Kyle Furlong' 14 | __copyright__ = 'Copyright (c) 2013-2017 AppDynamics Inc.' 15 | 16 | 17 | args = parse_argv() 18 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 19 | 20 | apps = c.get_applications() 21 | if len(apps) > 0: 22 | resp = c.get_backends(apps[0].id) 23 | print(resp) 24 | print(resp.by_exit_point_type("HTTP")) 25 | for backend in resp: 26 | for prop in backend.properties: 27 | print(prop['name']) 28 | print(prop['value']) 29 | else: 30 | print('Application, not found!') 31 | -------------------------------------------------------------------------------- /test/test_get_events.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to print a list of events from the specified controller and app. 6 | """ 7 | 8 | from __future__ import print_function 9 | 10 | from appd.cmdline import parse_argv 11 | from appd.request import AppDynamicsClient 12 | 13 | __author__ = 'Kyle Furlong' 14 | __copyright__ = 'Copyright (c) 2013-2017 AppDynamics Inc.' 15 | 16 | 17 | args = parse_argv() 18 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 19 | 20 | apps = c.get_applications() 21 | if len(apps) > 0: 22 | resp = c.get_events(apps[0].id, 23 | event_types='APPLICATION_CONFIG_CHANGE', 24 | severities='INFO,WARN,ERROR', 25 | time_range_type='BEFORE_NOW', 26 | duration_in_mins='60000') 27 | print(resp) 28 | else: 29 | print('Application, not found!') 30 | -------------------------------------------------------------------------------- /test/test_get_healthrule_violations.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to print a list of events from the specified controller and app. 6 | """ 7 | 8 | from __future__ import print_function 9 | 10 | from appd.cmdline import parse_argv 11 | from appd.request import AppDynamicsClient 12 | 13 | __author__ = 'Kyle Furlong' 14 | __copyright__ = 'Copyright (c) 2013-2017 AppDynamics Inc.' 15 | 16 | 17 | args = parse_argv() 18 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 19 | 20 | apps = c.get_applications() 21 | if len(apps) > 0: 22 | resp = c.get_healthrule_violations(apps[0].id, time_range_type='BEFORE_NOW', duration_in_mins=60) 23 | print(resp) 24 | else: 25 | print('Application, not found!') 26 | -------------------------------------------------------------------------------- /test/test_info_point_hr.py: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | from appd.request import AppDynamicsClient 4 | from appd.time import from_ts, to_ts 5 | from datetime import datetime 6 | from pprint import pprint 7 | 8 | appdProduction = AppDynamicsClient('https://fedex1.saas.appdynamics.com', 'RetailRelyAPI', 'F3d#x@M!45', 'fedex1' ) 9 | 10 | for app in appdProduction.get_applications(): 11 | if 'OFFICE' in app.name: 12 | appid = app.id 13 | print(app.name, app.id) 14 | 15 | officeNodes = appdProduction.get_nodes(app_id=appid) 16 | hrviolations = appdProduction.get_healthrule_violations(app_id=appid,time_range_type='BEFORE_NOW',duration_in_mins=15) 17 | pprint(hrviolations) 18 | -------------------------------------------------------------------------------- /test/test_mark_nodes_historical.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to print a list of events from the specified controller and app. 6 | """ 7 | 8 | from __future__ import print_function 9 | 10 | from appd.cmdline import parse_argv 11 | from appd.request import AppDynamicsClient 12 | 13 | __author__ = 'Kyle Furlong' 14 | __copyright__ = 'Copyright (c) 2013-2017 AppDynamics Inc.' 15 | 16 | 17 | args = parse_argv() 18 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 19 | 20 | resp = c.mark_nodes_historical('7') 21 | print(resp) 22 | -------------------------------------------------------------------------------- /test/test_set_config_value.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to print a list of events from the specified controller and app. 6 | """ 7 | 8 | from __future__ import print_function 9 | 10 | from appd.cmdline import parse_argv 11 | from appd.request import AppDynamicsClient 12 | 13 | __author__ = 'Kyle Furlong' 14 | __copyright__ = 'Copyright (c) 2013-2017 AppDynamics Inc.' 15 | 16 | 17 | args = parse_argv() 18 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 19 | 20 | resp = c.set_config('metrics.min.retention.period', 6) 21 | print(resp) 22 | resp = c.set_config('metrics.min.retention.period', 4) 23 | print(resp) 24 | -------------------------------------------------------------------------------- /test/test_set_controller_url.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to print a simple list of all the nodes registered with the controller. 6 | Output format: ``app_name,tier_name,node_name,host_name`` 7 | """ 8 | 9 | from __future__ import print_function 10 | 11 | from appd.cmdline import parse_argv 12 | from appd.request import AppDynamicsClient 13 | 14 | __author__ = 'Kyle Furlong' 15 | __copyright__ = 'Copyright (c) 2013-2017 AppDynamics Inc.' 16 | 17 | 18 | args = parse_argv() 19 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 20 | 21 | resp = c.set_controller_url(controllerURL="http://ec2-35-164-65-194.us-west-2.compute.amazonaws.com:8090") 22 | print(resp) 23 | -------------------------------------------------------------------------------- /test/test_set_metric_retention.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Sample script to print a simple list of all the nodes registered with the controller. 6 | Output format: ``app_name,tier_name,node_name,host_name`` 7 | """ 8 | 9 | from __future__ import print_function 10 | 11 | from appd.cmdline import parse_argv 12 | from appd.request import AppDynamicsClient 13 | 14 | __author__ = 'Kyle Furlong' 15 | __copyright__ = 'Copyright (c) 2013-2017 AppDynamics Inc.' 16 | 17 | 18 | args = parse_argv() 19 | c = AppDynamicsClient(args.url, args.username, args.password, args.account, args.verbose) 20 | 21 | resp = c.set_metric_retention(2, 1, 5) 22 | print(resp) 23 | -------------------------------------------------------------------------------- /test/test_v1_applications.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Unit tests for AppDynamics REST API 6 | """ 7 | 8 | from test import ApplicationApiTest 9 | 10 | class V1_ApplicationTest(ApplicationApiTest): 11 | 12 | def test_app_list(self): 13 | apps = self.c.get_applications() 14 | self.assertEqual(len(apps), 1) 15 | self.assertEqual(apps[0].id, 10) 16 | self.assertEqual(apps[0].name, 'ACME Book Store Application') 17 | 18 | def test_get_app_by_name(self): 19 | apps = self.c.get_applications() 20 | self.assertEqual(apps.by_name('ACME Book Store Application').id, 10) 21 | 22 | def test_get_config_by_name(self): 23 | config = self.c.get_config() 24 | val = config.by_name('metrics.retention.period') 25 | self.assertEqual(val.name, 'metrics.retention.period') 26 | self.assertEqual(val.value, '365') 27 | -------------------------------------------------------------------------------- /test/test_v2_accounts.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Unit tests for AppDynamics REST API 6 | """ 7 | 8 | from test import ApplicationApiTest 9 | 10 | 11 | class V2_AccountTest(ApplicationApiTest): 12 | 13 | def test_my_account(self): 14 | acct = self.c.get_my_account() 15 | self.assertEqual(acct.id, '2') 16 | self.assertEqual(acct.name, 'customer1') 17 | 18 | def test_get_account_by_id(self): 19 | acct = self.c.get_account(2) 20 | self.assertEqual(acct.id, '2') 21 | self.assertEqual(acct.name, 'customer1') 22 | -------------------------------------------------------------------------------- /test/test_v2_license_modules.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Unit tests for AppDynamics REST API 6 | """ 7 | 8 | import unittest 9 | from test import ApplicationApiTest 10 | 11 | 12 | class V2_LicenseModuleTest(ApplicationApiTest): 13 | 14 | def test_license_modules(self): 15 | mods = self.c.get_license_modules(2) 16 | self.assertIn('java', mods.modules) 17 | self.assertIn('dot-net', mods.modules) 18 | self.assertNotIn('wharrgarbl', mods.modules) 19 | 20 | 21 | if __name__ == '__main__': 22 | suite = unittest.TestLoader().loadTestsFromTestCase(V2_LicenseModuleTest) 23 | unittest.TextTestRunner(verbosity=2).run(suite) 24 | -------------------------------------------------------------------------------- /test/test_v2_license_usage.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Unit tests for AppDynamics REST API 6 | """ 7 | 8 | import unittest 9 | from test import ApplicationApiTest 10 | from datetime import datetime, timedelta 11 | import creds.demo2 as creds 12 | import appd 13 | import tzlocal 14 | 15 | 16 | def now(): 17 | mytz = tzlocal.get_localzone() 18 | return datetime.now(mytz).replace(microsecond=0) 19 | 20 | 21 | def midnight(): 22 | return now().replace(hour=0, minute=0, second=0, microsecond=0) 23 | 24 | 25 | class V2_LicenseUsageTest(ApplicationApiTest): 26 | 27 | def setUp(self): 28 | self.c = appd.request.AppDynamicsClient(creds.url, creds.user, creds.password, creds.account) 29 | 30 | def test_license_usage_java(self): 31 | usage = self.c.get_license_usage(2, 'java', midnight() - timedelta(hours=24), midnight() - timedelta(hours=1)) 32 | for x in usage.usages: 33 | self.assertEqual(x.license_module, 'java') 34 | self.assertEqual(x.account_id, 2) 35 | self.assertEqual(x.sample_count, 12) 36 | self.assertEqual(x.avg_units_provisioned, 1999) 37 | self.assertEqual(x.avg_units_allowed, 1999) 38 | self.assertEqual(len(usage.usages), 24, 'License usage should return 24 data points') 39 | 40 | def test_license_usage_java_5min(self): 41 | usage = self.c.get_license_usage_5min(2, 'java', now() - timedelta(hours=1), now()) 42 | for x in usage.usages: 43 | self.assertEqual(x.license_module, 'java') 44 | self.assertEqual(x.account_id, 2) 45 | self.assertEqual(x.units_allowed, 1999) 46 | self.assertEqual(x.units_provisioned, 1999) 47 | self.assertEqual(len(usage.usages), 12, 'License usage should return 12 data points') 48 | --------------------------------------------------------------------------------