From 0f82f2ec02524f21f11847bf625981fe702c4a44 Mon Sep 17 00:00:00 2001 From: Serg Melikyan Date: Tue, 9 Apr 2013 14:12:55 +0400 Subject: [PATCH] Removed all projects except Glazier Agent --- WindowsAgent/.gitignore => .gitignore | 0 .../1.CreatePrimaryDC/CreatePrimaryDC.json | 29 - .../1.CreatePrimaryDC/ExecutionPlan.txt | 9 - .../1.CreatePrimaryDC/GenerateJSON.bat | 1 - .../Install-RolePrimaryDomainController.ps1 | 53 - .../2.JoinDomain/ExecutionPlan.txt | 9 - .../2.JoinDomain/GenerateJSON.bat | 1 - .../ExecutionPlan/2.JoinDomain/out.json | 27 - .../3.CreateSecondaryDC/ExecutionPlan.txt | 8 - .../3.CreateSecondaryDC/GenerateJSON.bat | 1 - .../Install-RoleSecondaryDomainController.ps1 | 54 - .../3.CreateSecondaryDC/out.json | 23 - .../4.JoinAndPromote/ExecutionPlan.txt | 10 - .../4.JoinAndPromote/GenerateJSON.bat | 1 - .../Install-RoleSecondaryDomainController.ps1 | 54 - .../ExecutionPlan/4.JoinAndPromote/out.json | 39 - .../ExecutionPlan/ExecutionPlanGenerator.exe | Bin 9728 -> 0 bytes .../GetDnsIpAddressesOnDc/ExecutionPlan.txt | 7 - .../GetDnsIpAddressesOnDc/GenerateJSON.bat | 1 - .../Get-DnsListeningIpAddress.ps1 | 5 - .../GetDnsIpAddressesOnDc/out.json | 12 - .../InstallIIS/ExecutionPlan.txt | 7 - .../ExecutionPlan/InstallIIS/GenerateJSON.bat | 1 - .../InstallIIS/Install-WebServer.ps1 | 4 - Deployment/ExecutionPlan/InstallIIS/out.json | 12 - Deployment/ExecutionPlan/Newtonsoft.Json.dll | Bin 391680 -> 0 bytes .../Unattended/ws-2012-core-unattended.xml | 44 - .../Unattended/ws-2012-full-unattend.xml | 51 - .../Modules/CoreFunctions/Config.ps1 | 28 - .../Modules/CoreFunctions/CoreFunctions.psd1 | Bin 5262 -> 0 bytes .../Modules/CoreFunctions/CoreFunctions.psm1 | 36 - .../Modules/CoreFunctions/Ionic.Zip.dll | Bin 462336 -> 0 bytes .../en-US/about_CoreFunctions.help.txt | 0 .../Modules/CoreFunctions/include/Base64.ps1 | 99 - .../CoreFunctions/include/Functions.ps1 | 388 ---- .../Modules/CoreFunctions/include/Logger.ps1 | 137 -- .../Modules/CoreFunctions/include/Module.ps1 | 0 .../include/NotCoreFunctions.ps1 | 814 ------- .../CoreFunctions/include/SqlFunctions.ps1 | 1 - .../Modules/CoreFunctions/include/Zip.ps1 | 69 - .../Modules/CoreFunctions/log4net.config | 42 - .../Modules/CoreFunctions/log4net.dll | Bin 288768 -> 0 bytes .../cloudbase-init/config/cloudbase-init.conf | 11 - .../cloudbase-init/plugins/setagentconfig.py | 37 - .../cloudbase-init/plugins/sethostname.py | 20 - Deployment/cloudbase-init/plugins/userdata.py | 119 - Deployment/cloudbase-init/userdata/Sample.ps1 | 24 - .../devstack-scripts/compute/devstack.localrc | 51 - .../controller/devstack.localrc | 45 - .../devstack.standalone.localrc | 28 - Deployment/devstack-scripts/functions.sh | 148 -- .../devstack-scripts/install-devstack.sh | 21 - Deployment/devstack-scripts/localrc | 20 - Deployment/devstack-scripts/post-stack.sh | 109 - Deployment/devstack-scripts/post-unstack.sh | 36 - Deployment/devstack-scripts/pre-stack.sh | 55 - Deployment/devstack-scripts/pre-unstack.sh | 7 - .../ss1285.interfaces.example | 53 - .../ss1383.interfaces.example | 54 - .../standalone/devstack.localrc | 27 - Deployment/devstack-scripts/start-devstack.sh | 63 - Deployment/devstack-scripts/start-keero.sh | 26 - Deployment/devstack-scripts/start-vm.sh | 22 - Deployment/devstack-scripts/stop-devstack.sh | 45 - Deployment/devstack-scripts/stop-keero.sh | 13 - .../App.config | 0 .../ExecutionPlanGenerator.csproj | 0 .../Program.cs | 0 .../Properties/AssemblyInfo.cs | 0 .../packages.config | 0 {WindowsAgent/Tools => Tools}/NuGet.exe | Bin .../WindowsAgent.sln => WindowsAgent.sln | 0 WindowsAgent/{WindowsAgent => }/App.config | 0 .../{WindowsAgent => }/ExecutionPlan.cs | 0 WindowsAgent/{WindowsAgent => }/MqMessage.cs | 0 .../{WindowsAgent => }/PlanExecutor.cs | 0 WindowsAgent/{WindowsAgent => }/Program.cs | 0 .../Properties/AssemblyInfo.cs | 0 .../{WindowsAgent => }/RabbitMqClient.cs | 0 .../SampleExecutionPlan.json | 0 .../{WindowsAgent => }/ServiceManager.cs | 0 .../{WindowsAgent => }/WindowsAgent.csproj | 0 .../{WindowsAgent => }/WindowsService.cs | 0 .../WindowsServiceInstaller.cs | 0 .../{WindowsAgent => }/packages.config | 0 api/.gitignore | 23 - api/README.rst | 7 - api/babel.cfg | 1 - api/bin/glazier-api | 54 - api/doc/source/_static/.placeholder | 0 api/doc/source/_templates/.placeholder | 0 api/doc/source/_theme/theme.conf | 2 - api/doc/source/conf.py | 241 --- api/doc/source/index.rst | 114 - api/doc/source/man/glazierapi.rst | 69 - api/etc/glazier-api-paste.ini | 18 - api/etc/glazier-api.conf | 32 - api/glazierapi/__init__.py | 13 - api/glazierapi/api/__init__.py | 14 - api/glazierapi/api/middleware/__init__.py | 14 - api/glazierapi/api/middleware/context.py | 86 - api/glazierapi/api/v1/__init__.py | 110 - api/glazierapi/api/v1/active_directories.py | 85 - api/glazierapi/api/v1/environments.py | 132 -- api/glazierapi/api/v1/router.py | 106 - api/glazierapi/api/v1/sessions.py | 168 -- api/glazierapi/api/v1/webservers.py | 85 - api/glazierapi/common/__init__.py | 14 - api/glazierapi/common/config.py | 205 -- api/glazierapi/common/service.py | 116 - api/glazierapi/common/uuidutils.py | 19 - api/glazierapi/context.py | 57 - api/glazierapi/db/__init__.py | 26 - api/glazierapi/db/migrate_repo/README | 4 - api/glazierapi/db/migrate_repo/__init__.py | 1 - api/glazierapi/db/migrate_repo/manage.py | 20 - api/glazierapi/db/migrate_repo/migrate.cfg | 20 - .../versions/001_add_initial_tables.py | 48 - .../versions/002_add_session_table.py | 39 - .../versions/003_add_status_table.py | 40 - .../004_add_description_column_to_session.py | 31 - .../005_remove_obsolete_service_table.py | 42 - .../006_add_entity_id_column_to_status.py | 31 - .../db/migrate_repo/versions/__init__.py | 0 api/glazierapi/db/models.py | 155 -- api/glazierapi/db/session.py | 110 - .../locale/ru/LC_MESSAGES/glazierapi.po | 191 -- api/glazierapi/openstack/__init__.py | 0 api/glazierapi/openstack/common/__init__.py | 0 .../openstack/common/eventlet_backdoor.py | 87 - api/glazierapi/openstack/common/exception.py | 142 -- .../openstack/common/gettextutils.py | 33 - .../openstack/common/importutils.py | 67 - api/glazierapi/openstack/common/jsonutils.py | 147 -- api/glazierapi/openstack/common/local.py | 48 - api/glazierapi/openstack/common/log.py | 521 ----- .../openstack/common/loopingcall.py | 95 - .../openstack/common/notifier/__init__.py | 14 - .../openstack/common/notifier/api.py | 183 -- .../openstack/common/notifier/log_notifier.py | 35 - .../common/notifier/no_op_notifier.py | 19 - .../common/notifier/rabbit_notifier.py | 29 - .../openstack/common/notifier/rpc_notifier.py | 46 - .../common/notifier/rpc_notifier2.py | 52 - .../common/notifier/test_notifier.py | 22 - api/glazierapi/openstack/common/service.py | 332 --- api/glazierapi/openstack/common/setup.py | 359 ---- api/glazierapi/openstack/common/sslutils.py | 80 - .../openstack/common/threadgroup.py | 114 - api/glazierapi/openstack/common/timeutils.py | 182 -- api/glazierapi/openstack/common/uuidutils.py | 39 - api/glazierapi/openstack/common/version.py | 94 - api/glazierapi/openstack/common/wsgi.py | 797 ------- api/glazierapi/openstack/common/xmlutils.py | 74 - api/glazierapi/tests/__init__.py | 13 - api/glazierapi/tests/api/__init__.py | 13 - api/glazierapi/tests/api/simple_test.py | 20 - api/glazierapi/tests/sanity_tests.py | 40 - api/glazierapi/utils.py | 38 - api/glazierapi/version.py | 17 - api/openstack-common.conf | 7 - api/run_tests.sh | 123 -- api/setup.cfg | 33 - api/setup.py | 49 - api/tools/install_venv.py | 75 - api/tools/install_venv_common.py | 219 -- api/tools/pip-requires | 31 - api/tools/test-requires | 20 - api/tools/with_venv.sh | 4 - api/tox.ini | 46 - conductor/.gitignore | 20 - conductor/README.rst | 8 - conductor/babel.cfg | 1 - conductor/bin/conductor | 33 - conductor/conductor/__init__.py | 0 conductor/conductor/app.py | 105 - conductor/conductor/cloud_formation.py | 127 -- conductor/conductor/commands/__init__.py | 16 - .../conductor/commands/cloud_formation.py | 188 -- conductor/conductor/commands/command.py | 28 - conductor/conductor/commands/dispatcher.py | 48 - conductor/conductor/commands/windows_agent.py | 61 - conductor/conductor/config.py | 210 -- conductor/conductor/function_context.py | 67 - conductor/conductor/helpers.py | 64 - conductor/conductor/openstack/__init__.py | 0 .../conductor/openstack/common/__init__.py | 0 .../openstack/common/eventlet_backdoor.py | 87 - .../conductor/openstack/common/exception.py | 142 -- .../openstack/common/gettextutils.py | 33 - .../conductor/openstack/common/importutils.py | 67 - .../conductor/openstack/common/jsonutils.py | 141 -- conductor/conductor/openstack/common/local.py | 48 - conductor/conductor/openstack/common/log.py | 543 ----- .../conductor/openstack/common/loopingcall.py | 95 - .../openstack/common/notifier/__init__.py | 14 - .../openstack/common/notifier/api.py | 182 -- .../openstack/common/notifier/log_notifier.py | 35 - .../common/notifier/no_op_notifier.py | 19 - .../openstack/common/notifier/rpc_notifier.py | 46 - .../common/notifier/rpc_notifier2.py | 52 - .../common/notifier/test_notifier.py | 22 - .../conductor/openstack/common/service.py | 332 --- conductor/conductor/openstack/common/setup.py | 367 ---- .../conductor/openstack/common/sslutils.py | 80 - .../conductor/openstack/common/threadgroup.py | 114 - .../conductor/openstack/common/timeutils.py | 186 -- .../conductor/openstack/common/uuidutils.py | 39 - .../conductor/openstack/common/version.py | 94 - conductor/conductor/openstack/common/wsgi.py | 797 ------- .../conductor/openstack/common/xmlutils.py | 74 - conductor/conductor/rabbitmq.py | 141 -- conductor/conductor/reporting.py | 48 - conductor/conductor/version.py | 18 - conductor/conductor/windows_agent.py | 44 - conductor/conductor/workflow.py | 190 -- conductor/conductor/xml_code_engine.py | 137 -- conductor/data/init.ps1 | 33 - .../templates/agent-config/Default.template | 31 - .../data/templates/agent/AskDnsIp.template | 12 - .../templates/agent/CreatePrimaryDC.template | 21 - .../agent/CreateSecondaryDC.template | 23 - .../data/templates/agent/InstallIIS.template | 12 - .../data/templates/agent/JoinDomain.template | 27 - .../data/templates/agent/SetPassword.template | 22 - conductor/data/templates/cf/Windows.template | 62 - conductor/data/workflows/AD.xml | 218 -- conductor/data/workflows/Common.xml | 19 - conductor/data/workflows/IIS.xml | 65 - conductor/doc/source/_static/basic.css | 416 ---- conductor/doc/source/_static/default.css | 230 -- conductor/doc/source/_static/header-line.gif | Bin 48 -> 0 bytes conductor/doc/source/_static/header_bg.jpg | Bin 3738 -> 0 bytes conductor/doc/source/_static/jquery.tweet.js | 154 -- conductor/doc/source/_static/nature.css | 245 --- .../doc/source/_static/openstack_logo.png | Bin 3670 -> 0 bytes conductor/doc/source/_static/tweaks.css | 94 - conductor/doc/source/_templates/.placeholder | 0 conductor/doc/source/_theme/layout.html | 83 - conductor/doc/source/_theme/theme.conf | 4 - conductor/doc/source/conf.py | 242 --- conductor/doc/source/index.rst | 72 - conductor/etc/conductor-paste.ini | 0 conductor/etc/conductor.conf | 14 - conductor/logs/.gitignore | 4 - conductor/openstack-common.conf | 7 - conductor/run_tests.sh | 49 - conductor/setup.cfg | 33 - conductor/setup.py | 49 - conductor/test.json | 42 - conductor/tests/__init__.py | 14 - conductor/tests/conductor/__init__.py | 14 - conductor/tests/conductor/test_methods.py | 28 - .../tests/conductor/test_with_fake_service.py | 26 - conductor/tests/soapui.log | 0 conductor/tools/install_venv.py | 154 -- conductor/tools/install_venv_common.py | 220 -- conductor/tools/pip-requires | 10 - conductor/tools/test-requires | 8 - conductor/tools/with_venv.sh | 4 - conductor/tox.ini | 46 - dashboard/.gitignore | 19 - dashboard/README.rst | 7 - dashboard/bin/less/lessc | 139 -- dashboard/bin/lib/less/browser.js | 380 ---- dashboard/bin/lib/less/colors.js | 152 -- dashboard/bin/lib/less/cssmin.js | 355 --- dashboard/bin/lib/less/functions.js | 228 -- dashboard/bin/lib/less/index.js | 148 -- dashboard/bin/lib/less/parser.js | 1334 ------------ dashboard/bin/lib/less/rhino.js | 62 - dashboard/bin/lib/less/tree.js | 17 - dashboard/bin/lib/less/tree/alpha.js | 17 - dashboard/bin/lib/less/tree/anonymous.js | 13 - dashboard/bin/lib/less/tree/assignment.js | 17 - dashboard/bin/lib/less/tree/call.js | 48 - dashboard/bin/lib/less/tree/color.js | 101 - dashboard/bin/lib/less/tree/comment.js | 14 - dashboard/bin/lib/less/tree/condition.js | 42 - dashboard/bin/lib/less/tree/dimension.js | 49 - dashboard/bin/lib/less/tree/directive.js | 35 - dashboard/bin/lib/less/tree/element.js | 52 - dashboard/bin/lib/less/tree/expression.js | 23 - dashboard/bin/lib/less/tree/import.js | 83 - dashboard/bin/lib/less/tree/javascript.js | 51 - dashboard/bin/lib/less/tree/keyword.js | 19 - dashboard/bin/lib/less/tree/media.js | 114 - dashboard/bin/lib/less/tree/mixin.js | 146 -- dashboard/bin/lib/less/tree/operation.js | 32 - dashboard/bin/lib/less/tree/paren.js | 16 - dashboard/bin/lib/less/tree/quoted.js | 29 - dashboard/bin/lib/less/tree/rule.js | 42 - dashboard/bin/lib/less/tree/ruleset.js | 225 -- dashboard/bin/lib/less/tree/selector.js | 42 - dashboard/bin/lib/less/tree/url.js | 25 - dashboard/bin/lib/less/tree/value.js | 24 - dashboard/bin/lib/less/tree/variable.js | 26 - dashboard/doc/source/_static/.placeholder | 0 dashboard/doc/source/_templates/.placeholder | 0 dashboard/doc/source/_theme/theme.conf | 2 - dashboard/doc/source/conf.py | 237 -- dashboard/doc/source/index.rst | 65 - dashboard/glazierdashboard/__init__.py | 0 dashboard/glazierdashboard/local/__init__.py | 0 .../local/local_settings.py.example | 147 -- dashboard/glazierdashboard/models.py | 3 - .../glazierdashboard/openstack/__init__.py | 0 .../openstack/common/__init__.py | 0 .../openstack/common/importutils.py | 67 - .../openstack/common/setup.py | 367 ---- .../openstack/common/version.py | 94 - dashboard/glazierdashboard/settings.py | 148 -- dashboard/glazierdashboard/tabula/__init__.py | 14 - dashboard/glazierdashboard/tabula/api.py | 211 -- dashboard/glazierdashboard/tabula/forms.py | 120 -- .../glazierdashboard/tabula/overrides.py | 21 - dashboard/glazierdashboard/tabula/panel.py | 26 - dashboard/glazierdashboard/tabula/tables.py | 193 -- dashboard/glazierdashboard/tabula/tabs.py | 58 - dashboard/glazierdashboard/tabula/urls.py | 36 - dashboard/glazierdashboard/tabula/views.py | 204 -- .../glazierdashboard/tabula/workflows.py | 96 - .../templates/_data_center_help.html | 2 - .../glazierdashboard/templates/_dc_help.html | 3 - .../glazierdashboard/templates/_iis_help.html | 2 - .../templates/_service_logs.html | 7 - .../glazierdashboard/templates/_services.html | 16 - .../templates/_services_tabs.html | 57 - .../glazierdashboard/templates/create.html | 11 - .../glazierdashboard/templates/create_dc.html | 11 - .../glazierdashboard/templates/index.html | 11 - .../templates/service_details.html | 15 - .../glazierdashboard/templates/services.html | 11 - .../glazierdashboard/templates/update.html | 11 - dashboard/glazierdashboard/test/__init__.py | 0 dashboard/glazierdashboard/test/settings.py | 70 - dashboard/glazierdashboard/version.py | 20 - dashboard/manage.py | 10 - dashboard/openstack-common.conf | 7 - ...n-portasclient-2013.1.a345.ga70b44e.tar.gz | Bin 23774 -> 0 bytes dashboard/run_tests.sh | 442 ---- dashboard/setup.cfg | 9 - dashboard/setup.py | 51 - dashboard/tools/install_venv.py | 69 - dashboard/tools/install_venv_common.py | 219 -- dashboard/tools/pip-requires | 6 - dashboard/tools/rfc.sh | 145 -- dashboard/tools/test-requires | 18 - dashboard/tools/with_venv.sh | 4 - docs/.gitignore | 9 - docs/LICENSE | 176 -- docs/README.rst | 47 - docs/src/glazier-manual/pom.xml | 54 - .../src/docbkx/figures/api_workflow.png | Bin 26028 -> 0 bytes .../docbkx/figures/architecture_diagram.png | Bin 30105 -> 0 bytes .../src/docbkx/glazier-manual.xml | 1910 ----------------- .../packages => packages}/repositories.config | 0 python-glazierclient/.gitignore | 23 - python-glazierclient/README.rst | 9 - .../doc/source/_static/.placeholder | 0 .../doc/source/_templates/.placeholder | 0 .../doc/source/_theme/theme.conf | 2 - python-glazierclient/doc/source/conf.py | 73 - python-glazierclient/doc/source/index.rst | 48 - .../glazierclient/__init__.py | 0 python-glazierclient/glazierclient/client.py | 20 - .../glazierclient/common/__init__.py | 0 .../glazierclient/common/base.py | 166 -- .../glazierclient/common/exceptions.py | 163 -- .../glazierclient/common/http.py | 275 --- .../glazierclient/common/utils.py | 119 - .../glazierclient/openstack/__init__.py | 0 .../openstack/common/__init__.py | 0 .../openstack/common/importutils.py | 67 - .../glazierclient/openstack/common/setup.py | 359 ---- .../glazierclient/openstack/common/version.py | 94 - python-glazierclient/glazierclient/shell.py | 314 --- .../glazierclient/v1/__init__.py | 15 - .../glazierclient/v1/client.py | 34 - .../glazierclient/v1/environments.py | 43 - .../glazierclient/v1/services.py | 92 - .../glazierclient/v1/sessions.py | 68 - .../glazierclient/v1/shell.py | 23 - python-glazierclient/glazierclient/version.py | 18 - python-glazierclient/openstack-common.conf | 7 - python-glazierclient/run_tests.sh | 49 - python-glazierclient/setup.cfg | 15 - python-glazierclient/setup.py | 50 - python-glazierclient/tests/__init__.py | 13 - .../tests/glazierclient/__init__.py | 13 - .../test_client_with_fake_http.py | 208 -- .../tests/glazierclient/test_methods.py | 481 ----- python-glazierclient/tools/install_venv.py | 75 - .../tools/install_venv_common.py | 219 -- python-glazierclient/tools/pip-requires | 5 - python-glazierclient/tools/test-requires | 15 - python-glazierclient/tools/with_venv.sh | 4 - python-glazierclient/tox.ini | 46 - tests/REST_service/test.py | 172 -- tests/deploy.sh | 81 - tests/selenium/conf.ini | 4 - tests/selenium/environments_page.py | 61 - tests/selenium/login_page.py | 36 - tests/selenium/objects.xml | 73 - tests/selenium/page.py | 276 --- tests/selenium/services_details_page.py | 31 - tests/selenium/services_page.py | 69 - tests/selenium/test.py | 182 -- tests/selenium/tools/test_requires | 8 - tests/selenium/tox.ini | 28 - 410 files changed, 31887 deletions(-) rename WindowsAgent/.gitignore => .gitignore (100%) delete mode 100644 Deployment/ExecutionPlan/1.CreatePrimaryDC/CreatePrimaryDC.json delete mode 100644 Deployment/ExecutionPlan/1.CreatePrimaryDC/ExecutionPlan.txt delete mode 100644 Deployment/ExecutionPlan/1.CreatePrimaryDC/GenerateJSON.bat delete mode 100644 Deployment/ExecutionPlan/1.CreatePrimaryDC/Install-RolePrimaryDomainController.ps1 delete mode 100644 Deployment/ExecutionPlan/2.JoinDomain/ExecutionPlan.txt delete mode 100644 Deployment/ExecutionPlan/2.JoinDomain/GenerateJSON.bat delete mode 100644 Deployment/ExecutionPlan/2.JoinDomain/out.json delete mode 100644 Deployment/ExecutionPlan/3.CreateSecondaryDC/ExecutionPlan.txt delete mode 100644 Deployment/ExecutionPlan/3.CreateSecondaryDC/GenerateJSON.bat delete mode 100644 Deployment/ExecutionPlan/3.CreateSecondaryDC/Install-RoleSecondaryDomainController.ps1 delete mode 100644 Deployment/ExecutionPlan/3.CreateSecondaryDC/out.json delete mode 100644 Deployment/ExecutionPlan/4.JoinAndPromote/ExecutionPlan.txt delete mode 100644 Deployment/ExecutionPlan/4.JoinAndPromote/GenerateJSON.bat delete mode 100644 Deployment/ExecutionPlan/4.JoinAndPromote/Install-RoleSecondaryDomainController.ps1 delete mode 100644 Deployment/ExecutionPlan/4.JoinAndPromote/out.json delete mode 100644 Deployment/ExecutionPlan/ExecutionPlanGenerator.exe delete mode 100644 Deployment/ExecutionPlan/GetDnsIpAddressesOnDc/ExecutionPlan.txt delete mode 100644 Deployment/ExecutionPlan/GetDnsIpAddressesOnDc/GenerateJSON.bat delete mode 100644 Deployment/ExecutionPlan/GetDnsIpAddressesOnDc/Get-DnsListeningIpAddress.ps1 delete mode 100644 Deployment/ExecutionPlan/GetDnsIpAddressesOnDc/out.json delete mode 100644 Deployment/ExecutionPlan/InstallIIS/ExecutionPlan.txt delete mode 100644 Deployment/ExecutionPlan/InstallIIS/GenerateJSON.bat delete mode 100644 Deployment/ExecutionPlan/InstallIIS/Install-WebServer.ps1 delete mode 100644 Deployment/ExecutionPlan/InstallIIS/out.json delete mode 100644 Deployment/ExecutionPlan/Newtonsoft.Json.dll delete mode 100644 Deployment/Unattended/ws-2012-core-unattended.xml delete mode 100644 Deployment/Unattended/ws-2012-full-unattend.xml delete mode 100644 Deployment/WindowsPowerShell/Modules/CoreFunctions/Config.ps1 delete mode 100644 Deployment/WindowsPowerShell/Modules/CoreFunctions/CoreFunctions.psd1 delete mode 100644 Deployment/WindowsPowerShell/Modules/CoreFunctions/CoreFunctions.psm1 delete mode 100644 Deployment/WindowsPowerShell/Modules/CoreFunctions/Ionic.Zip.dll delete mode 100644 Deployment/WindowsPowerShell/Modules/CoreFunctions/en-US/about_CoreFunctions.help.txt delete mode 100644 Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Base64.ps1 delete mode 100644 Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Functions.ps1 delete mode 100644 Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Logger.ps1 delete mode 100644 Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Module.ps1 delete mode 100644 Deployment/WindowsPowerShell/Modules/CoreFunctions/include/NotCoreFunctions.ps1 delete mode 100644 Deployment/WindowsPowerShell/Modules/CoreFunctions/include/SqlFunctions.ps1 delete mode 100644 Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Zip.ps1 delete mode 100644 Deployment/WindowsPowerShell/Modules/CoreFunctions/log4net.config delete mode 100644 Deployment/WindowsPowerShell/Modules/CoreFunctions/log4net.dll delete mode 100644 Deployment/cloudbase-init/config/cloudbase-init.conf delete mode 100644 Deployment/cloudbase-init/plugins/setagentconfig.py delete mode 100644 Deployment/cloudbase-init/plugins/sethostname.py delete mode 100644 Deployment/cloudbase-init/plugins/userdata.py delete mode 100644 Deployment/cloudbase-init/userdata/Sample.ps1 delete mode 100644 Deployment/devstack-scripts/compute/devstack.localrc delete mode 100644 Deployment/devstack-scripts/controller/devstack.localrc delete mode 100644 Deployment/devstack-scripts/devstack.standalone.localrc delete mode 100644 Deployment/devstack-scripts/functions.sh delete mode 100644 Deployment/devstack-scripts/install-devstack.sh delete mode 100644 Deployment/devstack-scripts/localrc delete mode 100644 Deployment/devstack-scripts/post-stack.sh delete mode 100644 Deployment/devstack-scripts/post-unstack.sh delete mode 100644 Deployment/devstack-scripts/pre-stack.sh delete mode 100644 Deployment/devstack-scripts/pre-unstack.sh delete mode 100644 Deployment/devstack-scripts/ss1285.interfaces.example delete mode 100644 Deployment/devstack-scripts/ss1383.interfaces.example delete mode 100644 Deployment/devstack-scripts/standalone/devstack.localrc delete mode 100644 Deployment/devstack-scripts/start-devstack.sh delete mode 100644 Deployment/devstack-scripts/start-keero.sh delete mode 100644 Deployment/devstack-scripts/start-vm.sh delete mode 100644 Deployment/devstack-scripts/stop-devstack.sh delete mode 100644 Deployment/devstack-scripts/stop-keero.sh rename {WindowsAgent/ExecutionPlanGenerator => ExecutionPlanGenerator}/App.config (100%) rename {WindowsAgent/ExecutionPlanGenerator => ExecutionPlanGenerator}/ExecutionPlanGenerator.csproj (100%) rename {WindowsAgent/ExecutionPlanGenerator => ExecutionPlanGenerator}/Program.cs (100%) rename {WindowsAgent/ExecutionPlanGenerator => ExecutionPlanGenerator}/Properties/AssemblyInfo.cs (100%) rename {WindowsAgent/ExecutionPlanGenerator => ExecutionPlanGenerator}/packages.config (100%) rename {WindowsAgent/Tools => Tools}/NuGet.exe (100%) rename WindowsAgent/WindowsAgent.sln => WindowsAgent.sln (100%) rename WindowsAgent/{WindowsAgent => }/App.config (100%) rename WindowsAgent/{WindowsAgent => }/ExecutionPlan.cs (100%) rename WindowsAgent/{WindowsAgent => }/MqMessage.cs (100%) rename WindowsAgent/{WindowsAgent => }/PlanExecutor.cs (100%) rename WindowsAgent/{WindowsAgent => }/Program.cs (100%) rename WindowsAgent/{WindowsAgent => }/Properties/AssemblyInfo.cs (100%) rename WindowsAgent/{WindowsAgent => }/RabbitMqClient.cs (100%) rename WindowsAgent/{WindowsAgent => }/SampleExecutionPlan.json (100%) rename WindowsAgent/{WindowsAgent => }/ServiceManager.cs (100%) rename WindowsAgent/{WindowsAgent => }/WindowsAgent.csproj (100%) rename WindowsAgent/{WindowsAgent => }/WindowsService.cs (100%) rename WindowsAgent/{WindowsAgent => }/WindowsServiceInstaller.cs (100%) rename WindowsAgent/{WindowsAgent => }/packages.config (100%) delete mode 100644 api/.gitignore delete mode 100644 api/README.rst delete mode 100644 api/babel.cfg delete mode 100755 api/bin/glazier-api delete mode 100755 api/doc/source/_static/.placeholder delete mode 100755 api/doc/source/_templates/.placeholder delete mode 100755 api/doc/source/_theme/theme.conf delete mode 100644 api/doc/source/conf.py delete mode 100644 api/doc/source/index.rst delete mode 100644 api/doc/source/man/glazierapi.rst delete mode 100644 api/etc/glazier-api-paste.ini delete mode 100644 api/etc/glazier-api.conf delete mode 100644 api/glazierapi/__init__.py delete mode 100644 api/glazierapi/api/__init__.py delete mode 100644 api/glazierapi/api/middleware/__init__.py delete mode 100644 api/glazierapi/api/middleware/context.py delete mode 100644 api/glazierapi/api/v1/__init__.py delete mode 100644 api/glazierapi/api/v1/active_directories.py delete mode 100644 api/glazierapi/api/v1/environments.py delete mode 100644 api/glazierapi/api/v1/router.py delete mode 100644 api/glazierapi/api/v1/sessions.py delete mode 100644 api/glazierapi/api/v1/webservers.py delete mode 100644 api/glazierapi/common/__init__.py delete mode 100644 api/glazierapi/common/config.py delete mode 100644 api/glazierapi/common/service.py delete mode 100644 api/glazierapi/common/uuidutils.py delete mode 100644 api/glazierapi/context.py delete mode 100644 api/glazierapi/db/__init__.py delete mode 100644 api/glazierapi/db/migrate_repo/README delete mode 100644 api/glazierapi/db/migrate_repo/__init__.py delete mode 100644 api/glazierapi/db/migrate_repo/manage.py delete mode 100644 api/glazierapi/db/migrate_repo/migrate.cfg delete mode 100644 api/glazierapi/db/migrate_repo/versions/001_add_initial_tables.py delete mode 100644 api/glazierapi/db/migrate_repo/versions/002_add_session_table.py delete mode 100644 api/glazierapi/db/migrate_repo/versions/003_add_status_table.py delete mode 100644 api/glazierapi/db/migrate_repo/versions/004_add_description_column_to_session.py delete mode 100644 api/glazierapi/db/migrate_repo/versions/005_remove_obsolete_service_table.py delete mode 100644 api/glazierapi/db/migrate_repo/versions/006_add_entity_id_column_to_status.py delete mode 100644 api/glazierapi/db/migrate_repo/versions/__init__.py delete mode 100644 api/glazierapi/db/models.py delete mode 100644 api/glazierapi/db/session.py delete mode 100644 api/glazierapi/locale/ru/LC_MESSAGES/glazierapi.po delete mode 100644 api/glazierapi/openstack/__init__.py delete mode 100644 api/glazierapi/openstack/common/__init__.py delete mode 100644 api/glazierapi/openstack/common/eventlet_backdoor.py delete mode 100644 api/glazierapi/openstack/common/exception.py delete mode 100644 api/glazierapi/openstack/common/gettextutils.py delete mode 100644 api/glazierapi/openstack/common/importutils.py delete mode 100644 api/glazierapi/openstack/common/jsonutils.py delete mode 100644 api/glazierapi/openstack/common/local.py delete mode 100644 api/glazierapi/openstack/common/log.py delete mode 100644 api/glazierapi/openstack/common/loopingcall.py delete mode 100644 api/glazierapi/openstack/common/notifier/__init__.py delete mode 100644 api/glazierapi/openstack/common/notifier/api.py delete mode 100644 api/glazierapi/openstack/common/notifier/log_notifier.py delete mode 100644 api/glazierapi/openstack/common/notifier/no_op_notifier.py delete mode 100644 api/glazierapi/openstack/common/notifier/rabbit_notifier.py delete mode 100644 api/glazierapi/openstack/common/notifier/rpc_notifier.py delete mode 100644 api/glazierapi/openstack/common/notifier/rpc_notifier2.py delete mode 100644 api/glazierapi/openstack/common/notifier/test_notifier.py delete mode 100644 api/glazierapi/openstack/common/service.py delete mode 100644 api/glazierapi/openstack/common/setup.py delete mode 100644 api/glazierapi/openstack/common/sslutils.py delete mode 100644 api/glazierapi/openstack/common/threadgroup.py delete mode 100644 api/glazierapi/openstack/common/timeutils.py delete mode 100644 api/glazierapi/openstack/common/uuidutils.py delete mode 100644 api/glazierapi/openstack/common/version.py delete mode 100644 api/glazierapi/openstack/common/wsgi.py delete mode 100644 api/glazierapi/openstack/common/xmlutils.py delete mode 100644 api/glazierapi/tests/__init__.py delete mode 100644 api/glazierapi/tests/api/__init__.py delete mode 100644 api/glazierapi/tests/api/simple_test.py delete mode 100644 api/glazierapi/tests/sanity_tests.py delete mode 100644 api/glazierapi/utils.py delete mode 100644 api/glazierapi/version.py delete mode 100644 api/openstack-common.conf delete mode 100755 api/run_tests.sh delete mode 100644 api/setup.cfg delete mode 100644 api/setup.py delete mode 100644 api/tools/install_venv.py delete mode 100644 api/tools/install_venv_common.py delete mode 100644 api/tools/pip-requires delete mode 100644 api/tools/test-requires delete mode 100755 api/tools/with_venv.sh delete mode 100644 api/tox.ini delete mode 100644 conductor/.gitignore delete mode 100644 conductor/README.rst delete mode 100644 conductor/babel.cfg delete mode 100644 conductor/bin/conductor delete mode 100644 conductor/conductor/__init__.py delete mode 100644 conductor/conductor/app.py delete mode 100644 conductor/conductor/cloud_formation.py delete mode 100644 conductor/conductor/commands/__init__.py delete mode 100644 conductor/conductor/commands/cloud_formation.py delete mode 100644 conductor/conductor/commands/command.py delete mode 100644 conductor/conductor/commands/dispatcher.py delete mode 100644 conductor/conductor/commands/windows_agent.py delete mode 100644 conductor/conductor/config.py delete mode 100644 conductor/conductor/function_context.py delete mode 100644 conductor/conductor/helpers.py delete mode 100644 conductor/conductor/openstack/__init__.py delete mode 100644 conductor/conductor/openstack/common/__init__.py delete mode 100644 conductor/conductor/openstack/common/eventlet_backdoor.py delete mode 100644 conductor/conductor/openstack/common/exception.py delete mode 100644 conductor/conductor/openstack/common/gettextutils.py delete mode 100644 conductor/conductor/openstack/common/importutils.py delete mode 100644 conductor/conductor/openstack/common/jsonutils.py delete mode 100644 conductor/conductor/openstack/common/local.py delete mode 100644 conductor/conductor/openstack/common/log.py delete mode 100644 conductor/conductor/openstack/common/loopingcall.py delete mode 100644 conductor/conductor/openstack/common/notifier/__init__.py delete mode 100644 conductor/conductor/openstack/common/notifier/api.py delete mode 100644 conductor/conductor/openstack/common/notifier/log_notifier.py delete mode 100644 conductor/conductor/openstack/common/notifier/no_op_notifier.py delete mode 100644 conductor/conductor/openstack/common/notifier/rpc_notifier.py delete mode 100644 conductor/conductor/openstack/common/notifier/rpc_notifier2.py delete mode 100644 conductor/conductor/openstack/common/notifier/test_notifier.py delete mode 100644 conductor/conductor/openstack/common/service.py delete mode 100644 conductor/conductor/openstack/common/setup.py delete mode 100644 conductor/conductor/openstack/common/sslutils.py delete mode 100644 conductor/conductor/openstack/common/threadgroup.py delete mode 100644 conductor/conductor/openstack/common/timeutils.py delete mode 100644 conductor/conductor/openstack/common/uuidutils.py delete mode 100644 conductor/conductor/openstack/common/version.py delete mode 100644 conductor/conductor/openstack/common/wsgi.py delete mode 100644 conductor/conductor/openstack/common/xmlutils.py delete mode 100644 conductor/conductor/rabbitmq.py delete mode 100644 conductor/conductor/reporting.py delete mode 100644 conductor/conductor/version.py delete mode 100644 conductor/conductor/windows_agent.py delete mode 100644 conductor/conductor/workflow.py delete mode 100644 conductor/conductor/xml_code_engine.py delete mode 100644 conductor/data/init.ps1 delete mode 100644 conductor/data/templates/agent-config/Default.template delete mode 100644 conductor/data/templates/agent/AskDnsIp.template delete mode 100644 conductor/data/templates/agent/CreatePrimaryDC.template delete mode 100644 conductor/data/templates/agent/CreateSecondaryDC.template delete mode 100644 conductor/data/templates/agent/InstallIIS.template delete mode 100644 conductor/data/templates/agent/JoinDomain.template delete mode 100644 conductor/data/templates/agent/SetPassword.template delete mode 100644 conductor/data/templates/cf/Windows.template delete mode 100644 conductor/data/workflows/AD.xml delete mode 100644 conductor/data/workflows/Common.xml delete mode 100644 conductor/data/workflows/IIS.xml delete mode 100644 conductor/doc/source/_static/basic.css delete mode 100644 conductor/doc/source/_static/default.css delete mode 100644 conductor/doc/source/_static/header-line.gif delete mode 100644 conductor/doc/source/_static/header_bg.jpg delete mode 100644 conductor/doc/source/_static/jquery.tweet.js delete mode 100644 conductor/doc/source/_static/nature.css delete mode 100644 conductor/doc/source/_static/openstack_logo.png delete mode 100644 conductor/doc/source/_static/tweaks.css delete mode 100644 conductor/doc/source/_templates/.placeholder delete mode 100644 conductor/doc/source/_theme/layout.html delete mode 100644 conductor/doc/source/_theme/theme.conf delete mode 100644 conductor/doc/source/conf.py delete mode 100644 conductor/doc/source/index.rst delete mode 100644 conductor/etc/conductor-paste.ini delete mode 100644 conductor/etc/conductor.conf delete mode 100644 conductor/logs/.gitignore delete mode 100644 conductor/openstack-common.conf delete mode 100755 conductor/run_tests.sh delete mode 100644 conductor/setup.cfg delete mode 100644 conductor/setup.py delete mode 100644 conductor/test.json delete mode 100644 conductor/tests/__init__.py delete mode 100644 conductor/tests/conductor/__init__.py delete mode 100644 conductor/tests/conductor/test_methods.py delete mode 100644 conductor/tests/conductor/test_with_fake_service.py delete mode 100644 conductor/tests/soapui.log delete mode 100644 conductor/tools/install_venv.py delete mode 100644 conductor/tools/install_venv_common.py delete mode 100644 conductor/tools/pip-requires delete mode 100644 conductor/tools/test-requires delete mode 100755 conductor/tools/with_venv.sh delete mode 100644 conductor/tox.ini delete mode 100644 dashboard/.gitignore delete mode 100644 dashboard/README.rst delete mode 100755 dashboard/bin/less/lessc delete mode 100644 dashboard/bin/lib/less/browser.js delete mode 100644 dashboard/bin/lib/less/colors.js delete mode 100644 dashboard/bin/lib/less/cssmin.js delete mode 100644 dashboard/bin/lib/less/functions.js delete mode 100644 dashboard/bin/lib/less/index.js delete mode 100644 dashboard/bin/lib/less/parser.js delete mode 100644 dashboard/bin/lib/less/rhino.js delete mode 100644 dashboard/bin/lib/less/tree.js delete mode 100644 dashboard/bin/lib/less/tree/alpha.js delete mode 100644 dashboard/bin/lib/less/tree/anonymous.js delete mode 100644 dashboard/bin/lib/less/tree/assignment.js delete mode 100644 dashboard/bin/lib/less/tree/call.js delete mode 100644 dashboard/bin/lib/less/tree/color.js delete mode 100644 dashboard/bin/lib/less/tree/comment.js delete mode 100644 dashboard/bin/lib/less/tree/condition.js delete mode 100644 dashboard/bin/lib/less/tree/dimension.js delete mode 100644 dashboard/bin/lib/less/tree/directive.js delete mode 100644 dashboard/bin/lib/less/tree/element.js delete mode 100644 dashboard/bin/lib/less/tree/expression.js delete mode 100644 dashboard/bin/lib/less/tree/import.js delete mode 100644 dashboard/bin/lib/less/tree/javascript.js delete mode 100644 dashboard/bin/lib/less/tree/keyword.js delete mode 100644 dashboard/bin/lib/less/tree/media.js delete mode 100644 dashboard/bin/lib/less/tree/mixin.js delete mode 100644 dashboard/bin/lib/less/tree/operation.js delete mode 100644 dashboard/bin/lib/less/tree/paren.js delete mode 100644 dashboard/bin/lib/less/tree/quoted.js delete mode 100644 dashboard/bin/lib/less/tree/rule.js delete mode 100644 dashboard/bin/lib/less/tree/ruleset.js delete mode 100644 dashboard/bin/lib/less/tree/selector.js delete mode 100644 dashboard/bin/lib/less/tree/url.js delete mode 100644 dashboard/bin/lib/less/tree/value.js delete mode 100644 dashboard/bin/lib/less/tree/variable.js delete mode 100755 dashboard/doc/source/_static/.placeholder delete mode 100755 dashboard/doc/source/_templates/.placeholder delete mode 100755 dashboard/doc/source/_theme/theme.conf delete mode 100644 dashboard/doc/source/conf.py delete mode 100644 dashboard/doc/source/index.rst delete mode 100644 dashboard/glazierdashboard/__init__.py delete mode 100644 dashboard/glazierdashboard/local/__init__.py delete mode 100644 dashboard/glazierdashboard/local/local_settings.py.example delete mode 100644 dashboard/glazierdashboard/models.py delete mode 100644 dashboard/glazierdashboard/openstack/__init__.py delete mode 100644 dashboard/glazierdashboard/openstack/common/__init__.py delete mode 100644 dashboard/glazierdashboard/openstack/common/importutils.py delete mode 100644 dashboard/glazierdashboard/openstack/common/setup.py delete mode 100644 dashboard/glazierdashboard/openstack/common/version.py delete mode 100644 dashboard/glazierdashboard/settings.py delete mode 100644 dashboard/glazierdashboard/tabula/__init__.py delete mode 100644 dashboard/glazierdashboard/tabula/api.py delete mode 100644 dashboard/glazierdashboard/tabula/forms.py delete mode 100644 dashboard/glazierdashboard/tabula/overrides.py delete mode 100644 dashboard/glazierdashboard/tabula/panel.py delete mode 100644 dashboard/glazierdashboard/tabula/tables.py delete mode 100644 dashboard/glazierdashboard/tabula/tabs.py delete mode 100644 dashboard/glazierdashboard/tabula/urls.py delete mode 100644 dashboard/glazierdashboard/tabula/views.py delete mode 100644 dashboard/glazierdashboard/tabula/workflows.py delete mode 100644 dashboard/glazierdashboard/templates/_data_center_help.html delete mode 100644 dashboard/glazierdashboard/templates/_dc_help.html delete mode 100644 dashboard/glazierdashboard/templates/_iis_help.html delete mode 100644 dashboard/glazierdashboard/templates/_service_logs.html delete mode 100644 dashboard/glazierdashboard/templates/_services.html delete mode 100644 dashboard/glazierdashboard/templates/_services_tabs.html delete mode 100644 dashboard/glazierdashboard/templates/create.html delete mode 100644 dashboard/glazierdashboard/templates/create_dc.html delete mode 100644 dashboard/glazierdashboard/templates/index.html delete mode 100644 dashboard/glazierdashboard/templates/service_details.html delete mode 100644 dashboard/glazierdashboard/templates/services.html delete mode 100644 dashboard/glazierdashboard/templates/update.html delete mode 100644 dashboard/glazierdashboard/test/__init__.py delete mode 100644 dashboard/glazierdashboard/test/settings.py delete mode 100644 dashboard/glazierdashboard/version.py delete mode 100755 dashboard/manage.py delete mode 100644 dashboard/openstack-common.conf delete mode 100644 dashboard/packages/python-portasclient-2013.1.a345.ga70b44e.tar.gz delete mode 100755 dashboard/run_tests.sh delete mode 100644 dashboard/setup.cfg delete mode 100755 dashboard/setup.py delete mode 100644 dashboard/tools/install_venv.py delete mode 100644 dashboard/tools/install_venv_common.py delete mode 100644 dashboard/tools/pip-requires delete mode 100755 dashboard/tools/rfc.sh delete mode 100644 dashboard/tools/test-requires delete mode 100755 dashboard/tools/with_venv.sh delete mode 100644 docs/.gitignore delete mode 100644 docs/LICENSE delete mode 100644 docs/README.rst delete mode 100644 docs/src/glazier-manual/pom.xml delete mode 100755 docs/src/glazier-manual/src/docbkx/figures/api_workflow.png delete mode 100755 docs/src/glazier-manual/src/docbkx/figures/architecture_diagram.png delete mode 100644 docs/src/glazier-manual/src/docbkx/glazier-manual.xml rename {WindowsAgent/packages => packages}/repositories.config (100%) delete mode 100644 python-glazierclient/.gitignore delete mode 100644 python-glazierclient/README.rst delete mode 100755 python-glazierclient/doc/source/_static/.placeholder delete mode 100755 python-glazierclient/doc/source/_templates/.placeholder delete mode 100755 python-glazierclient/doc/source/_theme/theme.conf delete mode 100644 python-glazierclient/doc/source/conf.py delete mode 100644 python-glazierclient/doc/source/index.rst delete mode 100644 python-glazierclient/glazierclient/__init__.py delete mode 100644 python-glazierclient/glazierclient/client.py delete mode 100644 python-glazierclient/glazierclient/common/__init__.py delete mode 100644 python-glazierclient/glazierclient/common/base.py delete mode 100644 python-glazierclient/glazierclient/common/exceptions.py delete mode 100644 python-glazierclient/glazierclient/common/http.py delete mode 100644 python-glazierclient/glazierclient/common/utils.py delete mode 100644 python-glazierclient/glazierclient/openstack/__init__.py delete mode 100644 python-glazierclient/glazierclient/openstack/common/__init__.py delete mode 100644 python-glazierclient/glazierclient/openstack/common/importutils.py delete mode 100644 python-glazierclient/glazierclient/openstack/common/setup.py delete mode 100644 python-glazierclient/glazierclient/openstack/common/version.py delete mode 100644 python-glazierclient/glazierclient/shell.py delete mode 100644 python-glazierclient/glazierclient/v1/__init__.py delete mode 100644 python-glazierclient/glazierclient/v1/client.py delete mode 100644 python-glazierclient/glazierclient/v1/environments.py delete mode 100644 python-glazierclient/glazierclient/v1/services.py delete mode 100644 python-glazierclient/glazierclient/v1/sessions.py delete mode 100644 python-glazierclient/glazierclient/v1/shell.py delete mode 100644 python-glazierclient/glazierclient/version.py delete mode 100644 python-glazierclient/openstack-common.conf delete mode 100755 python-glazierclient/run_tests.sh delete mode 100644 python-glazierclient/setup.cfg delete mode 100644 python-glazierclient/setup.py delete mode 100644 python-glazierclient/tests/__init__.py delete mode 100644 python-glazierclient/tests/glazierclient/__init__.py delete mode 100644 python-glazierclient/tests/glazierclient/test_client_with_fake_http.py delete mode 100644 python-glazierclient/tests/glazierclient/test_methods.py delete mode 100644 python-glazierclient/tools/install_venv.py delete mode 100644 python-glazierclient/tools/install_venv_common.py delete mode 100644 python-glazierclient/tools/pip-requires delete mode 100644 python-glazierclient/tools/test-requires delete mode 100755 python-glazierclient/tools/with_venv.sh delete mode 100644 python-glazierclient/tox.ini delete mode 100644 tests/REST_service/test.py delete mode 100755 tests/deploy.sh delete mode 100644 tests/selenium/conf.ini delete mode 100644 tests/selenium/environments_page.py delete mode 100644 tests/selenium/login_page.py delete mode 100644 tests/selenium/objects.xml delete mode 100644 tests/selenium/page.py delete mode 100644 tests/selenium/services_details_page.py delete mode 100644 tests/selenium/services_page.py delete mode 100644 tests/selenium/test.py delete mode 100644 tests/selenium/tools/test_requires delete mode 100644 tests/selenium/tox.ini diff --git a/WindowsAgent/.gitignore b/.gitignore similarity index 100% rename from WindowsAgent/.gitignore rename to .gitignore diff --git a/Deployment/ExecutionPlan/1.CreatePrimaryDC/CreatePrimaryDC.json b/Deployment/ExecutionPlan/1.CreatePrimaryDC/CreatePrimaryDC.json deleted file mode 100644 index 519da701..00000000 --- a/Deployment/ExecutionPlan/1.CreatePrimaryDC/CreatePrimaryDC.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "Scripts": [ - "DQpGdW5jdGlvbiBJbnN0YWxsLVJvbGVQcmltYXJ5RG9tYWluQ29udHJvbGxlcg0Kew0KPCMNCi5TWU5PUFNJUw0KQ29uZmlndXJlIG5vZGUncyBuZXR3b3JrIGFkYXB0ZXJzLg0KQ3JlYXRlIGZpcnN0IGRvbWFpbiBjb250cm9sbGVyIGluIHRoZSBmb3Jlc3QuDQoNCi5FWEFNUExFDQpQUz4gSW5zdGFsbC1Sb2xlUHJpbWFyeURvbWFpbkNvbnRyb2xsZXIgLURvbWFpbk5hbWUgYWNtZS5sb2NhbCAtU2FmZU1vZGVQYXNzd29yZCAiUEBzc3cwcmQiDQoNCkluc3RhbGwgRE5TIGFuZCBBRERTLCBjcmVhdGUgZm9yZXN0IGFuZCBkb21haW4gJ2FjbWUubG9jYWwnLg0KU2V0IERDIHJlY292ZXJ5IG1vZGUgcGFzc3dvcmQgdG8gJ1BAc3N3MHJkJy4NCiM+DQoJDQoJcGFyYW0NCgkoDQoJCVtTdHJpbmddDQoJCSMgTmV3IGRvbWFpbiBuYW1lLg0KCQkkRG9tYWluTmFtZSwNCgkJDQoJCVtTdHJpbmddDQoJCSMgRG9tYWluIGNvbnRyb2xsZXIgcmVjb3ZlcnkgbW9kZSBwYXNzd29yZC4NCgkJJFNhZmVNb2RlUGFzc3dvcmQNCgkpDQoNCgl0cmFwIHsgU3RvcC1FeGVjdXRpb24gJF8gfQ0KDQogICAgICAgICMgQWRkIHJlcXVpcmVkIHdpbmRvd3MgZmVhdHVyZXMNCglBZGQtV2luZG93c0ZlYXR1cmVXcmFwcGVyIGANCgkJLU5hbWUgIkROUyIsIkFELURvbWFpbi1TZXJ2aWNlcyIsIlJTQVQtREZTLU1nbXQtQ29uIiBgDQoJCS1JbmNsdWRlTWFuYWdlbWVudFRvb2xzIGANCiAgICAgICAgLU5vdGlmeVJlc3RhcnQNCg0KDQoJV3JpdGUtTG9nICJDcmVhdGluZyBmaXJzdCBkb21haW4gY29udHJvbGxlciAuLi4iDQoJCQ0KCSRTTUFQID0gQ29udmVydFRvLVNlY3VyZVN0cmluZyAtU3RyaW5nICRTYWZlTW9kZVBhc3N3b3JkIC1Bc1BsYWluVGV4dCAtRm9yY2UNCgkJDQoJSW5zdGFsbC1BRERTRm9yZXN0IGANCgkJLURvbWFpbk5hbWUgJERvbWFpbk5hbWUgYA0KCQktU2FmZU1vZGVBZG1pbmlzdHJhdG9yUGFzc3dvcmQgJFNNQVAgYA0KCQktRG9tYWluTW9kZSBEZWZhdWx0IGANCgkJLUZvcmVzdE1vZGUgRGVmYXVsdCBgDQoJCS1Ob1JlYm9vdE9uQ29tcGxldGlvbiBgDQoJCS1Gb3JjZSBgDQoJCS1FcnJvckFjdGlvbiBTdG9wIHwgT3V0LU51bGwNCg0KCVdyaXRlLUxvZyAiV2FpdGluZyBmb3IgcmVib290IC4uLiIJCQ0KIwlTdG9wLUV4ZWN1dGlvbiAtRXhpdENvZGUgMzAxMCAtRXhpdFN0cmluZyAiQ29tcHV0ZXIgbXVzdCBiZSByZXN0YXJ0ZWQgdG8gZmluaXNoIGRvbWFpbiBjb250cm9sbGVyIHByb21vdGlvbi4iDQojCVdyaXRlLUxvZyAiUmVzdGFyaW5nIGNvbXB1dGVyIC4uLiINCiMJUmVzdGFydC1Db21wdXRlciAtRm9yY2UNCn0NCg==" - ], - "Commands": [ - { - "Name": "Import-Module", - "Arguments": { - "Name": "CoreFunctions" - } - }, - { - "Name": "Set-LocalUserPassword", - "Arguments": { - "UserName": "Administrator", - "Password": "P@ssw0rd123", - "Force": true - } - }, - { - "Name": "Install-RolePrimaryDomainController", - "Arguments": { - "DomainName": "acme.local", - "SafeModePassword": "P@ssw0rd" - } - } - ], - "RebootOnCompletion": 1 -} \ No newline at end of file diff --git a/Deployment/ExecutionPlan/1.CreatePrimaryDC/ExecutionPlan.txt b/Deployment/ExecutionPlan/1.CreatePrimaryDC/ExecutionPlan.txt deleted file mode 100644 index 74524776..00000000 --- a/Deployment/ExecutionPlan/1.CreatePrimaryDC/ExecutionPlan.txt +++ /dev/null @@ -1,9 +0,0 @@ -include Install-RolePrimaryDomainController.ps1 - -call Import-Module Name="CoreFunctions" -call Set-LocalUserPassword UserName="Administrator" , Password="P@ssw0rd123" , Force=true -call Install-RolePrimaryDomainController DomainName="acme.local" , SafeModePassword="P@ssw0rd" - -reboot 1 - -out CreatePrimaryDC.json diff --git a/Deployment/ExecutionPlan/1.CreatePrimaryDC/GenerateJSON.bat b/Deployment/ExecutionPlan/1.CreatePrimaryDC/GenerateJSON.bat deleted file mode 100644 index 0b6aae7e..00000000 --- a/Deployment/ExecutionPlan/1.CreatePrimaryDC/GenerateJSON.bat +++ /dev/null @@ -1 +0,0 @@ -..\ExecutionPlanGenerator.exe ExecutionPlan.txt \ No newline at end of file diff --git a/Deployment/ExecutionPlan/1.CreatePrimaryDC/Install-RolePrimaryDomainController.ps1 b/Deployment/ExecutionPlan/1.CreatePrimaryDC/Install-RolePrimaryDomainController.ps1 deleted file mode 100644 index 165b8562..00000000 --- a/Deployment/ExecutionPlan/1.CreatePrimaryDC/Install-RolePrimaryDomainController.ps1 +++ /dev/null @@ -1,53 +0,0 @@ - -Function Install-RolePrimaryDomainController -{ -<# -.SYNOPSIS -Configure node's network adapters. -Create first domain controller in the forest. - -.EXAMPLE -PS> Install-RolePrimaryDomainController -DomainName acme.local -SafeModePassword "P@ssw0rd" - -Install DNS and ADDS, create forest and domain 'acme.local'. -Set DC recovery mode password to 'P@ssw0rd'. -#> - - param - ( - [String] - # New domain name. - $DomainName, - - [String] - # Domain controller recovery mode password. - $SafeModePassword - ) - - trap { Stop-Execution $_ } - - # Add required windows features - Add-WindowsFeatureWrapper ` - -Name "DNS","AD-Domain-Services","RSAT-DFS-Mgmt-Con" ` - -IncludeManagementTools ` - -NotifyRestart - - - Write-Log "Creating first domain controller ..." - - $SMAP = ConvertTo-SecureString -String $SafeModePassword -AsPlainText -Force - - Install-ADDSForest ` - -DomainName $DomainName ` - -SafeModeAdministratorPassword $SMAP ` - -DomainMode Default ` - -ForestMode Default ` - -NoRebootOnCompletion ` - -Force ` - -ErrorAction Stop | Out-Null - - Write-Log "Waiting for reboot ..." -# Stop-Execution -ExitCode 3010 -ExitString "Computer must be restarted to finish domain controller promotion." -# Write-Log "Restaring computer ..." -# Restart-Computer -Force -} diff --git a/Deployment/ExecutionPlan/2.JoinDomain/ExecutionPlan.txt b/Deployment/ExecutionPlan/2.JoinDomain/ExecutionPlan.txt deleted file mode 100644 index dafdc217..00000000 --- a/Deployment/ExecutionPlan/2.JoinDomain/ExecutionPlan.txt +++ /dev/null @@ -1,9 +0,0 @@ -call Import-Module Name="CoreFunctions" -call Set-NetworkAdapterConfiguration FirstAvailable=true , DNSServer="10.0.102.2" -call Join-Domain DomainName="acme.local" , Username="Administrator" , Password="P@ssw0rd123" - -reboot 1 - -out out.json - - diff --git a/Deployment/ExecutionPlan/2.JoinDomain/GenerateJSON.bat b/Deployment/ExecutionPlan/2.JoinDomain/GenerateJSON.bat deleted file mode 100644 index 0b6aae7e..00000000 --- a/Deployment/ExecutionPlan/2.JoinDomain/GenerateJSON.bat +++ /dev/null @@ -1 +0,0 @@ -..\ExecutionPlanGenerator.exe ExecutionPlan.txt \ No newline at end of file diff --git a/Deployment/ExecutionPlan/2.JoinDomain/out.json b/Deployment/ExecutionPlan/2.JoinDomain/out.json deleted file mode 100644 index 6905daa9..00000000 --- a/Deployment/ExecutionPlan/2.JoinDomain/out.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "Scripts": [], - "Commands": [ - { - "Name": "Import-Module", - "Arguments": { - "Name": "CoreFunctions" - } - }, - { - "Name": "Set-NetworkAdapterConfiguration", - "Arguments": { - "FirstAvailable": true, - "DNSServer": "10.0.102.2" - } - }, - { - "Name": "Join-Domain", - "Arguments": { - "DomainName": "acme.local", - "Username": "Administrator", - "Password": "P@ssw0rd123" - } - } - ], - "RebootOnCompletion": 1 -} \ No newline at end of file diff --git a/Deployment/ExecutionPlan/3.CreateSecondaryDC/ExecutionPlan.txt b/Deployment/ExecutionPlan/3.CreateSecondaryDC/ExecutionPlan.txt deleted file mode 100644 index dcffeaad..00000000 --- a/Deployment/ExecutionPlan/3.CreateSecondaryDC/ExecutionPlan.txt +++ /dev/null @@ -1,8 +0,0 @@ -include Install-RoleSecondaryDomainController.ps1 - -call Import-Module Name="CoreFunctions" -call Install-RoleSecondaryDomainController DomainName="acme.local" , UserName="Administrator" , Password="P@ssw0rd123" , SafeModePassword="P@ssw0rd" - -reboot 1 - -out out.json diff --git a/Deployment/ExecutionPlan/3.CreateSecondaryDC/GenerateJSON.bat b/Deployment/ExecutionPlan/3.CreateSecondaryDC/GenerateJSON.bat deleted file mode 100644 index 0b6aae7e..00000000 --- a/Deployment/ExecutionPlan/3.CreateSecondaryDC/GenerateJSON.bat +++ /dev/null @@ -1 +0,0 @@ -..\ExecutionPlanGenerator.exe ExecutionPlan.txt \ No newline at end of file diff --git a/Deployment/ExecutionPlan/3.CreateSecondaryDC/Install-RoleSecondaryDomainController.ps1 b/Deployment/ExecutionPlan/3.CreateSecondaryDC/Install-RoleSecondaryDomainController.ps1 deleted file mode 100644 index a638ce4f..00000000 --- a/Deployment/ExecutionPlan/3.CreateSecondaryDC/Install-RoleSecondaryDomainController.ps1 +++ /dev/null @@ -1,54 +0,0 @@ -Function Install-RoleSecondaryDomainController -{ -<# -.SYNOPSIS -Install additional (secondary) domain controller. - -#> - param - ( - [String] - # Domain name to join to. - $DomainName, - - [String] - # Domain user who is allowed to join computer to domain. - $UserName, - - [String] - # User's password. - $Password, - - [String] - # Domain controller recovery mode password. - $SafeModePassword - ) - - trap { Stop-Execution $_ } - - $Credential = New-Credential -UserName "$DomainName\$UserName" -Password $Password - - # Add required windows features - Add-WindowsFeatureWrapper ` - -Name "DNS","AD-Domain-Services","RSAT-DFS-Mgmt-Con" ` - -IncludeManagementTools ` - -NotifyRestart - - - Write-Log "Adding secondary domain controller ..." - - $SMAP = ConvertTo-SecureString -String $SafeModePassword -AsPlainText -Force - - Install-ADDSDomainController ` - -DomainName $DomainName ` - -SafeModeAdministratorPassword $SMAP ` - -Credential $Credential ` - -NoRebootOnCompletion ` - -Force ` - -ErrorAction Stop | Out-Null - - Write-Log "Waiting for restart ..." -# Stop-Execution -ExitCode 3010 -ExitString "Computer must be restarted to finish domain controller promotion." -# Write-Log "Restarting computer ..." -# Restart-Computer -Force -} diff --git a/Deployment/ExecutionPlan/3.CreateSecondaryDC/out.json b/Deployment/ExecutionPlan/3.CreateSecondaryDC/out.json deleted file mode 100644 index 6f260a2b..00000000 --- a/Deployment/ExecutionPlan/3.CreateSecondaryDC/out.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "Scripts": [ - "RnVuY3Rpb24gSW5zdGFsbC1Sb2xlU2Vjb25kYXJ5RG9tYWluQ29udHJvbGxlcg0Kew0KPCMNCi5TWU5PUFNJUw0KSW5zdGFsbCBhZGRpdGlvbmFsIChzZWNvbmRhcnkpIGRvbWFpbiBjb250cm9sbGVyLg0KDQojPg0KCXBhcmFtDQoJKA0KCQlbU3RyaW5nXQ0KCQkjIERvbWFpbiBuYW1lIHRvIGpvaW4gdG8uDQoJCSREb21haW5OYW1lLA0KCQkNCgkJW1N0cmluZ10NCgkJIyBEb21haW4gdXNlciB3aG8gaXMgYWxsb3dlZCB0byBqb2luIGNvbXB1dGVyIHRvIGRvbWFpbi4NCgkJJFVzZXJOYW1lLA0KCQkNCgkJW1N0cmluZ10NCgkJIyBVc2VyJ3MgcGFzc3dvcmQuDQoJCSRQYXNzd29yZCwNCgkJDQoJCVtTdHJpbmddDQoJCSMgRG9tYWluIGNvbnRyb2xsZXIgcmVjb3ZlcnkgbW9kZSBwYXNzd29yZC4NCgkJJFNhZmVNb2RlUGFzc3dvcmQNCgkpDQoNCgl0cmFwIHsgU3RvcC1FeGVjdXRpb24gJF8gfQ0KCQ0KCSRDcmVkZW50aWFsID0gTmV3LUNyZWRlbnRpYWwgLVVzZXJOYW1lICIkRG9tYWluTmFtZVwkVXNlck5hbWUiIC1QYXNzd29yZCAkUGFzc3dvcmQNCgkJDQoJIyBBZGQgcmVxdWlyZWQgd2luZG93cyBmZWF0dXJlcw0KCUFkZC1XaW5kb3dzRmVhdHVyZVdyYXBwZXIgYA0KCQktTmFtZSAiRE5TIiwiQUQtRG9tYWluLVNlcnZpY2VzIiwiUlNBVC1ERlMtTWdtdC1Db24iIGANCgkJLUluY2x1ZGVNYW5hZ2VtZW50VG9vbHMgYA0KICAgICAgICAgICAgICAgIC1Ob3RpZnlSZXN0YXJ0DQoJCQ0KCQ0KICAgICAgICBXcml0ZS1Mb2cgIkFkZGluZyBzZWNvbmRhcnkgZG9tYWluIGNvbnRyb2xsZXIgLi4uIg0KICAgIA0KCSRTTUFQID0gQ29udmVydFRvLVNlY3VyZVN0cmluZyAtU3RyaW5nICRTYWZlTW9kZVBhc3N3b3JkIC1Bc1BsYWluVGV4dCAtRm9yY2UNCg0KCUluc3RhbGwtQUREU0RvbWFpbkNvbnRyb2xsZXIgYA0KCQktRG9tYWluTmFtZSAkRG9tYWluTmFtZSBgDQoJCS1TYWZlTW9kZUFkbWluaXN0cmF0b3JQYXNzd29yZCAkU01BUCBgDQoJCS1DcmVkZW50aWFsICRDcmVkZW50aWFsIGANCgkJLU5vUmVib290T25Db21wbGV0aW9uIGANCgkJLUZvcmNlIGANCgkJLUVycm9yQWN0aW9uIFN0b3AgfCBPdXQtTnVsbA0KDQoJV3JpdGUtTG9nICJXYWl0aW5nIGZvciByZXN0YXJ0IC4uLiINCiMJU3RvcC1FeGVjdXRpb24gLUV4aXRDb2RlIDMwMTAgLUV4aXRTdHJpbmcgIkNvbXB1dGVyIG11c3QgYmUgcmVzdGFydGVkIHRvIGZpbmlzaCBkb21haW4gY29udHJvbGxlciBwcm9tb3Rpb24uIg0KIwlXcml0ZS1Mb2cgIlJlc3RhcnRpbmcgY29tcHV0ZXIgLi4uIg0KIwlSZXN0YXJ0LUNvbXB1dGVyIC1Gb3JjZQ0KfQ0K" - ], - "Commands": [ - { - "Name": "Import-Module", - "Arguments": { - "Name": "CoreFunctions" - } - }, - { - "Name": "Install-RoleSecondaryDomainController", - "Arguments": { - "DomainName": "acme.local", - "UserName": "Administrator", - "Password": "P@ssw0rd123", - "SafeModePassword": "P@ssw0rd" - } - } - ], - "RebootOnCompletion": 1 -} \ No newline at end of file diff --git a/Deployment/ExecutionPlan/4.JoinAndPromote/ExecutionPlan.txt b/Deployment/ExecutionPlan/4.JoinAndPromote/ExecutionPlan.txt deleted file mode 100644 index 076f569b..00000000 --- a/Deployment/ExecutionPlan/4.JoinAndPromote/ExecutionPlan.txt +++ /dev/null @@ -1,10 +0,0 @@ -include Install-RoleSecondaryDomainController.ps1 - -call Import-Module Name="CoreFunctions" -call Set-NetworkAdapterConfiguration FirstAvailable=true , DNSServer="10.0.102.2" -call Join-Domain DomainName="acme.local" , Username="Administrator" , Password="P@ssw0rd123" , AllowRestart=true -call Install-RoleSecondaryDomainController DomainName="acme.local" , UserName="Administrator" , Password="P@ssw0rd123" , SafeModePassword="P@ssw0rd" - -reboot 1 - -out out.json diff --git a/Deployment/ExecutionPlan/4.JoinAndPromote/GenerateJSON.bat b/Deployment/ExecutionPlan/4.JoinAndPromote/GenerateJSON.bat deleted file mode 100644 index 0b6aae7e..00000000 --- a/Deployment/ExecutionPlan/4.JoinAndPromote/GenerateJSON.bat +++ /dev/null @@ -1 +0,0 @@ -..\ExecutionPlanGenerator.exe ExecutionPlan.txt \ No newline at end of file diff --git a/Deployment/ExecutionPlan/4.JoinAndPromote/Install-RoleSecondaryDomainController.ps1 b/Deployment/ExecutionPlan/4.JoinAndPromote/Install-RoleSecondaryDomainController.ps1 deleted file mode 100644 index a638ce4f..00000000 --- a/Deployment/ExecutionPlan/4.JoinAndPromote/Install-RoleSecondaryDomainController.ps1 +++ /dev/null @@ -1,54 +0,0 @@ -Function Install-RoleSecondaryDomainController -{ -<# -.SYNOPSIS -Install additional (secondary) domain controller. - -#> - param - ( - [String] - # Domain name to join to. - $DomainName, - - [String] - # Domain user who is allowed to join computer to domain. - $UserName, - - [String] - # User's password. - $Password, - - [String] - # Domain controller recovery mode password. - $SafeModePassword - ) - - trap { Stop-Execution $_ } - - $Credential = New-Credential -UserName "$DomainName\$UserName" -Password $Password - - # Add required windows features - Add-WindowsFeatureWrapper ` - -Name "DNS","AD-Domain-Services","RSAT-DFS-Mgmt-Con" ` - -IncludeManagementTools ` - -NotifyRestart - - - Write-Log "Adding secondary domain controller ..." - - $SMAP = ConvertTo-SecureString -String $SafeModePassword -AsPlainText -Force - - Install-ADDSDomainController ` - -DomainName $DomainName ` - -SafeModeAdministratorPassword $SMAP ` - -Credential $Credential ` - -NoRebootOnCompletion ` - -Force ` - -ErrorAction Stop | Out-Null - - Write-Log "Waiting for restart ..." -# Stop-Execution -ExitCode 3010 -ExitString "Computer must be restarted to finish domain controller promotion." -# Write-Log "Restarting computer ..." -# Restart-Computer -Force -} diff --git a/Deployment/ExecutionPlan/4.JoinAndPromote/out.json b/Deployment/ExecutionPlan/4.JoinAndPromote/out.json deleted file mode 100644 index cd6fe954..00000000 --- a/Deployment/ExecutionPlan/4.JoinAndPromote/out.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "Scripts": [ - "RnVuY3Rpb24gSW5zdGFsbC1Sb2xlU2Vjb25kYXJ5RG9tYWluQ29udHJvbGxlcg0Kew0KPCMNCi5TWU5PUFNJUw0KSW5zdGFsbCBhZGRpdGlvbmFsIChzZWNvbmRhcnkpIGRvbWFpbiBjb250cm9sbGVyLg0KDQojPg0KCXBhcmFtDQoJKA0KCQlbU3RyaW5nXQ0KCQkjIERvbWFpbiBuYW1lIHRvIGpvaW4gdG8uDQoJCSREb21haW5OYW1lLA0KCQkNCgkJW1N0cmluZ10NCgkJIyBEb21haW4gdXNlciB3aG8gaXMgYWxsb3dlZCB0byBqb2luIGNvbXB1dGVyIHRvIGRvbWFpbi4NCgkJJFVzZXJOYW1lLA0KCQkNCgkJW1N0cmluZ10NCgkJIyBVc2VyJ3MgcGFzc3dvcmQuDQoJCSRQYXNzd29yZCwNCgkJDQoJCVtTdHJpbmddDQoJCSMgRG9tYWluIGNvbnRyb2xsZXIgcmVjb3ZlcnkgbW9kZSBwYXNzd29yZC4NCgkJJFNhZmVNb2RlUGFzc3dvcmQNCgkpDQoNCgl0cmFwIHsgU3RvcC1FeGVjdXRpb24gJF8gfQ0KCQ0KCSRDcmVkZW50aWFsID0gTmV3LUNyZWRlbnRpYWwgLVVzZXJOYW1lICIkRG9tYWluTmFtZVwkVXNlck5hbWUiIC1QYXNzd29yZCAkUGFzc3dvcmQNCgkJDQoJIyBBZGQgcmVxdWlyZWQgd2luZG93cyBmZWF0dXJlcw0KCUFkZC1XaW5kb3dzRmVhdHVyZVdyYXBwZXIgYA0KCQktTmFtZSAiRE5TIiwiQUQtRG9tYWluLVNlcnZpY2VzIiwiUlNBVC1ERlMtTWdtdC1Db24iIGANCgkJLUluY2x1ZGVNYW5hZ2VtZW50VG9vbHMgYA0KICAgICAgICAgICAgICAgIC1Ob3RpZnlSZXN0YXJ0DQoJCQ0KCQ0KICAgICAgICBXcml0ZS1Mb2cgIkFkZGluZyBzZWNvbmRhcnkgZG9tYWluIGNvbnRyb2xsZXIgLi4uIg0KICAgIA0KCSRTTUFQID0gQ29udmVydFRvLVNlY3VyZVN0cmluZyAtU3RyaW5nICRTYWZlTW9kZVBhc3N3b3JkIC1Bc1BsYWluVGV4dCAtRm9yY2UNCg0KCUluc3RhbGwtQUREU0RvbWFpbkNvbnRyb2xsZXIgYA0KCQktRG9tYWluTmFtZSAkRG9tYWluTmFtZSBgDQoJCS1TYWZlTW9kZUFkbWluaXN0cmF0b3JQYXNzd29yZCAkU01BUCBgDQoJCS1DcmVkZW50aWFsICRDcmVkZW50aWFsIGANCgkJLU5vUmVib290T25Db21wbGV0aW9uIGANCgkJLUZvcmNlIGANCgkJLUVycm9yQWN0aW9uIFN0b3AgfCBPdXQtTnVsbA0KDQoJV3JpdGUtTG9nICJXYWl0aW5nIGZvciByZXN0YXJ0IC4uLiINCiMJU3RvcC1FeGVjdXRpb24gLUV4aXRDb2RlIDMwMTAgLUV4aXRTdHJpbmcgIkNvbXB1dGVyIG11c3QgYmUgcmVzdGFydGVkIHRvIGZpbmlzaCBkb21haW4gY29udHJvbGxlciBwcm9tb3Rpb24uIg0KIwlXcml0ZS1Mb2cgIlJlc3RhcnRpbmcgY29tcHV0ZXIgLi4uIg0KIwlSZXN0YXJ0LUNvbXB1dGVyIC1Gb3JjZQ0KfQ0K" - ], - "Commands": [ - { - "Name": "Import-Module", - "Arguments": { - "Name": "CoreFunctions" - } - }, - { - "Name": "Set-NetworkAdapterConfiguration", - "Arguments": { - "FirstAvailable": true, - "DNSServer": "10.0.102.2" - } - }, - { - "Name": "Join-Domain", - "Arguments": { - "DomainName": "acme.local", - "Username": "Administrator", - "Password": "P@ssw0rd123", - "AllowRestart": true - } - }, - { - "Name": "Install-RoleSecondaryDomainController", - "Arguments": { - "DomainName": "acme.local", - "UserName": "Administrator", - "Password": "P@ssw0rd123", - "SafeModePassword": "P@ssw0rd" - } - } - ], - "RebootOnCompletion": 1 -} \ No newline at end of file diff --git a/Deployment/ExecutionPlan/ExecutionPlanGenerator.exe b/Deployment/ExecutionPlan/ExecutionPlanGenerator.exe deleted file mode 100644 index ef78c02d1d3c13d40968ec453e4dcab08b34ef19..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9728 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 jfI`p4>Ii}0nez# diff --git a/Deployment/ExecutionPlan/GetDnsIpAddressesOnDc/ExecutionPlan.txt b/Deployment/ExecutionPlan/GetDnsIpAddressesOnDc/ExecutionPlan.txt deleted file mode 100644 index 8440ea57..00000000 --- a/Deployment/ExecutionPlan/GetDnsIpAddressesOnDc/ExecutionPlan.txt +++ /dev/null @@ -1,7 +0,0 @@ -include Get-DnsListeningIpAddress.ps1 - -call Get-DnsListeningIpAddress - -reboot 0 - -out out.json diff --git a/Deployment/ExecutionPlan/GetDnsIpAddressesOnDc/GenerateJSON.bat b/Deployment/ExecutionPlan/GetDnsIpAddressesOnDc/GenerateJSON.bat deleted file mode 100644 index 0b6aae7e..00000000 --- a/Deployment/ExecutionPlan/GetDnsIpAddressesOnDc/GenerateJSON.bat +++ /dev/null @@ -1 +0,0 @@ -..\ExecutionPlanGenerator.exe ExecutionPlan.txt \ No newline at end of file diff --git a/Deployment/ExecutionPlan/GetDnsIpAddressesOnDc/Get-DnsListeningIpAddress.ps1 b/Deployment/ExecutionPlan/GetDnsIpAddressesOnDc/Get-DnsListeningIpAddress.ps1 deleted file mode 100644 index 421464d7..00000000 --- a/Deployment/ExecutionPlan/GetDnsIpAddressesOnDc/Get-DnsListeningIpAddress.ps1 +++ /dev/null @@ -1,5 +0,0 @@ -function Get-DnsListeningIpAddress { - Import-Module DnsServer - (Get-DNSServer -ComputerName localhost).ServerSetting.ListeningIpAddress | - Where-Object { $_ -match "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" } -} diff --git a/Deployment/ExecutionPlan/GetDnsIpAddressesOnDc/out.json b/Deployment/ExecutionPlan/GetDnsIpAddressesOnDc/out.json deleted file mode 100644 index a9f6ee38..00000000 --- a/Deployment/ExecutionPlan/GetDnsIpAddressesOnDc/out.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "Scripts": [ - "ZnVuY3Rpb24gR2V0LURuc0xpc3RlbmluZ0lwQWRkcmVzcyB7DQogICAgSW1wb3J0LU1vZHVsZSBEbnNTZXJ2ZXINCiAgICAoR2V0LUROU1NlcnZlciAtQ29tcHV0ZXJOYW1lIGxvY2FsaG9zdCkuU2VydmVyU2V0dGluZy5MaXN0ZW5pbmdJcEFkZHJlc3MgfA0KICAgICAgICBXaGVyZS1PYmplY3QgeyAkXyAtbWF0Y2ggIlxkezEsM31cLlxkezEsM31cLlxkezEsM31cLlxkezEsM30iIH0NCn0NCg==" - ], - "Commands": [ - { - "Name": "Get-DnsListeningIpAddress", - "Arguments": {} - } - ], - "RebootOnCompletion": 0 -} \ No newline at end of file diff --git a/Deployment/ExecutionPlan/InstallIIS/ExecutionPlan.txt b/Deployment/ExecutionPlan/InstallIIS/ExecutionPlan.txt deleted file mode 100644 index bf3d2e97..00000000 --- a/Deployment/ExecutionPlan/InstallIIS/ExecutionPlan.txt +++ /dev/null @@ -1,7 +0,0 @@ -include Install-WebServer.ps1 - -call Install-WebServer - -reboot 0 - -out out.json diff --git a/Deployment/ExecutionPlan/InstallIIS/GenerateJSON.bat b/Deployment/ExecutionPlan/InstallIIS/GenerateJSON.bat deleted file mode 100644 index 0b6aae7e..00000000 --- a/Deployment/ExecutionPlan/InstallIIS/GenerateJSON.bat +++ /dev/null @@ -1 +0,0 @@ -..\ExecutionPlanGenerator.exe ExecutionPlan.txt \ No newline at end of file diff --git a/Deployment/ExecutionPlan/InstallIIS/Install-WebServer.ps1 b/Deployment/ExecutionPlan/InstallIIS/Install-WebServer.ps1 deleted file mode 100644 index 5ec0a4be..00000000 --- a/Deployment/ExecutionPlan/InstallIIS/Install-WebServer.ps1 +++ /dev/null @@ -1,4 +0,0 @@ -function Install-WebServer { - Import-Module ServerManager - Install-WindowsFeature Web-Server -IncludeManagementTools -} diff --git a/Deployment/ExecutionPlan/InstallIIS/out.json b/Deployment/ExecutionPlan/InstallIIS/out.json deleted file mode 100644 index baeaec15..00000000 --- a/Deployment/ExecutionPlan/InstallIIS/out.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "Scripts": [ - "ZnVuY3Rpb24gSW5zdGFsbC1XZWJTZXJ2ZXIgew0KICAgIEltcG9ydC1Nb2R1bGUgU2VydmVyTWFuYWdlcg0KICAgIEluc3RhbGwtV2luZG93c0ZlYXR1cmUgV2ViLVNlcnZlciAtSW5jbHVkZU1hbmFnZW1lbnRUb29scw0KfQ0K" - ], - "Commands": [ - { - "Name": "Install-WebServer", - "Arguments": {} - } - ], - "RebootOnCompletion": 0 -} \ No newline at end of file diff --git a/Deployment/ExecutionPlan/Newtonsoft.Json.dll b/Deployment/ExecutionPlan/Newtonsoft.Json.dll deleted file mode 100644 index 81639f9b1c0b66d09cc50f92c7492747d1022a4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 391680 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�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 diff --git a/Deployment/Unattended/ws-2012-core-unattended.xml b/Deployment/Unattended/ws-2012-core-unattended.xml deleted file mode 100644 index b01499dd..00000000 --- a/Deployment/Unattended/ws-2012-core-unattended.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - Pacific Standard Time - - - - - - - UABAAHMAcwB3ADAAcgBkAFAAYQBzAHMAdwBvAHIAZAA= -

false</PlainText> - </Password> - <Username>Administrator</Username> - <LogonCount>1</LogonCount> - <Enabled>false</Enabled> - </AutoLogon> - <UserAccounts> - <AdministratorPassword> - <Value>UABAAHMAcwB3ADAAcgBkAEEAZABtAGkAbgBpAHMAdAByAGEAdABvAHIAUABhAHMAcwB3AG8AcgBkAA==</Value> - <PlainText>false</PlainText> - </AdministratorPassword> - </UserAccounts> - <RegisteredOrganization>-</RegisteredOrganization> - <RegisteredOwner>-</RegisteredOwner> - <OOBE> - <HideEULAPage>true</HideEULAPage> - </OOBE> - </component> - <component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <InputLocale>en-US</InputLocale> - <SystemLocale>en-US</SystemLocale> - <UILanguage>en-US</UILanguage> - <UserLocale>en-US</UserLocale> - </component> - </settings> - <settings pass="generalize"> - <component name="Microsoft-Windows-Security-SPP" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <SkipRearm>1</SkipRearm> - </component> - </settings> - <cpi:offlineImage cpi:source="wim:d:/aik/install.wim#Windows Server 2012 SERVERSTANDARDCORE" xmlns:cpi="urn:schemas-microsoft-com:cpi" /> -</unattend> diff --git a/Deployment/Unattended/ws-2012-full-unattend.xml b/Deployment/Unattended/ws-2012-full-unattend.xml deleted file mode 100644 index 78961363..00000000 --- a/Deployment/Unattended/ws-2012-full-unattend.xml +++ /dev/null @@ -1,51 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<unattend xmlns="urn:schemas-microsoft-com:unattend"> - <settings pass="specialize"> - <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <TimeZone>Pacific Stantard Time</TimeZone> - </component> - </settings> - <settings pass="oobeSystem"> - <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <AutoLogon> - <Password> - <Value>UABAAHMAcwB3ADAAcgBkAFAAYQBzAHMAdwBvAHIAZAA=</Value> - <PlainText>false</PlainText> - </Password> - <LogonCount>1</LogonCount> - <Username>Administrator</Username> - <Enabled>true</Enabled> - </AutoLogon> - <UserAccounts> - <AdministratorPassword> - <Value>UABAAHMAcwB3ADAAcgBkAEEAZABtAGkAbgBpAHMAdAByAGEAdABvAHIAUABhAHMAcwB3AG8AcgBkAA==</Value> - <PlainText>false</PlainText> - </AdministratorPassword> - </UserAccounts> - <RegisteredOrganization>-</RegisteredOrganization> - <RegisteredOwner>-</RegisteredOwner> - <OOBE> - <HideEULAPage>true</HideEULAPage> - </OOBE> - <FirstLogonCommands> - <SynchronousCommand wcm:action="add"> - <RequiresUserInput>true</RequiresUserInput> - <CommandLine>powershell.exe -File C:\Deploy\Scripts\FirstLogon.ps1</CommandLine> - <Order>1</Order> - </SynchronousCommand> - </FirstLogonCommands> - </component> - <component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <InputLocale>en-US</InputLocale> - <SystemLocale>en-US</SystemLocale> - <UILanguage>en-US</UILanguage> - <UserLocale>en-US</UserLocale> - </component> - </settings> - <settings pass="generalize"> - <component name="Microsoft-Windows-Security-SPP" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <SkipRearm>1</SkipRearm> - </component> - </settings> - <cpi:offlineImage cpi:source="wim:d:/aik/install.wim#Windows Server 2012 SERVERSTANDARD" xmlns:cpi="urn:schemas-microsoft-com:cpi" /> -</unattend> diff --git a/Deployment/WindowsPowerShell/Modules/CoreFunctions/Config.ps1 b/Deployment/WindowsPowerShell/Modules/CoreFunctions/Config.ps1 deleted file mode 100644 index 79a2f9e2..00000000 --- a/Deployment/WindowsPowerShell/Modules/CoreFunctions/Config.ps1 +++ /dev/null @@ -1,28 +0,0 @@ -<# -Naming convention: - -== Normal variables -** Set: $NormalVar = 123 -** Get: Write-Host $NormalVar - -== Script-scope variables -** Set: $script:__ScriptScopeVar = 123 -** Get: Write-Host $__ScriptScopeVar - -== Global-scope variables -** Set: $global:__GlobalScopeVar__ = 123 -** Get: Write-Host $__GlobalScopeVar__ -#> - -$script:__ModulePath = $PsScriptRoot -$script:__ModuleName = $PsScriptRoot.Split("\")[-1] -$script:__DefaultLogPath = [IO.Path]::Combine([IO.Path]::GetTempPath(), "PowerShell_$__ModuleName.log") - - -$script:__RequiredModules = @("ServerManager", "DnsClient") -$script:__ImportModulesExplicitely = $true -$script:__ImportModulesErrorAction = "Stop" - - -$global:__StopExecutionPreference__ = "Exit" - diff --git a/Deployment/WindowsPowerShell/Modules/CoreFunctions/CoreFunctions.psd1 b/Deployment/WindowsPowerShell/Modules/CoreFunctions/CoreFunctions.psd1 deleted file mode 100644 index 0d52bf7006afd03c839d39c1800904cce15f092e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5262 zcmchbYj0ac5QgV7692)HKO{xOtxKB#5>l<YX(P3zazo*(?^yT}Y=^W${B_`ccD&uQ z_Hkl|1X;)Dwlnk2efIqGch$bMFRW@6dt)=}*`<xGue*u0ZE7PM=x$+4JsIfzJiIBc zJFxfG)$@^EXw^tGLpv3hsh)B_)78QbR#Kv@m*P6rF_)C3&OPz%*eCm5=Z5Ykfzv2R z_BF_M>g#wQ4IXuSsN+QckL{7JzA4Z`|IGdjPa?0P4(MLg(6#OaL~Wt?v|)pR-nrG< zLc9=lS5J~=SGrr;ucG50tv2mj3wT<hooQ9>iyyT9z@F>PT(;V@ZcVmd2z7mB=cv_( zVBv)%VwZ}JORegJrxiULtRYHTJDMVjbzY9CZZ+B0y&I*TSyOy3g~<K7lqBwnrr&k$ z%U0NMBtH`cM9CmLDOx9Cf$vNE$(*mteJZIZ`t@byfjzY&JGSq1b)u^$IzQIkk&a`@ zc%-Y2{*U#0qN6K2)wQCd>l}GQ=)m9}zs^2j%2zwCW{BM7DoW0U4=arAH|aQClZHPh z@*H`FJzVM^!6v;OG2}eniq}N+6>~IiB$e!O8^<h+euDNwlF8knv?j;NbdOv3K$r4< zyLZYsIf6r3`+<1GETqchm}+|4kcGxNCnRJDv5xiThD~~kwA@?G@V;1kJj#>bu*4ul zUaCcHNg^Au_=R}S#N8t?)}TB~^Ej-EYMWfwILC;##Qj7lCZa`?xwMKInPdGqtj70O zT2rxi;R=fP@(*!@mGH|jtVBPoL{?$dR7Y>pp^S5RhUfaE2)hk(GRu3(-JyQ9;+c^B zUw*hxZFcd+ez8@vxAwx$6)~rZ6t5FkvfW&@>9$eS6#Y_s`}*JHdiOeXC2yji=lM8P zwxMFbt$grYpv%xtyb2zwXTG*ps6e-iIH%u5lXtQteV>}e1i+-kw9pM5pBaZ5bg3sG zJ(ShZqHka8?ic%3y@;!ZkoQ8LqH5j_^CptURvhKIl4>4Rh`LWtU}mAKbHxln2d96b zYn<VzcHZxyV$D!BYo_(oU9Vl#TWmEAUh*D8C8j1~HK#A~_Nnrwn%0uMVkIn2FX(}; z&t&-r+2q#<{^|=$M+isaalOuH(+-mh{o+1wmm?}ipU<sv4n>1Q^-HG0dWinyLt--J z3O-~qjNa-7KQYVB)otNJx4CPN`E*$o_agp<s?$t8ub4;MymqeseG&91*ZHITIbLbb z^y%@M`|c`4DBABIYf=6_m`_7|v#;{0%J&SpukIUn5yL*%*K=tzN2zw`LQSJa_$)%L zj(bYp=2JuQEwd*cW#@!Xo!+CfCA#%p(Zg(yv>7>Z%B4F+JnLGN=hWu@D%PaFeH2Eo zcTAQ<DpMWV6L)n<Ul45a0_ls4kJsR@)~4I%;@ndV(_68eTYP^6IiFpne~>q+$n8)S zV@)KJ_D8VEYgZKP3&6lG;J?@)=d-Ky*TJXcsz)syj2>2uT2Qfr>uQSfDTJ<2zTZ!g zk?LPDB9pxGxJ>VJJjDvQKbDv$r_z>aOH#^FP@hNY2);|KC`P$zOKXo%5HJD7``B5E zR=clU&#^T>Bl8T)cQUV}>sY=!Q^n%TRz+VJTDptg-k#G{ty!mzv}KamFAju-FKa{- zGXqaXssd?u9cNp1>%CyVVy1aDys6@_m;Q0J$HYr~U-9b1nY-;cnC#-5b*31Pb?zvu hvlV?y!O9VTisW2Nz6da>yEa9N_-!J2<FC*2*uT4$V8j3b diff --git a/Deployment/WindowsPowerShell/Modules/CoreFunctions/CoreFunctions.psm1 b/Deployment/WindowsPowerShell/Modules/CoreFunctions/CoreFunctions.psm1 deleted file mode 100644 index 9e72136b..00000000 --- a/Deployment/WindowsPowerShell/Modules/CoreFunctions/CoreFunctions.psm1 +++ /dev/null @@ -1,36 +0,0 @@ -# Import config first -. "$PsScriptRoot\Config.ps1" - -# Import functions from 'Include' subfolder -Get-ChildItem "$PsScriptRoot\Include" -Filter "*.ps1" | - ForEach-Object { - . "$($_.FullName)" - } - -trap { Stop-Execution $_ } - -Export-ModuleMember -Function * -Alias * - -<# -if ($__ImportModulesExplicitely) { - foreach ($Module in $__RequiredModules) { - Write-Log "Importing module '$Module' ..." - Import-Module -Name "$Module" -ErrorAction "$__ImportModulesErrorAction" - } -} -#> - -Write-Log "Module loaded from '$PsScriptRoot'" - -#------------------------------------------------------------------------------- - -switch ($Args[0]) { - 'installTo' { - Install-Module -InstallPath $args[1] -ModulePath $PsScriptRoot - } - 'register' { - Register-Module "$PsScriptRoot" - } - default { - } -} diff --git a/Deployment/WindowsPowerShell/Modules/CoreFunctions/Ionic.Zip.dll b/Deployment/WindowsPowerShell/Modules/CoreFunctions/Ionic.Zip.dll deleted file mode 100644 index 95fa92885578745360df875d90d9231a7e618e78..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 462336 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%0Dw=H<TmGr5{!Y@;3GF;j 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%0Dx)4q>+mQbyTqfus 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 C=XJ9H diff --git a/Deployment/WindowsPowerShell/Modules/CoreFunctions/en-US/about_CoreFunctions.help.txt b/Deployment/WindowsPowerShell/Modules/CoreFunctions/en-US/about_CoreFunctions.help.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Base64.ps1 b/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Base64.ps1 deleted file mode 100644 index 50bb4cf7..00000000 --- a/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Base64.ps1 +++ /dev/null @@ -1,99 +0,0 @@ - -Function ConvertTo-Base64String { -<# -#> - [CmdletBinding(DefaultParameterSetName="FromString")] - param ( - [Parameter(Position=1,ParameterSetName="FromString")] - [String] $String, - - [Parameter(ParameterSetName="FromFile")] - [String] $Path, - - [Parameter(ParameterSetName="FromFile")] - [Int] $ChunkSize = 5KB, - - [Parameter(ParameterSetName="FromFile")] - [Switch] $OneChunk - ) - switch($PSCmdlet.ParameterSetName) { - "FromString" { - [System.Text.Encoding]::UTF8.GetBytes($String) - [System.Convert]::ToBase64String($Bytes) - } - "FromFile" { - $FileStream = [IO.File]::Open($Path, [System.IO.FileMode]::Open) - $BytesToRead = $FileStream.Length - - if ($OneChunk) { - $ChunkSize = $BytesToRead - } - - $Bytes = New-Object Byte[] $ChunkSize - while ($BytesToRead -gt 0) { - if ($BytesToRead -lt $ChunkSize) { - $ChunkSize = $BytesToRead - $Bytes = New-Object Byte[] $ChunkSize - } - #Write-Host ("BytesToRead: {0}, ChunkSize: {1}" -f $BytesToRead, $ChunkSize ) - $BytesRead = $FileStream.Read($Bytes, 0, $ChunkSize) - $BytesToRead -= $BytesRead - - [System.Convert]::ToBase64String($Bytes) - } - $FileStream.Close() - } - } -} - - - -Function ConvertFrom-Base64String { -<# -#> - [CmdletBinding(DefaultParameterSetName="ToByteArray")] - param ( - [Parameter(Position=1,ValueFromPipeline=$true)] - [String] $Base64String, - - [Parameter(ParameterSetName="ToFile")] - [String] $Path, - - [Parameter(ParameterSetName="ToString")] - [Switch] $ToString - ) - begin { - switch($PSCmdlet.ParameterSetName) { - "ToFile" { - if ([IO.File]::Exists($Path)) { - [IO.File]::Delete($Path) - } - $FileStream = [IO.File]::Open($Path, [IO.FileMode]::Append) - } - } - } - process { - foreach( $Line in ($Base64String -split '\n')) { - $Bytes = [System.Convert]::FromBase64String($Line) - - switch($PSCmdlet.ParameterSetName) { - "ToFile" { - $FileStream.Write($Bytes, 0, $Bytes.Length) - } - "ToString" { - [System.Text.Encoding]::UTF8.GetString($Bytes) - } - "ToByteArray" { - $Bytes - } - } - } - } - end { - switch($PSCmdlet.ParameterSetName) { - "ToFile" { - $FileStream.Close() - } - } - } -} diff --git a/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Functions.ps1 b/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Functions.ps1 deleted file mode 100644 index 86cb0fb6..00000000 --- a/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Functions.ps1 +++ /dev/null @@ -1,388 +0,0 @@ -Function Stop-Execution { -<# -.SYNOPSIS -Breaks execution with specified error code. - -.DESCRIPTION -Function break script execution with error code provided. Error code may be 0 in case of non-error stop. - -It also tries to parse ErrorRecord or Exception object (if provided) and logs this information. -#> - [CmdletBinding(DefaultParameterSetName="Exception")] - - param ( - [Parameter(Position=1,ParameterSetName="Exception")] - $InputObject = $null, - - [Parameter(ParameterSetName="ErrorString")] - [String] $ExitString = "", - - [Parameter(ParameterSetName="ErrorString")] - [Int] $ExitCode = 0, - - [Parameter(ParameterSetName="ErrorString")] - [Switch] $Success - ) - - Function Exit-Function { - if ($ExitCode -eq 0) { - Write-LogInfo ( "STOP ({0}):`n{1}" -f $ExitCode, $ExitString ) - } - else { - Write-LogFatal ( "STOP ({0}):`n{1}" -f $ExitCode, $ExitString ) - } - - Write-Log "__StopExecutionPreference__ = '$__StopExecutionPreference__'" - switch ("$__StopExecutionPreference__") { - "Exit" { - exit $ExitCode - } - "ThrowIfException" { - if ($InputObject -eq $null) { - exit $ExitCode - } - else { - throw $InputObject - } - } - "ThrowAlways" { - throw $InputObject - } - default { - throw "Unknown value for __StopExecutionPreference__: '$__StopExecutionPreference__'" - } - } - } - - switch($PSCmdlet.ParameterSetName) { - "Exception" { - #---------- - if ($InputObject -eq $null) { - $ExitString = "***** SCRIPT INTERRUPTED *****" - $ExitCode = 255 - Exit-Function - } - #---------- - - - #---------- - try { - $ErrorRecord = [System.Management.Automation.ErrorRecord] $InputObject -<# - $ExitString = @" -$($ErrorRecord.ToString()) - -*** Invocation Info *** -$($ErrorRecord.InvocationInfo.PositionMessage) - -*** CategoryInfo *** -$($ErrorRecord.CategoryInfo.ToString()) - -*** FullyQualifiedErrorId *** -$($ErrorRecord.FullyQualifiedErrorId.ToString()) - -*** ScriptStackTrace *** -$($ErrorRecord.ScriptStackTrace.ToString()) -*** *** *** -"@ -#> - $ExitString = Out-String -InputObject $InputObject - $ExitCode = 255 - Exit-Function - } - catch { - $ErrorRecord = $null - Write-LogWarning "Unable to cast InputObject to [System.Management.Automation.ErrorRecord]" - } - #---------- - - - #---------- - try { - $Exception = [System.Exception] $InputObject - #$ExitString = $Exception.ToString() - $ExitString = Out-String -InputObject $InputObject - $ExitCode = 255 - Exit-Function - } - catch { - $Exception = $null - Write-LogWarning "Unable to cast InputObject to [System.Exception]" - } - #---------- - - - #---------- - try { - $ExitString = Out-String -InputObject $InputObject - $ExitCode = 255 - Exit-Function - } - catch { - Write-LogWarning "Unable to cast InputObject of type [$($InputObject.GetType())] to any of supported types." - } - #---------- - } - "ErrorString" { - if ($Success) { - $ExitString = "Script stopped with NO ERROR." - $ExitCode = 0 - } - - Exit-Function - } - } - - $ExitString = "Unknown error occured in Stop-Execution" - $ExitCode = 255 - Exit-Function -} - - - -Function Get-PasswordAsSecureString { -<# -.SYNOPSIS -Convert to / request password as secure string. -#> - [CmdletBinding()] - param ( - [String] $Password = "", - [String] $Prompt = "Please enter password" - ) - - if ($Password -eq "") { - Read-Host -Prompt $Prompt -AsSecureString - } - else { - ConvertTo-SecureString -String "$Password" -AsPlainText -Force - } -} - - - -Function New-Credential { -<# -.SYNOPSIS -Create new creadential object with username and password provided. -#> - [CmdletBinding()] - param ( - [Parameter(Mandatory=$true)] - [String] $UserName, - - [String] $Password - ) - - $SecurePassword = Get-PasswordAsSecureString -Password "$Password" - New-Object System.Management.Automation.PSCredential( "$UserName", $SecurePassword ) -} - - - -Function Invoke-WMSettingsChange { - if (-not ("win32.nativemethods" -as [type])) { - # Import SendMessageTimeout from Win32 - Add-Type -Namespace Win32 -Name NativeMethods -MemberDefinition @" -[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] -public static extern IntPtr SendMessageTimeout( - IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam, - uint fuFlags, uint uTimeout, out UIntPtr lpdwResult); -"@ - } - - $HWND_BROADCAST = [IntPtr]0xFFFF - $WM_SETTINGCHANGE = 0x001A - $result = [UIntPtr]::Zero - - # Notify all windows of environment block change - Write-Log "Executing 'SendMessageTimeout' ..." - - $retval = [Win32.NativeMethods]::SendMessageTimeout($HWND_BROADCAST, $WM_SETTINGCHANGE, - [UIntPtr]::Zero, "Environment", 2, 5000, [ref] $result) - - Write-Log "'SendMessageTimeout' returned '$retval' (non-zero is OK)" -} - - - -Function Start-Program { - param ( - [String] $FilePath, - [String[]] $ArgumentList = @(' '), - [Int] $Timeout = 0, - [Switch] $NoWait, - [Switch] $PassThru, - [String] $WorkingDir = (Get-Location).ProviderPath - ) - - trap { - Write-LogError $_.Exception.Message - return $null - } - - Write-Log "Starting program: $FilePath $ArgumentList" - - $ProcessStartInfo = New-Object System.Diagnostics.ProcessStartInfo - $ProcessStartInfo.FileName = $FilePath - $ProcessStartInfo.Arguments = $ArgumentList - $ProcessStartInfo.CreateNoWindow = $true - $ProcessStartInfo.RedirectStandardOutput = $true - $ProcessStartInfo.RedirectStandardError = $true - $ProcessStartInfo.UseShellExecute = $false - $ProcessStartInfo.WorkingDirectory = $WorkingDir - - $Process = [System.Diagnostics.Process]::Start($ProcessStartInfo) - - if ($NoWait) { - if ($PassThru) { - return $Process - } - else { - return $null - } - } - else { - if ($Timeout -eq 0) { - $Process.WaitForExit() - } - else { - $Process.WaitForExit($Timeout) - } - } - - $ProcessResult = New-Object PSObject | - Add-Member -Name "ExitCode" -MemberType NoteProperty -Value $Process.ExitCode -PassThru | - Add-Member -Name "StdOut" -MemberType NoteProperty -Value $Process.StandardOutput.ReadToEnd() -PassThru | - Add-Member -Name "StdErr" -MemberType NoteProperty -Value $Process.StandardError.ReadToEnd() -PassThru - - $Process = $null - - Write-Log ( "STDOUT:`n{0}" -f $ProcessResult.StdOut ) - Write-Log ":STDOUT" - - Write-Log ( "STDERR:`n{0}" -f $ProcessResult.StdErr ) - Write-Log ":STDERR" - - Write-Log "Program has finished with exit code ($($ProcessResult.ExitCode))" - - if ($PassThru) { - return $ProcessResult - } - else { - return $null - } -} -New-Alias -Name Exec -Value Start-Program - - - -Function Backup-File { - param ( - [String] $Path - ) - - $BackupFile = "$Path.bak" - - if (-not [IO.File]::Exists($Path)) { - Write-LogError "Unable to backup file '$Path': file not exists." - return - } - - if ([IO.File]::Exists($BackupFile)) { - try { - [IO.File]::Delete($BackupFile) - } - catch { - Write-LogError "Unable to delete existing .bak file '$BackupFile'." - return - } - } - - Write-Log "Backing up file '$Path' to '$BackupFile'" - [IO.File]::Copy($Path, $BackupFile, $true) -} - - - -Function Install-Module { - param ( - [String] $InstallPath, - [String] $ModulePath, - [String] $ModuleName - ) - - if ($ModuleName -eq "") { - if ($ModulePath -eq "") { - Stop-Execution -ExitString "Don't know which module should be installed." - } - else { - $ModuleName = $ModulePath.Split("\")[-1] - } - } - - if ($InstallPath -eq "") { - Stop-Execution -ExitString "To install the module destination path must be provided." - } - else { - Write-Log "Installing the module to '$InstallPath'" - - $NewModulePath = [IO.Path]::Combine($InstallPath, $ModuleName) - if ([IO.Directory]::Exists($NewModulePath)) { - [IO.Directory]::Delete($NewModulePath, $true) - } - - Copy-Item -Path $ModulePath -Destination $InstallPath -Recurse -Force -ErrorAction Stop - - Update-PsModulePath -AddPath "$InstallPath" - } -} - - - -Function Register-Module { - param ( - [String] $ModulePath - ) - $ModuleRoot = Split-Path -Path $ModulePath -Parent - Write-Log "Registering the module at '$ModuleRoot'" - Update-PsModulePath -AddPath "$ModuleRoot" -} - - - -function Test-ModuleVersion { -<# -.SYNOPSIS -Test module version. - -.DESCRIPTION -Function specified module (current module by default), and compares it's version to version provided. -Returned values: -* -2 : error occured -* -1 : module's version is lower than one provided -* 0 : module's version is equal to one provided -* 1 : module's version is greater than one provided -#> - param ( - [String] $Name = "$__ModuleName", - [String] $Version - ) - - $ModuleVersion = (Get-Module -Name $Name -ListAvailable).Version - - if ($ModuleVersion -eq $null) { - Write-Log "Module '$Name' not found." - return -2 - } - - try { - $RequiredVersion = [System.Version]::Parse($Version) - } - catch { - Write-Log "'$Version' is not a correct version string." - return -2 - } - - $ModuleVersion.CompareTo($RequiredVersion) -} diff --git a/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Logger.ps1 b/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Logger.ps1 deleted file mode 100644 index 9bcb6b75..00000000 --- a/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Logger.ps1 +++ /dev/null @@ -1,137 +0,0 @@ -Function Initialize-Logger { - param ( - [String] $ModuleName = $__ModuleName, - [String] $LogPath = $__DefaultLogPath - ) - - if (-not ("log4net.LogManager" -as [type])) { - $FileStream = ([System.IO.FileInfo] (Get-Item "$__ModulePath\log4net.dll")).OpenRead() - - $AssemblyBytes = New-Object Byte[] $FileStream.Length - [Void] $FileStream.Read($AssemblyBytes, 0, $FileStream.Length) - - $FileStream.Close() - - [Void] [System.Reflection.Assembly]::Load($AssemblyBytes) - } - - [log4net.GlobalContext]::Properties["LogPath"] = $LogPath - [log4net.GlobalContext]::Properties["ModuleName"] = $ModuleName - - $script:__Logger = [log4net.LogManager]::GetLogger("PowerShell") - - $Log4NetConfig = New-Object System.IO.FileInfo("$__ModulePath\log4net.config") - - [log4net.Config.XmlConfigurator]::Configure($Log4NetConfig) - - $__Logger.info("Logger initialized. Log file: '$LogPath'`n") -} - - -Function Out-LogInfo { - param ( - [Parameter(ValueFromPipeline=$true)] - $InputObject, - [Switch] $EntireObject - ) - process { - if ($EntireObject) { - $__Logger.info("`n$(Out-String -InputObject $InputObject)") - } - else { - foreach ($Object in $InputObject) { - $__Logger.info((Out-String -InputObject $Object)) - } - } - } -} -New-Alias -Name Out-Log -Value Out-LogInfo -New-Alias -Name Write-Log -Value Out-LogInfo - - - -Function Out-LogWarning { - param ( - [Parameter(ValueFromPipeline=$true)] - $InputObject, - [Switch] $EntireObject - ) - process { - if ($EntireObject) { - $__Logger.warn("`n$(Out-String -InputObject $InputObject)") - } - else { - foreach ($Object in $InputObject) { - $__Logger.warn((Out-String -InputObject $Object)) - } - } - } -} -New-Alias -Name Write-LogWarning -Value Out-LogWarning - - - -Function Out-LogError { - param ( - [Parameter(ValueFromPipeline=$true)] - $InputObject, - [Switch] $EntireObject - ) - process { - if ($EntireObject) { - $__Logger.error("`n$(Out-String -InputObject $InputObject)") - } - else { - foreach ($Object in $InputObject) { - $__Logger.error((Out-String -InputObject $Object)) - } - } - } -} -New-Alias -Name Write-LogError -Value Out-LogError - - - -Function Out-LogFatal { - param ( - [Parameter(ValueFromPipeline=$true)] - $InputObject, - [Switch] $EntireObject - ) - process { - if ($EntireObject) { - $__Logger.fatal("`n$(Out-String -InputObject $InputObject)") - } - else { - foreach ($Object in $InputObject) { - $__Logger.fatal((Out-String -InputObject $Object)) - } - } - } -} -New-Alias -Name Write-LogFatal -Value Out-LogFatal - - - -Function Out-LogDebug { - param ( - [Parameter(ValueFromPipeline=$true)] - $InputObject, - [Switch] $EntireObject - ) - process { - if ($EntireObject) { - $__Logger.debug("`n$(Out-String -InputObject $InputObject)") - } - else { - foreach ($Object in $InputObject) { - $__Logger.debug((Out-String -InputObject $Object)) - } - } - } -} -New-Alias -Name Write-LogDebug -Value Out-LogDebug - - - -Initialize-Logger diff --git a/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Module.ps1 b/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Module.ps1 deleted file mode 100644 index e69de29b..00000000 diff --git a/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/NotCoreFunctions.ps1 b/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/NotCoreFunctions.ps1 deleted file mode 100644 index 62ac8bc0..00000000 --- a/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/NotCoreFunctions.ps1 +++ /dev/null @@ -1,814 +0,0 @@ -Function Set-LocalUserPassword { - param ( - [String] $UserName, - [String] $Password, - [Switch] $Force - ) - - trap { Stop-Execution $_ } - - if ((Get-WmiObject Win32_UserAccount -Filter "LocalAccount = 'True' AND Name='$UserName'") -eq $null) { - throw "Unable to find local user account '$UserName'" - } - - if ($Force) { - Write-Log "Changing password for user '$UserName' to '*****'" # :) - ([ADSI] "WinNT://./$UserName").SetPassword($Password) | Out-Null - } - else { - Write-LogWarning "You are trying to change the password for the user '$UserName'. To do this please run the command again with -Force parameter." - } -} - - - -Function Set-AutoLogonCredentials { - param ( - [String] $DomainName, - [String] $UserName, - [String] $Password - ) - - $KeyName = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" - - if ($DomainName -ne "") { - $UserName = "$DomainName\$UserName" - } - - Write-Log "Setting AutoLogon credentials ..." - try { - [Microsoft.Win32.Registry]::SetValue($KeyName, "DefaultUserName", "$UserName", [Microsoft.Win32.RegistryValueKind]::String) - [Microsoft.Win32.Registry]::SetValue($KeyName, "DefaultPassword", "$Password", [Microsoft.Win32.RegistryValueKind]::String) - [Microsoft.Win32.Registry]::SetValue($KeyName, "AutoAdminLogon", "1", [Microsoft.Win32.RegistryValueKind]::String) - [Microsoft.Win32.Registry]::SetValue($KeyName, "ForceAutoLogon", "1", [Microsoft.Win32.RegistryValueKind]::String) - } - catch { - Write-LogError "FAILED" - return - } - - Write-Log "SUCCESS" -} - - - -Function Join-Domain { -<# -.SYNOPSIS -Executes "Join domain" action. -#> - param ( - [String] $DomainName, - [String] $UserName, - [String] $Password, - [Switch] $AllowRestart - ) - - $Credential = New-Credential -UserName "$DomainName\$UserName" -Password $Password - - # Add the computer to the domain - if (Test-ComputerName -DomainName $DomainName) { - #Stop-Execution -Success -ExitString "Computer already joined to domain '$DomainName'" - Write-LogWarning "Computer already joined to domain '$DomainName'" - } - else { - Write-Log "Joining computer to domain '$DomainName' ..." - - Add-Computer -DomainName $DomainName -Credential $Credential -Force -ErrorAction Stop - - if ($AllowRestart) { - Write-Log "Restarting computer ..." - Restart-Computer -Force - } - else { - #Stop-Execution -ExitCode 3010 -ExitString "Please restart the computer now." - Write-Log "Please restart the computer now." - } - } -} - - - -Function Expand-Template { - param ( - [String] $TemplateFile, - [String] $OutputFile, - [System.Collections.Hashtable] $ReplacementList, - [String] $Encoding = "Ascii" - ) - - if (-not [IO.File]::Exists($TemplateFile)) { - Write-Error "File '$TemplateFile' not exists" - return $null - } - - if ([IO.File]::Exists($OutputFile)) { - [IO.File]::Delete($OutputFile) - } - - Get-Content $TemplateFile -Encoding $Encoding | - ForEach-Object { - $Line = $_ - foreach ($Key in $ReplacementList.Keys) { - $Line = $Line.Replace("%_$($Key)_%", $ReplacementList[$Key]) - } - Add-Content -Path $OutputFile -Encoding $Encoding -Value $Line - } -} - - - -Function Add-WindowsFeatureWrapper { -<# -.SYNOPSIS -Wraps Install-WindowsFeature function. - -.DESCRIPTION -This function adds some logic to multiple feature installation. - -It fails if any of required features fails. - -It reports that reboot required if it is required, or restarts the computer. -#> - param ( - [Parameter(Mandatory=$true)] - [String[]] $Name, - [Switch] $IncludeManagementTools, - [Switch] $AllowRestart, - [Switch] $NotifyRestart - ) - - $RestartNeeded = $false - - foreach ($Feature in $Name) { - Write-Log "Installing feature '$Feature' ..." - $Action = Install-WindowsFeature ` - -Name $Feature ` - -IncludeManagementTools:$IncludeManagementTools ` - -ErrorAction Stop - - if ($Action.Success -eq $true) { - if ($Action.FeatureResult.RestartNeeded -eq $true) { - Write-LogWarning "Restart required" - $RestartNeeded = $true - } - Write-Log "Feature '$Feature' installed successfully" - } - else { - Stop-Execution "Failed to install feature '$Feature'" - } - } - - if ($RestartNeeded) { - Write-Log "Restart required to finish feature(s) installation." - if ($AllowRestart) { - Write-Log "Restarting computer ..." - Restart-Computer -Force - } - elseif ($NotifyRestart) { - Stop-Execution -ExitCode 3010 -ExitString "Please restart the computer now." - } - } -} - - - -Function Test-WmiReturnValue { -<# -.SYNOPSIS -Check the ReturnValue property of the object provided. - -.DESCRIPTION -This funciton checks if ReturnValue property is equal to 0. - -=== TODO === -If it is not, then funtion should try to provide desctiption for the error code based on the WMI object type. -WMI object type must be provided explicitely. -#> - param ( - [Parameter(ValueFromPipeline=$true,Mandatory=$true)] - $InputObject, - [String] $Type = "" - ) - - try { - $ReturnValue = $InputObject.ReturnValue - } - catch { - throw "Property 'ReturnValue' not found on this object" - } - - if ($ReturnValue -eq 0) { - Write-Log "WMI operation completed successfully" - } - else { - throw "Operation failed with status code = $ReturnValue" - } -} - - - -Function Set-NetworkAdapterConfiguration { -<# -.SYNOPSIS -Set network adapter configuration. - -.DESCRIPTION - - -.EXAMPLE -PS> Set-NetworkAdapterConfiguration -MACAddress aa:bb:cc:dd:ee:ff -Auto - -Convert "dynamic" parameters (DHCP) to "static" (manual) for network adapter with MAC address aa:bb:cc:dd:ee:ff - -.EXAMPLE -PS> Set-NetworkAdapterConfiguration -MACAddress aa:bb:cc:dd:ee:ff -DNSServer "192.168.0.1","192.168.0.2" - -Configure DNS servers list for network adapter with MAC address aa:bb:cc:dd:ee:ff - -#> - param ( - [String] $MACAddress = "", - - [Parameter(ParameterSetName="ManualConfig")] - [String] $IPAddress = "", - - [Parameter(ParameterSetName="ManualConfig")] - [String] $IPNetmask = "", - - [Parameter(ParameterSetName="ManualConfig")] - [String[]] $IPGateway = @(), - - [Parameter(ParameterSetName="ManualConfig")] - [String[]] $DNSServer = @(), - - [Parameter(ParameterSetName="ManualConfig")] - [Switch] $FirstAvailable, - - [String] $Name = "", - - [Parameter(ParameterSetName="AutoConfig",Mandatory=$true)] - [Switch] $Auto, - - [Parameter(ParameterSetName="AutoConfig")] - [Switch] $All - ) - - Write-Log "Configuring network adapter(s) ..." - - :SetIPAddress switch($PSCmdlet.ParameterSetName) { - "AutoConfig" { - Write-Log "'auto' mode" - - $IPv4RegExp = "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" - - if ($All -eq $true) { - $Filter = { $_.AdapterTypeId -eq 0 } - $Name = "" - } - else { - $Filter = { $_.MACAddress -eq $MACAddress } - } - - Get-WmiObject Win32_NetworkAdapter | - Where-Object $Filter | - ForEach-Object { - $NetworkAdapter = $_ - $AdapterConfig = Get-WmiObject Win32_NetworkAdapterConfiguration | - Where-Object { $_.Index -eq $NetworkAdapter.DeviceId } - - Write-Log "Configuring '$($NetworkAdapter.Name)' ..." - - for ($i = 0; $i -lt $AdapterConfig.IPAddress.Length; $i++) { - if ($AdapterConfig.IPAddress[$i] -match $IPv4RegExp) { - $IPAddress = $AdapterConfig.IPAddress[$i] - $IPNetmask = $AdapterConfig.IPSubnet[$i] - $IPGateway = $AdapterConfig.DefaultIPGateway - $DNSServer = $AdapterConfig.DNSServerSearchOrder - - Write-Log "Setting IP address ($IPAddress), netmask ($IPNetmask) ..." - $AdapterConfig.EnableStatic($IPAddress, $IPNetmask) | Out-Null - - Write-Log "Setting default gateways ($IPGateway) ..." - $AdapterConfig.SetGateways($IPGateway) | Out-Null - - Write-Log "Setting DNS servers ($DNSServer) ..." - $AdapterConfig.SetDNSServerSearchOrder($DNSServer) | Out-Null - } - } - - Write-Log "'$($NetworkAdapter.Name)' configured" - } - } - "ManualConfig" { - Write-Log "'manual' mode" - if ( $FirstAvailable ) { - Write-Log "Selecting first available network adapter ..." - $NetworkAdapter = Get-WmiObject Win32_NetworkAdapter | - Where-Object { $_.AdapterTypeId -eq 0 } | - Select-Object -First 1 - } - else { - $NetworkAdapter = Get-WmiObject Win32_NetworkAdapter | - Where-Object { $_.MACAddress -eq $MACAddress } - } - - if ( $NetworkAdapter -eq $null ) { - Write-LogError "Network adapter with MAC = '$MACAddress' not found." - return - } - - $AdapterConfig = Get-WmiObject Win32_NetworkAdapterConfiguration | - Where-Object { $_.Index -eq $NetworkAdapter.DeviceId } - - if (($IPAddress -ne "") -and ($IPNetmask -ne "")) { - Write-Log "Configuring IP address / netmask for '$($NetworkAdapter.Name)' ..." - - <# - for ($i = 0; $i -lt $AdapterConfig.IPAddress.Length; $i++) - { - if (($AdapterConfig.IPAddress[$i] -eq $IPAddress) -and ($AdapterConfig.IPSubnet[$i] -eq $IPNetmask)) - { - Write-Log "There is an adapter with required configuration." - break SetIPAddress - } - } - #> - Write-Log "Setting IP address $IPAddress, netmask $IPNetmask" - $AdapterConfig.EnableStatic("$IPAddress", "$IPNetmask") | Out-Null - - Write-Log "IP address configured." - } - - if ($IPGateway.Count -gt 0) { - Write-Log "Configuring IP gateway for '$($NetworkAdapter.Name)' ..." - - $AdapterConfig.SetGateways($IPGateway) | Out-Null - - Write-Log "IP gateway configured." - } - - if ($DNSServer.Count -gt 0) { - Write-Log "Configuring DNS server(s) for '$($NetworkAdapter.Name)' ..." - - $AdapterConfig.SetDNSServerSearchOrder($DNSServer) | Out-Null - - Write-Log "DNS configured." - } - } - } - - if ($Name -ne "") { - Write-Log "Changing adapter name '$($NetworkAdapter.NetConnectionId)' --> '$Name'" - $NetworkAdapter.NetConnectionId = "$Name" - $NetworkAdapter.Put() | Out-Null - } -} - - - -Function Show-EthernetNetworkAdapters { - Get-WmiObject Win32_NetworkAdapter -Filter "PhysicalAdapter = 'True' AND AdapterTypeId = '0'" | - Select-Object 'Index','MACAddress','NetConnectionId' -} - - - -Function Test-ComputerName { -<# -.SYNOPSIS -Test if computer name is set, and the computer belongs to specified domain / workgroup. - -.DESCRIPTION -Function tests the following conditions: -* the computer name is equal to the provided one -* the computer is a part of domain -* the computer belongs to the specified domain -* the computer belongs to the specified workgroup - -Multiple checks are logically ANDed. -#> - [CmdletBinding()] - param ( - [String] $ComputerName, - [String] $DomainName, - [String] $WorkgroupName, - [Switch] $PartOfDomain - ) - process { - $ComputerSystem = Get-WmiObject Win32_ComputerSystem - - if (($ComputerName -ne "") -and ($ComputerSystem.Name -ne "$ComputerName")) { - Write-Error "ComputerName is not equal to '$ComputerName'" - return $false - } - - if (($DomainName -ne "") -and ($ComputerSystem.Domain -ne "$DomainName")) { - Write-Error "DomainName is not equal to '$DomainName'" - return $false - } - - if (($WorkgroupName -ne "") -and ($ComputerSystem.Workgroup -ne "$WorkgroupName")) { - Write-Error "WorkgroupName is not equal to '$WorkgroupName'" - return $false - } - - if (($PartOfDOmain -eq $true) -and ($ComputerSystem.PartOfDomain -eq $false)) { - Write-Error "Computer is not the part of any domain." - return $false - } - - return $true - } -} - - - -Function Set-ComputerName { - param ( - [String] $Name - ) - - - # Rename the computer - if ($Name -ne "") { - if (Test-ComputerName -ComputerName $Name) { - Stop-Execution -Success -ExitString "Computer name already configured" - } - else { - Write-Log "Renaming computer to '$Name'" - - Rename-Computer -NewName $NewName -Force -ErrorAction Stop - - Stop-Execution -ExitCode 3010 -ExitString "Please restart the computer now" - } - } -} - - - - -Function Resolve-LdapDnsName { - param ( - [String] $DomainName - ) - - Resolve-DNSName -Type "SRV" -Name "_ldap._tcp.dc._msdcs.$DomainName" | - Where-Object { $_.Type -eq "A" } | - Select-Object -Property Name,IPAddress -} - - - -Function Wait-LdapServerAvailable { - param ( - [String] $DomainName, - [Int] $PingSeqCountThreshold = 10, - [Int] $PingSeqPerHostThreshold = 5 - ) - - $LdapServerList = @( Resolve-LdapDnsName $DomainName ) - Write-Log @( "Ldap server list:", ( $LdapServerList | Out-String ) ) - - :MainLoop foreach ($LdapServer in $LdapServerList) { - $PingSeqCount = 0 - $PingSeqPerHost = 0 - while ($PingSeqPerHost -lt $PingSeqPerHostThreshold) { - if (Test-Connection -ComputerName $LdapServer.IpAddress -Count 1 -Quiet) { - Write-Log "Ping '$($LdapServer.Name)' OK" - $PingSeqCount++ - } - else { - Write-Log "Ping '$($LdapServer.Name)' FAILED" - $PingSeqCount = 0 - $PingSeqPerHost++ - } - - if ($PingSeqCount -ge $PingSeqCountThreshold) { - Write-Log "Returning true" - return $true - } - - Start-Sleep -Seconds 1 - } - } - - Write-Log "Returning false" - return $false -} - - - -Function Get-ConfigDriveObject { - [CmdletBinding()] - param ( - [Parameter(ParameterSetName="MetaData")] - [Switch] $MetaData, - - [Parameter(ParameterSetName="UserData")] - [Switch] $UserData, - - [Parameter(ParameterSetName="CustomObject")] - [String] $CustomObject, - - [String] $Path = "openstack/latest" - ) - - $ConfigDrivePrefix = "http://169.254.169.154/$Path" - - try { - switch($PSCmdlet.ParameterSetName) { - "MetaData" { - $ConfigDriveObjectUrl = "$ConfigDrivePrefix/meta_data.json" - $MetaData = Invoke-WebRequest $ConfigDriveObjectUrl - ConvertFrom-Json $MetaData.Content - } - "UserData" { - $ConfigDriveObjectUrl = "$ConfigDrivePrefix/user_data" - $UserData = Invoke-WebRequest $ConfigDriveObjectUrl - $UserData.Content - } - "CustomObject" { - $ConfigDriveObjectUrl = "$ConfigDrivePrefix/$CustomObject" - $CustomObject = Invoke-WebRequest $ConfigDriveObjectUrl - $CustomObject.Content - } - } - } - catch { - Write-Error "Unable to retrieve object from 'ConfigDriveObjectUrl'" - return $null - } -} - - - -Function Update-AgentConfig { - param ( - [String] $RootPath = "C:\Keero\Agent" - ) - - try { - $MetaData = Get-ConfigDriveObject -MetaData -ErrorAction Stop - if ($MetaData.meta -ne $null) { - Stop-Service "Keero Agent" -Force - Expand-Template -TemplateFile "$RootPath\WindowsAgent.exe.config.template" -OutputFile "$RootPath\WindowsAgent.exe.config" -ReplacementList $MetaData.meta - Start-Service "Keero Agent" - } - } - catch { - Write-LogError "Failed to update agent configuration" - } -} - - - -Function Update-PsModulePath { - param ( - [String] $AddPath = "" - ) - - $NewPsModulePath = ( - @([Environment]::GetEnvironmentVariable("PsModulePath", [EnvironmentVariableTarget]::Machine) -split ";") + @($AddPath) ` - | Select-Object -Unique - ) -join ';' - - [Environment]::SetEnvironmentVariable("PsModulePath", $NewPsModulePath, [EnvironmentVariableTarget]::Machine) - - Invoke-WMSettingsChange -} - - - -Function Get-ModuleHelp { - param ( - [String] $ModuleName = $__ModuleName, - [String] $Path = "", - [Switch] $File, - [Int] $Width = 80 - ) - - $sb = { - $Module = Get-Module $ModuleName - - "`n" - "Module: $($Module.Name)" - "Module version: $($Module.Version)" - "`n" - "{0} Module Description {0}" -f ('=' * 30) - "`n" - - Get-Help "about_$($Module.Name)" | Out-String -Width $Width - - "{0} Exported Functions {0}" -f ('=' * 30) - "`n" - - foreach ($CommandName in $Module.ExportedCommands.Keys) { - '-' * 80 - Get-Help -Name $CommandName -Detailed | Out-String -Width $Width - } - } - - if (($File) -and ($Path -eq "")) { - $Path = [IO.Path]::GetTempFileName() - } - - if ($Path -ne "") { - & $sb | Out-File -FilePath $Path -Force - } - else { - & $sb | Out-Default - } - - if ($File) { - notepad.exe "$Path" - } -} - - - -Function New-ModuleTemplate { - param ( - [Parameter(Mandatory=$true)] - [String] $Name, - - [String] $Path = "$($Env:USERPROFILE)\Documents\WindowsPowerShell\Modules", - - [Switch] $Force - ) - if ([IO.Directory]::Exists("$Path\$Name")) { - if ($Force) { - [IO.Directory]::Delete("$Path\$Name", $true) - } - else { - Write-Error "Folder '$Path\$Name' already exists. Remove it manually or specify -Force switch." - return - } - } - - - [IO.Directory]::CreateDirectory("$Path\$Name") - [IO.Directory]::CreateDirectory("$Path\$Name\en-US") - [IO.Directory]::CreateDirectory("$Path\$Name\include") - - - Set-Content -Path "$Path\$Name\en-US\about_$Name.help.txt" -Value @' -'@ - - - Set-Content -Path "$Path\$Name\Config.ps1" -Value @' -$script:__ModulePath = $PsScriptRoot -$script:__ModuleName = $PsScriptRoot.Split("\")[-1] -$script:__DefaultLogPath = [IO.Path]::Combine([IO.Path]::GetTempPath(), "PowerShell_$__ModuleName.log") - -$global:__StopExecutionExitsSession__ = $false -'@ - - - Set-Content -Path "$Path\$Name\$Name.psm1" -Value @' -# Import config first -. "$PsScriptRoot\Config.ps1" - -# Import functions from 'Include' subfolder -Get-ChildItem "$PsScriptRoot\Include" -Filter "*.ps1" | - ForEach-Object { - . "$($_.FullName)" - } - -Export-ModuleMember -Function * -Alias * - -Initialize-Logger -ModuleName $__ModuleName -LogPath $__DefaultLogPath - -Write-Log "Module loaded from '$PsScriptRoot'" -'@ - - - New-ModuleManifest ` - -Path "$Path\$Name\$Name.psd1" ` - -ModuleToProcess "$Name.psm1" ` - -RequiredModules "CoreFunctions" - -} - - - -function New-SqlServerConnection { - param ( - [String] $ServerName, - [String] $UserName = '', - [String] $Password = '', - $Credentials, - [Switch] $SqlAuth - ) - - if ($Credentials -eq $null) { - if ($UserName -eq '') { - throw "User name must be provided in order to create credentials object!" - } - - $Credentials = New-Credential -UserName $UserName -Password $Password - } - - $Server = New-Object ` - -TypeName Microsoft.SqlServer.Management.Smo.Server ` - -ArgumentList $ServerName - - $LoginName = $Credentials.UserName -replace("^\\", "") - - try { - if ($SqlAuth) { - $Server.ConnectionContext.set_LoginSecure($false) - $Server.ConnectionContext.set_Login($LoginName) - $Server.ConnectionContext.set_SecurePassword($Credentials.Password) - } - else { - throw "Not implemented!" - } - } - catch { - return $null - } - - $Server -} - - - -function Import-SqlServerAssemblies { -<# -.SYNOPSIS -Import assemblies required to work with Sql Server instance from PowerShell - -.DESCRIPTION -Possible assembly list: - "Microsoft.SqlServer.Management.Common" - "Microsoft.SqlServer.Smo" - "Microsoft.SqlServer.Dmf" - "Microsoft.SqlServer.Instapi" - "Microsoft.SqlServer.SqlWmiManagement" - "Microsoft.SqlServer.ConnectionInfo" - "Microsoft.SqlServer.SmoExtended" - "Microsoft.SqlServer.SqlTDiagM" - "Microsoft.SqlServer.SString" - "Microsoft.SqlServer.Management.RegisteredServers" - "Microsoft.SqlServer.Management.Sdk.Sfc" - "Microsoft.SqlServer.SqlEnum" - "Microsoft.SqlServer.RegSvrEnum" - "Microsoft.SqlServer.WmiEnum" - "Microsoft.SqlServer.ServiceBrokerEnum" - "Microsoft.SqlServer.ConnectionInfoExtended" - "Microsoft.SqlServer.Management.Collector" - "Microsoft.SqlServer.Management.CollectorEnum" - "Microsoft.SqlServer.Management.Dac" - "Microsoft.SqlServer.Management.DacEnum" - "Microsoft.SqlServer.Management.Utility" - -.LINKS -http://msdn.microsoft.com/en-us/library/cc281962%28v=sql.105%29.aspx -#> - $AssemblyList = @( - "Microsoft.SqlServer.Smo" - "Microsoft.SqlServer.SmoExtended" - ) - - foreach ($asm in $AssemblyList) { - [System.Reflection.Assembly]::LoadWithPartialName($asm) | Out-Null - } -} - - - -function Import-SqlServerProvider { - $SqlPsReg="HKLM:\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.SqlServer.Management.PowerShell.sqlps" - - if (Get-ChildItem $SqlPsReg -ErrorAction "SilentlyContinue") { - throw "SQL Server Provider for Windows PowerShell is not installed." - } - else { - $Item = Get-ItemProperty $SqlPsReg - $SqlPsPath = [System.IO.Path]::GetDirectoryName($Item.Path) - } - - # - # Set mandatory variables for the SQL Server provider - # - $global:SqlServerMaximumChildItems = 0 - $global:SqlServerConnectionTimeout = 30 - $global:SqlServerIncludeSystemObjects = $false - $global:SqlServerMaximumTabCompletion = 1000 - - # - # Load the snapins, type data, format data - # - Push-Location - Set-Location $sqlpsPath - - Add-PSSnapin SqlServerCmdletSnapin100 - Add-PSSnapin SqlServerProviderSnapin100 - - Update-TypeData -PrependPath SQLProvider.Types.ps1xml - Update-FormatData -PrependPath SQLProvider.Format.ps1xml - - Pop-Location -} - diff --git a/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/SqlFunctions.ps1 b/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/SqlFunctions.ps1 deleted file mode 100644 index 5f282702..00000000 --- a/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/SqlFunctions.ps1 +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Zip.ps1 b/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Zip.ps1 deleted file mode 100644 index 0980da8b..00000000 --- a/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Zip.ps1 +++ /dev/null @@ -1,69 +0,0 @@ -[Void] [System.Reflection.Assembly]::LoadFrom("$__ModulePath\Ionic.Zip.dll") - - -Function Compress-Folder { -<# -#> - [CmdLetBinding()] - param ( - [Parameter(Mandatory=$true)] - [String] $Path, - - [String] $ZipFile = "", - - [Switch] $TempFile, - - [Switch] $ContentOnly - ) - - if (-not [IO.Directory]::Exists($Path)) { - Write-LogError "Directory '$Path' not found." - return - } - - if ($TempFile) { - $ZipFile = [IO.Path]::GetTempFileName() - } - - if ($ZipFile -eq "") { - $ZipFile = "$Path.zip" - } - - if ([IO.File]::Exists($ZipFile)) { - [IO.File]::Delete($ZipFile) - } - - $zip = New-Object Ionic.Zip.ZipFile - if ($ContentOnly) { - [Void] $zip.AddDirectory($Path) - } - else { - [Void] $zip.AddDirectory($Path, (Split-Path -Path $Path -Leaf)) - } - $zip.Save($ZipFile) - $zip.Dispose() - - return $ZipFile -} - - -Function Expand-Zip { -<# -#> - [CmdletBinding()] - param ( - [Parameter(Mandatory=$true)] - [String] $Path, - - [String] $Destination - ) - - if (-not [IO.File]::Exists($Path)) { - Write-LogError "File not found '$Path'" - return - } - - $zip = [Ionic.Zip.ZipFile]::Read($Path) - $zip.ExtractAll($Destination, [Ionic.Zip.ExtractExistingFileAction]::OverwriteSilently) - $zip.Dispose() -} diff --git a/Deployment/WindowsPowerShell/Modules/CoreFunctions/log4net.config b/Deployment/WindowsPowerShell/Modules/CoreFunctions/log4net.config deleted file mode 100644 index 1b0b20bc..00000000 --- a/Deployment/WindowsPowerShell/Modules/CoreFunctions/log4net.config +++ /dev/null @@ -1,42 +0,0 @@ -<log4net> - <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender" > - <param name="File" type="log4net.Util.PatternString" value="%property{LogPath}" /> - <param name="AppendToFile" value="true" /> - <param name="RollingStyle" value="Size" /> - <param name="MaxSizeRollBackups" value="100" /> - <param name="MaximumFileSize" value="1024KB" /> - <param name="StaticLogFileName" value="true" /> - <lockingModel type="log4net.Appender.FileAppender+MinimalLock" /> - <layout type="log4net.Layout.PatternLayout"> - <param name="ConversionPattern" value="%date [%-5level] [%property{ModuleName}] %message" /> - </layout> - </appender> - - <appender name="ColoredConsoleAppender" type="log4net.Appender.ColoredConsoleAppender"> - <mapping> - <level value="error" /> - <foreColor value="Red, HighIntensity" /> - </mapping> - <mapping> - <level value="warn" /> - <foreColor value="Yellow, HighIntensity" /> - </mapping> - <mapping> - <level value="info" /> - <foreColor value="Green, HighIntensity" /> - </mapping> - <layout type="log4net.Layout.PatternLayout"> - <conversionPattern value="%date [%-5level] [%property{ModuleName}] %message" /> - </layout> - </appender> - - <!--root> - <level value="info" /> - </root--> - - <logger name="PowerShell" additivity="false"> - <!--level value="info" /--> - <appender-ref ref="ColoredConsoleAppender" /> - <appender-ref ref="RollingFileAppender" /> - </logger> -</log4net> \ No newline at end of file diff --git a/Deployment/WindowsPowerShell/Modules/CoreFunctions/log4net.dll b/Deployment/WindowsPowerShell/Modules/CoreFunctions/log4net.dll deleted file mode 100644 index 1e66c82abda444cd72c064c62a38ed216254b3d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 288768 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%0Ab7xahvn`?~19&@)|h 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 Cp?ie@ diff --git a/Deployment/cloudbase-init/config/cloudbase-init.conf b/Deployment/cloudbase-init/config/cloudbase-init.conf deleted file mode 100644 index 0b167a0c..00000000 --- a/Deployment/cloudbase-init/config/cloudbase-init.conf +++ /dev/null @@ -1,11 +0,0 @@ -[DEFAULT] -username=Admin -groups=Administrators -inject_user_password=false -network_adapters= -config_drive_raw_hdd=false -config_drive_cdrom=false -verbose=true -logdir=C:\Program Files (x86)\Cloudbase Solutions\Cloudbase-Init\log\ -logfile=cloudbase-init.log -plugins=cloudbaseinit.plugins.windows.userdata.UserDataPlugin \ No newline at end of file diff --git a/Deployment/cloudbase-init/plugins/setagentconfig.py b/Deployment/cloudbase-init/plugins/setagentconfig.py deleted file mode 100644 index 25f93ced..00000000 --- a/Deployment/cloudbase-init/plugins/setagentconfig.py +++ /dev/null @@ -1,37 +0,0 @@ -import codecs - -from cloudbaseinit.osutils.factory import * -from cloudbaseinit.plugins.base import * -from cloudbaseinit.openstack.common import log as logging - -opts = [ - cfg.StrOpt('agent_config_file', default='C:\\Keero\\Agent\\WindowsAgent.exe.config', help='') -] - -CONF = cfg.CONF -CONF.register_opts(opts) - -LOG = logging.getLogger(__name__) - - -class SetHostNamePlugin(BasePlugin): - def execute(self, service): - meta_data = service.get_meta_data('openstack') - if 'meta' not in meta_data: - LOG.debug("Section 'meta' not found in metadata") - return False - - if 'agent_config_xml' not in meta_data['meta']: - LOG.debug("Config for agent not found in metadata section") - return False - - try: - configFile=codecs.open(CONF.agent_config_file, encoding='utf-8', mode='w+') - configFile.write(meta_data['meta']['agent_config_xml']) - configFile.close() - except: - LOG.error("Unable to update agent file.") - return False - - return True - diff --git a/Deployment/cloudbase-init/plugins/sethostname.py b/Deployment/cloudbase-init/plugins/sethostname.py deleted file mode 100644 index 9aa2e8d2..00000000 --- a/Deployment/cloudbase-init/plugins/sethostname.py +++ /dev/null @@ -1,20 +0,0 @@ - -from cloudbaseinit.osutils.factory import * -from cloudbaseinit.plugins.base import * -from cloudbaseinit.openstack.common import log as logging - -LOG = logging.getLogger(__name__) - - -class SetHostNamePlugin(BasePlugin): - def execute(self, service): - meta_data = service.get_meta_data('openstack') - if 'name' not in meta_data: - LOG.debug('Name not found in metadata') - return False - - osutils = OSUtilsFactory().get_os_utils() - - new_host_name = meta_data['name'].replace('.', '-') - return osutils.set_host_name(new_host_name) - diff --git a/Deployment/cloudbase-init/plugins/userdata.py b/Deployment/cloudbase-init/plugins/userdata.py deleted file mode 100644 index 076b58a7..00000000 --- a/Deployment/cloudbase-init/plugins/userdata.py +++ /dev/null @@ -1,119 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2012 Cloudbase Solutions Srl -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import re -import tempfile -import uuid -import email -import tempfile -import os -import errno - -from cloudbaseinit.openstack.common import cfg -from cloudbaseinit.openstack.common import log as logging -from cloudbaseinit.osutils.factory import * -from cloudbaseinit.plugins.base import * - -LOG = logging.getLogger(__name__) - -opts = [ - cfg.StrOpt('user_data_folder', default='cloud-data', - help='Specifies a folder to store multipart data files.'), - ] - -CONF = cfg.CONF -CONF.register_opts(opts) - -class UserDataPlugin(): - def __init__(self, cfg=CONF): - self.cfg = cfg - self.msg = None - return - - def execute(self, service): - user_data = service.get_user_data('openstack') - if not user_data: - return False - - LOG.debug('User data content:\n%s' % user_data) - - if user_data.startswith('Content-Type: multipart'): - for part in self.parse_MIME(user_data): - self.process_part(part) - else: - self.handle(user_data) - return - - def process_part(self, part): - if part.get_filename() == 'cfn-userdata': - self.handle(part.get_payload()) - return - - def parse_MIME(self, user_data): - folder = self.cfg.user_data_folder - self.create_folder(folder) - - self.msg = email.message_from_string(user_data) - return self.msg.walk() - - - def create_folder(self, folder): - try: - os.mkdir(folder) - except os.OSError, e: - if e.errno != errno.EEXIST: - raise e - return - - def handle(self, user_data): - - osutils = OSUtilsFactory().get_os_utils() - - target_path = os.path.join(tempfile.gettempdir(), str(uuid.uuid4())) - if re.search(r'^rem cmd\s', user_data, re.I): - target_path += '.cmd' - args = [target_path] - shell = True - elif re.search(r'^#!', user_data, re.I): - target_path += '.sh' - args = ['bash.exe', target_path] - shell = False - elif re.search(r'^#ps1\s', user_data, re.I): - target_path += '.ps1' - args = ['powershell.exe', '-ExecutionPolicy', 'RemoteSigned', - '-NonInteractive', target_path] - shell = False - else: - # Unsupported - LOG.warning('Unsupported user_data format') - return False - - try: - with open(target_path, 'wb') as f: - f.write(user_data) - (out, err, ret_val) = osutils.execute_process(args, shell) - - LOG.info('User_data script ended with return code: %d' % ret_val) - LOG.debug('User_data stdout:\n%s' % out) - LOG.debug('User_data stderr:\n%s' % err) - except Exception, ex: - LOG.warning('An error occurred during user_data execution: \'%s\'' % ex) - finally: - if os.path.exists(target_path): - os.remove(target_path) - - return False - diff --git a/Deployment/cloudbase-init/userdata/Sample.ps1 b/Deployment/cloudbase-init/userdata/Sample.ps1 deleted file mode 100644 index 878106cd..00000000 --- a/Deployment/cloudbase-init/userdata/Sample.ps1 +++ /dev/null @@ -1,24 +0,0 @@ -Import-Module CoreFunctions - -$ModuleBase = "C:\Keero\Modules" - - -$NewModule_Name = "ModuleName" -$NewModule_Base64 = @' -%BASE64_STRINGS% -'@ - - -$AgentConfig_Path = "C:\Keero\Agent\WindowsAgent.exe.config" -$AgentConfig_Base64 = @' -%AGENT_CONFIG_BASE64% -'@ - - -ConvertFrom-Base64String -Base64String $NewModule_Base64 -Path "$ModuleBase\$NewModule_Name.zip" -Remove-Item -Path "$ModuleBase\$NewModule_Name" -Recurse -Force -Expand-Zip -Path "$ModuleBase\$NewModule_Name.zip" -Destination "$ModuleBase\$NewModule_Name" - - -Remove-Item -Path $AgentConfig_Path -Force -ConvertFrom-Base64String -Base64String $NewModule_Base64 -Path $AgentConfig_Path diff --git a/Deployment/devstack-scripts/compute/devstack.localrc b/Deployment/devstack-scripts/compute/devstack.localrc deleted file mode 100644 index 6088490b..00000000 --- a/Deployment/devstack-scripts/compute/devstack.localrc +++ /dev/null @@ -1,51 +0,0 @@ -# Devstack's config file for COMPUTE intallation - - -lab_id=102 -lab_password=swordfish -lab_controller=172.18.124.100 - - -SERVICE_HOST=$lab_controller -HOST_IP=172.18.124.${lab_id} -MULTI_HOST=1 - - -FLAT_INTERFACE=eth1 -#PUBLIC_INTERFACE=eth0.261 - - -FIXED_RANGE=192.168.102.0/24 -FIXED_NETWORK_SIZE=254 -FLOATING_RANGE=172.18.124.228/27 - - -MYSQL_HOST=$lab_controller -RABBIT_HOST=$lab_controller -GLANCE_HOSTPORT=$lab_controller:9292 -KEYSTONE_AUTH_HOST=$lab_controller -KEYSTONE_SERVICE_HOST=$lab_controller - - -VNCSERVER_LISTEN=$HOST_IP -VNCSERVER_PROXYCLIENT_ADDRESS=$HOST_IP - - -ADMIN_PASSWORD=$lab_password -MYSQL_PASSWORD=$lab_password -RABBIT_PASSWORD=$lab_password -SERVICE_PASSWORD=$lab_password -SERVICE_TOKEN=tokentoken - - -ENABLED_SERVICES=n-cpu,n-net,n-api,n-vol,n-novnc - - -SCREEN_LOGDIR=/opt/stack/log/ -LOGFILE=$SCREEN_LOGDIR/stack.sh.log - - -API_RATE_LIMIT=False - - -EXTRA_OPTS=(force_config_drive=true libvirt_images_type=qcow2 force_raw_images=false sql_connection=mysql://root:${MYSQL_PASSWORD}@${MYSQL_HOST}/nova?charset=utf8) diff --git a/Deployment/devstack-scripts/controller/devstack.localrc b/Deployment/devstack-scripts/controller/devstack.localrc deleted file mode 100644 index 23513cb7..00000000 --- a/Deployment/devstack-scripts/controller/devstack.localrc +++ /dev/null @@ -1,45 +0,0 @@ -# Devstack's localrc for CONTROLLER installation - - -lab_id=100 -lab_password=swordfish - - -HOST_IP=172.18.124.${lab_id} - - -FLAT_INTERFACE=eth1 -#PUBLIC_INTERFACE=eth0.261 - - -FIXED_RANGE=192.168.102.0/24 -FIXED_NETWORK_SIZE=254 -PUBLIC_RANGE=172.18.124.228/27 - - -ADMIN_PASSWORD=$lab_password -MYSQL_PASSWORD=$lab_password -RABBIT_PASSWORD=$lab_password -SERVICE_PASSWORD=$lab_password -SERVICE_TOKEN=tokentoken - - -disable_service n-cpu -disable_service n-vol - - -ENABLED_SERVICES+=,heat,h-api,h-api-cfn,h-api-cw,h-eng -ENABLED_SERVICES+=,conductor,portas - - -SCREEN_LOGDIR=/opt/stack/log/ -LOGFILE=$SCREEN_LOGDIR/stack.sh.log - - -API_RATE_LIMIT=False - - -MULTI_HOST=1 - - -EXTRA_OPTS=(force_config_drive=true libvirt_images_type=qcow2 force_raw_images=false) diff --git a/Deployment/devstack-scripts/devstack.standalone.localrc b/Deployment/devstack-scripts/devstack.standalone.localrc deleted file mode 100644 index 3295cdf9..00000000 --- a/Deployment/devstack-scripts/devstack.standalone.localrc +++ /dev/null @@ -1,28 +0,0 @@ -lab_id=101 -lab_password=swordfish - -HOST_IP=172.18.124.${lab_id} -#PUBLIC_INTERFACE=eth1 - -FIXED_RANGE=10.0.${lab_id}.0/24 -NETWORK_GATEWAY=10.0.${lab_id}.1 - -#PUBLIC_INTERFACE=eth0 -FLAT_INTERFACE=eth1 - -ADMIN_PASSWORD=$lab_password -MYSQL_PASSWORD=$lab_password -RABBIT_PASSWORD=$lab_password -SERVICE_PASSWORD=$lab_password -SERVICE_TOKEN=tokentoken -ENABLED_SERVICES+=,heat,h-api,h-api-cfn,h-api-cw,h-eng -ENABLED_SERVICES+=,conductor,portas - -LOGFILE=/opt/stack/devstack/stack.sh.log -SCREEN_LOGDIR=/opt/stack/log/ -#SCREEN_LOGDIR=/dev/null - -API_RATE_LIMIT=False - -EXTRA_OPTS=(force_config_drive=true libvirt_images_type=qcow2 force_raw_images=false) - diff --git a/Deployment/devstack-scripts/functions.sh b/Deployment/devstack-scripts/functions.sh deleted file mode 100644 index b99ce7dc..00000000 --- a/Deployment/devstack-scripts/functions.sh +++ /dev/null @@ -1,148 +0,0 @@ -#!/bin/bash - - -# Checks an environment variable is not set or has length 0 OR if the -# exit code is non-zero and prints "message" and exits -# NOTE: env-var is the variable name without a '$' -# die_if_not_set env-var "message" -function die_if_not_set() { - local exitcode=$? - set +o xtrace - local evar=$1; shift - if ! is_set $evar || [ $exitcode != 0 ]; then - if [[ -z "$1" ]] ; then - die "Env var '$evar' is not set!" - else - die $@ - fi - fi -} - - - -function restart_service { - while [[ -n "$1" ]] ; do - _echo "Restarting service '$1' ..." - sudo service $1 restart - shift 1 - done -} - - - -function move_mysql_data_to_ramdrive { - # Moving MySQL database to tmpfs - #------------------------------- - if [[ $(trueorfalse True $MYSQL_DB_TMPFS) = "True" ]] ; then - die_if_not_set MYSQL_DB_TMPFS_SIZE - mount_dir=/var/lib/mysql - sudo -s << EOF -echo "Stopping MySQL Server" -service mysql stop - -umount $mount_dir -mount -t tmpfs -o size=$MYSQL_DB_TMPFS_SIZE tmpfs $mount_dir -chmod 700 $mount_dir -chown mysql:mysql $mount_dir - -mysql_install_db - -/usr/bin/mysqld_safe --skip-grant-tables & -sleep 5 -EOF - - sudo mysql << EOF -FLUSH PRIVILEGES; -SET PASSWORD FOR 'root'@'localhost' = PASSWORD('swordfish'); -SET PASSWORD FOR 'root'@'127.0.0.1' = PASSWORD('swordfish'); -EOF - - sudo -s << EOF -killall mysqld -sleep 5 - -echo "Starting MySQL Server" -service mysql start -EOF - else - _echo "MYSQL_DB_TMPFS = '$MYSQL_DB_TMPFS'" - fi - #------------------------------- -} - - -function move_nova_cache_to_ramdrive { - # Moving nova images cache to tmpfs - #---------------------------------- - if [[ $(trueorfalse True $NOVA_CACHE_TMPFS) = "True" ]] ; then - die_if_not_set NOVA_CACHE_TMPFS_SIZE - mount_dir=/opt/stack/data/nova/instances - sudo -s << EOF -umount $mount_dir -mount -t tmpfs -o size=$NOVA_CACHE_TMPFS_SIZE tmpfs $mount_dir -chmod 775 $mount_dir -chown stack:stack $mount_dir -EOF - else - _echo "NOVA_CACHE_TMPFS = '$NOVA_CACHE_TMPFS'" - fi - #---------------------------------- -} - - -function check_if_folder_exists { - if [[ ! -d "$1" ]] ; then - _echo "Folder '$1' not exists!" - return 1 - fi - return 0 -} - - -function validate_install_mode { - case $INSTALL_MODE in - 'standalone') - check_if_folder_exists "$SCRIPTS_DIR/standalone" || exit - ;; - 'multihost') - check_if_folder_exists "$SCRIPTS_DIR/controller" || exit - check_if_folder_exists "$SCRIPTS_DIR/compute" || exit - ;; - 'controller') - check_if_folder_exists "$SCRIPTS_DIR/controller" || exit - ;; - 'compute') - check_if_folder_exists "$SCRIPTS_DIR/compute" || exit - ;; - *) - _echo "Wrong install mode '$INSTALL_MODE'" - exit - ;; - esac -} - - -function update_devstack_localrc { - local $__install_mode=$1 - - [[ -z "$__install_mode" ]] \ - && die "Install mode for update_devstack_localrc not provided!" - - # Replacing devstack's localrc config - #------------------------------------ - devstack_localrc="$SCRIPTS_DIR/$__install_mode/devstack.localrc" - if [[ -f $devstack_localrc ]] ; then - rm -f "$DEVSTACK_DIR/localrc" - cp $devstack_localrc "$DEVSTACK_DIR/localrc" - else - _echo "File '$devstack_localrc' not found!" - fi - #------------------------------------ -} - - -function _echo { - echo "[$(hostname)] $@" -} - - diff --git a/Deployment/devstack-scripts/install-devstack.sh b/Deployment/devstack-scripts/install-devstack.sh deleted file mode 100644 index 7400b1f1..00000000 --- a/Deployment/devstack-scripts/install-devstack.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -SCRIPTS_DIR=$( cd $( dirname "$0" ) && pwd ) - -source $SCRIPTS_DIR/localrc - -groupadd stack -useradd -g stack -s /bin/bash -m stack - -echo 'stack ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/stack -chmod 0440 /etc/sudoers.d/stack - -mkdir -p $DEVSTACK_INSTALL_DIR -chmod stack:stack $DEVSTACK_INSTALL_DIR - -sudo -u stack << EOF -cd -rm -rf devstack -git clone git://github.com/openstack-dev/devstack.git -EOF - diff --git a/Deployment/devstack-scripts/localrc b/Deployment/devstack-scripts/localrc deleted file mode 100644 index 366c09de..00000000 --- a/Deployment/devstack-scripts/localrc +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -DEVSTACK_DIR=/home/stack/devstack -DEVSTACK_INSTALL_DIR=/opt/stack - -MYSQL_DB_TMPFS=true -MYSQL_DB_TMPFS_SIZE=128M - -NOVA_CACHE_TMPFS=true -NOVA_CACHE_TMPFS_SIZE=24G - -GLANCE_IMAGE_LIST="" -COMPUTE_NODE_LIST="172.18.124.102" - -#====================================== -if [[ -d "$DEVSTACK_DIR" ]] ; then - source $DEVSTACK_DIR/openrc admin admin -fi -source $SCRIPTS_DIR/functions.sh - diff --git a/Deployment/devstack-scripts/post-stack.sh b/Deployment/devstack-scripts/post-stack.sh deleted file mode 100644 index 7abe0bcd..00000000 --- a/Deployment/devstack-scripts/post-stack.sh +++ /dev/null @@ -1,109 +0,0 @@ -#!/bin/bash - -if [[ -z "$1" ]] ; then - SCRIPTS_DIR=$( cd $( dirname "$0" ) && pwd ) - source $SCRIPTS_DIR/localrc -fi - - -function glance_image_create { - local __image_path=$1 - - if [[ -z "$__image_path" ]] ; then - echo "Image path is missed!" - return - fi - - local __image_name=${__image_path##*/} - __image_name=${__image_name%.*} - - echo "Importing image '$__image_name' into Glance ..." - glance image-delete "$__image_name" - if [[ ^http =~ $__image_path]] ; then - glance image-create \ - --name "$__image_name" \ - --disk-format qcow2 \ - --container-format bare \ - --is-public true \ - --copy-from "$__image_path" - else - glance image-create \ - --name "$__image_name" \ - --disk-format qcow2 \ - --container-format bare \ - --is-public true \ - --file "$__image_path" - fi -} - -# Executing post-stack actions -#=============================================================================== - -if [[ ,$INSTALL_MODE, =~ ',standalone,compute,' ]] ; then - echo "Adding iptables rule to allow Internet access from instances..." - __iptables_rule="POSTROUTING -t nat -s '$FIXED_RANGE' ! -d '$FIXED_RANGE' -j MASQUERADE" - sudo iptables -C $__iptables_rule - if [[ $? == 0 ]] ; then - echo "Iptables rule already exists." - else - sudo iptables -A $__iptables_rule - fi -fi - - -if [[ $INSTALL_MODE == 'compute' ]] ; then - return -fi - - -if [[ -z "$(sudo rabbitmqctl list_users | grep keero)" ]] ; then - echo "Adding RabbitMQ 'keero' user" - sudo rabbitmqctl add_user keero keero -else - echo "User 'Keero' already exists." -fi - - -if [[ -z "$(sudo rabbitmq-plugins list -e | grep rabbitmq_management)" ]] ; then - echo "Enabling RabbitMQ management plugin" - sudo rabbitmq-plugins enable rabbitmq_management - - echo "Restarting RabbitMQ ..." - restart_service rabbitmq-server -else - echo "RabbitMQ management plugin already enabled." -fi - - -echo "* Removing nova flavors ..." -for id in $(nova flavor-list | awk '$2 ~ /[[:digit:]]/ {print $2}') ; do - echo "** Removing flavor '$id'" - nova flavor-delete $id -done - - -echo "* Creating new flavors ..." -nova flavor-create m1.small auto 768 40 1 -nova flavor-create m1.medium auto 1024 40 1 -nova flavor-create m1.large auto 1280 40 2 - - -echo "* Creating security group rules ..." -nova secgroup-add-rule default tcp 1 65535 0.0.0.0/0 -nova secgroup-add-rule default udp 1 65535 0.0.0.0/0 -nova secgroup-add-rule default icmp 0 8 0.0.0.0/0 - - -if [[ -z "$(nova keypair-list | grep keero_key)" ]] ; then - echo "Creating keypair 'keero_key' ..." - nova keypair-add keero_key -else - echo "Keypair 'keero_key' already exists" -fi - -#=============================================================================== - -for $image in $GLANCE_IMAGE_LIST ; do - glance_image_create "$image" -done - diff --git a/Deployment/devstack-scripts/post-unstack.sh b/Deployment/devstack-scripts/post-unstack.sh deleted file mode 100644 index 5e71c716..00000000 --- a/Deployment/devstack-scripts/post-unstack.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -if [[ -z "$1" ]] ; then - SCRIPTS_DIR=$( cd $( dirname "$0" ) && pwd ) - source $SCRIPTS_DIR/localrc -fi - - -#Remove certificates -echo "* Removing old certificate files" -for file in $(sudo find $DEVSTACK_DIR/accrc/ -type f -regex ".+.pem.*") ; do - echo "Removing file '$file'" - sudo rm -f "$file" -done - - -# Remove logs -echo "* Removing 'devstack' logs ..." -sudo rm -f /opt/stack/log/* - - -echo "* Removing 'apache2' logs ..." -for file in $(sudo find /var/log/apache2 -type f) ; do - echo "Removing file '$file'" - sudo rm -f "$file" -done - - -echo "* Stopping all VMs ..." -sudo killall kvm -sleep 2 - - -echo "* Unmounting ramdrive ..." -umount /opt/stack/data/nova/instances - diff --git a/Deployment/devstack-scripts/pre-stack.sh b/Deployment/devstack-scripts/pre-stack.sh deleted file mode 100644 index 7163cebb..00000000 --- a/Deployment/devstack-scripts/pre-stack.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/bash - -if [[ -z "$1" ]] ; then - SCRIPTS_DIR=$( cd $( dirname "$0" ) && pwd ) - source $SCRIPTS_DIR/localrc -fi - - -# Executing pre-stack actions -#=============================================================================== - -# Executing checks -#----------------- -die_if_not_set DEVSTACK_DIR -die_if_not_set MYSQL_DB_TMPFS_SIZE -die_if_not_set NOVA_CACHE_TMPFS_SIZE -#----------------- - - -if [[ ,$INSTALL_MODE, =~ ',standalone,multihost,controller,' ]] ; then - restart_service dbus rabbitmq-server -fi - - -if [[ ,$INSTALL_MODE, =~ ',standalone,multihost,controller,' ]] ; then - move_mysql_data_to_ramdrive -fi - - -# Devstack log folder -#-------------------- -sudo -s << EOF -mkdir -p $SCREEN_LOGDIR -chown stack:stack $SCREEN_LOGDIR -EOF -#-------------------- - - -case $INSTALL_MODE in - 'standalone') - update_devstack_localrc 'standalone' - ;; - 'multihost') - update_devstack_localrc 'controller' - ;; - 'controller') - update_devstack_localrc 'controller' - ;; - 'compute') - update_devstack_localrc 'compute' - ;; -esac - -#=============================================================================== - diff --git a/Deployment/devstack-scripts/pre-unstack.sh b/Deployment/devstack-scripts/pre-unstack.sh deleted file mode 100644 index 90c0c953..00000000 --- a/Deployment/devstack-scripts/pre-unstack.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -if [[ -z "$1" ]] ; then - SCRIPTS_DIR=$( cd $( dirname "$0" ) && pwd ) - source $SCRIPTS_DIR/localrc -fi - diff --git a/Deployment/devstack-scripts/ss1285.interfaces.example b/Deployment/devstack-scripts/ss1285.interfaces.example deleted file mode 100644 index df25828d..00000000 --- a/Deployment/devstack-scripts/ss1285.interfaces.example +++ /dev/null @@ -1,53 +0,0 @@ -# This file describes the network interfaces available on your system -# and how to activate them. For more information, see interfaces(5). - -# The loopback network interface -auto lo -iface lo inet loopback - -# The primary network interface -auto eth0 -iface eth0 inet static - address 67.207.197.34 - netmask 255.255.255.240 - gateway 67.207.197.33 - # dns-* options are implemented by the resolvconf package, if installed - dns-nameservers 208.166.50.13 67.207.201.6 - dns-search svwh.net - - -auto eth0.260 -iface eth0.260 inet manual - vlan-raw-device eth0 - post-up ifconfig $IFACE 172.18.124.102/29 -# post-up route add -net 172.18.0.0/16 gw 172.18.124.97 -# pre-down route del -net 172.18.0.0/16 gw 172.18.124.97 - post-up ip route add default via 172.18.124.97 dev $IFACE table 260 - post-up ip rule add from 172.18.124.96/29 to 172.18.0.0/16 table 260 - pre-down ip rule del from 172.18.124.96/29 to 172.18.0.0/16 - pre-down ip route del table 260 - pre-down ifconfig $IFACE 0.0.0.0 - - -auto eth0.261 -iface eth0.261 inet manual - vlan-raw-device eth0 - post-up ifconfig $IFACE 172.18.124.202/26 - post-up route add -net 172.18.0.0/16 gw 172.18.124.193 dev $IFACE - pre-down route del -net 172.18.0.0/16 gw 172.18.124.193 dev $IFACE - pre-down ifconfig $IFACE 0.0.0.0 - - -auto eth1 -iface eth1 inet manual - up ifconfig $IFACE up - down ifconfig $IFACE down -# up ifconfig $IFACE 0.0.0.0 - - -#auto eth1.221 -#iface eth1.221 inet manual -# vlan-raw-device eth1 -# pre-up ifconfig $IFACE up -# post-down ifconfig $IFACE down -# up ifconfig $IFACE 0.0.0.0 diff --git a/Deployment/devstack-scripts/ss1383.interfaces.example b/Deployment/devstack-scripts/ss1383.interfaces.example deleted file mode 100644 index ae7e24e2..00000000 --- a/Deployment/devstack-scripts/ss1383.interfaces.example +++ /dev/null @@ -1,54 +0,0 @@ -# This file describes the network interfaces available on your system -# and how to activate them. For more information, see interfaces(5). - -# The loopback network interface -auto lo -iface lo inet loopback - -# The primary network interface -auto eth0 -iface eth0 inet static - address 67.207.197.36 - netmask 255.255.255.240 - network 67.207.197.32 - broadcast 67.207.197.47 - gateway 67.207.197.33 - # dns-* options are implemented by the resolvconf package, if installed - dns-nameservers 208.166.50.13 67.207.206.1 - dns-search svwh.net - - -auto eth0.260 -iface eth0.260 inet manual - vlan-raw-device eth0 - post-up ifconfig $IFACE 172.18.124.100/29 - post-up ip route add default via 172.18.124.97 dev $IFACE table 260 - post-up ip rule add from 172.18.124.96/29 to 172.18.0.0/16 table 260 - pre-down ip rule del from 172.18.124.96/29 to 172.18.0.0/16 table 260 - pre-down ip route del table 260 - pre-down ifconfig $IFACE 0.0.0.0 -# post-up route add -net 172.18.0.0/16 gw 172.18.124.97 -# post-down route del -net 172.18.0.0/16 gw 172.18.124.97 - - -auto eth1 -iface eth1 inet manual - up ifconfig $IFACE up - down ifconfig $IFACE down -# up ifconfig $IFACE 0.0.0.0 - - -auto eth0.261 -iface eth0.261 inet manual - vlan-raw-device eth0 - post-up ifconfig $IFACE 172.18.124.200/26 - post-up route add -net 172.18.0.0/16 gw 172.18.124.193 dev $IFACE - pre-down route del -net 172.18.0.0/16 gw 172.18.124.192 dev $IFACE - pre-down ifconfig $IFACE 0.0.0.0 - - -#auto eth1.221 -#iface eth1.221 inet manual -# vlan-raw-device eth1 -# pre-up ifconfig $IFACE up -# post-down ifcofnig $IFACE down diff --git a/Deployment/devstack-scripts/standalone/devstack.localrc b/Deployment/devstack-scripts/standalone/devstack.localrc deleted file mode 100644 index 441f9157..00000000 --- a/Deployment/devstack-scripts/standalone/devstack.localrc +++ /dev/null @@ -1,27 +0,0 @@ -# Devstack's config file for STANDALONE installation - -lab_id=101 -lab_password=swordfish - -HOST_IP=172.18.124.${lab_id} - -FIXED_RANGE=10.0.${lab_id}.0/24 - -FLAT_INTERFACE=eth1 - -ADMIN_PASSWORD=$lab_password -MYSQL_PASSWORD=$lab_password -RABBIT_PASSWORD=$lab_password -SERVICE_PASSWORD=$lab_password -SERVICE_TOKEN=tokentoken - -ENABLED_SERVICES+=,heat,h-api,h-api-cfn,h-api-cw,h-eng -ENABLED_SERVICES+=,conductor,portas - -SCREEN_LOGDIR=/opt/stack/log/ -LOGFILE=$SCREEN_LOGDIR/stack.sh.log - -API_RATE_LIMIT=False - -EXTRA_OPTS=(force_config_drive=true libvirt_images_type=qcow2 force_raw_images=false) - diff --git a/Deployment/devstack-scripts/start-devstack.sh b/Deployment/devstack-scripts/start-devstack.sh deleted file mode 100644 index 3797b833..00000000 --- a/Deployment/devstack-scripts/start-devstack.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/bin/bash - -SCRIPTS_DIR=$( cd $( dirname "$0" ) && pwd ) - -source $SCRIPTS_DIR/localrc - -INSTALL_MODE="$1" - -validate_install_mode - - - -# Update devstack-scripts if multihost -#=============================================================================== -if [[ "$INSTALL_MODE" == 'multihost' ]] ; then - _echo "* Copying devstack-scripts to compute nodes ..." - for __compute_node in $COMPUTE_NODE_LIST ; do - _echo "** Removing devstack-scripts on '$__compute_node' ..." - ssh stack@$__compute_node rm -rf ~/devstack-scripts - _echo "** Copying devstack-scripts to '$__compute_node' ..." - scp -r $SCRIPTS_DIR stack@$__compute_node:~/ - done -fi -#=============================================================================== - - - -# Executing pre-stack actions -#=============================================================================== -_echo "* Executing pre-stack actions ..." -source $SCRIPTS_DIR/pre-stack.sh no-localrc -#=============================================================================== - - - -# Creating stack -#=============================================================================== -_echo "* Starting devstack ..." -$DEVSTACK_DIR/stack.sh -#=============================================================================== - - - -# Executing post-stack actions -#=============================================================================== -_echo "* Executing post-stack actions ..." -source $SCRIPTS_DIR/post-stack.sh no-localrc -#source $SCRIPTS_DIR/start-keero.sh no-localrc -#=============================================================================== - - - -# Start installation on compute nodes -#=============================================================================== -if [[ "$INSTALL_MODE" == 'multihost' ]] ; then - _echo "* Starting devstack on compute nodes ..." - for __compute_node in $COMPUTE_NODE_LIST ; do - _echo "** Starting devstack on '$__compute_node' ..." - ssh stack@$__compute_node $SCRIPTS_DIR/start-devstack.sh compute - done -fi -#=============================================================================== - diff --git a/Deployment/devstack-scripts/start-keero.sh b/Deployment/devstack-scripts/start-keero.sh deleted file mode 100644 index 82d45497..00000000 --- a/Deployment/devstack-scripts/start-keero.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -if [[ -z "$1" ]] ; then - SCRIPTS_DIR=$( cd $( dirname "$0" ) && pwd ) - source $SCRIPTS_DIR/localrc -fi - -die_if_not_set INSTALL_DIR - -# Starting Portas -#================ -if [[ ! -d "$INSTALL_DIR/portas" ]] ; then - mkdir -p "$INSTALL_DIR/portas" -fi - -cp "$INSTALL_DIR/keero/portas/etc" "$INSTALL_DIR/portas/etc" - -screen_it portas "cd $INSTALL_DIR/portas && portas-api --config-file=$INSTALL_DIR/portas/etc/portas-api.conf" -#================ - - - -# Starting Conductor -#=================== -screen_it conductor "cd $INSTALL_DIR/keero/conductor && bash ./tools/with_venv.sh ./bin/app.py" -#=================== diff --git a/Deployment/devstack-scripts/start-vm.sh b/Deployment/devstack-scripts/start-vm.sh deleted file mode 100644 index a71b63ec..00000000 --- a/Deployment/devstack-scripts/start-vm.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -image_file=$1 - -function die { - echo "$@" - exit 1 -} - -[ -z "$image_file" ] && die "VM name MUST be provided!" -[ -f "$image_file" ] || die "File '$image_file' not found." - -echo "Starting VM '$image_file' ..." - -kvm \ - -m 2048 \ - -drive file="$image_file",if=virtio \ - -redir tcp:3389::3389 -redir tcp:3390::3390 \ - -nographic \ - -usbdevice tablet \ - -vnc :20 - diff --git a/Deployment/devstack-scripts/stop-devstack.sh b/Deployment/devstack-scripts/stop-devstack.sh deleted file mode 100644 index a88c5baf..00000000 --- a/Deployment/devstack-scripts/stop-devstack.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -SCRIPTS_DIR=$( cd $( dirname "$0" ) && pwd ) - -source $SCRIPTS_DIR/localrc - -INSTALL_MODE="$1" - -validate_install_mode - - -# Executing pre-unstack actions -#=============================================================================== -_echo "* Executing pre-unstack actions ..." -source $SCRIPTS_DIR/pre-unstack.sh no-localrc -#=============================================================================== - - -# Executing unstack.sh -#=============================================================================== -_echo "* Executing stop devstack ..." -$DEVSTACK_DIR/unstack.sh -#=============================================================================== - - -# Executing post-unstack actions -#=============================================================================== -_echo "* Executing post-unstack actions ..." -source $SCRIPTS_DIR/post-unstack.sh no-localrc -#source $SCRIPTS_DIR/stop-keero.sh no-localrc -#=============================================================================== - - - -# Stop installation on compute nodes -#=============================================================================== -if [[ "$INSTALL_MODE" == 'multihost' ]] ; then - _echo "* Stopping devstack on compute nodes ..." - for $__compute_node in $COMPUTE_NODE_LIST ; do - _echo "** Stopping devstack on '$__compute_node' ..." - ssh stack@$__compute_node $SCRIPTS_DIR/stop-devstack.sh - done -fi -#=============================================================================== - diff --git a/Deployment/devstack-scripts/stop-keero.sh b/Deployment/devstack-scripts/stop-keero.sh deleted file mode 100644 index bf9ff5b9..00000000 --- a/Deployment/devstack-scripts/stop-keero.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -if [[ -z "$1" ]] ; then - SCRIPTS_DIR=$( cd $( dirname "$0" ) && pwd ) - source $SCRIPTS_DIR/localrc -fi - -# Stopping Keero components -#========================== -for serv in conductor portas ; do - screen -S $SCREEN_NAME -p $serv -X kill -done -#========================== diff --git a/WindowsAgent/ExecutionPlanGenerator/App.config b/ExecutionPlanGenerator/App.config similarity index 100% rename from WindowsAgent/ExecutionPlanGenerator/App.config rename to ExecutionPlanGenerator/App.config diff --git a/WindowsAgent/ExecutionPlanGenerator/ExecutionPlanGenerator.csproj b/ExecutionPlanGenerator/ExecutionPlanGenerator.csproj similarity index 100% rename from WindowsAgent/ExecutionPlanGenerator/ExecutionPlanGenerator.csproj rename to ExecutionPlanGenerator/ExecutionPlanGenerator.csproj diff --git a/WindowsAgent/ExecutionPlanGenerator/Program.cs b/ExecutionPlanGenerator/Program.cs similarity index 100% rename from WindowsAgent/ExecutionPlanGenerator/Program.cs rename to ExecutionPlanGenerator/Program.cs diff --git a/WindowsAgent/ExecutionPlanGenerator/Properties/AssemblyInfo.cs b/ExecutionPlanGenerator/Properties/AssemblyInfo.cs similarity index 100% rename from WindowsAgent/ExecutionPlanGenerator/Properties/AssemblyInfo.cs rename to ExecutionPlanGenerator/Properties/AssemblyInfo.cs diff --git a/WindowsAgent/ExecutionPlanGenerator/packages.config b/ExecutionPlanGenerator/packages.config similarity index 100% rename from WindowsAgent/ExecutionPlanGenerator/packages.config rename to ExecutionPlanGenerator/packages.config diff --git a/WindowsAgent/Tools/NuGet.exe b/Tools/NuGet.exe similarity index 100% rename from WindowsAgent/Tools/NuGet.exe rename to Tools/NuGet.exe diff --git a/WindowsAgent/WindowsAgent.sln b/WindowsAgent.sln similarity index 100% rename from WindowsAgent/WindowsAgent.sln rename to WindowsAgent.sln diff --git a/WindowsAgent/WindowsAgent/App.config b/WindowsAgent/App.config similarity index 100% rename from WindowsAgent/WindowsAgent/App.config rename to WindowsAgent/App.config diff --git a/WindowsAgent/WindowsAgent/ExecutionPlan.cs b/WindowsAgent/ExecutionPlan.cs similarity index 100% rename from WindowsAgent/WindowsAgent/ExecutionPlan.cs rename to WindowsAgent/ExecutionPlan.cs diff --git a/WindowsAgent/WindowsAgent/MqMessage.cs b/WindowsAgent/MqMessage.cs similarity index 100% rename from WindowsAgent/WindowsAgent/MqMessage.cs rename to WindowsAgent/MqMessage.cs diff --git a/WindowsAgent/WindowsAgent/PlanExecutor.cs b/WindowsAgent/PlanExecutor.cs similarity index 100% rename from WindowsAgent/WindowsAgent/PlanExecutor.cs rename to WindowsAgent/PlanExecutor.cs diff --git a/WindowsAgent/WindowsAgent/Program.cs b/WindowsAgent/Program.cs similarity index 100% rename from WindowsAgent/WindowsAgent/Program.cs rename to WindowsAgent/Program.cs diff --git a/WindowsAgent/WindowsAgent/Properties/AssemblyInfo.cs b/WindowsAgent/Properties/AssemblyInfo.cs similarity index 100% rename from WindowsAgent/WindowsAgent/Properties/AssemblyInfo.cs rename to WindowsAgent/Properties/AssemblyInfo.cs diff --git a/WindowsAgent/WindowsAgent/RabbitMqClient.cs b/WindowsAgent/RabbitMqClient.cs similarity index 100% rename from WindowsAgent/WindowsAgent/RabbitMqClient.cs rename to WindowsAgent/RabbitMqClient.cs diff --git a/WindowsAgent/WindowsAgent/SampleExecutionPlan.json b/WindowsAgent/SampleExecutionPlan.json similarity index 100% rename from WindowsAgent/WindowsAgent/SampleExecutionPlan.json rename to WindowsAgent/SampleExecutionPlan.json diff --git a/WindowsAgent/WindowsAgent/ServiceManager.cs b/WindowsAgent/ServiceManager.cs similarity index 100% rename from WindowsAgent/WindowsAgent/ServiceManager.cs rename to WindowsAgent/ServiceManager.cs diff --git a/WindowsAgent/WindowsAgent/WindowsAgent.csproj b/WindowsAgent/WindowsAgent.csproj similarity index 100% rename from WindowsAgent/WindowsAgent/WindowsAgent.csproj rename to WindowsAgent/WindowsAgent.csproj diff --git a/WindowsAgent/WindowsAgent/WindowsService.cs b/WindowsAgent/WindowsService.cs similarity index 100% rename from WindowsAgent/WindowsAgent/WindowsService.cs rename to WindowsAgent/WindowsService.cs diff --git a/WindowsAgent/WindowsAgent/WindowsServiceInstaller.cs b/WindowsAgent/WindowsServiceInstaller.cs similarity index 100% rename from WindowsAgent/WindowsAgent/WindowsServiceInstaller.cs rename to WindowsAgent/WindowsServiceInstaller.cs diff --git a/WindowsAgent/WindowsAgent/packages.config b/WindowsAgent/packages.config similarity index 100% rename from WindowsAgent/WindowsAgent/packages.config rename to WindowsAgent/packages.config diff --git a/api/.gitignore b/api/.gitignore deleted file mode 100644 index f5f39680..00000000 --- a/api/.gitignore +++ /dev/null @@ -1,23 +0,0 @@ -#IntelJ Idea -.idea/ - -#virtualenv -.venv/ - -#Build results -build/ -dist/ -*.egg-info/ - -#Python -*.pyc - -#Translation build -*.mo -*.pot - -#SQLite Database files -*.sqlite - -#Autogenerated Documentation -doc/source/api \ No newline at end of file diff --git a/api/README.rst b/api/README.rst deleted file mode 100644 index 02ee2a7c..00000000 --- a/api/README.rst +++ /dev/null @@ -1,7 +0,0 @@ -Glazier API README -===================== -Glazier API is a project that provides access to engine via API. - -SEE ALSO --------- -* `Glazier <http://glazier.mirantis.com>`__ diff --git a/api/babel.cfg b/api/babel.cfg deleted file mode 100644 index efceab81..00000000 --- a/api/babel.cfg +++ /dev/null @@ -1 +0,0 @@ -[python: **.py] diff --git a/api/bin/glazier-api b/api/bin/glazier-api deleted file mode 100755 index 24962190..00000000 --- a/api/bin/glazier-api +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License.from oslo.config import cfg - -import gettext -import os -import sys - -# If ../glazierapi/__init__.py exists, add ../ to Python search path, so that -# it will override what happens to be installed in /usr/(local/)lib/python... -from glazierapi.common.service import TaskResultHandlerService - -possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) -if os.path.exists(os.path.join(possible_topdir, 'portas', '__init__.py')): - sys.path.insert(0, possible_topdir) - -from glazierapi.common import config -from glazierapi.openstack.common import log -from glazierapi.openstack.common import wsgi -from glazierapi.openstack.common import service - -gettext.install('glazierapi', './glazierapi/locale', unicode=1) - -if __name__ == '__main__': - try: - config.parse_args() - log.setup('glazierapi') - - launcher = service.ServiceLauncher() - - api_service = wsgi.Service(config.load_paste_app(), - port=config.CONF.bind_port, - host=config.CONF.bind_host) - - launcher.launch_service(api_service) - launcher.launch_service(TaskResultHandlerService()) - launcher.wait() - except RuntimeError, e: - sys.stderr.write("ERROR: %s\n" % e) - sys.exit(1) diff --git a/api/doc/source/_static/.placeholder b/api/doc/source/_static/.placeholder deleted file mode 100755 index e69de29b..00000000 diff --git a/api/doc/source/_templates/.placeholder b/api/doc/source/_templates/.placeholder deleted file mode 100755 index e69de29b..00000000 diff --git a/api/doc/source/_theme/theme.conf b/api/doc/source/_theme/theme.conf deleted file mode 100755 index e8748699..00000000 --- a/api/doc/source/_theme/theme.conf +++ /dev/null @@ -1,2 +0,0 @@ -[theme] -inherit = default diff --git a/api/doc/source/conf.py b/api/doc/source/conf.py deleted file mode 100644 index 28c8b685..00000000 --- a/api/doc/source/conf.py +++ /dev/null @@ -1,241 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2010 OpenStack Foundation. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# -# Portas documentation build configuration file, created by -# sphinx-quickstart on Tue February 28 13:50:15 2013. -# -# This file is execfile()'d with the current directory set to its containing -# dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import os -import sys - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path = [os.path.abspath('../../glazierapi'), - os.path.abspath('../..'), - os.path.abspath('../../bin')] + sys.path - -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', - 'sphinx.ext.intersphinx', - 'sphinx.ext.coverage', - 'sphinx.ext.pngmath', - 'sphinx.ext.ifconfig', - 'sphinx.ext.graphviz'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = [] -if os.getenv('HUDSON_PUBLISH_DOCS'): - templates_path = ['_ga', '_templates'] -else: - templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'Glazier APIs' -copyright = u'2013, Mirantis, Inc' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -from glazierapi.version import version_info as portas_version -# The full version, including alpha/beta/rc tags. -release = portas_version.version_string_with_vcs() -# The short X.Y version. -version = portas_version.canonical_version_string() - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of documents that shouldn't be included in the build. -#unused_docs = [] - -# List of directories, relative to source directory, that shouldn't be searched -# for source files. -exclude_trees = ['api'] - -# The reST default role (for this markup: `text`) to use for all documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -show_authors = True - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -modindex_common_prefix = ['glazierapi.'] - -# -- Options for man page output -------------------------------------------- - -# Grouping the document tree for man pages. -# List of tuples 'sourcefile', 'target', u'title', u'Authors name', 'manual' - -man_pages = [ - ('man/glazierapi', 'glazier-api', u'Glazier API Server', - [u'Mirantis, Inc'], 1) -] - - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. Major themes that come with -# Sphinx are currently 'default' and 'sphinxdoc'. -html_theme_path = ["."] -html_theme = '_theme' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = ['_theme'] - -# The name for this set of Sphinx documents. If None, it defaults to -# "<project> v<release> documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' -git_cmd = "git log --pretty=format:'%ad, commit %h' --date=local -n1" -html_last_updated_fmt = os.popen(git_cmd).read() - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -html_use_modindex = False - -# If false, no index is generated. -html_use_index = False - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a <link> tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = '' - -# Output file base name for HTML help builder. -htmlhelp_basename = 'glazierapidoc' - - -# -- Options for LaTeX output ------------------------------------------------ - -# The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' - -# The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, -# documentclass [howto/manual]). -latex_documents = [ - ('index', 'Glazier.tex', u'Glazier Documentation', - u'Mirantis, Inc', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# Additional stuff for the LaTeX preamble. -#latex_preamble = '' - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_use_modindex = True - -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'python': ('http://docs.python.org/', None)} diff --git a/api/doc/source/index.rst b/api/doc/source/index.rst deleted file mode 100644 index 2073f005..00000000 --- a/api/doc/source/index.rst +++ /dev/null @@ -1,114 +0,0 @@ -.. - Copyright (c) 2013 Mirantis, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); you may - not use this file except in compliance with the License. You may obtain - a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - License for the specific language governing permissions and limitations - under the License. - -============================================== -Welcome to Glazier API Service! -============================================== - -Glazier API is a project that provides access to engine via API. - -This document describes Glazier API for contributors of the project, -and assumes that you are already familiar with Glazier API from an -`end-user perspective`_. - -.. _`end-user perspective`: http://glazier.mirantis.com/ - -This documentation is generated by the Sphinx toolkit and lives in the source -tree. - -Installation Guide -================== -Install -------- -1. Check out sources to some directory (<home>/glazier):: - - user@work:~/$ git clone ssh://<user>@gerrit.mirantis.com:29418/keero/keero.git - -2. Install Glazier API:: - - user@work:~/$ cd glazier/api && sudo python setup.py install - -Configure ---------- -1. Open first configuration file for editing:: - - user@work:~/$ cd glazier/api/etc && nano glazier-api.conf - -2. Configure according to you environment (please note rabbitmq section):: - - [DEFAULT] - # Show more verbose log output (sets INFO log level output) - verbose = True - # Show debugging output in logs (sets DEBUG log level output) - debug = True - # Address to bind the server to - bind_host = 0.0.0.0 - # Port the bind the server to - bind_port = 8082 - # Log to this file. Make sure the user running skeleton-api has - # permissions to write to this file! - log_file = /tmp/glazier-api.log - #A valid SQLAlchemy connection string for the metadata database - sql_connection = sqlite:///glazier.sqlite - - [reports] - results_exchange = task-results - results_queue = task-results - reports_exchange = task-reports - reports_queue = task-reports - - [rabbitmq] - host = localhost - port = 5672 - virtual_host = keero - login = keero - password = keero - -3. Open second configuration file for editing:: - - smelikyan@work:~/cd glazier/api/etc && nano glazier-api.conf - -4. Configure according to you environment (please note filter:authtoken section):: - - [pipeline:glazier-api] - pipeline = authtoken context apiv1app - [app:apiv1app] - paste.app_factory = glazierapi.api.v1.router:API.factory - [filter:context] - paste.filter_factory = glazierapi.api.middleware.context:ContextMiddleware.factory - - [filter:authtoken] - paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory - auth_host = localhost - auth_port = 35357 - auth_protocol = http - admin_tenant_name = admin - admin_user = admin - admin_password = password - signing_dir = /tmp/keystone-signing-glazierapi - -Run ----- -Run Glazier API and supply valid configuration file:: - - user@work:~/$ glazier-api --config-file=./glazier/api/etc/glazier-api.conf - -Man Pages -========= - -.. toctree:: - :maxdepth: 1 - - man/glazierapi diff --git a/api/doc/source/man/glazierapi.rst b/api/doc/source/man/glazierapi.rst deleted file mode 100644 index a488935b..00000000 --- a/api/doc/source/man/glazierapi.rst +++ /dev/null @@ -1,69 +0,0 @@ -.. - Copyright (c) 2013 Mirantis, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); you may - not use this file except in compliance with the License. You may obtain - a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - License for the specific language governing permissions and limitations - under the License. - -========== -glazier-api -========== - ------------------------------ -Glazier API Server ------------------------------ - -:Author: smelikyan@mirantis.com -:Date: 2013-04-04 -:Copyright: Mirantis, Inc. -:Version: 2013.1-dev -:Manual section: 1 -:Manual group: cloud computing - - -SYNOPSIS -======== - - glazier-api [options] - -DESCRIPTION -=========== - -glazier-api is a server daemon that serves the Glazier API - -OPTIONS -======= - - **General options** - - **-v, --verbose** - Print more verbose output - - **--config-file** - Config file used for running service - - **--bind-host=HOST** - Address of host running ``glazier-api``. Defaults to `0.0.0.0`. - - **--bind-port=PORT** - Port that ``glazier-api`` listens on. Defaults to `8082`. - - -FILES -===== - -* /etc/glazier/glazier-api.conf -* /etc/glazier/glazier-api-paste.conf - -SEE ALSO -======== - -* `Glazier <http://glazier.mirantis.com>`__ diff --git a/api/etc/glazier-api-paste.ini b/api/etc/glazier-api-paste.ini deleted file mode 100644 index f02d0c14..00000000 --- a/api/etc/glazier-api-paste.ini +++ /dev/null @@ -1,18 +0,0 @@ -[pipeline:glazier-api] -pipeline = authtoken context apiv1app - -[app:apiv1app] -paste.app_factory = glazierapi.api.v1.router:API.factory - -[filter:context] -paste.filter_factory = glazierapi.api.middleware.context:ContextMiddleware.factory - -[filter:authtoken] -paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory -auth_host = localhost -auth_port = 35357 -auth_protocol = http -admin_tenant_name = admin -admin_user = admin -admin_password = password -signing_dir = /tmp/keystone-signing-portas diff --git a/api/etc/glazier-api.conf b/api/etc/glazier-api.conf deleted file mode 100644 index 5cf698e8..00000000 --- a/api/etc/glazier-api.conf +++ /dev/null @@ -1,32 +0,0 @@ -[DEFAULT] -# Show more verbose log output (sets INFO log level output) -verbose = True - -# Show debugging output in logs (sets DEBUG log level output) -debug = True - -# Address to bind the server to -bind_host = 0.0.0.0 - -# Port the bind the server to -bind_port = 8082 - -# Log to this file. Make sure the user running skeleton-api has -# permissions to write to this file! -log_file = /tmp/portas-api.log - -#A valid SQLAlchemy connection string for the metadata database -sql_connection = sqlite:///glazier.sqlite - -[reports] -results_exchange = task-results -results_queue = task-results -reports_exchange = task-reports -reports_queue = task-reports - -[rabbitmq] -host = localhost -port = 5672 -virtual_host = keero -login = keero -password = keero diff --git a/api/glazierapi/__init__.py b/api/glazierapi/__init__.py deleted file mode 100644 index 207fa154..00000000 --- a/api/glazierapi/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. \ No newline at end of file diff --git a/api/glazierapi/api/__init__.py b/api/glazierapi/api/__init__.py deleted file mode 100644 index fc46f490..00000000 --- a/api/glazierapi/api/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License.from oslo.config import cfg - diff --git a/api/glazierapi/api/middleware/__init__.py b/api/glazierapi/api/middleware/__init__.py deleted file mode 100644 index fc46f490..00000000 --- a/api/glazierapi/api/middleware/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License.from oslo.config import cfg - diff --git a/api/glazierapi/api/middleware/context.py b/api/glazierapi/api/middleware/context.py deleted file mode 100644 index 6d82abde..00000000 --- a/api/glazierapi/api/middleware/context.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License.from oslo.config import cfg - - -import json -from oslo.config import cfg - -import webob.exc - -from glazierapi.openstack.common import wsgi -import glazierapi.context -import glazierapi.openstack.common.log as logging - -CONF = cfg.CONF -LOG = logging.getLogger(__name__) - - -class BaseContextMiddleware(wsgi.Middleware): - def process_response(self, resp): - try: - request_id = resp.request.context.request_id - except AttributeError: - LOG.warn(_('Unable to retrieve request id from context')) - else: - resp.headers['x-openstack-request-id'] = 'req-%s' % request_id - return resp - - -class ContextMiddleware(BaseContextMiddleware): - def process_request(self, req): - """Convert authentication information into a request context - - Generate a glazierapi.context.RequestContext object from the available - authentication headers and store on the 'context' attribute - of the req object. - - :param req: wsgi request object that will be given the context object - :raises webob.exc.HTTPUnauthorized: when value of the X-Identity-Status - header is not 'Confirmed' and - anonymous access is disallowed - """ - if req.headers.get('X-Identity-Status') == 'Confirmed': - roles_header = req.headers.get('X-Roles', '') - roles = [r.strip().lower() for r in roles_header.split(',')] - - #NOTE(bcwaldon): This header is deprecated in favor of X-Auth-Token - deprecated_token = req.headers.get('X-Storage-Token') - - service_catalog = None - if req.headers.get('X-Service-Catalog') is not None: - try: - catalog_header = req.headers.get('X-Service-Catalog') - service_catalog = json.loads(catalog_header) - except ValueError: - raise webob.exc.HTTPInternalServerError( - _('Invalid service catalog json.')) - - kwargs = { - 'user': req.headers.get('X-User-Id'), - 'tenant': req.headers.get('X-Tenant-Id'), - 'roles': roles, - 'auth_token': req.headers.get('X-Auth-Token', - deprecated_token), - 'service_catalog': service_catalog, - 'session': req.headers.get('X-Configuration-Session') - } - req.context = glazierapi.context.RequestContext(**kwargs) - else: - raise webob.exc.HTTPUnauthorized() - - @classmethod - def factory(cls, global_conf, **local_conf): - def filter(app): - return cls(app) - return filter diff --git a/api/glazierapi/api/v1/__init__.py b/api/glazierapi/api/v1/__init__.py deleted file mode 100644 index 7a46398e..00000000 --- a/api/glazierapi/api/v1/__init__.py +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License.from oslo.config import cfg - -from glazierapi.db.models import Session, Environment, Status -from glazierapi.db.session import get_session - - -def get_draft(environment_id=None, session_id=None): - unit = get_session() - #TODO: When session is deployed should be returned env.description - if session_id: - session = unit.query(Session).get(session_id) - return session.description - else: - environment = unit.query(Environment).get(environment_id) - return environment.description - - -def save_draft(session_id, draft): - unit = get_session() - session = unit.query(Session).get(session_id) - - session.description = draft - session.save(unit) - - -def get_env_status(environment_id, session_id): - status = 'draft' - - unit = get_session() - - if not session_id: - variants = ['open', 'deploying'] - session = unit.query(Session).filter( - Session.environment_id == environment_id and - Session.state.in_(variants) - ).first() - if session: - session_id = session.id - else: - return status - - session_state = unit.query(Session).get(session_id).state - reports_count = unit.query(Status).filter_by(environment_id=environment_id, - session_id=session_id).count() - - if session_state == 'deployed': - status = 'finished' - - if session_state == 'deploying' and reports_count > 1: - status = 'pending' - - draft = get_draft(environment_id, session_id) - - if not 'services' in draft: - return 'pending' - - def get_statuses(type): - if type in draft['services']: - services = draft['services'][type] - return [get_service_status(environment_id, - session_id, - service) for service in services] - else: - return [] - - is_inprogress = filter(lambda item: item == 'inprogress', - get_statuses('activeDirectories') + - get_statuses('webServers')) - - if session_state == 'deploying' and is_inprogress > 1: - status = 'inprogress' - - return status - - -def get_service_status(environment_id, session_id, service): - status = 'draft' - - unit = get_session() - session_state = unit.query(Session).get(session_id).state - - entities = [u['id'] for u in service['units']] - reports_count = unit.query(Status).filter( - Status.environment_id == environment_id - and Status.session_id == session_id - and Status.entity_id.in_(entities) - ).count() - - if session_state == 'deployed': - status = 'finished' - - if session_state == 'deploying' and reports_count == 0: - status = 'pending' - - if session_state == 'deploying' and reports_count > 0: - status = 'inprogress' - - return status diff --git a/api/glazierapi/api/v1/active_directories.py b/api/glazierapi/api/v1/active_directories.py deleted file mode 100644 index 12416b1c..00000000 --- a/api/glazierapi/api/v1/active_directories.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License.from oslo.config import cfg - -from glazierapi import utils -from glazierapi.api.v1 import save_draft, get_draft, get_service_status -from glazierapi.common import uuidutils -from glazierapi.openstack.common import wsgi, timeutils -from glazierapi.openstack.common import log as logging - -log = logging.getLogger(__name__) - - -class Controller(object): - def index(self, request, environment_id): - log.debug(_('ActiveDirectory:Index <EnvId: {0}>'. - format(environment_id))) - - draft = prepare_draft(get_draft(environment_id, - request.context.session)) - - for dc in draft['services']['activeDirectories']: - dc['status'] = get_service_status(environment_id, - request.context.session, - dc) - - return {'activeDirectories': draft['services']['activeDirectories']} - - @utils.verify_session - def create(self, request, environment_id, body): - log.debug(_('ActiveDirectory:Create <EnvId: {0}, Body: {1}>'. - format(environment_id, body))) - - draft = get_draft(session_id=request.context.session) - - active_directory = body.copy() - active_directory['id'] = uuidutils.generate_uuid() - active_directory['created'] = str(timeutils.utcnow()) - active_directory['updated'] = str(timeutils.utcnow()) - - unit_count = 0 - for unit in active_directory['units']: - unit_count += 1 - unit['id'] = uuidutils.generate_uuid() - unit['name'] = 'dc{0}'.format(unit_count) - - draft = prepare_draft(draft) - draft['services']['activeDirectories'].append(active_directory) - save_draft(request.context.session, draft) - - return active_directory - - def delete(self, request, environment_id, active_directory_id): - log.debug(_('ActiveDirectory:Delete <EnvId: {0}, Id: {1}>'. - format(environment_id, active_directory_id))) - - draft = get_draft(request.context.session) - items = [service for service in draft['services']['activeDirectories'] - if service['id'] != active_directory_id] - draft['services']['activeDirectories'] = items - save_draft(request.context.session, draft) - - -def prepare_draft(draft): - if not 'services' in draft: - draft['services'] = {} - - if not 'activeDirectories' in draft['services']: - draft['services']['activeDirectories'] = [] - - return draft - - -def create_resource(): - return wsgi.Resource(Controller()) diff --git a/api/glazierapi/api/v1/environments.py b/api/glazierapi/api/v1/environments.py deleted file mode 100644 index 5bb67022..00000000 --- a/api/glazierapi/api/v1/environments.py +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License.from oslo.config import cfg - -from amqplib.client_0_8 import Message -import anyjson -import eventlet -from webob import exc -from glazierapi.common import config -from glazierapi.api.v1 import get_env_status -from glazierapi.db.session import get_session -from glazierapi.db.models import Environment -from glazierapi.openstack.common import wsgi -from glazierapi.openstack.common import log as logging - -amqp = eventlet.patcher.import_patched('amqplib.client_0_8') -rabbitmq = config.CONF.rabbitmq - -log = logging.getLogger(__name__) - - -class Controller(object): - def index(self, request): - log.debug(_('Environments:List')) - - #Only environments from same tenant as users should be shown - filters = {'tenant_id': request.context.tenant} - - session = get_session() - environments = session.query(Environment).filter_by(**filters) - environments = [env.to_dict() for env in environments] - - for env in environments: - env['status'] = get_env_status(env['id'], request.context.session) - - return {"environments": environments} - - def create(self, request, body): - log.debug(_('Environments:Create <Body {0}>'.format(body))) - - #tagging environment by tenant_id for later checks - params = body.copy() - params['tenant_id'] = request.context.tenant - - environment = Environment() - environment.update(params) - - session = get_session() - with session.begin(): - session.add(environment) - - #saving environment as Json to itself - environment.update({"description": environment.to_dict()}) - environment.save(session) - - return environment.to_dict() - - def show(self, request, environment_id): - log.debug(_('Environments:Show <Id: {0}>'.format(environment_id))) - - session = get_session() - environment = session.query(Environment).get(environment_id) - - if environment.tenant_id != request.context.tenant: - log.info('User is not authorized to access this tenant resources.') - raise exc.HTTPUnauthorized - - env = environment.to_dict() - env['status'] = get_env_status(environment_id, request.context.session) - - return env - - def update(self, request, environment_id, body): - log.debug(_('Environments:Update <Id: {0}, Body: {1}>'. - format(environment_id, body))) - - session = get_session() - environment = session.query(Environment).get(environment_id) - - if environment.tenant_id != request.context.tenant: - log.info('User is not authorized to access this tenant resources.') - raise exc.HTTPUnauthorized - - environment.update(body) - environment.save(session) - - return environment.to_dict() - - def delete(self, request, environment_id): - log.debug(_('Environments:Delete <Id: {0}>'.format(environment_id))) - - session = get_session() - environment = session.query(Environment).get(environment_id) - - with session.begin(): - session.delete(environment) - - #preparing data for removal from conductor - env = environment.description - env['services'] = [] - env['deleted'] = True - #Set X-Auth-Token for conductor - env['token'] = request.context.auth_token - - connection = amqp.Connection('{0}:{1}'. - format(rabbitmq.host, rabbitmq.port), - virtual_host=rabbitmq.virtual_host, - userid=rabbitmq.login, - password=rabbitmq.password, - ssl=rabbitmq.use_ssl, insist=True) - channel = connection.channel() - channel.exchange_declare('tasks', 'direct', durable=True, - auto_delete=False) - - channel.basic_publish(Message(body=anyjson.serialize(env)), 'tasks', - 'tasks') - - return None - - -def create_resource(): - return wsgi.Resource(Controller()) diff --git a/api/glazierapi/api/v1/router.py b/api/glazierapi/api/v1/router.py deleted file mode 100644 index 998ee316..00000000 --- a/api/glazierapi/api/v1/router.py +++ /dev/null @@ -1,106 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License.from oslo.config import cfg - -import routes -from glazierapi.openstack.common import wsgi -from glazierapi.api.v1 import (environments, sessions, - active_directories, webservers) - - -class API(wsgi.Router): - @classmethod - def factory(cls, global_conf, **local_conf): - return cls(routes.Mapper()) - - def __init__(self, mapper): - environments_resource = environments.create_resource() - mapper.connect('/environments', - controller=environments_resource, - action='index', - conditions={'method': ['GET']}) - mapper.connect('/environments', - controller=environments_resource, - action='create', - conditions={'method': ['POST']}) - mapper.connect('/environments/{environment_id}', - controller=environments_resource, - action='update', - conditions={'method': ['PUT']}) - mapper.connect('/environments/{environment_id}', - controller=environments_resource, - action='show', - conditions={'method': ['GET']}) - mapper.connect('/environments/{environment_id}', - controller=environments_resource, - action='delete', - conditions={'method': ['DELETE']}) - - sessions_resource = sessions.create_resource() - mapper.connect('/environments/{environment_id}/sessions', - controller=sessions_resource, - action='index', - conditions={'method': ['GET']}) - mapper.connect('/environments/{environment_id}/configure', - controller=sessions_resource, - action='configure', - conditions={'method': ['POST']}) - mapper.connect('/environments/{environment_id}/sessions/{session_id}', - controller=sessions_resource, - action='show', - conditions={'method': ['GET']}) - mapper.connect('/environments/{environment_id}/sessions/{session_id}', - controller=sessions_resource, - action='delete', - conditions={'method': ['DELETE']}) - mapper.connect('/environments/{environment_id}/sessions/' - '{session_id}/reports', - controller=sessions_resource, - action='reports', - conditions={'method': ['GET']}) - mapper.connect('/environments/{environment_id}/sessions/' - '{session_id}/deploy', - controller=sessions_resource, - action='deploy', - conditions={'method': ['POST']}) - - activeDirectories_resource = active_directories.create_resource() - mapper.connect('/environments/{environment_id}/activeDirectories', - controller=activeDirectories_resource, - action='index', - conditions={'method': ['GET']}) - mapper.connect('/environments/{environment_id}/activeDirectories', - controller=activeDirectories_resource, - action='create', - conditions={'method': ['POST']}) - mapper.connect('/environments/{environment_id}/activeDirectories/' - '{active_directory_id}', - controller=activeDirectories_resource, - action='delete', - conditions={'method': ['DELETE']}) - - webServers_resource = webservers.create_resource() - mapper.connect('/environments/{environment_id}/webServers', - controller=webServers_resource, - action='index', - conditions={'method': ['GET']}) - mapper.connect('/environments/{environment_id}/webServers', - controller=webServers_resource, - action='create', - conditions={'method': ['POST']}) - mapper.connect('/environments/{environment_id}/webServers/' - '{web_server_id}', - controller=webServers_resource, - action='delete', - conditions={'method': ['DELETE']}) - super(API, self).__init__(mapper) diff --git a/api/glazierapi/api/v1/sessions.py b/api/glazierapi/api/v1/sessions.py deleted file mode 100644 index 15654461..00000000 --- a/api/glazierapi/api/v1/sessions.py +++ /dev/null @@ -1,168 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License.from oslo.config import cfg - -from amqplib.client_0_8 import Message -import anyjson -import eventlet -from webob import exc -from glazierapi.common import config -from glazierapi.db.models import Session, Status, Environment -from glazierapi.db.session import get_session -from glazierapi.openstack.common import wsgi -from glazierapi.openstack.common import log as logging - -amqp = eventlet.patcher.import_patched('amqplib.client_0_8') -rabbitmq = config.CONF.rabbitmq -log = logging.getLogger(__name__) - - -class Controller(object): - def index(self, request, environment_id): - log.debug(_('Session:List <EnvId: {0}>'.format(environment_id))) - - filters = {'environment_id': environment_id, - 'user_id': request.context.user} - - unit = get_session() - configuration_sessions = unit.query(Session).filter_by(**filters) - - sessions = [session.to_dict() for session in configuration_sessions if - session.environment.tenant_id == request.context.tenant] - return {"sessions": sessions} - - def configure(self, request, environment_id): - log.debug(_('Session:Configure <EnvId: {0}>'.format(environment_id))) - - params = {'environment_id': environment_id, - 'user_id': request.context.user, 'state': 'open'} - - session = Session() - session.update(params) - - unit = get_session() - if unit.query(Session).filter( - Session.environment_id == environment_id and - Session.state.in_(['open', 'deploying']) - ).first(): - log.info('There is already open session for this environment') - raise exc.HTTPConflict - - #create draft for apply later changes - environment = unit.query(Environment).get(environment_id) - session.description = environment.description - - with unit.begin(): - unit.add(session) - - return session.to_dict() - - def show(self, request, environment_id, session_id): - log.debug(_('Session:Show <EnvId: {0}, ' - 'SessionId: {1}>'.format(environment_id, session_id))) - - unit = get_session() - session = unit.query(Session).get(session_id) - - if session.environment.tenant_id != request.context.tenant: - log.info('User is not authorized to access this tenant resources.') - raise exc.HTTPUnauthorized - - return session.to_dict() - - def delete(self, request, environment_id, session_id): - log.debug(_('Session:Delete <EnvId: {0}, ' - 'SessionId: {1}>'.format(environment_id, session_id))) - - unit = get_session() - session = unit.query(Session).get(session_id) - - comment = 'Session object in \'deploying\' state could not be deleted' - if session.state == 'deploying': - log.info(comment) - raise exc.HTTPForbidden(comment=comment) - - with unit.begin(): - unit.delete(session) - - return None - - def reports(self, request, environment_id, session_id): - log.debug(_('Session:Reports <EnvId: {0}, ' - 'SessionId: {1}>'.format(environment_id, session_id))) - - unit = get_session() - statuses = unit.query(Status).filter_by(session_id=session_id).all() - result = statuses - - if 'service_id' in request.GET: - service_id = request.GET['service_id'] - - environment = unit.query(Session).get(session_id).description - services = [] - if 'services' in environment and 'activeDirectories' in\ - environment['services']: - services += environment['services']['activeDirectories'] - - if 'services' in environment and 'webServers' in\ - environment['services']: - services += environment['services']['webServers'] - - service = [service for service in services - if service['id'] == service_id][0] - - if service: - entities = [u['id'] for u in service['units']] - entities.append(service_id) - result = [] - for status in statuses: - if status.entity_id in entities: - result.append(status) - - return {'reports': [status.to_dict() for status in result]} - - def deploy(self, request, environment_id, session_id): - log.debug(_('Session:Deploy <EnvId: {0}, ' - 'SessionId: {1}>'.format(environment_id, session_id))) - - unit = get_session() - session = unit.query(Session).get(session_id) - - msg = _('Could not deploy session. Session is already ' - 'deployed or in deployment state') - if session.state != 'open': - log.warn(msg) - - session.state = 'deploying' - session.save(unit) - - #Set X-Auth-Token for conductor - env = session.description - env['token'] = request.context.auth_token - - connection = amqp.Connection('{0}:{1}'. - format(rabbitmq.host, rabbitmq.port), - virtual_host=rabbitmq.virtual_host, - userid=rabbitmq.login, - password=rabbitmq.password, - ssl=rabbitmq.use_ssl, insist=True) - channel = connection.channel() - channel.exchange_declare('tasks', 'direct', durable=True, - auto_delete=False) - - channel.basic_publish(Message(body=anyjson.serialize(env)), 'tasks', - 'tasks') - - -def create_resource(): - return wsgi.Resource(Controller()) diff --git a/api/glazierapi/api/v1/webservers.py b/api/glazierapi/api/v1/webservers.py deleted file mode 100644 index 876c9d2c..00000000 --- a/api/glazierapi/api/v1/webservers.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License.from oslo.config import cfg - -from glazierapi import utils -from glazierapi.api.v1 import save_draft, get_draft, get_service_status -from glazierapi.common import uuidutils -from glazierapi.openstack.common import wsgi, timeutils -from glazierapi.openstack.common import log as logging - -log = logging.getLogger(__name__) - - -class Controller(object): - def index(self, request, environment_id): - log.debug(_('WebServer:List <EnvId: {0}>'.format(environment_id))) - - draft = prepare_draft(get_draft(environment_id, - request.context.session)) - - for dc in draft['services']['webServers']: - dc['status'] = get_service_status(environment_id, - request.context.session, dc) - - return {'webServers': draft['services']['webServers']} - - @utils.verify_session - def create(self, request, environment_id, body): - log.debug(_('WebServer:Create <EnvId: {0}, Body: {1}>'. - format(environment_id, body))) - - draft = get_draft(session_id=request.context.session) - - webServer = body.copy() - webServer['id'] = uuidutils.generate_uuid() - webServer['created'] = str(timeutils.utcnow()) - webServer['updated'] = str(timeutils.utcnow()) - - unit_count = 0 - for unit in webServer['units']: - unit_count += 1 - unit['id'] = uuidutils.generate_uuid() - unit['name'] = webServer['name'] + '_instance_' + str(unit_count) - - draft = prepare_draft(draft) - draft['services']['webServers'].append(webServer) - save_draft(request.context.session, draft) - - return webServer - - @utils.verify_session - def delete(self, request, environment_id, web_server_id): - log.debug(_('WebServer:Delete <EnvId: {0}, Id: {1}>'. - format(environment_id, web_server_id))) - - draft = get_draft(session_id=request.context.session) - - elements = [service for service in draft['services']['webServers'] if - service['id'] != web_server_id] - draft['services']['webServers'] = elements - save_draft(request.context.session, draft) - - -def prepare_draft(draft): - if not 'services' in draft: - draft['services'] = {} - - if not 'webServers' in draft['services']: - draft['services']['webServers'] = [] - - return draft - - -def create_resource(): - return wsgi.Resource(Controller()) diff --git a/api/glazierapi/common/__init__.py b/api/glazierapi/common/__init__.py deleted file mode 100644 index fc46f490..00000000 --- a/api/glazierapi/common/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License.from oslo.config import cfg - diff --git a/api/glazierapi/common/config.py b/api/glazierapi/common/config.py deleted file mode 100644 index a5bfd854..00000000 --- a/api/glazierapi/common/config.py +++ /dev/null @@ -1,205 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Routines for configuring Glance -""" - -import logging -import logging.config -import logging.handlers -import os -import sys - -from oslo.config import cfg -from paste import deploy - -from glazierapi.version import version_info as version - -paste_deploy_opts = [ - cfg.StrOpt('flavor'), - cfg.StrOpt('config_file'), -] - -bind_opts = [ - cfg.StrOpt('bind-host', default='0.0.0.0'), - cfg.IntOpt('bind-port', default='8082'), -] - -reports_opts = [ - cfg.StrOpt('results_exchange', default='task-results'), - cfg.StrOpt('results_queue', default='task-results'), - cfg.StrOpt('reports_exchange', default='task-reports'), - cfg.StrOpt('reports_queue', default='task-reports') -] - -rabbit_opts = [ - cfg.StrOpt('host', default='localhost'), - cfg.IntOpt('port', default=5672), - cfg.BoolOpt('use_ssl', default=False), - cfg.StrOpt('login', default='guest'), - cfg.StrOpt('password', default='guest'), - cfg.StrOpt('virtual_host', default='/'), -] - -CONF = cfg.CONF -CONF.register_opts(paste_deploy_opts, group='paste_deploy') -CONF.register_cli_opts(bind_opts) -# CONF.register_opts(bind_opts) -CONF.register_opts(reports_opts, group='reports') -CONF.register_opts(rabbit_opts, group='rabbitmq') - - -CONF.import_opt('verbose', 'glazierapi.openstack.common.log') -CONF.import_opt('debug', 'glazierapi.openstack.common.log') -CONF.import_opt('log_dir', 'glazierapi.openstack.common.log') -CONF.import_opt('log_file', 'glazierapi.openstack.common.log') -CONF.import_opt('log_config', 'glazierapi.openstack.common.log') -CONF.import_opt('log_format', 'glazierapi.openstack.common.log') -CONF.import_opt('log_date_format', 'glazierapi.openstack.common.log') -CONF.import_opt('use_syslog', 'glazierapi.openstack.common.log') -CONF.import_opt('syslog_log_facility', 'glazierapi.openstack.common.log') - - -def parse_args(args=None, usage=None, default_config_files=None): - CONF(args=args, - project='glazierapi', - version=version.cached_version_string(), - usage=usage, - default_config_files=default_config_files) - - -def setup_logging(): - """ - Sets up the logging options for a log with supplied name - """ - - if CONF.log_config: - # Use a logging configuration file for all settings... - if os.path.exists(CONF.log_config): - logging.config.fileConfig(CONF.log_config) - return - else: - raise RuntimeError("Unable to locate specified logging " - "config file: %s" % CONF.log_config) - - root_logger = logging.root - if CONF.debug: - root_logger.setLevel(logging.DEBUG) - elif CONF.verbose: - root_logger.setLevel(logging.INFO) - else: - root_logger.setLevel(logging.WARNING) - - formatter = logging.Formatter(CONF.log_format, CONF.log_date_format) - - if CONF.use_syslog: - try: - facility = getattr(logging.handlers.SysLogHandler, - CONF.syslog_log_facility) - except AttributeError: - raise ValueError(_("Invalid syslog facility")) - - handler = logging.handlers.SysLogHandler(address='/dev/log', - facility=facility) - elif CONF.log_file: - logfile = CONF.log_file - if CONF.log_dir: - logfile = os.path.join(CONF.log_dir, logfile) - handler = logging.handlers.WatchedFileHandler(logfile) - else: - handler = logging.StreamHandler(sys.stdout) - - handler.setFormatter(formatter) - root_logger.addHandler(handler) - - -def _get_deployment_flavor(): - """ - Retrieve the paste_deploy.flavor config item, formatted appropriately - for appending to the application name. - """ - flavor = CONF.paste_deploy.flavor - return '' if not flavor else ('-' + flavor) - - -def _get_paste_config_path(): - paste_suffix = '-paste.ini' - conf_suffix = '.conf' - if CONF.config_file: - # Assume paste config is in a paste.ini file corresponding - # to the last config file - path = CONF.config_file[-1].replace(conf_suffix, paste_suffix) - else: - path = CONF.prog + '-paste.ini' - return CONF.find_file(os.path.basename(path)) - - -def _get_deployment_config_file(): - """ - Retrieve the deployment_config_file config item, formatted as an - absolute pathname. - """ - path = CONF.paste_deploy.config_file - if not path: - path = _get_paste_config_path() - if not path: - msg = "Unable to locate paste config file for %s." % CONF.prog - raise RuntimeError(msg) - return os.path.abspath(path) - - -def load_paste_app(app_name=None): - """ - Builds and returns a WSGI app from a paste config file. - - We assume the last config file specified in the supplied ConfigOpts - object is the paste config file. - - :param app_name: name of the application to load - - :raises RuntimeError when config file cannot be located or application - cannot be loaded from config file - """ - if app_name is None: - app_name = CONF.prog - - # append the deployment flavor to the application name, - # in order to identify the appropriate paste pipeline - app_name += _get_deployment_flavor() - - conf_file = _get_deployment_config_file() - - try: - logger = logging.getLogger(__name__) - logger.debug(_("Loading %(app_name)s from %(conf_file)s"), - {'conf_file': conf_file, 'app_name': app_name}) - - app = deploy.loadapp("config:%s" % conf_file, name=app_name) - - # Log the options used when starting if we're in debug mode... - if CONF.debug: - CONF.log_opt_values(logger, logging.DEBUG) - - return app - except (LookupError, ImportError), e: - msg = _("Unable to load %(app_name)s from " - "configuration file %(conf_file)s." - "\nGot: %(e)r") % locals() - logger.error(msg) - raise RuntimeError(msg) diff --git a/api/glazierapi/common/service.py b/api/glazierapi/common/service.py deleted file mode 100644 index 8b2d7342..00000000 --- a/api/glazierapi/common/service.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License.from oslo.config import cfg - -import anyjson -from eventlet import patcher -from glazierapi.db.models import Status, Session, Environment -from glazierapi.db.session import get_session - -amqp = patcher.import_patched('amqplib.client_0_8') - -from glazierapi.openstack.common import service -from glazierapi.openstack.common import log as logging -from glazierapi.common import config - -conf = config.CONF.reports -rabbitmq = config.CONF.rabbitmq -log = logging.getLogger(__name__) -channel = None - - -class TaskResultHandlerService(service.Service): - def __init__(self, threads=1000): - super(TaskResultHandlerService, self).__init__(threads) - - def start(self): - super(TaskResultHandlerService, self).start() - self.tg.add_thread(self._handle_results) - - def stop(self): - super(TaskResultHandlerService, self).stop() - - def _handle_results(self): - connection = amqp.Connection('{0}:{1}'. - format(rabbitmq.host, rabbitmq.port), - virtual_host=rabbitmq.virtual_host, - userid=rabbitmq.login, - password=rabbitmq.password, - ssl=rabbitmq.use_ssl, insist=True) - ch = connection.channel() - - def bind(exchange, queue): - if not exchange: - ch.exchange_declare(exchange, 'direct', durable=True, - auto_delete=False) - ch.queue_declare(queue, durable=True, auto_delete=False) - if not exchange: - ch.queue_bind(queue, exchange, queue) - - bind(conf.results_exchange, conf.results_queue) - bind(conf.reports_exchange, conf.reports_queue) - - ch.basic_consume(conf.results_exchange, callback=handle_result) - ch.basic_consume(conf.reports_exchange, callback=handle_report, - no_ack=True) - while ch.callbacks: - ch.wait() - - -def handle_report(msg): - log.debug(_('Got report message from orchestration engine:\n{0}'. - format(msg.body))) - - params = anyjson.deserialize(msg.body) - params['entity_id'] = params['id'] - del params['id'] - - status = Status() - status.update(params) - - session = get_session() - #connect with session - conf_session = session.query(Session).filter_by( - **{'environment_id': status.environment_id, - 'state': 'deploying'}).first() - status.session_id = conf_session.id - - with session.begin(): - session.add(status) - - -def handle_result(msg): - log.debug(_('Got result message from ' - 'orchestration engine:\n{0}'.format(msg.body))) - - environment_result = anyjson.deserialize(msg.body) - if 'deleted' in environment_result: - log.debug(_('Result for environment {0} is dropped. ' - 'Environment is deleted'.format(environment_result['id']))) - - msg.channel.basic_ack(msg.delivery_tag) - return - - session = get_session() - environment = session.query(Environment).get(environment_result['id']) - - environment.description = environment_result - environment.save(session) - - #close session - conf_session = session.query(Session).filter_by( - **{'environment_id': environment.id, 'state': 'deploying'}).first() - conf_session.state = 'deployed' - conf_session.save(session) - - msg.channel.basic_ack(msg.delivery_tag) diff --git a/api/glazierapi/common/uuidutils.py b/api/glazierapi/common/uuidutils.py deleted file mode 100644 index 86b85193..00000000 --- a/api/glazierapi/common/uuidutils.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License.from oslo.config import cfg - -import uuid - - -def generate_uuid(): - return str(uuid.uuid4()).replace('-', '') diff --git a/api/glazierapi/context.py b/api/glazierapi/context.py deleted file mode 100644 index 2bea96f5..00000000 --- a/api/glazierapi/context.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from glazierapi.openstack.common import uuidutils - - -class RequestContext(object): - """ - Stores information about the security context under which the user - accesses the system, as well as additional request information. - """ - - def __init__(self, auth_token=None, user=None, tenant=None, - roles=None, service_catalog=None, session=None): - - self.auth_token = auth_token - self.user = user - self.tenant = tenant - self.roles = roles or [] - self.request_id = uuidutils.generate_uuid() - self.service_catalog = service_catalog - self.session = session - - def to_dict(self): - # NOTE(ameade): These keys are named to correspond with the default - # format string for logging the context in openstack common - return { - 'request_id': self.request_id, - - #NOTE(bcwaldon): openstack-common logging expects 'user' - 'user': self.user, - 'user_id': self.user, - - #NOTE(bcwaldon): openstack-common logging expects 'tenant' - 'tenant': self.tenant, - 'tenant_id': self.tenant, - 'project_id': self.tenant, - - 'roles': self.roles, - 'auth_token': self.auth_token, - 'session': self.session - } - - @classmethod - def from_dict(cls, values): - return cls(**values) diff --git a/api/glazierapi/db/__init__.py b/api/glazierapi/db/__init__.py deleted file mode 100644 index 374c1328..00000000 --- a/api/glazierapi/db/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License.from oslo.config import cfg - -from oslo.config import cfg - -sql_connection_opt = cfg.StrOpt('sql_connection', - default='sqlite:///glazierapi.sqlite', - secret=True, - metavar='CONNECTION', - help='A valid SQLAlchemy connection ' - 'string for the metadata database. ' - 'Default: %(default)s') - -CONF = cfg.CONF -CONF.register_opt(sql_connection_opt) diff --git a/api/glazierapi/db/migrate_repo/README b/api/glazierapi/db/migrate_repo/README deleted file mode 100644 index 6218f8ca..00000000 --- a/api/glazierapi/db/migrate_repo/README +++ /dev/null @@ -1,4 +0,0 @@ -This is a database migration repository. - -More information at -http://code.google.com/p/sqlalchemy-migrate/ diff --git a/api/glazierapi/db/migrate_repo/__init__.py b/api/glazierapi/db/migrate_repo/__init__.py deleted file mode 100644 index 2f288d3c..00000000 --- a/api/glazierapi/db/migrate_repo/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# template repository default module diff --git a/api/glazierapi/db/migrate_repo/manage.py b/api/glazierapi/db/migrate_repo/manage.py deleted file mode 100644 index cc59d86b..00000000 --- a/api/glazierapi/db/migrate_repo/manage.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License.from oslo.config import cfg - - -from migrate.versioning.shell import main - -# This should probably be a console script entry point. -if __name__ == '__main__': - main(debug='False', repository='.') diff --git a/api/glazierapi/db/migrate_repo/migrate.cfg b/api/glazierapi/db/migrate_repo/migrate.cfg deleted file mode 100644 index 0f60e5a1..00000000 --- a/api/glazierapi/db/migrate_repo/migrate.cfg +++ /dev/null @@ -1,20 +0,0 @@ -[db_settings] -# Used to identify which repository this database is versioned under. -# You can use the name of your project. -repository_id=Glazier Migrations - -# The name of the database table used to track the schema version. -# This name shouldn't already be used by your project. -# If this is changed once a database is under version control, you'll need to -# change the table name in each database too. -version_table=migrate_version - -# When committing a change script, Migrate will attempt to generate the -# sql for all supported databases; normally, if one of them fails - probably -# because you don't have that database installed - it is ignored and the -# commit continues, perhaps ending successfully. -# Databases in this list MUST compile successfully during a commit, or the -# entire commit will fail. List the databases your application will actually -# be using to ensure your updates to that database work properly. -# This must be a list; example: ['glazier','sqlite'] -required_dbs=[] diff --git a/api/glazierapi/db/migrate_repo/versions/001_add_initial_tables.py b/api/glazierapi/db/migrate_repo/versions/001_add_initial_tables.py deleted file mode 100644 index 8f74d10c..00000000 --- a/api/glazierapi/db/migrate_repo/versions/001_add_initial_tables.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License.from oslo.config import cfg - -from sqlalchemy.schema import MetaData, Table, Column, ForeignKey -from sqlalchemy.types import String, Text, DateTime - - -meta = MetaData() - -Table('environment', meta, - Column('id', String(32), primary_key=True), - Column('name', String(255), nullable=False), - Column('created', DateTime(), nullable=False), - Column('updated', DateTime(), nullable=False), - Column('tenant_id', String(32), nullable=False), - Column('description', Text(), nullable=False), - ) - -Table('service', meta, - Column('id', String(32), primary_key=True), - Column('name', String(255), nullable=False), - Column('type', String(40), nullable=False), - Column('environment_id', String(32), ForeignKey('environment.id')), - Column('created', DateTime, nullable=False), - Column('updated', DateTime, nullable=False), - Column('description', Text(), nullable=False), - ) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - meta.create_all() - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - meta.drop_all() diff --git a/api/glazierapi/db/migrate_repo/versions/002_add_session_table.py b/api/glazierapi/db/migrate_repo/versions/002_add_session_table.py deleted file mode 100644 index cb794938..00000000 --- a/api/glazierapi/db/migrate_repo/versions/002_add_session_table.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License.from oslo.config import cfg - -from sqlalchemy.schema import MetaData, Table, Column, ForeignKey -from sqlalchemy.types import String, Text, DateTime - -meta = MetaData() - -session = Table('session', meta, - Column('id', String(32), primary_key=True), - Column('environment_id', String(32), - ForeignKey('environment.id')), - Column('created', DateTime, nullable=False), - Column('updated', DateTime, nullable=False), - Column('user_id', String(32), nullable=False), - Column('state', Text(), nullable=False), - ) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - meta.reflect() - session.create() - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - session.drop() diff --git a/api/glazierapi/db/migrate_repo/versions/003_add_status_table.py b/api/glazierapi/db/migrate_repo/versions/003_add_status_table.py deleted file mode 100644 index 135c0bcd..00000000 --- a/api/glazierapi/db/migrate_repo/versions/003_add_status_table.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License.from oslo.config import cfg - -from sqlalchemy.schema import MetaData, Table, Column, ForeignKey -from sqlalchemy.types import String, Text, DateTime - -meta = MetaData() - -status = Table('status', meta, - Column('id', String(32), primary_key=True), - Column('created', DateTime, nullable=False), - Column('updated', DateTime, nullable=False), - Column('entity', String(10), nullable=False), - Column('environment_id', String(32), - ForeignKey('environment.id')), - Column('session_id', String(32), ForeignKey('session.id')), - Column('text', Text(), nullable=False), - ) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - meta.reflect() - status.create() - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - status.drop() diff --git a/api/glazierapi/db/migrate_repo/versions/004_add_description_column_to_session.py b/api/glazierapi/db/migrate_repo/versions/004_add_description_column_to_session.py deleted file mode 100644 index 27ff7c7d..00000000 --- a/api/glazierapi/db/migrate_repo/versions/004_add_description_column_to_session.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License.from oslo.config import cfg - -from sqlalchemy.schema import MetaData, Table, Column -from sqlalchemy.types import Text - -meta = MetaData() - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - session = Table('session', meta, autoload=True) - description = Column('description', Text(), nullable=True, default='{}') - description.create(session) - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - session = Table('session', meta, autoload=True) - session.c.description.drop() diff --git a/api/glazierapi/db/migrate_repo/versions/005_remove_obsolete_service_table.py b/api/glazierapi/db/migrate_repo/versions/005_remove_obsolete_service_table.py deleted file mode 100644 index 57be93da..00000000 --- a/api/glazierapi/db/migrate_repo/versions/005_remove_obsolete_service_table.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License.from oslo.config import cfg - -from sqlalchemy.schema import MetaData, Table, Column, ForeignKey -from sqlalchemy.types import String, Text, DateTime - - -meta = MetaData() - -service = Table('service', meta, - Column('id', String(32), primary_key=True), - Column('name', String(255), nullable=False), - Column('type', String(40), nullable=False), - Column('environment_id', String(32), - ForeignKey('environment.id')), - Column('created', DateTime, nullable=False), - Column('updated', DateTime, nullable=False), - Column('description', Text(), nullable=False), - ) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - meta.reflect() - service.drop() - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - meta.reflect() - service.create() diff --git a/api/glazierapi/db/migrate_repo/versions/006_add_entity_id_column_to_status.py b/api/glazierapi/db/migrate_repo/versions/006_add_entity_id_column_to_status.py deleted file mode 100644 index 2e938d1c..00000000 --- a/api/glazierapi/db/migrate_repo/versions/006_add_entity_id_column_to_status.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License.from oslo.config import cfg - -from sqlalchemy.schema import MetaData, Table, Column -from sqlalchemy.types import String - -meta = MetaData() - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - status = Table('status', meta, autoload=True) - entity_id = Column('entity_id', String(32), nullable=True) - entity_id.create(status) - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - status = Table('status', meta, autoload=True) - status.c.entity_id.drop() diff --git a/api/glazierapi/db/migrate_repo/versions/__init__.py b/api/glazierapi/db/migrate_repo/versions/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/api/glazierapi/db/models.py b/api/glazierapi/db/models.py deleted file mode 100644 index ac373d23..00000000 --- a/api/glazierapi/db/models.py +++ /dev/null @@ -1,155 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -SQLAlchemy models for glazierapi data -""" -import anyjson - -from sqlalchemy import Column, String, BigInteger, TypeDecorator, ForeignKey -from sqlalchemy.ext.compiler import compiles -from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy import DateTime, Text -from sqlalchemy.orm import relationship, backref, object_mapper -from glazierapi.common import uuidutils - -from glazierapi.openstack.common import timeutils -from glazierapi.db.session import get_session - -BASE = declarative_base() - - -@compiles(BigInteger, 'sqlite') -def compile_big_int_sqlite(type_, compiler, **kw): - return 'INTEGER' - - -class ModelBase(object): - __protected_attributes__ = {"created", "updated"} - - created = Column(DateTime, default=timeutils.utcnow, - nullable=False) - updated = Column(DateTime, default=timeutils.utcnow, - nullable=False, onupdate=timeutils.utcnow) - - def save(self, session=None): - """Save this object""" - session = session or get_session() - session.add(self) - session.flush() - - def update(self, values): - """dict.update() behaviour.""" - for k, v in values.iteritems(): - self[k] = v - - def __setitem__(self, key, value): - setattr(self, key, value) - - def __getitem__(self, key): - return getattr(self, key) - - def __iter__(self): - self._i = iter(object_mapper(self).columns) - return self - - def next(self): - n = self._i.next().name - return n, getattr(self, n) - - def keys(self): - return self.__dict__.keys() - - def values(self): - return self.__dict__.values() - - def items(self): - return self.__dict__.items() - - def to_dict(self): - dictionary = self.__dict__.copy() - return {k: v for k, v in dictionary.iteritems() - if k != '_sa_instance_state'} - - -class JsonBlob(TypeDecorator): - impl = Text - - def process_bind_param(self, value, dialect): - return anyjson.serialize(value) - - def process_result_value(self, value, dialect): - return anyjson.deserialize(value) - - -class Environment(BASE, ModelBase): - """Represents a Environment in the metadata-store""" - __tablename__ = 'environment' - - id = Column(String(32), primary_key=True, default=uuidutils.generate_uuid) - name = Column(String(255), nullable=False) - tenant_id = Column(String(32), nullable=False) - description = Column(JsonBlob(), nullable=False, default='{}') - - def to_dict(self): - dictionary = super(Environment, self).to_dict() - del dictionary['description'] - return dictionary - - -class Session(BASE, ModelBase): - __tablename__ = 'session' - - id = Column(String(32), primary_key=True, default=uuidutils.generate_uuid) - environment_id = Column(String(32), ForeignKey('environment.id')) - environment = relationship(Environment, - backref=backref('session'), - uselist=False, lazy='joined') - user_id = Column(String(36), nullable=False) - state = Column(String(36), nullable=False) - description = Column(JsonBlob(), nullable=False) - - def to_dict(self): - dictionary = super(Session, self).to_dict() - del dictionary['description'] - return dictionary - - -class Status(BASE, ModelBase): - __tablename__ = 'status' - - id = Column(String(32), primary_key=True, default=uuidutils.generate_uuid) - entity_id = Column(String(32), nullable=False) - entity = Column(String(10), nullable=False) - environment_id = Column(String(32), ForeignKey('environment.id')) - session_id = Column(String(32), ForeignKey('session.id')) - text = Column(Text(), nullable=False) - - -def register_models(engine): - """ - Creates database tables for all models with the given engine - """ - models = (Environment, Status, Session) - for model in models: - model.metadata.create_all(engine) - - -def unregister_models(engine): - """ - Drops database tables for all models with the given engine - """ - models = (Environment, Status, Session) - for model in models: - model.metadata.drop_all(engine) diff --git a/api/glazierapi/db/session.py b/api/glazierapi/db/session.py deleted file mode 100644 index 2d94b973..00000000 --- a/api/glazierapi/db/session.py +++ /dev/null @@ -1,110 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# Copyright 2011 Piston Cloud Computing, Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -"""Session management functions.""" - -import os -import logging - -from migrate.versioning import api as versioning_api -from migrate import exceptions as versioning_exceptions -from sqlalchemy import create_engine -from sqlalchemy.engine.url import make_url -from sqlalchemy.orm import sessionmaker -from sqlalchemy.pool import NullPool -from sqlalchemy.exc import DisconnectionError - -from glazierapi.common.config import CONF as conf - -from glazierapi.db import migrate_repo - - -MAKER = None -ENGINE = None - - -class MySQLPingListener(object): - """ - Ensures that MySQL connections checked out of the - pool are alive. - - Borrowed from: - http://groups.google.com/group/sqlalchemy/msg/a4ce563d802c929f - - Error codes caught: - * 2006 MySQL server has gone away - * 2013 Lost connection to MySQL server during query - * 2014 Commands out of sync; you can't run this command now - * 2045 Can't open shared memory; no answer from server (%lu) - * 2055 Lost connection to MySQL server at '%s', system error: %d - - from http://dev.mysql.com/doc/refman/5.6/ru_RU/error-messages-client.html - """ - - def checkout(self, dbapi_con, con_record, con_proxy): - try: - dbapi_con.cursor().execute('select 1') - except dbapi_con.OperationalError, ex: - if ex.args[0] in (2006, 2013, 2014, 2045, 2055): - logging.warn('Got mysql server has gone away: %s', ex) - raise DisconnectionError("Database server went away") - else: - raise - - -def get_session(autocommit=True, expire_on_commit=False): - """Return a SQLAlchemy session.""" - global MAKER - - if MAKER is None: - MAKER = sessionmaker(autocommit=autocommit, - expire_on_commit=expire_on_commit) - engine = get_engine() - MAKER.configure(bind=engine) - session = MAKER() - return session - - -def get_engine(): - """Return a SQLAlchemy engine.""" - global ENGINE - - connection_url = make_url(conf.sql_connection) - if ENGINE is None or not ENGINE.url == connection_url: - engine_args = {'pool_recycle': 3600, - 'echo': False, - 'convert_unicode': True - } - if 'sqlite' in connection_url.drivername: - engine_args['poolclass'] = NullPool - if 'mysql' in connection_url.drivername: - engine_args['listeners'] = [MySQLPingListener()] - ENGINE = create_engine(conf.sql_connection, **engine_args) - - sync() - return ENGINE - - -def sync(): - repo_path = os.path.abspath(os.path.dirname(migrate_repo.__file__)) - try: - versioning_api.upgrade(conf.sql_connection, repo_path) - except versioning_exceptions.DatabaseNotControlledError: - versioning_api.version_control(conf.sql_connection, repo_path) - versioning_api.upgrade(conf.sql_connection, repo_path) diff --git a/api/glazierapi/locale/ru/LC_MESSAGES/glazierapi.po b/api/glazierapi/locale/ru/LC_MESSAGES/glazierapi.po deleted file mode 100644 index 4ff98950..00000000 --- a/api/glazierapi/locale/ru/LC_MESSAGES/glazierapi.po +++ /dev/null @@ -1,191 +0,0 @@ -# Russian translations for PROJECT. -# Copyright (C) 2012 ORGANIZATION -# This file is distributed under the same license as the PROJECT project. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: PROJECT VERSION\n" -"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2013-03-01 17:33+0400\n" -"PO-Revision-Date: 2012-04-26 17:40+0800\n" -"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" -"Language-Team: ru <LL@li.org>\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " -"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.6\n" - -#: portas/exefile.py:2 -msgid "Hello" -msgstr "Привет!" - -#: portas/openstack/common/exception.py:104 -msgid "Uncaught exception" -msgstr "" - -#: portas/openstack/common/jsonutils.py:88 -#, python-format -msgid "Max serialization depth exceeded on object: %d %s" -msgstr "" - -#: portas/openstack/common/log.py:225 -#, python-format -msgid "Deprecated: %s" -msgstr "" - -#: portas/openstack/common/log.py:358 -#, python-format -msgid "syslog facility must be one of: %s" -msgstr "" - -#: portas/openstack/common/log.py:518 -#, python-format -msgid "Fatal call to deprecated config: %(msg)s" -msgstr "" - -#: portas/openstack/common/service.py:112 -#: portas/openstack/common/service.py:275 -msgid "Full set of CONF:" -msgstr "" - -#: portas/openstack/common/service.py:121 -#: portas/openstack/common/service.py:218 -#, python-format -msgid "Caught %s, exiting" -msgstr "" - -#: portas/openstack/common/service.py:164 -msgid "Parent process has died unexpectedly, exiting" -msgstr "" - -#: portas/openstack/common/service.py:200 -msgid "Forking too fast, sleeping" -msgstr "" - -#: portas/openstack/common/service.py:223 -msgid "Unhandled exception" -msgstr "" - -#: portas/openstack/common/service.py:230 -#, python-format -msgid "Started child %d" -msgstr "" - -#: portas/openstack/common/service.py:240 -#, python-format -msgid "Starting %d workers" -msgstr "" - -#: portas/openstack/common/service.py:257 -#, python-format -msgid "Child %(pid)d killed by signal %(sig)d" -msgstr "" - -#: portas/openstack/common/service.py:261 -#, python-format -msgid "Child %(pid)s exited with status %(code)d" -msgstr "" - -#: portas/openstack/common/service.py:265 -#, python-format -msgid "pid %d not in child list" -msgstr "" - -#: portas/openstack/common/service.py:293 -#, python-format -msgid "Caught %s, stopping children" -msgstr "" - -#: portas/openstack/common/service.py:304 -#, python-format -msgid "Waiting on %d children to exit" -msgstr "" - -#: portas/openstack/common/sslutils.py:52 -#, python-format -msgid "Unable to find cert_file : %s" -msgstr "" - -#: portas/openstack/common/sslutils.py:55 -#, python-format -msgid "Unable to find ca_file : %s" -msgstr "" - -#: portas/openstack/common/sslutils.py:58 -#, python-format -msgid "Unable to find key_file : %s" -msgstr "" - -#: portas/openstack/common/sslutils.py:61 -msgid "" -"When running server in SSL mode, you must specify both a cert_file and " -"key_file option value in your configuration file" -msgstr "" - -#: portas/openstack/common/wsgi.py:110 -#, python-format -msgid "Could not bind to %(host)s:%(port)s after trying for 30 seconds" -msgstr "" - -#: portas/openstack/common/wsgi.py:372 -msgid "Unsupported Content-Type" -msgstr "" - -#: portas/openstack/common/wsgi.py:375 -msgid "Malformed request body" -msgstr "" - -#: portas/openstack/common/wsgi.py:660 -msgid "Empty body provided in request" -msgstr "" - -#: portas/openstack/common/wsgi.py:666 -msgid "Unrecognized Content-Type provided in request" -msgstr "" - -#: portas/openstack/common/wsgi.py:670 -msgid "No Content-Type provided in request" -msgstr "" - -#: portas/openstack/common/wsgi.py:676 -msgid "Unable to deserialize body as provided Content-Type" -msgstr "" - -#: portas/openstack/common/wsgi.py:726 -msgid "cannot understand JSON" -msgstr "" - -#: portas/openstack/common/wsgi.py:750 -msgid "cannot understand XML" -msgstr "" - -#: portas/openstack/common/notifier/api.py:126 -#, python-format -msgid "%s not in valid priorities" -msgstr "" - -#: portas/openstack/common/notifier/api.py:142 -#, python-format -msgid "" -"Problem '%(e)s' attempting to send to notification system. " -"Payload=%(payload)s" -msgstr "" - -#: portas/openstack/common/notifier/api.py:172 -#, python-format -msgid "Failed to load notifier %s. These notifications will not be sent." -msgstr "" - -#: portas/openstack/common/notifier/rabbit_notifier.py:27 -msgid "The rabbit_notifier is now deprecated. Please use rpc_notifier instead." -msgstr "" - -#: portas/openstack/common/notifier/rpc_notifier.py:45 -#: portas/openstack/common/notifier/rpc_notifier2.py:51 -#, python-format -msgid "Could not send notification to %(topic)s. Payload=%(message)s" -msgstr "" - diff --git a/api/glazierapi/openstack/__init__.py b/api/glazierapi/openstack/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/api/glazierapi/openstack/common/__init__.py b/api/glazierapi/openstack/common/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/api/glazierapi/openstack/common/eventlet_backdoor.py b/api/glazierapi/openstack/common/eventlet_backdoor.py deleted file mode 100644 index 8b81ebf8..00000000 --- a/api/glazierapi/openstack/common/eventlet_backdoor.py +++ /dev/null @@ -1,87 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (c) 2012 Openstack, LLC. -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import gc -import pprint -import sys -import traceback - -import eventlet -import eventlet.backdoor -import greenlet -from oslo.config import cfg - -eventlet_backdoor_opts = [ - cfg.IntOpt('backdoor_port', - default=None, - help='port for eventlet backdoor to listen') -] - -CONF = cfg.CONF -CONF.register_opts(eventlet_backdoor_opts) - - -def _dont_use_this(): - print "Don't use this, just disconnect instead" - - -def _find_objects(t): - return filter(lambda o: isinstance(o, t), gc.get_objects()) - - -def _print_greenthreads(): - for i, gt in enumerate(_find_objects(greenlet.greenlet)): - print i, gt - traceback.print_stack(gt.gr_frame) - print - - -def _print_nativethreads(): - for threadId, stack in sys._current_frames().items(): - print threadId - traceback.print_stack(stack) - print - - -def initialize_if_enabled(): - backdoor_locals = { - 'exit': _dont_use_this, # So we don't exit the entire process - 'quit': _dont_use_this, # So we don't exit the entire process - 'fo': _find_objects, - 'pgt': _print_greenthreads, - 'pnt': _print_nativethreads, - } - - if CONF.backdoor_port is None: - return None - - # NOTE(johannes): The standard sys.displayhook will print the value of - # the last expression and set it to __builtin__._, which overwrites - # the __builtin__._ that gettext sets. Let's switch to using pprint - # since it won't interact poorly with gettext, and it's easier to - # read the output too. - def displayhook(val): - if val is not None: - pprint.pprint(val) - sys.displayhook = displayhook - - sock = eventlet.listen(('localhost', CONF.backdoor_port)) - port = sock.getsockname()[1] - eventlet.spawn_n(eventlet.backdoor.backdoor_server, sock, - locals=backdoor_locals) - return port diff --git a/api/glazierapi/openstack/common/exception.py b/api/glazierapi/openstack/common/exception.py deleted file mode 100644 index 02dffa4d..00000000 --- a/api/glazierapi/openstack/common/exception.py +++ /dev/null @@ -1,142 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Exceptions common to OpenStack projects -""" - -import logging - -from glazierapi.openstack.common.gettextutils import _ - -_FATAL_EXCEPTION_FORMAT_ERRORS = False - - -class Error(Exception): - def __init__(self, message=None): - super(Error, self).__init__(message) - - -class ApiError(Error): - def __init__(self, message='Unknown', code='Unknown'): - self.message = message - self.code = code - super(ApiError, self).__init__('%s: %s' % (code, message)) - - -class NotFound(Error): - pass - - -class UnknownScheme(Error): - - msg = "Unknown scheme '%s' found in URI" - - def __init__(self, scheme): - msg = self.__class__.msg % scheme - super(UnknownScheme, self).__init__(msg) - - -class BadStoreUri(Error): - - msg = "The Store URI %s was malformed. Reason: %s" - - def __init__(self, uri, reason): - msg = self.__class__.msg % (uri, reason) - super(BadStoreUri, self).__init__(msg) - - -class Duplicate(Error): - pass - - -class NotAuthorized(Error): - pass - - -class NotEmpty(Error): - pass - - -class Invalid(Error): - pass - - -class BadInputError(Exception): - """Error resulting from a client sending bad input to a server""" - pass - - -class MissingArgumentError(Error): - pass - - -class DatabaseMigrationError(Error): - pass - - -class ClientConnectionError(Exception): - """Error resulting from a client connecting to a server""" - pass - - -def wrap_exception(f): - def _wrap(*args, **kw): - try: - return f(*args, **kw) - except Exception, e: - if not isinstance(e, Error): - #exc_type, exc_value, exc_traceback = sys.exc_info() - logging.exception(_('Uncaught exception')) - #logging.error(traceback.extract_stack(exc_traceback)) - raise Error(str(e)) - raise - _wrap.func_name = f.func_name - return _wrap - - -class OpenstackException(Exception): - """ - Base Exception - - To correctly use this class, inherit from it and define - a 'message' property. That message will get printf'd - with the keyword arguments provided to the constructor. - """ - message = "An unknown exception occurred" - - def __init__(self, **kwargs): - try: - self._error_string = self.message % kwargs - - except Exception as e: - if _FATAL_EXCEPTION_FORMAT_ERRORS: - raise e - else: - # at least get the core message out if something happened - self._error_string = self.message - - def __str__(self): - return self._error_string - - -class MalformedRequestBody(OpenstackException): - message = "Malformed message body: %(reason)s" - - -class InvalidContentType(OpenstackException): - message = "Invalid content type %(content_type)s" diff --git a/api/glazierapi/openstack/common/gettextutils.py b/api/glazierapi/openstack/common/gettextutils.py deleted file mode 100644 index fb1a1b7b..00000000 --- a/api/glazierapi/openstack/common/gettextutils.py +++ /dev/null @@ -1,33 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2012 Red Hat, Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -gettext for openstack-common modules. - -Usual usage in an openstack.common module: - - from glazierapi.openstack.common.gettextutils import _ -""" - -import gettext - - -t = gettext.translation('openstack-common', 'locale', fallback=True) - - -def _(msg): - return t.ugettext(msg) diff --git a/api/glazierapi/openstack/common/importutils.py b/api/glazierapi/openstack/common/importutils.py deleted file mode 100644 index 9dec764f..00000000 --- a/api/glazierapi/openstack/common/importutils.py +++ /dev/null @@ -1,67 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Import related utilities and helper functions. -""" - -import sys -import traceback - - -def import_class(import_str): - """Returns a class from a string including module and class""" - mod_str, _sep, class_str = import_str.rpartition('.') - try: - __import__(mod_str) - return getattr(sys.modules[mod_str], class_str) - except (ValueError, AttributeError): - raise ImportError('Class %s cannot be found (%s)' % - (class_str, - traceback.format_exception(*sys.exc_info()))) - - -def import_object(import_str, *args, **kwargs): - """Import a class and return an instance of it.""" - return import_class(import_str)(*args, **kwargs) - - -def import_object_ns(name_space, import_str, *args, **kwargs): - """ - Import a class and return an instance of it, first by trying - to find the class in a default namespace, then failing back to - a full path if not found in the default namespace. - """ - import_value = "%s.%s" % (name_space, import_str) - try: - return import_class(import_value)(*args, **kwargs) - except ImportError: - return import_class(import_str)(*args, **kwargs) - - -def import_module(import_str): - """Import a module.""" - __import__(import_str) - return sys.modules[import_str] - - -def try_import(import_str, default=None): - """Try to import a module and if it fails return default.""" - try: - return import_module(import_str) - except ImportError: - return default diff --git a/api/glazierapi/openstack/common/jsonutils.py b/api/glazierapi/openstack/common/jsonutils.py deleted file mode 100644 index 4ef48ea6..00000000 --- a/api/glazierapi/openstack/common/jsonutils.py +++ /dev/null @@ -1,147 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# Copyright 2011 Justin Santa Barbara -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -''' -JSON related utilities. - -This module provides a few things: - - 1) A handy function for getting an object down to something that can be - JSON serialized. See to_primitive(). - - 2) Wrappers around loads() and dumps(). The dumps() wrapper will - automatically use to_primitive() for you if needed. - - 3) This sets up anyjson to use the loads() and dumps() wrappers if anyjson - is available. -''' - - -import datetime -import functools -import inspect -import itertools -import json -import logging -import xmlrpclib - -from glazierapi.openstack.common.gettextutils import _ -from glazierapi.openstack.common import timeutils - -LOG = logging.getLogger(__name__) - - -def to_primitive(value, convert_instances=False, convert_datetime=True, - level=0, max_depth=3): - """Convert a complex object into primitives. - - Handy for JSON serialization. We can optionally handle instances, - but since this is a recursive function, we could have cyclical - data structures. - - To handle cyclical data structures we could track the actual objects - visited in a set, but not all objects are hashable. Instead we just - track the depth of the object inspections and don't go too deep. - - Therefore, convert_instances=True is lossy ... be aware. - - """ - nasty = [inspect.ismodule, inspect.isclass, inspect.ismethod, - inspect.isfunction, inspect.isgeneratorfunction, - inspect.isgenerator, inspect.istraceback, inspect.isframe, - inspect.iscode, inspect.isbuiltin, inspect.isroutine, - inspect.isabstract] - for test in nasty: - if test(value): - return unicode(value) - - # value of itertools.count doesn't get caught by inspects - # above and results in infinite loop when list(value) is called. - if type(value) == itertools.count: - return unicode(value) - - # FIXME(vish): Workaround for LP bug 852095. Without this workaround, - # tests that raise an exception in a mocked method that - # has a @wrap_exception with a notifier will fail. If - # we up the dependency to 0.5.4 (when it is released) we - # can remove this workaround. - if getattr(value, '__module__', None) == 'mox': - return 'mock' - - if level > max_depth: - LOG.error(_('Max serialization depth exceeded on object: %d %s'), - level, value) - return '?' - - # The try block may not be necessary after the class check above, - # but just in case ... - try: - recursive = functools.partial(to_primitive, - convert_instances=convert_instances, - convert_datetime=convert_datetime, - level=level, - max_depth=max_depth) - # It's not clear why xmlrpclib created their own DateTime type, but - # for our purposes, make it a datetime type which is explicitly - # handled - if isinstance(value, xmlrpclib.DateTime): - value = datetime.datetime(*tuple(value.timetuple())[:6]) - - if isinstance(value, (list, tuple)): - return [recursive(v) for v in value] - elif isinstance(value, dict): - return dict((k, recursive(v)) for k, v in value.iteritems()) - elif convert_datetime and isinstance(value, datetime.datetime): - return timeutils.strtime(value) - elif hasattr(value, 'iteritems'): - return recursive(dict(value.iteritems()), level=level + 1) - elif hasattr(value, '__iter__'): - return recursive(list(value)) - elif convert_instances and hasattr(value, '__dict__'): - # Likely an instance of something. Watch for cycles. - # Ignore class member vars. - return recursive(value.__dict__, level=level + 1) - else: - return value - except TypeError: - # Class objects are tricky since they may define something like - # __iter__ defined but it isn't callable as list(). - return unicode(value) - - -def dumps(value, default=to_primitive, **kwargs): - return json.dumps(value, default=default, **kwargs) - - -def loads(s): - return json.loads(s) - - -def load(s): - return json.load(s) - - -try: - import anyjson -except ImportError: - pass -else: - anyjson._modules.append((__name__, 'dumps', TypeError, - 'loads', ValueError, 'load')) - anyjson.force_implementation(__name__) diff --git a/api/glazierapi/openstack/common/local.py b/api/glazierapi/openstack/common/local.py deleted file mode 100644 index 8bdc837a..00000000 --- a/api/glazierapi/openstack/common/local.py +++ /dev/null @@ -1,48 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Greenthread local storage of variables using weak references""" - -import weakref - -from eventlet import corolocal - - -class WeakLocal(corolocal.local): - def __getattribute__(self, attr): - rval = corolocal.local.__getattribute__(self, attr) - if rval: - # NOTE(mikal): this bit is confusing. What is stored is a weak - # reference, not the value itself. We therefore need to lookup - # the weak reference and return the inner value here. - rval = rval() - return rval - - def __setattr__(self, attr, value): - value = weakref.ref(value) - return corolocal.local.__setattr__(self, attr, value) - - -# NOTE(mikal): the name "store" should be deprecated in the future -store = WeakLocal() - -# A "weak" store uses weak references and allows an object to fall out of scope -# when it falls out of scope in the code that uses the thread local storage. A -# "strong" store will hold a reference to the object so that it never falls out -# of scope. -weak_store = WeakLocal() -strong_store = corolocal.local diff --git a/api/glazierapi/openstack/common/log.py b/api/glazierapi/openstack/common/log.py deleted file mode 100644 index e8caddb9..00000000 --- a/api/glazierapi/openstack/common/log.py +++ /dev/null @@ -1,521 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Openstack logging handler. - -This module adds to logging functionality by adding the option to specify -a context object when calling the various log methods. If the context object -is not specified, default formatting is used. Additionally, an instance uuid -may be passed as part of the log message, which is intended to make it easier -for admins to find messages related to a specific instance. - -It also allows setting of formatting information through conf. - -""" - -import cStringIO -import inspect -import itertools -import logging -import logging.config -import logging.handlers -import os -import stat -import sys -import traceback - -from oslo.config import cfg - -from glazierapi.openstack.common.gettextutils import _ -from glazierapi.openstack.common import jsonutils -from glazierapi.openstack.common import local -from glazierapi.openstack.common import notifier - - -_DEFAULT_LOG_FORMAT = "%(asctime)s %(levelname)8s [%(name)s] %(message)s" -_DEFAULT_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S" - -common_cli_opts = [ - cfg.BoolOpt('debug', - short='d', - default=False, - help='Print debugging output (set logging level to ' - 'DEBUG instead of default WARNING level).'), - cfg.BoolOpt('verbose', - short='v', - default=False, - help='Print more verbose output (set logging level to ' - 'INFO instead of default WARNING level).'), -] - -logging_cli_opts = [ - cfg.StrOpt('log-config', - metavar='PATH', - help='If this option is specified, the logging configuration ' - 'file specified is used and overrides any other logging ' - 'options specified. Please see the Python logging module ' - 'documentation for details on logging configuration ' - 'files.'), - cfg.StrOpt('log-format', - default=_DEFAULT_LOG_FORMAT, - metavar='FORMAT', - help='A logging.Formatter log message format string which may ' - 'use any of the available logging.LogRecord attributes. ' - 'Default: %(default)s'), - cfg.StrOpt('log-date-format', - default=_DEFAULT_LOG_DATE_FORMAT, - metavar='DATE_FORMAT', - help='Format string for %%(asctime)s in log records. ' - 'Default: %(default)s'), - cfg.StrOpt('log-file', - metavar='PATH', - deprecated_name='logfile', - help='(Optional) Name of log file to output to. ' - 'If not set, logging will go to stdout.'), - cfg.StrOpt('log-dir', - deprecated_name='logdir', - help='(Optional) The directory to keep log files in ' - '(will be prepended to --log-file)'), - cfg.BoolOpt('use-syslog', - default=False, - help='Use syslog for logging.'), - cfg.StrOpt('syslog-log-facility', - default='LOG_USER', - help='syslog facility to receive log lines') -] - -generic_log_opts = [ - cfg.BoolOpt('use_stderr', - default=True, - help='Log output to standard error'), - cfg.StrOpt('logfile_mode', - default='0644', - help='Default file mode used when creating log files'), -] - -log_opts = [ - cfg.StrOpt('logging_context_format_string', - default='%(asctime)s.%(msecs)03d %(levelname)s %(name)s ' - '[%(request_id)s %(user)s %(tenant)s] %(instance)s' - '%(message)s', - help='format string to use for log messages with context'), - cfg.StrOpt('logging_default_format_string', - default='%(asctime)s.%(msecs)03d %(process)d %(levelname)s ' - '%(name)s [-] %(instance)s%(message)s', - help='format string to use for log messages without context'), - cfg.StrOpt('logging_debug_format_suffix', - default='%(funcName)s %(pathname)s:%(lineno)d', - help='data to append to log format when level is DEBUG'), - cfg.StrOpt('logging_exception_prefix', - default='%(asctime)s.%(msecs)03d %(process)d TRACE %(name)s ' - '%(instance)s', - help='prefix each line of exception output with this format'), - cfg.ListOpt('default_log_levels', - default=[ - 'amqplib=WARN', - 'sqlalchemy=WARN', - 'boto=WARN', - 'suds=INFO', - 'keystone=INFO', - 'eventlet.wsgi.server=WARN' - ], - help='list of logger=LEVEL pairs'), - cfg.BoolOpt('publish_errors', - default=False, - help='publish error events'), - cfg.BoolOpt('fatal_deprecations', - default=False, - help='make deprecations fatal'), - - # NOTE(mikal): there are two options here because sometimes we are handed - # a full instance (and could include more information), and other times we - # are just handed a UUID for the instance. - cfg.StrOpt('instance_format', - default='[instance: %(uuid)s] ', - help='If an instance is passed with the log message, format ' - 'it like this'), - cfg.StrOpt('instance_uuid_format', - default='[instance: %(uuid)s] ', - help='If an instance UUID is passed with the log message, ' - 'format it like this'), -] - -CONF = cfg.CONF -CONF.register_cli_opts(common_cli_opts) -CONF.register_cli_opts(logging_cli_opts) -CONF.register_opts(generic_log_opts) -CONF.register_opts(log_opts) - -# our new audit level -# NOTE(jkoelker) Since we synthesized an audit level, make the logging -# module aware of it so it acts like other levels. -logging.AUDIT = logging.INFO + 1 -logging.addLevelName(logging.AUDIT, 'AUDIT') - - -try: - NullHandler = logging.NullHandler -except AttributeError: # NOTE(jkoelker) NullHandler added in Python 2.7 - class NullHandler(logging.Handler): - def handle(self, record): - pass - - def emit(self, record): - pass - - def createLock(self): - self.lock = None - - -def _dictify_context(context): - if context is None: - return None - if not isinstance(context, dict) and getattr(context, 'to_dict', None): - context = context.to_dict() - return context - - -def _get_binary_name(): - return os.path.basename(inspect.stack()[-1][1]) - - -def _get_log_file_path(binary=None): - logfile = CONF.log_file - logdir = CONF.log_dir - - if logfile and not logdir: - return logfile - - if logfile and logdir: - return os.path.join(logdir, logfile) - - if logdir: - binary = binary or _get_binary_name() - return '%s.log' % (os.path.join(logdir, binary),) - - -class ContextAdapter(logging.LoggerAdapter): - warn = logging.LoggerAdapter.warning - - def __init__(self, logger, project_name, version_string): - self.logger = logger - self.project = project_name - self.version = version_string - - def audit(self, msg, *args, **kwargs): - self.log(logging.AUDIT, msg, *args, **kwargs) - - def deprecated(self, msg, *args, **kwargs): - stdmsg = _("Deprecated: %s") % msg - if CONF.fatal_deprecations: - self.critical(stdmsg, *args, **kwargs) - raise DeprecatedConfig(msg=stdmsg) - else: - self.warn(stdmsg, *args, **kwargs) - - def process(self, msg, kwargs): - if 'extra' not in kwargs: - kwargs['extra'] = {} - extra = kwargs['extra'] - - context = kwargs.pop('context', None) - if not context: - context = getattr(local.store, 'context', None) - if context: - extra.update(_dictify_context(context)) - - instance = kwargs.pop('instance', None) - instance_extra = '' - if instance: - instance_extra = CONF.instance_format % instance - else: - instance_uuid = kwargs.pop('instance_uuid', None) - if instance_uuid: - instance_extra = (CONF.instance_uuid_format - % {'uuid': instance_uuid}) - extra.update({'instance': instance_extra}) - - extra.update({"project": self.project}) - extra.update({"version": self.version}) - extra['extra'] = extra.copy() - return msg, kwargs - - -class JSONFormatter(logging.Formatter): - def __init__(self, fmt=None, datefmt=None): - # NOTE(jkoelker) we ignore the fmt argument, but its still there - # since logging.config.fileConfig passes it. - self.datefmt = datefmt - - def formatException(self, ei, strip_newlines=True): - lines = traceback.format_exception(*ei) - if strip_newlines: - lines = [itertools.ifilter( - lambda x: x, - line.rstrip().splitlines()) for line in lines] - lines = list(itertools.chain(*lines)) - return lines - - def format(self, record): - message = {'message': record.getMessage(), - 'asctime': self.formatTime(record, self.datefmt), - 'name': record.name, - 'msg': record.msg, - 'args': record.args, - 'levelname': record.levelname, - 'levelno': record.levelno, - 'pathname': record.pathname, - 'filename': record.filename, - 'module': record.module, - 'lineno': record.lineno, - 'funcname': record.funcName, - 'created': record.created, - 'msecs': record.msecs, - 'relative_created': record.relativeCreated, - 'thread': record.thread, - 'thread_name': record.threadName, - 'process_name': record.processName, - 'process': record.process, - 'traceback': None} - - if hasattr(record, 'extra'): - message['extra'] = record.extra - - if record.exc_info: - message['traceback'] = self.formatException(record.exc_info) - - return jsonutils.dumps(message) - - -class PublishErrorsHandler(logging.Handler): - def emit(self, record): - if ('glazierapi.openstack.common.notifier.log_notifier' in - CONF.notification_driver): - return - notifier.api.notify(None, 'error.publisher', - 'error_notification', - notifier.api.ERROR, - dict(error=record.msg)) - - -def _create_logging_excepthook(product_name): - def logging_excepthook(type, value, tb): - extra = {} - if CONF.verbose: - extra['exc_info'] = (type, value, tb) - getLogger(product_name).critical(str(value), **extra) - return logging_excepthook - - -def setup(product_name): - """Setup logging.""" - if CONF.log_config: - logging.config.fileConfig(CONF.log_config) - else: - _setup_logging_from_conf(product_name) - sys.excepthook = _create_logging_excepthook(product_name) - - -def set_defaults(logging_context_format_string): - cfg.set_defaults(log_opts, - logging_context_format_string= - logging_context_format_string) - - -def _find_facility_from_conf(): - facility_names = logging.handlers.SysLogHandler.facility_names - facility = getattr(logging.handlers.SysLogHandler, - CONF.syslog_log_facility, - None) - - if facility is None and CONF.syslog_log_facility in facility_names: - facility = facility_names.get(CONF.syslog_log_facility) - - if facility is None: - valid_facilities = facility_names.keys() - consts = ['LOG_AUTH', 'LOG_AUTHPRIV', 'LOG_CRON', 'LOG_DAEMON', - 'LOG_FTP', 'LOG_KERN', 'LOG_LPR', 'LOG_MAIL', 'LOG_NEWS', - 'LOG_AUTH', 'LOG_SYSLOG', 'LOG_USER', 'LOG_UUCP', - 'LOG_LOCAL0', 'LOG_LOCAL1', 'LOG_LOCAL2', 'LOG_LOCAL3', - 'LOG_LOCAL4', 'LOG_LOCAL5', 'LOG_LOCAL6', 'LOG_LOCAL7'] - valid_facilities.extend(consts) - raise TypeError(_('syslog facility must be one of: %s') % - ', '.join("'%s'" % fac - for fac in valid_facilities)) - - return facility - - -def _setup_logging_from_conf(product_name): - log_root = getLogger(product_name).logger - for handler in log_root.handlers: - log_root.removeHandler(handler) - - if CONF.use_syslog: - facility = _find_facility_from_conf() - syslog = logging.handlers.SysLogHandler(address='/dev/log', - facility=facility) - log_root.addHandler(syslog) - - logpath = _get_log_file_path() - if logpath: - filelog = logging.handlers.WatchedFileHandler(logpath) - log_root.addHandler(filelog) - - mode = int(CONF.logfile_mode, 8) - st = os.stat(logpath) - if st.st_mode != (stat.S_IFREG | mode): - os.chmod(logpath, mode) - - if CONF.use_stderr: - streamlog = ColorHandler() - log_root.addHandler(streamlog) - - elif not CONF.log_file: - # pass sys.stdout as a positional argument - # python2.6 calls the argument strm, in 2.7 it's stream - streamlog = logging.StreamHandler(sys.stdout) - log_root.addHandler(streamlog) - - if CONF.publish_errors: - log_root.addHandler(PublishErrorsHandler(logging.ERROR)) - - for handler in log_root.handlers: - datefmt = CONF.log_date_format - if CONF.log_format: - handler.setFormatter(logging.Formatter(fmt=CONF.log_format, - datefmt=datefmt)) - handler.setFormatter(LegacyFormatter(datefmt=datefmt)) - - if CONF.debug: - log_root.setLevel(logging.DEBUG) - elif CONF.verbose: - log_root.setLevel(logging.INFO) - else: - log_root.setLevel(logging.WARNING) - - level = logging.NOTSET - for pair in CONF.default_log_levels: - mod, _sep, level_name = pair.partition('=') - level = logging.getLevelName(level_name) - logger = logging.getLogger(mod) - logger.setLevel(level) - for handler in log_root.handlers: - logger.addHandler(handler) - -_loggers = {} - - -def getLogger(name='unknown', version='unknown'): - if name not in _loggers: - _loggers[name] = ContextAdapter(logging.getLogger(name), - name, - version) - return _loggers[name] - - -class WritableLogger(object): - """A thin wrapper that responds to `write` and logs.""" - - def __init__(self, logger, level=logging.INFO): - self.logger = logger - self.level = level - - def write(self, msg): - self.logger.log(self.level, msg) - - -class LegacyFormatter(logging.Formatter): - """A context.RequestContext aware formatter configured through flags. - - The flags used to set format strings are: logging_context_format_string - and logging_default_format_string. You can also specify - logging_debug_format_suffix to append extra formatting if the log level is - debug. - - For information about what variables are available for the formatter see: - http://docs.python.org/library/logging.html#formatter - - """ - - def format(self, record): - """Uses contextstring if request_id is set, otherwise default.""" - # NOTE(sdague): default the fancier formating params - # to an empty string so we don't throw an exception if - # they get used - for key in ('instance', 'color'): - if key not in record.__dict__: - record.__dict__[key] = '' - - if record.__dict__.get('request_id', None): - self._fmt = CONF.logging_context_format_string - else: - self._fmt = CONF.logging_default_format_string - - if (record.levelno == logging.DEBUG and - CONF.logging_debug_format_suffix): - self._fmt += " " + CONF.logging_debug_format_suffix - - # Cache this on the record, Logger will respect our formated copy - if record.exc_info: - record.exc_text = self.formatException(record.exc_info, record) - return logging.Formatter.format(self, record) - - def formatException(self, exc_info, record=None): - """Format exception output with CONF.logging_exception_prefix.""" - if not record: - return logging.Formatter.formatException(self, exc_info) - - stringbuffer = cStringIO.StringIO() - traceback.print_exception(exc_info[0], exc_info[1], exc_info[2], - None, stringbuffer) - lines = stringbuffer.getvalue().split('\n') - stringbuffer.close() - - if CONF.logging_exception_prefix.find('%(asctime)') != -1: - record.asctime = self.formatTime(record, self.datefmt) - - formatted_lines = [] - for line in lines: - pl = CONF.logging_exception_prefix % record.__dict__ - fl = '%s%s' % (pl, line) - formatted_lines.append(fl) - return '\n'.join(formatted_lines) - - -class ColorHandler(logging.StreamHandler): - LEVEL_COLORS = { - logging.DEBUG: '\033[00;32m', # GREEN - logging.INFO: '\033[00;36m', # CYAN - logging.AUDIT: '\033[01;36m', # BOLD CYAN - logging.WARN: '\033[01;33m', # BOLD YELLOW - logging.ERROR: '\033[01;31m', # BOLD RED - logging.CRITICAL: '\033[01;31m', # BOLD RED - } - - def format(self, record): - record.color = self.LEVEL_COLORS[record.levelno] - return logging.StreamHandler.format(self, record) - - -class DeprecatedConfig(Exception): - message = _("Fatal call to deprecated config: %(msg)s") - - def __init__(self, msg): - super(Exception, self).__init__(self.message % dict(msg=msg)) diff --git a/api/glazierapi/openstack/common/loopingcall.py b/api/glazierapi/openstack/common/loopingcall.py deleted file mode 100644 index 02420da4..00000000 --- a/api/glazierapi/openstack/common/loopingcall.py +++ /dev/null @@ -1,95 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# Copyright 2011 Justin Santa Barbara -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import sys - -from eventlet import event -from eventlet import greenthread - -from glazierapi.openstack.common.gettextutils import _ -from glazierapi.openstack.common import log as logging -from glazierapi.openstack.common import timeutils - -LOG = logging.getLogger(__name__) - - -class LoopingCallDone(Exception): - """Exception to break out and stop a LoopingCall. - - The poll-function passed to LoopingCall can raise this exception to - break out of the loop normally. This is somewhat analogous to - StopIteration. - - An optional return-value can be included as the argument to the exception; - this return-value will be returned by LoopingCall.wait() - - """ - - def __init__(self, retvalue=True): - """:param retvalue: Value that LoopingCall.wait() should return.""" - self.retvalue = retvalue - - -class LoopingCall(object): - def __init__(self, f=None, *args, **kw): - self.args = args - self.kw = kw - self.f = f - self._running = False - - def start(self, interval, initial_delay=None): - self._running = True - done = event.Event() - - def _inner(): - if initial_delay: - greenthread.sleep(initial_delay) - - try: - while self._running: - start = timeutils.utcnow() - self.f(*self.args, **self.kw) - end = timeutils.utcnow() - if not self._running: - break - delay = interval - timeutils.delta_seconds(start, end) - if delay <= 0: - LOG.warn(_('task run outlasted interval by %s sec') % - -delay) - greenthread.sleep(delay if delay > 0 else 0) - except LoopingCallDone, e: - self.stop() - done.send(e.retvalue) - except Exception: - LOG.exception(_('in looping call')) - done.send_exception(*sys.exc_info()) - return - else: - done.send(True) - - self.done = done - - greenthread.spawn_n(_inner) - return self.done - - def stop(self): - self._running = False - - def wait(self): - return self.done.wait() diff --git a/api/glazierapi/openstack/common/notifier/__init__.py b/api/glazierapi/openstack/common/notifier/__init__.py deleted file mode 100644 index 482d54e4..00000000 --- a/api/glazierapi/openstack/common/notifier/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. diff --git a/api/glazierapi/openstack/common/notifier/api.py b/api/glazierapi/openstack/common/notifier/api.py deleted file mode 100644 index 10e5f6cb..00000000 --- a/api/glazierapi/openstack/common/notifier/api.py +++ /dev/null @@ -1,183 +0,0 @@ -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import uuid - -from oslo.config import cfg - -from glazierapi.openstack.common import context -from glazierapi.openstack.common.gettextutils import _ -from glazierapi.openstack.common import importutils -from glazierapi.openstack.common import jsonutils -from glazierapi.openstack.common import log as logging -from glazierapi.openstack.common import timeutils - - -LOG = logging.getLogger(__name__) - -notifier_opts = [ - cfg.MultiStrOpt('notification_driver', - default=[], - deprecated_name='list_notifier_drivers', - help='Driver or drivers to handle sending notifications'), - cfg.StrOpt('default_notification_level', - default='INFO', - help='Default notification level for outgoing notifications'), - cfg.StrOpt('default_publisher_id', - default='$host', - help='Default publisher_id for outgoing notifications'), -] - -CONF = cfg.CONF -CONF.register_opts(notifier_opts) - -WARN = 'WARN' -INFO = 'INFO' -ERROR = 'ERROR' -CRITICAL = 'CRITICAL' -DEBUG = 'DEBUG' - -log_levels = (DEBUG, WARN, INFO, ERROR, CRITICAL) - - -class BadPriorityException(Exception): - pass - - -def notify_decorator(name, fn): - """ decorator for notify which is used from utils.monkey_patch() - - :param name: name of the function - :param function: - object of the function - :returns: function -- decorated function - - """ - def wrapped_func(*args, **kwarg): - body = {} - body['args'] = [] - body['kwarg'] = {} - for arg in args: - body['args'].append(arg) - for key in kwarg: - body['kwarg'][key] = kwarg[key] - - ctxt = context.get_context_from_function_and_args(fn, args, kwarg) - notify(ctxt, - CONF.default_publisher_id, - name, - CONF.default_notification_level, - body) - return fn(*args, **kwarg) - return wrapped_func - - -def publisher_id(service, host=None): - if not host: - host = CONF.host - return "%s.%s" % (service, host) - - -def notify(context, publisher_id, event_type, priority, payload): - """Sends a notification using the specified driver - - :param publisher_id: the source worker_type.host of the message - :param event_type: the literal type of event (ex. Instance Creation) - :param priority: patterned after the enumeration of Python logging - levels in the set (DEBUG, WARN, INFO, ERROR, CRITICAL) - :param payload: A python dictionary of attributes - - Outgoing message format includes the above parameters, and appends the - following: - - message_id - a UUID representing the id for this notification - - timestamp - the GMT timestamp the notification was sent at - - The composite message will be constructed as a dictionary of the above - attributes, which will then be sent via the transport mechanism defined - by the driver. - - Message example:: - - {'message_id': str(uuid.uuid4()), - 'publisher_id': 'compute.host1', - 'timestamp': timeutils.utcnow(), - 'priority': 'WARN', - 'event_type': 'compute.create_instance', - 'payload': {'instance_id': 12, ... }} - - """ - if priority not in log_levels: - raise BadPriorityException( - _('%s not in valid priorities') % priority) - - # Ensure everything is JSON serializable. - payload = jsonutils.to_primitive(payload, convert_instances=True) - - msg = dict(message_id=str(uuid.uuid4()), - publisher_id=publisher_id, - event_type=event_type, - priority=priority, - payload=payload, - timestamp=str(timeutils.utcnow())) - - for driver in _get_drivers(): - try: - driver.notify(context, msg) - except Exception as e: - LOG.exception(_("Problem '%(e)s' attempting to " - "send to notification system. " - "Payload=%(payload)s") - % dict(e=e, payload=payload)) - - -_drivers = None - - -def _get_drivers(): - """Instantiate, cache, and return drivers based on the CONF.""" - global _drivers - if _drivers is None: - _drivers = {} - for notification_driver in CONF.notification_driver: - add_driver(notification_driver) - - return _drivers.values() - - -def add_driver(notification_driver): - """Add a notification driver at runtime.""" - # Make sure the driver list is initialized. - _get_drivers() - if isinstance(notification_driver, basestring): - # Load and add - try: - driver = importutils.import_module(notification_driver) - _drivers[notification_driver] = driver - except ImportError: - LOG.exception(_("Failed to load notifier %s. " - "These notifications will not be sent.") % - notification_driver) - else: - # Driver is already loaded; just add the object. - _drivers[notification_driver] = notification_driver - - -def _reset_drivers(): - """Used by unit tests to reset the drivers.""" - global _drivers - _drivers = None diff --git a/api/glazierapi/openstack/common/notifier/log_notifier.py b/api/glazierapi/openstack/common/notifier/log_notifier.py deleted file mode 100644 index aa571abd..00000000 --- a/api/glazierapi/openstack/common/notifier/log_notifier.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo.config import cfg - -from glazierapi.openstack.common import jsonutils -from glazierapi.openstack.common import log as logging - - -CONF = cfg.CONF - - -def notify(_context, message): - """Notifies the recipient of the desired event given the model. - Log notifications using openstack's default logging system""" - - priority = message.get('priority', - CONF.default_notification_level) - priority = priority.lower() - logger = logging.getLogger( - 'glazierapi.openstack.common.notification.%s' % - message['event_type']) - getattr(logger, priority)(jsonutils.dumps(message)) diff --git a/api/glazierapi/openstack/common/notifier/no_op_notifier.py b/api/glazierapi/openstack/common/notifier/no_op_notifier.py deleted file mode 100644 index ee1ddbdc..00000000 --- a/api/glazierapi/openstack/common/notifier/no_op_notifier.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - - -def notify(_context, message): - """Notifies the recipient of the desired event given the model""" - pass diff --git a/api/glazierapi/openstack/common/notifier/rabbit_notifier.py b/api/glazierapi/openstack/common/notifier/rabbit_notifier.py deleted file mode 100644 index 3d9c5e4d..00000000 --- a/api/glazierapi/openstack/common/notifier/rabbit_notifier.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2012 Red Hat, Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - - -from glazierapi.openstack.common.gettextutils import _ -from glazierapi.openstack.common import log as logging -from glazierapi.openstack.common.notifier import rpc_notifier - -LOG = logging.getLogger(__name__) - - -def notify(context, message): - """Deprecated in Grizzly. Please use rpc_notifier instead.""" - - LOG.deprecated(_("The rabbit_notifier is now deprecated." - " Please use rpc_notifier instead.")) - rpc_notifier.notify(context, message) diff --git a/api/glazierapi/openstack/common/notifier/rpc_notifier.py b/api/glazierapi/openstack/common/notifier/rpc_notifier.py deleted file mode 100644 index 145ac180..00000000 --- a/api/glazierapi/openstack/common/notifier/rpc_notifier.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo.config import cfg - -from glazierapi.openstack.common import context as req_context -from glazierapi.openstack.common.gettextutils import _ -from glazierapi.openstack.common import log as logging -from glazierapi.openstack.common import rpc - -LOG = logging.getLogger(__name__) - -notification_topic_opt = cfg.ListOpt( - 'notification_topics', default=['notifications', ], - help='AMQP topic used for openstack notifications') - -CONF = cfg.CONF -CONF.register_opt(notification_topic_opt) - - -def notify(context, message): - """Sends a notification via RPC""" - if not context: - context = req_context.get_admin_context() - priority = message.get('priority', - CONF.default_notification_level) - priority = priority.lower() - for topic in CONF.notification_topics: - topic = '%s.%s' % (topic, priority) - try: - rpc.notify(context, topic, message) - except Exception: - LOG.exception(_("Could not send notification to %(topic)s. " - "Payload=%(message)s"), locals()) diff --git a/api/glazierapi/openstack/common/notifier/rpc_notifier2.py b/api/glazierapi/openstack/common/notifier/rpc_notifier2.py deleted file mode 100644 index f1e51ca6..00000000 --- a/api/glazierapi/openstack/common/notifier/rpc_notifier2.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -'''messaging based notification driver, with message envelopes''' - -from oslo.config import cfg - -from glazierapi.openstack.common import context as req_context -from glazierapi.openstack.common.gettextutils import _ -from glazierapi.openstack.common import log as logging -from glazierapi.openstack.common import rpc - -LOG = logging.getLogger(__name__) - -notification_topic_opt = cfg.ListOpt( - 'topics', default=['notifications', ], - help='AMQP topic(s) used for openstack notifications') - -opt_group = cfg.OptGroup(name='rpc_notifier2', - title='Options for rpc_notifier2') - -CONF = cfg.CONF -CONF.register_group(opt_group) -CONF.register_opt(notification_topic_opt, opt_group) - - -def notify(context, message): - """Sends a notification via RPC""" - if not context: - context = req_context.get_admin_context() - priority = message.get('priority', - CONF.default_notification_level) - priority = priority.lower() - for topic in CONF.rpc_notifier2.topics: - topic = '%s.%s' % (topic, priority) - try: - rpc.notify(context, topic, message, envelope=True) - except Exception: - LOG.exception(_("Could not send notification to %(topic)s. " - "Payload=%(message)s"), locals()) diff --git a/api/glazierapi/openstack/common/notifier/test_notifier.py b/api/glazierapi/openstack/common/notifier/test_notifier.py deleted file mode 100644 index 5e348803..00000000 --- a/api/glazierapi/openstack/common/notifier/test_notifier.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - - -NOTIFICATIONS = [] - - -def notify(_context, message): - """Test notifier, stores notifications in memory for unittests.""" - NOTIFICATIONS.append(message) diff --git a/api/glazierapi/openstack/common/service.py b/api/glazierapi/openstack/common/service.py deleted file mode 100644 index 1b89d44e..00000000 --- a/api/glazierapi/openstack/common/service.py +++ /dev/null @@ -1,332 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# Copyright 2011 Justin Santa Barbara -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Generic Node base class for all workers that run on hosts.""" - -import errno -import os -import random -import signal -import sys -import time - -import eventlet -import logging as std_logging -from oslo.config import cfg - -from glazierapi.openstack.common import eventlet_backdoor -from glazierapi.openstack.common.gettextutils import _ -from glazierapi.openstack.common import importutils -from glazierapi.openstack.common import log as logging -from glazierapi.openstack.common import threadgroup - - -rpc = importutils.try_import('glazierapi.openstack.common.rpc') -CONF = cfg.CONF -LOG = logging.getLogger(__name__) - - -class Launcher(object): - """Launch one or more services and wait for them to complete.""" - - def __init__(self): - """Initialize the service launcher. - - :returns: None - - """ - self._services = threadgroup.ThreadGroup() - eventlet_backdoor.initialize_if_enabled() - - @staticmethod - def run_service(service): - """Start and wait for a service to finish. - - :param service: service to run and wait for. - :returns: None - - """ - service.start() - service.wait() - - def launch_service(self, service): - """Load and start the given service. - - :param service: The service you would like to start. - :returns: None - - """ - self._services.add_thread(self.run_service, service) - - def stop(self): - """Stop all services which are currently running. - - :returns: None - - """ - self._services.stop() - - def wait(self): - """Waits until all services have been stopped, and then returns. - - :returns: None - - """ - self._services.wait() - - -class SignalExit(SystemExit): - def __init__(self, signo, exccode=1): - super(SignalExit, self).__init__(exccode) - self.signo = signo - - -class ServiceLauncher(Launcher): - def _handle_signal(self, signo, frame): - # Allow the process to be killed again and die from natural causes - signal.signal(signal.SIGTERM, signal.SIG_DFL) - signal.signal(signal.SIGINT, signal.SIG_DFL) - - raise SignalExit(signo) - - def wait(self): - signal.signal(signal.SIGTERM, self._handle_signal) - signal.signal(signal.SIGINT, self._handle_signal) - - LOG.debug(_('Full set of CONF:')) - CONF.log_opt_values(LOG, std_logging.DEBUG) - - status = None - try: - super(ServiceLauncher, self).wait() - except SignalExit as exc: - signame = {signal.SIGTERM: 'SIGTERM', - signal.SIGINT: 'SIGINT'}[exc.signo] - LOG.info(_('Caught %s, exiting'), signame) - status = exc.code - except SystemExit as exc: - status = exc.code - finally: - if rpc: - rpc.cleanup() - self.stop() - return status - - -class ServiceWrapper(object): - def __init__(self, service, workers): - self.service = service - self.workers = workers - self.children = set() - self.forktimes = [] - - -class ProcessLauncher(object): - def __init__(self): - self.children = {} - self.sigcaught = None - self.running = True - rfd, self.writepipe = os.pipe() - self.readpipe = eventlet.greenio.GreenPipe(rfd, 'r') - - signal.signal(signal.SIGTERM, self._handle_signal) - signal.signal(signal.SIGINT, self._handle_signal) - - def _handle_signal(self, signo, frame): - self.sigcaught = signo - self.running = False - - # Allow the process to be killed again and die from natural causes - signal.signal(signal.SIGTERM, signal.SIG_DFL) - signal.signal(signal.SIGINT, signal.SIG_DFL) - - def _pipe_watcher(self): - # This will block until the write end is closed when the parent - # dies unexpectedly - self.readpipe.read() - - LOG.info(_('Parent process has died unexpectedly, exiting')) - - sys.exit(1) - - def _child_process(self, service): - # Setup child signal handlers differently - def _sigterm(*args): - signal.signal(signal.SIGTERM, signal.SIG_DFL) - raise SignalExit(signal.SIGTERM) - - signal.signal(signal.SIGTERM, _sigterm) - # Block SIGINT and let the parent send us a SIGTERM - signal.signal(signal.SIGINT, signal.SIG_IGN) - - # Reopen the eventlet hub to make sure we don't share an epoll - # fd with parent and/or siblings, which would be bad - eventlet.hubs.use_hub() - - # Close write to ensure only parent has it open - os.close(self.writepipe) - # Create greenthread to watch for parent to close pipe - eventlet.spawn_n(self._pipe_watcher) - - # Reseed random number generator - random.seed() - - launcher = Launcher() - launcher.run_service(service) - - def _start_child(self, wrap): - if len(wrap.forktimes) > wrap.workers: - # Limit ourselves to one process a second (over the period of - # number of workers * 1 second). This will allow workers to - # start up quickly but ensure we don't fork off children that - # die instantly too quickly. - if time.time() - wrap.forktimes[0] < wrap.workers: - LOG.info(_('Forking too fast, sleeping')) - time.sleep(1) - - wrap.forktimes.pop(0) - - wrap.forktimes.append(time.time()) - - pid = os.fork() - if pid == 0: - # NOTE(johannes): All exceptions are caught to ensure this - # doesn't fallback into the loop spawning children. It would - # be bad for a child to spawn more children. - status = 0 - try: - self._child_process(wrap.service) - except SignalExit as exc: - signame = {signal.SIGTERM: 'SIGTERM', - signal.SIGINT: 'SIGINT'}[exc.signo] - LOG.info(_('Caught %s, exiting'), signame) - status = exc.code - except SystemExit as exc: - status = exc.code - except BaseException: - LOG.exception(_('Unhandled exception')) - status = 2 - finally: - wrap.service.stop() - - os._exit(status) - - LOG.info(_('Started child %d'), pid) - - wrap.children.add(pid) - self.children[pid] = wrap - - return pid - - def launch_service(self, service, workers=1): - wrap = ServiceWrapper(service, workers) - - LOG.info(_('Starting %d workers'), wrap.workers) - while self.running and len(wrap.children) < wrap.workers: - self._start_child(wrap) - - def _wait_child(self): - try: - # Don't block if no child processes have exited - pid, status = os.waitpid(0, os.WNOHANG) - if not pid: - return None - except OSError as exc: - if exc.errno not in (errno.EINTR, errno.ECHILD): - raise - return None - - if os.WIFSIGNALED(status): - sig = os.WTERMSIG(status) - LOG.info(_('Child %(pid)d killed by signal %(sig)d'), - dict(pid=pid, sig=sig)) - else: - code = os.WEXITSTATUS(status) - LOG.info(_('Child %(pid)s exited with status %(code)d'), - dict(pid=pid, code=code)) - - if pid not in self.children: - LOG.warning(_('pid %d not in child list'), pid) - return None - - wrap = self.children.pop(pid) - wrap.children.remove(pid) - return wrap - - def wait(self): - """Loop waiting on children to die and respawning as necessary""" - - LOG.debug(_('Full set of CONF:')) - CONF.log_opt_values(LOG, std_logging.DEBUG) - - while self.running: - wrap = self._wait_child() - if not wrap: - # Yield to other threads if no children have exited - # Sleep for a short time to avoid excessive CPU usage - # (see bug #1095346) - eventlet.greenthread.sleep(.01) - continue - - while self.running and len(wrap.children) < wrap.workers: - self._start_child(wrap) - - if self.sigcaught: - signame = {signal.SIGTERM: 'SIGTERM', - signal.SIGINT: 'SIGINT'}[self.sigcaught] - LOG.info(_('Caught %s, stopping children'), signame) - - for pid in self.children: - try: - os.kill(pid, signal.SIGTERM) - except OSError as exc: - if exc.errno != errno.ESRCH: - raise - - # Wait for children to die - if self.children: - LOG.info(_('Waiting on %d children to exit'), len(self.children)) - while self.children: - self._wait_child() - - -class Service(object): - """Service object for binaries running on hosts.""" - - def __init__(self, threads=1000): - self.tg = threadgroup.ThreadGroup(threads) - - def start(self): - pass - - def stop(self): - self.tg.stop() - - def wait(self): - self.tg.wait() - - -def launch(service, workers=None): - if workers: - launcher = ProcessLauncher() - launcher.launch_service(service, workers=workers) - else: - launcher = ServiceLauncher() - launcher.launch_service(service) - return launcher diff --git a/api/glazierapi/openstack/common/setup.py b/api/glazierapi/openstack/common/setup.py deleted file mode 100644 index 80a0ecee..00000000 --- a/api/glazierapi/openstack/common/setup.py +++ /dev/null @@ -1,359 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. -# Copyright 2012-2013 Hewlett-Packard Development Company, L.P. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Utilities with minimum-depends for use in setup.py -""" - -import email -import os -import re -import subprocess -import sys - -from setuptools.command import sdist - - -def parse_mailmap(mailmap='.mailmap'): - mapping = {} - if os.path.exists(mailmap): - with open(mailmap, 'r') as fp: - for l in fp: - try: - canonical_email, alias = re.match( - r'[^#]*?(<.+>).*(<.+>).*', l).groups() - except AttributeError: - continue - mapping[alias] = canonical_email - return mapping - - -def _parse_git_mailmap(git_dir, mailmap='.mailmap'): - mailmap = os.path.join(os.path.dirname(git_dir), mailmap) - return parse_mailmap(mailmap) - - -def canonicalize_emails(changelog, mapping): - """Takes in a string and an email alias mapping and replaces all - instances of the aliases in the string with their real email. - """ - for alias, email_address in mapping.iteritems(): - changelog = changelog.replace(alias, email_address) - return changelog - - -# Get requirements from the first file that exists -def get_reqs_from_files(requirements_files): - for requirements_file in requirements_files: - if os.path.exists(requirements_file): - with open(requirements_file, 'r') as fil: - return fil.read().split('\n') - return [] - - -def parse_requirements(requirements_files=['requirements.txt', - 'tools/pip-requires']): - requirements = [] - for line in get_reqs_from_files(requirements_files): - # For the requirements list, we need to inject only the portion - # after egg= so that distutils knows the package it's looking for - # such as: - # -e git://github.com/openstack/nova/master#egg=nova - if re.match(r'\s*-e\s+', line): - requirements.append(re.sub(r'\s*-e\s+.*#egg=(.*)$', r'\1', - line)) - # such as: - # http://github.com/openstack/nova/zipball/master#egg=nova - elif re.match(r'\s*https?:', line): - requirements.append(re.sub(r'\s*https?:.*#egg=(.*)$', r'\1', - line)) - # -f lines are for index locations, and don't get used here - elif re.match(r'\s*-f\s+', line): - pass - # argparse is part of the standard library starting with 2.7 - # adding it to the requirements list screws distro installs - elif line == 'argparse' and sys.version_info >= (2, 7): - pass - else: - requirements.append(line) - - return requirements - - -def parse_dependency_links(requirements_files=['requirements.txt', - 'tools/pip-requires']): - dependency_links = [] - # dependency_links inject alternate locations to find packages listed - # in requirements - for line in get_reqs_from_files(requirements_files): - # skip comments and blank lines - if re.match(r'(\s*#)|(\s*$)', line): - continue - # lines with -e or -f need the whole line, minus the flag - if re.match(r'\s*-[ef]\s+', line): - dependency_links.append(re.sub(r'\s*-[ef]\s+', '', line)) - # lines that are only urls can go in unmolested - elif re.match(r'\s*https?:', line): - dependency_links.append(line) - return dependency_links - - -def _run_shell_command(cmd, throw_on_error=False): - if os.name == 'nt': - output = subprocess.Popen(["cmd.exe", "/C", cmd], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - else: - output = subprocess.Popen(["/bin/sh", "-c", cmd], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - out = output.communicate() - if output.returncode and throw_on_error: - raise Exception("%s returned %d" % cmd, output.returncode) - if len(out) == 0: - return None - if len(out[0].strip()) == 0: - return None - return out[0].strip() - - -def _get_git_directory(): - parent_dir = os.path.dirname(__file__) - while True: - git_dir = os.path.join(parent_dir, '.git') - if os.path.exists(git_dir): - return git_dir - parent_dir, child = os.path.split(parent_dir) - if not child: # reached to root dir - return None - - -def write_git_changelog(): - """Write a changelog based on the git changelog.""" - new_changelog = 'ChangeLog' - git_dir = _get_git_directory() - if not os.getenv('SKIP_WRITE_GIT_CHANGELOG'): - if git_dir: - git_log_cmd = 'git --git-dir=%s log --stat' % git_dir - changelog = _run_shell_command(git_log_cmd) - mailmap = _parse_git_mailmap(git_dir) - with open(new_changelog, "w") as changelog_file: - changelog_file.write(canonicalize_emails(changelog, mailmap)) - else: - open(new_changelog, 'w').close() - - -def generate_authors(): - """Create AUTHORS file using git commits.""" - jenkins_email = 'jenkins@review.(openstack|stackforge).org' - old_authors = 'AUTHORS.in' - new_authors = 'AUTHORS' - git_dir = _get_git_directory() - if not os.getenv('SKIP_GENERATE_AUTHORS'): - if git_dir: - # don't include jenkins email address in AUTHORS file - git_log_cmd = ("git --git-dir=" + git_dir + - " log --format='%aN <%aE>' | sort -u | " - "egrep -v '" + jenkins_email + "'") - changelog = _run_shell_command(git_log_cmd) - mailmap = _parse_git_mailmap(git_dir) - with open(new_authors, 'w') as new_authors_fh: - new_authors_fh.write(canonicalize_emails(changelog, mailmap)) - if os.path.exists(old_authors): - with open(old_authors, "r") as old_authors_fh: - new_authors_fh.write('\n' + old_authors_fh.read()) - else: - open(new_authors, 'w').close() - - -_rst_template = """%(heading)s -%(underline)s - -.. automodule:: %(module)s - :members: - :undoc-members: - :show-inheritance: -""" - - -def get_cmdclass(): - """Return dict of commands to run from setup.py.""" - - cmdclass = dict() - - def _find_modules(arg, dirname, files): - for filename in files: - if filename.endswith('.py') and filename != '__init__.py': - arg["%s.%s" % (dirname.replace('/', '.'), - filename[:-3])] = True - - class LocalSDist(sdist.sdist): - """Builds the ChangeLog and Authors files from VC first.""" - - def run(self): - write_git_changelog() - generate_authors() - # sdist.sdist is an old style class, can't use super() - sdist.sdist.run(self) - - cmdclass['sdist'] = LocalSDist - - # If Sphinx is installed on the box running setup.py, - # enable setup.py to build the documentation, otherwise, - # just ignore it - try: - from sphinx.setup_command import BuildDoc - - class LocalBuildDoc(BuildDoc): - - builders = ['html', 'man'] - - def generate_autoindex(self): - print "**Autodocumenting from %s" % os.path.abspath(os.curdir) - modules = {} - option_dict = self.distribution.get_option_dict('build_sphinx') - source_dir = os.path.join(option_dict['source_dir'][1], 'api') - if not os.path.exists(source_dir): - os.makedirs(source_dir) - for pkg in self.distribution.packages: - if '.' not in pkg: - os.path.walk(pkg, _find_modules, modules) - module_list = modules.keys() - module_list.sort() - autoindex_filename = os.path.join(source_dir, 'autoindex.rst') - with open(autoindex_filename, 'w') as autoindex: - autoindex.write(""".. toctree:: - :maxdepth: 1 - -""") - for module in module_list: - output_filename = os.path.join(source_dir, - "%s.rst" % module) - heading = "The :mod:`%s` Module" % module - underline = "=" * len(heading) - values = dict(module=module, heading=heading, - underline=underline) - - print "Generating %s" % output_filename - with open(output_filename, 'w') as output_file: - output_file.write(_rst_template % values) - autoindex.write(" %s.rst\n" % module) - - def run(self): - if not os.getenv('SPHINX_DEBUG'): - self.generate_autoindex() - - for builder in self.builders: - self.builder = builder - self.finalize_options() - self.project = self.distribution.get_name() - self.version = self.distribution.get_version() - self.release = self.distribution.get_version() - BuildDoc.run(self) - - class LocalBuildLatex(LocalBuildDoc): - builders = ['latex'] - - cmdclass['build_sphinx'] = LocalBuildDoc - cmdclass['build_sphinx_latex'] = LocalBuildLatex - except ImportError: - pass - - return cmdclass - - -def _get_revno(git_dir): - """Return the number of commits since the most recent tag. - - We use git-describe to find this out, but if there are no - tags then we fall back to counting commits since the beginning - of time. - """ - describe = _run_shell_command( - "git --git-dir=%s describe --always" % git_dir) - if "-" in describe: - return describe.rsplit("-", 2)[-2] - - # no tags found - revlist = _run_shell_command( - "git --git-dir=%s rev-list --abbrev-commit HEAD" % git_dir) - return len(revlist.splitlines()) - - -def _get_version_from_git(pre_version): - """Return a version which is equal to the tag that's on the current - revision if there is one, or tag plus number of additional revisions - if the current revision has no tag.""" - - git_dir = _get_git_directory() - if git_dir: - if pre_version: - try: - return _run_shell_command( - "git --git-dir=" + git_dir + " describe --exact-match", - throw_on_error=True).replace('-', '.') - except Exception: - sha = _run_shell_command( - "git --git-dir=" + git_dir + " log -n1 --pretty=format:%h") - return "%s.a%s.g%s" % (pre_version, _get_revno(git_dir), sha) - else: - return _run_shell_command( - "git --git-dir=" + git_dir + " describe --always").replace( - '-', '.') - return None - - -def _get_version_from_pkg_info(package_name): - """Get the version from PKG-INFO file if we can.""" - try: - pkg_info_file = open('PKG-INFO', 'r') - except (IOError, OSError): - return None - try: - pkg_info = email.message_from_file(pkg_info_file) - except email.MessageError: - return None - # Check to make sure we're in our own dir - if pkg_info.get('Name', None) != package_name: - return None - return pkg_info.get('Version', None) - - -def get_version(package_name, pre_version=None): - """Get the version of the project. First, try getting it from PKG-INFO, if - it exists. If it does, that means we're in a distribution tarball or that - install has happened. Otherwise, if there is no PKG-INFO file, pull the - version from git. - - We do not support setup.py version sanity in git archive tarballs, nor do - we support packagers directly sucking our git repo into theirs. We expect - that a source tarball be made from our git repo - or that if someone wants - to make a source tarball from a fork of our repo with additional tags in it - that they understand and desire the results of doing that. - """ - version = os.environ.get("OSLO_PACKAGE_VERSION", None) - if version: - return version - version = _get_version_from_pkg_info(package_name) - if version: - return version - version = _get_version_from_git(pre_version) - if version: - return version - raise Exception("Versioning for this project requires either an sdist" - " tarball, or access to an upstream git repository.") diff --git a/api/glazierapi/openstack/common/sslutils.py b/api/glazierapi/openstack/common/sslutils.py deleted file mode 100644 index 998f4fdb..00000000 --- a/api/glazierapi/openstack/common/sslutils.py +++ /dev/null @@ -1,80 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 IBM -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import os -import ssl - -from oslo.config import cfg - -from glazierapi.openstack.common.gettextutils import _ - - -ssl_opts = [ - cfg.StrOpt('ca_file', - default=None, - help="CA certificate file to use to verify " - "connecting clients"), - cfg.StrOpt('cert_file', - default=None, - help="Certificate file to use when starting " - "the server securely"), - cfg.StrOpt('key_file', - default=None, - help="Private key file to use when starting " - "the server securely"), -] - - -CONF = cfg.CONF -CONF.register_opts(ssl_opts, "ssl") - - -def is_enabled(): - cert_file = CONF.ssl.cert_file - key_file = CONF.ssl.key_file - ca_file = CONF.ssl.ca_file - use_ssl = cert_file or key_file - - if cert_file and not os.path.exists(cert_file): - raise RuntimeError(_("Unable to find cert_file : %s") % cert_file) - - if ca_file and not os.path.exists(ca_file): - raise RuntimeError(_("Unable to find ca_file : %s") % ca_file) - - if key_file and not os.path.exists(key_file): - raise RuntimeError(_("Unable to find key_file : %s") % key_file) - - if use_ssl and (not cert_file or not key_file): - raise RuntimeError(_("When running server in SSL mode, you must " - "specify both a cert_file and key_file " - "option value in your configuration file")) - - return use_ssl - - -def wrap(sock): - ssl_kwargs = { - 'server_side': True, - 'certfile': CONF.ssl.cert_file, - 'keyfile': CONF.ssl.key_file, - 'cert_reqs': ssl.CERT_NONE, - } - - if CONF.ssl.ca_file: - ssl_kwargs['ca_certs'] = CONF.ssl.ca_file - ssl_kwargs['cert_reqs'] = ssl.CERT_REQUIRED - - return ssl.wrap_socket(sock, **ssl_kwargs) diff --git a/api/glazierapi/openstack/common/threadgroup.py b/api/glazierapi/openstack/common/threadgroup.py deleted file mode 100644 index f38e7464..00000000 --- a/api/glazierapi/openstack/common/threadgroup.py +++ /dev/null @@ -1,114 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2012 Red Hat, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from eventlet import greenlet -from eventlet import greenpool -from eventlet import greenthread - -from glazierapi.openstack.common import log as logging -from glazierapi.openstack.common import loopingcall - - -LOG = logging.getLogger(__name__) - - -def _thread_done(gt, *args, **kwargs): - """ Callback function to be passed to GreenThread.link() when we spawn() - Calls the :class:`ThreadGroup` to notify if. - - """ - kwargs['group'].thread_done(kwargs['thread']) - - -class Thread(object): - """ Wrapper around a greenthread, that holds a reference to the - :class:`ThreadGroup`. The Thread will notify the :class:`ThreadGroup` when - it has done so it can be removed from the threads list. - """ - def __init__(self, thread, group): - self.thread = thread - self.thread.link(_thread_done, group=group, thread=self) - - def stop(self): - self.thread.kill() - - def wait(self): - return self.thread.wait() - - -class ThreadGroup(object): - """ The point of the ThreadGroup classis to: - - * keep track of timers and greenthreads (making it easier to stop them - when need be). - * provide an easy API to add timers. - """ - def __init__(self, thread_pool_size=10): - self.pool = greenpool.GreenPool(thread_pool_size) - self.threads = [] - self.timers = [] - - def add_timer(self, interval, callback, initial_delay=None, - *args, **kwargs): - pulse = loopingcall.LoopingCall(callback, *args, **kwargs) - pulse.start(interval=interval, - initial_delay=initial_delay) - self.timers.append(pulse) - - def add_thread(self, callback, *args, **kwargs): - gt = self.pool.spawn(callback, *args, **kwargs) - th = Thread(gt, self) - self.threads.append(th) - - def thread_done(self, thread): - self.threads.remove(thread) - - def stop(self): - current = greenthread.getcurrent() - for x in self.threads: - if x is current: - # don't kill the current thread. - continue - try: - x.stop() - except Exception as ex: - LOG.exception(ex) - - for x in self.timers: - try: - x.stop() - except Exception as ex: - LOG.exception(ex) - self.timers = [] - - def wait(self): - for x in self.timers: - try: - x.wait() - except greenlet.GreenletExit: - pass - except Exception as ex: - LOG.exception(ex) - current = greenthread.getcurrent() - for x in self.threads: - if x is current: - continue - try: - x.wait() - except greenlet.GreenletExit: - pass - except Exception as ex: - LOG.exception(ex) diff --git a/api/glazierapi/openstack/common/timeutils.py b/api/glazierapi/openstack/common/timeutils.py deleted file mode 100644 index e2c27405..00000000 --- a/api/glazierapi/openstack/common/timeutils.py +++ /dev/null @@ -1,182 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Time related utilities and helper functions. -""" - -import calendar -import datetime - -import iso8601 - - -TIME_FORMAT = "%Y-%m-%dT%H:%M:%S" -PERFECT_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f" - - -def isotime(at=None): - """Stringify time in ISO 8601 format""" - if not at: - at = utcnow() - str = at.strftime(TIME_FORMAT) - tz = at.tzinfo.tzname(None) if at.tzinfo else 'UTC' - str += ('Z' if tz == 'UTC' else tz) - return str - - -def parse_isotime(timestr): - """Parse time from ISO 8601 format""" - try: - return iso8601.parse_date(timestr) - except iso8601.ParseError as e: - raise ValueError(e.message) - except TypeError as e: - raise ValueError(e.message) - - -def strtime(at=None, fmt=PERFECT_TIME_FORMAT): - """Returns formatted utcnow.""" - if not at: - at = utcnow() - return at.strftime(fmt) - - -def parse_strtime(timestr, fmt=PERFECT_TIME_FORMAT): - """Turn a formatted time back into a datetime.""" - return datetime.datetime.strptime(timestr, fmt) - - -def normalize_time(timestamp): - """Normalize time in arbitrary timezone to UTC naive object""" - offset = timestamp.utcoffset() - if offset is None: - return timestamp - return timestamp.replace(tzinfo=None) - offset - - -def is_older_than(before, seconds): - """Return True if before is older than seconds.""" - if isinstance(before, basestring): - before = parse_strtime(before).replace(tzinfo=None) - return utcnow() - before > datetime.timedelta(seconds=seconds) - - -def is_newer_than(after, seconds): - """Return True if after is newer than seconds.""" - if isinstance(after, basestring): - after = parse_strtime(after).replace(tzinfo=None) - return after - utcnow() > datetime.timedelta(seconds=seconds) - - -def utcnow_ts(): - """Timestamp version of our utcnow function.""" - return calendar.timegm(utcnow().timetuple()) - - -def utcnow(): - """Overridable version of utils.utcnow.""" - if utcnow.override_time: - try: - return utcnow.override_time.pop(0) - except AttributeError: - return utcnow.override_time - return datetime.datetime.utcnow() - - -def iso8601_from_timestamp(timestamp): - """Returns a iso8601 formated date from timestamp""" - return isotime(datetime.datetime.utcfromtimestamp(timestamp)) - - -utcnow.override_time = None - - -def set_time_override(override_time=datetime.datetime.utcnow()): - """ - Override utils.utcnow to return a constant time or a list thereof, - one at a time. - """ - utcnow.override_time = override_time - - -def advance_time_delta(timedelta): - """Advance overridden time using a datetime.timedelta.""" - assert(not utcnow.override_time is None) - try: - for dt in utcnow.override_time: - dt += timedelta - except TypeError: - utcnow.override_time += timedelta - - -def advance_time_seconds(seconds): - """Advance overridden time by seconds.""" - advance_time_delta(datetime.timedelta(0, seconds)) - - -def clear_time_override(): - """Remove the overridden time.""" - utcnow.override_time = None - - -def marshall_now(now=None): - """Make an rpc-safe datetime with microseconds. - - Note: tzinfo is stripped, but not required for relative times.""" - if not now: - now = utcnow() - return dict(day=now.day, month=now.month, year=now.year, hour=now.hour, - minute=now.minute, second=now.second, - microsecond=now.microsecond) - - -def unmarshall_time(tyme): - """Unmarshall a datetime dict.""" - return datetime.datetime(day=tyme['day'], - month=tyme['month'], - year=tyme['year'], - hour=tyme['hour'], - minute=tyme['minute'], - second=tyme['second'], - microsecond=tyme['microsecond']) - - -def delta_seconds(before, after): - """ - Compute the difference in seconds between two date, time, or - datetime objects (as a float, to microsecond resolution). - """ - delta = after - before - try: - return delta.total_seconds() - except AttributeError: - return ((delta.days * 24 * 3600) + delta.seconds + - float(delta.microseconds) / (10 ** 6)) - - -def is_soon(dt, window): - """ - Determines if time is going to happen in the next window seconds. - - :params dt: the time - :params window: minimum seconds to remain to consider the time not soon - - :return: True if expiration is within the given duration - """ - soon = (utcnow() + datetime.timedelta(seconds=window)) - return normalize_time(dt) <= soon diff --git a/api/glazierapi/openstack/common/uuidutils.py b/api/glazierapi/openstack/common/uuidutils.py deleted file mode 100644 index 7608acb9..00000000 --- a/api/glazierapi/openstack/common/uuidutils.py +++ /dev/null @@ -1,39 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (c) 2012 Intel Corporation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -UUID related utilities and helper functions. -""" - -import uuid - - -def generate_uuid(): - return str(uuid.uuid4()) - - -def is_uuid_like(val): - """Returns validation of a value as a UUID. - - For our purposes, a UUID is a canonical form string: - aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa - - """ - try: - return str(uuid.UUID(val)) == val - except (TypeError, ValueError, AttributeError): - return False diff --git a/api/glazierapi/openstack/common/version.py b/api/glazierapi/openstack/common/version.py deleted file mode 100644 index e4eb6530..00000000 --- a/api/glazierapi/openstack/common/version.py +++ /dev/null @@ -1,94 +0,0 @@ - -# Copyright 2012 OpenStack LLC -# Copyright 2012-2013 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Utilities for consuming the version from pkg_resources. -""" - -import pkg_resources - - -class VersionInfo(object): - - def __init__(self, package): - """Object that understands versioning for a package - :param package: name of the python package, such as glance, or - python-glanceclient - """ - self.package = package - self.release = None - self.version = None - self._cached_version = None - - def __str__(self): - """Make the VersionInfo object behave like a string.""" - return self.version_string() - - def __repr__(self): - """Include the name.""" - return "VersionInfo(%s:%s)" % (self.package, self.version_string()) - - def _get_version_from_pkg_resources(self): - """Get the version of the package from the pkg_resources record - associated with the package.""" - try: - requirement = pkg_resources.Requirement.parse(self.package) - provider = pkg_resources.get_provider(requirement) - return provider.version - except pkg_resources.DistributionNotFound: - # The most likely cause for this is running tests in a tree - # produced from a tarball where the package itself has not been - # installed into anything. Revert to setup-time logic. - from glazierapi.openstack.common import setup - return setup.get_version(self.package) - - def release_string(self): - """Return the full version of the package including suffixes indicating - VCS status. - """ - if self.release is None: - self.release = self._get_version_from_pkg_resources() - - return self.release - - def version_string(self): - """Return the short version minus any alpha/beta tags.""" - if self.version is None: - parts = [] - for part in self.release_string().split('.'): - if part[0].isdigit(): - parts.append(part) - else: - break - self.version = ".".join(parts) - - return self.version - - # Compatibility functions - canonical_version_string = version_string - version_string_with_vcs = release_string - - def cached_version_string(self, prefix=""): - """Generate an object which will expand in a string context to - the results of version_string(). We do this so that don't - call into pkg_resources every time we start up a program when - passing version information into the CONF constructor, but - rather only do the calculation when and if a version is requested - """ - if not self._cached_version: - self._cached_version = "%s%s" % (prefix, - self.version_string()) - return self._cached_version diff --git a/api/glazierapi/openstack/common/wsgi.py b/api/glazierapi/openstack/common/wsgi.py deleted file mode 100644 index e4f15fc0..00000000 --- a/api/glazierapi/openstack/common/wsgi.py +++ /dev/null @@ -1,797 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Utility methods for working with WSGI servers.""" - -import eventlet -eventlet.patcher.monkey_patch(all=False, socket=True) - -import datetime -import errno -import socket -import sys -import time - -import eventlet.wsgi -from oslo.config import cfg -import routes -import routes.middleware -import webob.dec -import webob.exc -from xml.dom import minidom -from xml.parsers import expat - -from glazierapi.openstack.common import exception -from glazierapi.openstack.common.gettextutils import _ -from glazierapi.openstack.common import jsonutils -from glazierapi.openstack.common import log as logging -from glazierapi.openstack.common import service -from glazierapi.openstack.common import sslutils -from glazierapi.openstack.common import xmlutils - -socket_opts = [ - cfg.IntOpt('backlog', - default=4096, - help="Number of backlog requests to configure the socket with"), - cfg.IntOpt('tcp_keepidle', - default=600, - help="Sets the value of TCP_KEEPIDLE in seconds for each " - "server socket. Not supported on OS X."), -] - -CONF = cfg.CONF -CONF.register_opts(socket_opts) - -LOG = logging.getLogger(__name__) - - -def run_server(application, port): - """Run a WSGI server with the given application.""" - sock = eventlet.listen(('0.0.0.0', port)) - eventlet.wsgi.server(sock, application) - - -class Service(service.Service): - """ - Provides a Service API for wsgi servers. - - This gives us the ability to launch wsgi servers with the - Launcher classes in service.py. - """ - - def __init__(self, application, port, - host='0.0.0.0', backlog=4096, threads=1000): - self.application = application - self._port = port - self._host = host - self._backlog = backlog if backlog else CONF.backlog - super(Service, self).__init__(threads) - - def _get_socket(self, host, port, backlog): - # TODO(dims): eventlet's green dns/socket module does not actually - # support IPv6 in getaddrinfo(). We need to get around this in the - # future or monitor upstream for a fix - info = socket.getaddrinfo(host, - port, - socket.AF_UNSPEC, - socket.SOCK_STREAM)[0] - family = info[0] - bind_addr = info[-1] - - sock = None - retry_until = time.time() + 30 - while not sock and time.time() < retry_until: - try: - sock = eventlet.listen(bind_addr, - backlog=backlog, - family=family) - if sslutils.is_enabled(): - sock = sslutils.wrap(sock) - - except socket.error, err: - if err.args[0] != errno.EADDRINUSE: - raise - eventlet.sleep(0.1) - if not sock: - raise RuntimeError(_("Could not bind to %(host)s:%(port)s " - "after trying for 30 seconds") % - {'host': host, 'port': port}) - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - # sockets can hang around forever without keepalive - sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) - - # This option isn't available in the OS X version of eventlet - if hasattr(socket, 'TCP_KEEPIDLE'): - sock.setsockopt(socket.IPPROTO_TCP, - socket.TCP_KEEPIDLE, - CONF.tcp_keepidle) - - return sock - - def start(self): - """Start serving this service using the provided server instance. - - :returns: None - - """ - super(Service, self).start() - self._socket = self._get_socket(self._host, self._port, self._backlog) - self.tg.add_thread(self._run, self.application, self._socket) - - @property - def backlog(self): - return self._backlog - - @property - def host(self): - return self._socket.getsockname()[0] if self._socket else self._host - - @property - def port(self): - return self._socket.getsockname()[1] if self._socket else self._port - - def stop(self): - """Stop serving this API. - - :returns: None - - """ - super(Service, self).stop() - - def _run(self, application, socket): - """Start a WSGI server in a new green thread.""" - logger = logging.getLogger('eventlet.wsgi') - eventlet.wsgi.server(socket, - application, - custom_pool=self.tg.pool, - log=logging.WritableLogger(logger)) - - -class Middleware(object): - """ - Base WSGI middleware wrapper. These classes require an application to be - initialized that will be called next. By default the middleware will - simply call its wrapped app, or you can override __call__ to customize its - behavior. - """ - - def __init__(self, application): - self.application = application - - def process_request(self, req): - """ - Called on each request. - - If this returns None, the next application down the stack will be - executed. If it returns a response then that response will be returned - and execution will stop here. - """ - return None - - def process_response(self, response): - """Do whatever you'd like to the response.""" - return response - - @webob.dec.wsgify - def __call__(self, req): - response = self.process_request(req) - if response: - return response - response = req.get_response(self.application) - return self.process_response(response) - - -class Debug(Middleware): - """ - Helper class that can be inserted into any WSGI application chain - to get information about the request and response. - """ - - @webob.dec.wsgify - def __call__(self, req): - print ("*" * 40) + " REQUEST ENVIRON" - for key, value in req.environ.items(): - print key, "=", value - print - resp = req.get_response(self.application) - - print ("*" * 40) + " RESPONSE HEADERS" - for (key, value) in resp.headers.iteritems(): - print key, "=", value - print - - resp.app_iter = self.print_generator(resp.app_iter) - - return resp - - @staticmethod - def print_generator(app_iter): - """ - Iterator that prints the contents of a wrapper string iterator - when iterated. - """ - print ("*" * 40) + " BODY" - for part in app_iter: - sys.stdout.write(part) - sys.stdout.flush() - yield part - print - - -class Router(object): - - """ - WSGI middleware that maps incoming requests to WSGI apps. - """ - - def __init__(self, mapper): - """ - Create a router for the given routes.Mapper. - - Each route in `mapper` must specify a 'controller', which is a - WSGI app to call. You'll probably want to specify an 'action' as - well and have your controller be a wsgi.Controller, who will route - the request to the action method. - - Examples: - mapper = routes.Mapper() - sc = ServerController() - - # Explicit mapping of one route to a controller+action - mapper.connect(None, "/svrlist", controller=sc, action="list") - - # Actions are all implicitly defined - mapper.resource("server", "servers", controller=sc) - - # Pointing to an arbitrary WSGI app. You can specify the - # {path_info:.*} parameter so the target app can be handed just that - # section of the URL. - mapper.connect(None, "/v1.0/{path_info:.*}", controller=BlogApp()) - """ - self.map = mapper - self._router = routes.middleware.RoutesMiddleware(self._dispatch, - self.map) - - @webob.dec.wsgify - def __call__(self, req): - """ - Route the incoming request to a controller based on self.map. - If no match, return a 404. - """ - return self._router - - @staticmethod - @webob.dec.wsgify - def _dispatch(req): - """ - Called by self._router after matching the incoming request to a route - and putting the information into req.environ. Either returns 404 - or the routed WSGI app's response. - """ - match = req.environ['wsgiorg.routing_args'][1] - if not match: - return webob.exc.HTTPNotFound() - app = match['controller'] - return app - - -class Request(webob.Request): - """Add some Openstack API-specific logic to the base webob.Request.""" - - default_request_content_types = ('application/json', 'application/xml') - default_accept_types = ('application/json', 'application/xml') - default_accept_type = 'application/json' - - def best_match_content_type(self, supported_content_types=None): - """Determine the requested response content-type. - - Based on the query extension then the Accept header. - Defaults to default_accept_type if we don't find a preference - - """ - supported_content_types = (supported_content_types or - self.default_accept_types) - - parts = self.path.rsplit('.', 1) - if len(parts) > 1: - ctype = 'application/{0}'.format(parts[1]) - if ctype in supported_content_types: - return ctype - - bm = self.accept.best_match(supported_content_types) - return bm or self.default_accept_type - - def get_content_type(self, allowed_content_types=None): - """Determine content type of the request body. - - Does not do any body introspection, only checks header - - """ - if "Content-Type" not in self.headers: - return None - - content_type = self.content_type - allowed_content_types = (allowed_content_types or - self.default_request_content_types) - - if content_type not in allowed_content_types: - raise exception.InvalidContentType(content_type=content_type) - return content_type - - -class Resource(object): - """ - WSGI app that handles (de)serialization and controller dispatch. - - Reads routing information supplied by RoutesMiddleware and calls - the requested action method upon its deserializer, controller, - and serializer. Those three objects may implement any of the basic - controller action methods (create, update, show, index, delete) - along with any that may be specified in the api router. A 'default' - method may also be implemented to be used in place of any - non-implemented actions. Deserializer methods must accept a request - argument and return a dictionary. Controller methods must accept a - request argument. Additionally, they must also accept keyword - arguments that represent the keys returned by the Deserializer. They - may raise a webob.exc exception or return a dict, which will be - serialized by requested content type. - """ - def __init__(self, controller, deserializer=None, serializer=None): - """ - :param controller: object that implement methods created by routes lib - :param deserializer: object that supports webob request deserialization - through controller-like actions - :param serializer: object that supports webob response serialization - through controller-like actions - """ - self.controller = controller - self.serializer = serializer or ResponseSerializer() - self.deserializer = deserializer or RequestDeserializer() - - @webob.dec.wsgify(RequestClass=Request) - def __call__(self, request): - """WSGI method that controls (de)serialization and method dispatch.""" - - try: - action, action_args, accept = self.deserialize_request(request) - except exception.InvalidContentType: - msg = _("Unsupported Content-Type") - return webob.exc.HTTPUnsupportedMediaType(explanation=msg) - except exception.MalformedRequestBody: - msg = _("Malformed request body") - return webob.exc.HTTPBadRequest(explanation=msg) - - action_result = self.execute_action(action, request, **action_args) - try: - return self.serialize_response(action, action_result, accept) - # return unserializable result (typically a webob exc) - except Exception: - return action_result - - def deserialize_request(self, request): - return self.deserializer.deserialize(request) - - def serialize_response(self, action, action_result, accept): - return self.serializer.serialize(action_result, accept, action) - - def execute_action(self, action, request, **action_args): - return self.dispatch(self.controller, action, request, **action_args) - - def dispatch(self, obj, action, *args, **kwargs): - """Find action-specific method on self and call it.""" - try: - method = getattr(obj, action) - except AttributeError: - method = getattr(obj, 'default') - - return method(*args, **kwargs) - - def get_action_args(self, request_environment): - """Parse dictionary created by routes library.""" - try: - args = request_environment['wsgiorg.routing_args'][1].copy() - except Exception: - return {} - - try: - del args['controller'] - except KeyError: - pass - - try: - del args['format'] - except KeyError: - pass - - return args - - -class ActionDispatcher(object): - """Maps method name to local methods through action name.""" - - def dispatch(self, *args, **kwargs): - """Find and call local method.""" - action = kwargs.pop('action', 'default') - action_method = getattr(self, str(action), self.default) - return action_method(*args, **kwargs) - - def default(self, data): - raise NotImplementedError() - - -class DictSerializer(ActionDispatcher): - """Default request body serialization""" - - def serialize(self, data, action='default'): - return self.dispatch(data, action=action) - - def default(self, data): - return "" - - -class JSONDictSerializer(DictSerializer): - """Default JSON request body serialization""" - - def default(self, data): - def sanitizer(obj): - if isinstance(obj, datetime.datetime): - _dtime = obj - datetime.timedelta(microseconds=obj.microsecond) - return _dtime.isoformat() - return unicode(obj) - return jsonutils.dumps(data, default=sanitizer) - - -class XMLDictSerializer(DictSerializer): - - def __init__(self, metadata=None, xmlns=None): - """ - :param metadata: information needed to deserialize xml into - a dictionary. - :param xmlns: XML namespace to include with serialized xml - """ - super(XMLDictSerializer, self).__init__() - self.metadata = metadata or {} - self.xmlns = xmlns - - def default(self, data): - # We expect data to contain a single key which is the XML root. - root_key = data.keys()[0] - doc = minidom.Document() - node = self._to_xml_node(doc, self.metadata, root_key, data[root_key]) - - return self.to_xml_string(node) - - def to_xml_string(self, node, has_atom=False): - self._add_xmlns(node, has_atom) - return node.toprettyxml(indent=' ', encoding='UTF-8') - - #NOTE (ameade): the has_atom should be removed after all of the - # xml serializers and view builders have been updated to the current - # spec that required all responses include the xmlns:atom, the has_atom - # flag is to prevent current tests from breaking - def _add_xmlns(self, node, has_atom=False): - if self.xmlns is not None: - node.setAttribute('xmlns', self.xmlns) - if has_atom: - node.setAttribute('xmlns:atom', "http://www.w3.org/2005/Atom") - - def _to_xml_node(self, doc, metadata, nodename, data): - """Recursive method to convert data members to XML nodes.""" - result = doc.createElement(nodename) - - # Set the xml namespace if one is specified - # TODO(justinsb): We could also use prefixes on the keys - xmlns = metadata.get('xmlns', None) - if xmlns: - result.setAttribute('xmlns', xmlns) - - #TODO(bcwaldon): accomplish this without a type-check - if type(data) is list: - collections = metadata.get('list_collections', {}) - if nodename in collections: - metadata = collections[nodename] - for item in data: - node = doc.createElement(metadata['item_name']) - node.setAttribute(metadata['item_key'], str(item)) - result.appendChild(node) - return result - singular = metadata.get('plurals', {}).get(nodename, None) - if singular is None: - if nodename.endswith('s'): - singular = nodename[:-1] - else: - singular = 'item' - for item in data: - node = self._to_xml_node(doc, metadata, singular, item) - result.appendChild(node) - #TODO(bcwaldon): accomplish this without a type-check - elif type(data) is dict: - collections = metadata.get('dict_collections', {}) - if nodename in collections: - metadata = collections[nodename] - for k, v in data.items(): - node = doc.createElement(metadata['item_name']) - node.setAttribute(metadata['item_key'], str(k)) - text = doc.createTextNode(str(v)) - node.appendChild(text) - result.appendChild(node) - return result - attrs = metadata.get('attributes', {}).get(nodename, {}) - for k, v in data.items(): - if k in attrs: - result.setAttribute(k, str(v)) - else: - node = self._to_xml_node(doc, metadata, k, v) - result.appendChild(node) - else: - # Type is atom - node = doc.createTextNode(str(data)) - result.appendChild(node) - return result - - def _create_link_nodes(self, xml_doc, links): - link_nodes = [] - for link in links: - link_node = xml_doc.createElement('atom:link') - link_node.setAttribute('rel', link['rel']) - link_node.setAttribute('href', link['href']) - if 'type' in link: - link_node.setAttribute('type', link['type']) - link_nodes.append(link_node) - return link_nodes - - -class ResponseHeadersSerializer(ActionDispatcher): - """Default response headers serialization""" - - def serialize(self, response, data, action): - self.dispatch(response, data, action=action) - - def default(self, response, data): - response.status_int = 200 - - -class ResponseSerializer(object): - """Encode the necessary pieces into a response object""" - - def __init__(self, body_serializers=None, headers_serializer=None): - self.body_serializers = { - 'application/xml': XMLDictSerializer(), - 'application/json': JSONDictSerializer(), - } - self.body_serializers.update(body_serializers or {}) - - self.headers_serializer = (headers_serializer or - ResponseHeadersSerializer()) - - def serialize(self, response_data, content_type, action='default'): - """Serialize a dict into a string and wrap in a wsgi.Request object. - - :param response_data: dict produced by the Controller - :param content_type: expected mimetype of serialized response body - - """ - response = webob.Response() - self.serialize_headers(response, response_data, action) - self.serialize_body(response, response_data, content_type, action) - return response - - def serialize_headers(self, response, data, action): - self.headers_serializer.serialize(response, data, action) - - def serialize_body(self, response, data, content_type, action): - response.headers['Content-Type'] = content_type - if data is not None: - serializer = self.get_body_serializer(content_type) - response.body = serializer.serialize(data, action) - - def get_body_serializer(self, content_type): - try: - return self.body_serializers[content_type] - except (KeyError, TypeError): - raise exception.InvalidContentType(content_type=content_type) - - -class RequestHeadersDeserializer(ActionDispatcher): - """Default request headers deserializer""" - - def deserialize(self, request, action): - return self.dispatch(request, action=action) - - def default(self, request): - return {} - - -class RequestDeserializer(object): - """Break up a Request object into more useful pieces.""" - - def __init__(self, body_deserializers=None, headers_deserializer=None, - supported_content_types=None): - - self.supported_content_types = supported_content_types - - self.body_deserializers = { - 'application/xml': XMLDeserializer(), - 'application/json': JSONDeserializer(), - } - self.body_deserializers.update(body_deserializers or {}) - - self.headers_deserializer = (headers_deserializer or - RequestHeadersDeserializer()) - - def deserialize(self, request): - """Extract necessary pieces of the request. - - :param request: Request object - :returns: tuple of (expected controller action name, dictionary of - keyword arguments to pass to the controller, the expected - content type of the response) - - """ - action_args = self.get_action_args(request.environ) - action = action_args.pop('action', None) - - action_args.update(self.deserialize_headers(request, action)) - action_args.update(self.deserialize_body(request, action)) - - accept = self.get_expected_content_type(request) - - return (action, action_args, accept) - - def deserialize_headers(self, request, action): - return self.headers_deserializer.deserialize(request, action) - - def deserialize_body(self, request, action): - if not len(request.body) > 0: - LOG.debug(_("Empty body provided in request")) - return {} - - try: - content_type = request.get_content_type() - except exception.InvalidContentType: - LOG.debug(_("Unrecognized Content-Type provided in request")) - raise - - if content_type is None: - LOG.debug(_("No Content-Type provided in request")) - return {} - - try: - deserializer = self.get_body_deserializer(content_type) - except exception.InvalidContentType: - LOG.debug(_("Unable to deserialize body as provided Content-Type")) - raise - - return deserializer.deserialize(request.body, action) - - def get_body_deserializer(self, content_type): - try: - return self.body_deserializers[content_type] - except (KeyError, TypeError): - raise exception.InvalidContentType(content_type=content_type) - - def get_expected_content_type(self, request): - return request.best_match_content_type(self.supported_content_types) - - def get_action_args(self, request_environment): - """Parse dictionary created by routes library.""" - try: - args = request_environment['wsgiorg.routing_args'][1].copy() - except Exception: - return {} - - try: - del args['controller'] - except KeyError: - pass - - try: - del args['format'] - except KeyError: - pass - - return args - - -class TextDeserializer(ActionDispatcher): - """Default request body deserialization""" - - def deserialize(self, datastring, action='default'): - return self.dispatch(datastring, action=action) - - def default(self, datastring): - return {} - - -class JSONDeserializer(TextDeserializer): - - def _from_json(self, datastring): - try: - return jsonutils.loads(datastring) - except ValueError: - msg = _("cannot understand JSON") - raise exception.MalformedRequestBody(reason=msg) - - def default(self, datastring): - return {'body': self._from_json(datastring)} - - -class XMLDeserializer(TextDeserializer): - - def __init__(self, metadata=None): - """ - :param metadata: information needed to deserialize xml into - a dictionary. - """ - super(XMLDeserializer, self).__init__() - self.metadata = metadata or {} - - def _from_xml(self, datastring): - plurals = set(self.metadata.get('plurals', {})) - - try: - node = xmlutils.safe_minidom_parse_string(datastring).childNodes[0] - return {node.nodeName: self._from_xml_node(node, plurals)} - except expat.ExpatError: - msg = _("cannot understand XML") - raise exception.MalformedRequestBody(reason=msg) - - def _from_xml_node(self, node, listnames): - """Convert a minidom node to a simple Python type. - - :param listnames: list of XML node names whose subnodes should - be considered list items. - - """ - - if len(node.childNodes) == 1 and node.childNodes[0].nodeType == 3: - return node.childNodes[0].nodeValue - elif node.nodeName in listnames: - return [self._from_xml_node(n, listnames) for n in node.childNodes] - else: - result = dict() - for attr in node.attributes.keys(): - result[attr] = node.attributes[attr].nodeValue - for child in node.childNodes: - if child.nodeType != node.TEXT_NODE: - result[child.nodeName] = self._from_xml_node(child, - listnames) - return result - - def find_first_child_named(self, parent, name): - """Search a nodes children for the first child with a given name""" - for node in parent.childNodes: - if node.nodeName == name: - return node - return None - - def find_children_named(self, parent, name): - """Return all of a nodes children who have the given name""" - for node in parent.childNodes: - if node.nodeName == name: - yield node - - def extract_text(self, node): - """Get the text field contained by the given node""" - if len(node.childNodes) == 1: - child = node.childNodes[0] - if child.nodeType == child.TEXT_NODE: - return child.nodeValue - return "" - - def default(self, datastring): - return {'body': self._from_xml(datastring)} diff --git a/api/glazierapi/openstack/common/xmlutils.py b/api/glazierapi/openstack/common/xmlutils.py deleted file mode 100644 index 33700485..00000000 --- a/api/glazierapi/openstack/common/xmlutils.py +++ /dev/null @@ -1,74 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 IBM -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from xml.dom import minidom -from xml.parsers import expat -from xml import sax -from xml.sax import expatreader - - -class ProtectedExpatParser(expatreader.ExpatParser): - """An expat parser which disables DTD's and entities by default.""" - - def __init__(self, forbid_dtd=True, forbid_entities=True, - *args, **kwargs): - # Python 2.x old style class - expatreader.ExpatParser.__init__(self, *args, **kwargs) - self.forbid_dtd = forbid_dtd - self.forbid_entities = forbid_entities - - def start_doctype_decl(self, name, sysid, pubid, has_internal_subset): - raise ValueError("Inline DTD forbidden") - - def entity_decl(self, entityName, is_parameter_entity, value, base, - systemId, publicId, notationName): - raise ValueError("<!ENTITY> entity declaration forbidden") - - def unparsed_entity_decl(self, name, base, sysid, pubid, notation_name): - # expat 1.2 - raise ValueError("<!ENTITY> unparsed entity forbidden") - - def external_entity_ref(self, context, base, systemId, publicId): - raise ValueError("<!ENTITY> external entity forbidden") - - def notation_decl(self, name, base, sysid, pubid): - raise ValueError("<!ENTITY> notation forbidden") - - def reset(self): - expatreader.ExpatParser.reset(self) - if self.forbid_dtd: - self._parser.StartDoctypeDeclHandler = self.start_doctype_decl - self._parser.EndDoctypeDeclHandler = None - if self.forbid_entities: - self._parser.EntityDeclHandler = self.entity_decl - self._parser.UnparsedEntityDeclHandler = self.unparsed_entity_decl - self._parser.ExternalEntityRefHandler = self.external_entity_ref - self._parser.NotationDeclHandler = self.notation_decl - try: - self._parser.SkippedEntityHandler = None - except AttributeError: - # some pyexpat versions do not support SkippedEntity - pass - - -def safe_minidom_parse_string(xml_string): - """Parse an XML string using minidom safely. - - """ - try: - return minidom.parseString(xml_string, parser=ProtectedExpatParser()) - except sax.SAXParseException: - raise expat.ExpatError() diff --git a/api/glazierapi/tests/__init__.py b/api/glazierapi/tests/__init__.py deleted file mode 100644 index 7d93825c..00000000 --- a/api/glazierapi/tests/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. diff --git a/api/glazierapi/tests/api/__init__.py b/api/glazierapi/tests/api/__init__.py deleted file mode 100644 index 207fa154..00000000 --- a/api/glazierapi/tests/api/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. \ No newline at end of file diff --git a/api/glazierapi/tests/api/simple_test.py b/api/glazierapi/tests/api/simple_test.py deleted file mode 100644 index 72034e29..00000000 --- a/api/glazierapi/tests/api/simple_test.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import unittest - - -class Test(unittest.TestCase): - def test(self): - assert True diff --git a/api/glazierapi/tests/sanity_tests.py b/api/glazierapi/tests/sanity_tests.py deleted file mode 100644 index 29e0df4f..00000000 --- a/api/glazierapi/tests/sanity_tests.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import unittest2 -from mock import MagicMock - -import glazierapi.api.v1.router as router - - -def my_mock(link, controller, action, conditions): - return [link, controller, action, conditions] - - -def func_mock(): - return True - - -class SanityUnitTests(unittest2.TestCase): - - def test_api(self): - router.webservers = MagicMock(create_resource=func_mock) - router.sessions = MagicMock(create_resource=func_mock) - router.active_directories = MagicMock(create_resource=func_mock) - router.environments = MagicMock(create_resource=func_mock) - mapper = MagicMock(connect=my_mock) - - object = router.API(mapper) - - assert object._router is not None diff --git a/api/glazierapi/utils.py b/api/glazierapi/utils.py deleted file mode 100644 index 26fa6ae1..00000000 --- a/api/glazierapi/utils.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import functools -import logging -from webob import exc -from glazierapi.db.models import Session -from glazierapi.db.session import get_session - -log = logging.getLogger(__name__) - - -def verify_session(func): - @functools.wraps(func) - def __inner(self, request, *args, **kwargs): - if hasattr(request, 'context') and request.context.session: - uw = get_session().query(Session) - configuration_session = uw.get(request.context.session) - - if configuration_session.state != 'open': - log.info('Session is already deployed') - raise exc.HTTPUnauthorized - else: - log.info('No session is supplied') - raise exc.HTTPUnauthorized - return func(self, request, *args, **kwargs) - return __inner diff --git a/api/glazierapi/version.py b/api/glazierapi/version.py deleted file mode 100644 index a0085f36..00000000 --- a/api/glazierapi/version.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from glazierapi.openstack.common import version as common_version - -version_info = common_version.VersionInfo('glazierapi') diff --git a/api/openstack-common.conf b/api/openstack-common.conf deleted file mode 100644 index 2bafe632..00000000 --- a/api/openstack-common.conf +++ /dev/null @@ -1,7 +0,0 @@ -[DEFAULT] - -# The list of modules to copy from openstack-common -modules=setup,wsgi,config,exception,gettextutils,importutils,jsonutils,log,xmlutils,sslutils,service,notifier,local,install_venv_common,version,timeutils,eventlet_backdoor,threadgroup,loopingcall,uuidutils - -# The base module to hold the copy of openstack.common -base=glazierapi \ No newline at end of file diff --git a/api/run_tests.sh b/api/run_tests.sh deleted file mode 100755 index ec7721b3..00000000 --- a/api/run_tests.sh +++ /dev/null @@ -1,123 +0,0 @@ -#!/bin/bash - -function usage { - echo "Usage: $0 [OPTION]..." - echo "Run Glazier API's test suite(s)" - echo "" - echo " -V, --virtual-env Always use virtualenv. Install automatically if not present" - echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local environment" - echo " -f, --force Force a clean re-build of the virtual environment. Useful when dependencies have been added." - echo " -u, --update Update the virtual environment with any newer package versions" - echo " --unittests-only Run unit tests only, exclude functional tests." - echo " -p, --pep8 Just run pep8" - echo " -P, --no-pep8 Don't run static code checks" - echo " -h, --help Print this usage message" - echo "" - echo "Note: with no options specified, the script will try to run the tests in a virtual environment," - echo " If no virtualenv is found, the script will ask if you would like to create one. If you " - echo " prefer to run tests NOT in a virtual environment, simply pass the -N option." - exit -} - -function process_option { - case "$1" in - -h|--help) usage;; - -V|--virtual-env) let always_venv=1; let never_venv=0;; - -N|--no-virtual-env) let always_venv=0; let never_venv=1;; - -p|--pep8) let just_pep8=1;; - -P|--no-pep8) let no_pep8=1;; - -f|--force) let force=1;; - -u|--update) update=1;; - --unittests-only) noseopts="$noseopts --exclude-dir=portas/tests/functional";; - -c|--coverage) noseopts="$noseopts --with-coverage --cover-package=portas";; - -*) noseopts="$noseopts $1";; - *) noseargs="$noseargs $1" - esac -} - -venv=.venv -with_venv=tools/with_venv.sh -always_venv=0 -never_venv=0 -force=0 -noseopts= -noseargs= -wrapper="" -just_pep8=0 -no_pep8=0 -update=0 - -export NOSE_WITH_OPENSTACK=1 -export NOSE_OPENSTACK_COLOR=1 -export NOSE_OPENSTACK_RED=0.05 -export NOSE_OPENSTACK_YELLOW=0.025 -export NOSE_OPENSTACK_SHOW_ELAPSED=1 -export NOSE_OPENSTACK_STDOUT=1 - -for arg in "$@"; do - process_option $arg -done - -function run_tests { - # Cleanup *pyc - ${wrapper} find . -type f -name "*.pyc" -delete - # Just run the test suites in current environment - ${wrapper} rm -f tests.sqlite - ${wrapper} $NOSETESTS -} - -function run_pep8 { - echo "Running pep8 ..." - PEP8_EXCLUDE=".venv,.tox,dist,doc,openstack" - PEP8_OPTIONS="--exclude=$PEP8_EXCLUDE --repeat" - PEP8_IGNORE="--ignore=E125,E126,E711,E712" - PEP8_INCLUDE=". bin/*" - - ${wrapper} pep8 $PEP8_OPTIONS $PEP8_INCLUDE $PEP8_IGNORE -} - - -NOSETESTS="nosetests $noseopts $noseargs" - -if [ $never_venv -eq 0 ] -then - # Remove the virtual environment if --force used - if [ $force -eq 1 ]; then - echo "Cleaning virtualenv..." - rm -rf ${venv} - fi - if [ $update -eq 1 ]; then - echo "Updating virtualenv..." - python tools/install_venv.py - fi - if [ -e ${venv} ]; then - wrapper="${with_venv}" - else - if [ $always_venv -eq 1 ]; then - # Automatically install the virtualenv - python tools/install_venv.py - wrapper="${with_venv}" - else - echo -e "No virtual environment found...create one? (Y/n) \c" - read use_ve - if [ "x$use_ve" = "xY" -o "x$use_ve" = "x" -o "x$use_ve" = "xy" ]; then - # Install the virtualenv and run the test suite in it - python tools/install_venv.py - wrapper=${with_venv} - fi - fi - fi -fi - -if [ $just_pep8 -eq 1 ]; then - run_pep8 - exit -fi - -run_tests || exit - -if [ -z "$noseargs" ]; then - if [ $no_pep8 -eq 0 ]; then - run_pep8 - fi -fi diff --git a/api/setup.cfg b/api/setup.cfg deleted file mode 100644 index 2b3b06e3..00000000 --- a/api/setup.cfg +++ /dev/null @@ -1,33 +0,0 @@ -[build_sphinx] -all_files = 1 -build-dir = doc/build -source-dir = doc/source - -[egg_info] -tag_build = -tag_date = 0 -tag_svn_revision = 0 - -[compile_catalog] -directory = glazierapi/locale -domain = glazierapi - -[update_catalog] -domain = glazierapi -output_dir = glazierapi/locale -input_file = glazierapi/locale/glazierapi.pot - -[extract_messages] -keywords = _ gettext ngettext l_ lazy_gettext -mapping_file = babel.cfg -output_file = glazierapi/locale/glazierapi.pot - -[nosetests] -# NOTE(jkoelker) To run the test suite under nose install the following -# coverage http://pypi.python.org/pypi/coverage -# tissue http://pypi.python.org/pypi/tissue (pep8 checker) -# openstack-nose https://github.com/jkoelker/openstack-nose -verbosity=2 -cover-package = portas -cover-html = true -cover-erase = true \ No newline at end of file diff --git a/api/setup.py b/api/setup.py deleted file mode 100644 index 01d3ce91..00000000 --- a/api/setup.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/python -# -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import setuptools - -from glazierapi.openstack.common import setup - -requires = setup.parse_requirements() -depend_links = setup.parse_dependency_links() -project = 'glazierapi' - -setuptools.setup( - name=project, - version=setup.get_version(project, '2013.1'), - description='The Glazier Project API', - license='Apache License (2.0)', - author='Mirantis, Inc', - author_email='smelikyan@mirantis.com', - url='http://glazierapi.mirantis.com/', - packages=setuptools.find_packages(exclude=['bin']), - test_suite='nose.collector', - cmdclass=setup.get_cmdclass(), - include_package_data=True, - install_requires=requires, - dependency_links=depend_links, - classifiers=[ - 'Development Status :: 4 - Beta', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: POSIX :: Linux', - 'Programming Language :: Python :: 2.7', - 'Environment :: No Input/Output (Daemon)', - 'Environment :: OpenStack', - ], - scripts=['bin/glazierapi-api'], - py_modules=[] -) diff --git a/api/tools/install_venv.py b/api/tools/install_venv.py deleted file mode 100644 index ecf3c45f..00000000 --- a/api/tools/install_venv.py +++ /dev/null @@ -1,75 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Copyright 2010 OpenStack LLC. -# Copyright 2013 IBM Corp. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Installation script for Glance's development virtualenv -""" - -import os -import subprocess -import sys - -import install_venv_common as install_venv - - -def print_help(): - help = """ - Glazier API development environment setup is complete. - - Glazier API development uses virtualenv to track and manage Python dependencies - while in development and testing. - - To activate the Glazier API virtualenv for the extent of your current shell session - you can run: - - $ source .venv/bin/activate - - Or, if you prefer, you can run commands in the virtualenv on a case by case - basis by running: - - $ tools/with_venv.sh <your command> - - Also, make test will automatically use the virtualenv. - """ - print help - - -def main(argv): - root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - venv = os.path.join(root, '.venv') - pip_requires = os.path.join(root, 'tools', 'pip-requires') - test_requires = os.path.join(root, 'tools', 'test-requires') - py_version = "python%s.%s" % (sys.version_info[0], sys.version_info[1]) - project = 'glazierapi' - install = install_venv.InstallVenv(root, venv, pip_requires, test_requires, - py_version, project) - options = install.parse_args(argv) - install.check_python_version() - install.check_dependencies() - install.create_virtualenv(no_site_packages=options.no_site_packages) - install.install_dependencies() - install.run_command([os.path.join(venv, 'bin/python'), - 'setup.py', 'develop']) - install.post_process() - print_help() - -if __name__ == '__main__': - main(sys.argv) diff --git a/api/tools/install_venv_common.py b/api/tools/install_venv_common.py deleted file mode 100644 index fd9076f0..00000000 --- a/api/tools/install_venv_common.py +++ /dev/null @@ -1,219 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 OpenStack, LLC -# Copyright 2013 IBM Corp. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Provides methods needed by installation script for OpenStack development -virtual environments. - -Synced in from openstack-common -""" - -import argparse -import os -import subprocess -import sys - - -class InstallVenv(object): - - def __init__(self, root, venv, pip_requires, test_requires, py_version, - project): - self.root = root - self.venv = venv - self.pip_requires = pip_requires - self.test_requires = test_requires - self.py_version = py_version - self.project = project - - def die(self, message, *args): - print >> sys.stderr, message % args - sys.exit(1) - - def check_python_version(self): - if sys.version_info < (2, 6): - self.die("Need Python Version >= 2.6") - - def run_command_with_code(self, cmd, redirect_output=True, - check_exit_code=True): - """Runs a command in an out-of-process shell. - - Returns the output of that command. Working directory is self.root. - """ - if redirect_output: - stdout = subprocess.PIPE - else: - stdout = None - - proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout) - output = proc.communicate()[0] - if check_exit_code and proc.returncode != 0: - self.die('Command "%s" failed.\n%s', ' '.join(cmd), output) - return (output, proc.returncode) - - def run_command(self, cmd, redirect_output=True, check_exit_code=True): - return self.run_command_with_code(cmd, redirect_output, - check_exit_code)[0] - - def get_distro(self): - if (os.path.exists('/etc/fedora-release') or - os.path.exists('/etc/redhat-release')): - return Fedora(self.root, self.venv, self.pip_requires, - self.test_requires, self.py_version, self.project) - else: - return Distro(self.root, self.venv, self.pip_requires, - self.test_requires, self.py_version, self.project) - - def check_dependencies(self): - self.get_distro().install_virtualenv() - - def create_virtualenv(self, no_site_packages=True): - """Creates the virtual environment and installs PIP. - - Creates the virtual environment and installs PIP only into the - virtual environment. - """ - if not os.path.isdir(self.venv): - print 'Creating venv...', - if no_site_packages: - self.run_command(['virtualenv', '-q', '--no-site-packages', - self.venv]) - else: - self.run_command(['virtualenv', '-q', self.venv]) - print 'done.' - print 'Installing pip in venv...', - if not self.run_command(['tools/with_venv.sh', 'easy_install', - 'pip>1.0']).strip(): - self.die("Failed to install pip.") - print 'done.' - else: - print "venv already exists..." - pass - - def pip_install(self, *args): - self.run_command(['tools/with_venv.sh', - 'pip', 'install', '--upgrade'] + list(args), - redirect_output=False) - - def install_dependencies(self): - print 'Installing dependencies with pip (this can take a while)...' - - # First things first, make sure our venv has the latest pip and - # distribute. - # NOTE: we keep pip at version 1.1 since the most recent version causes - # the .venv creation to fail. See: - # https://bugs.launchpad.net/nova/+bug/1047120 - self.pip_install('pip==1.1') - self.pip_install('distribute') - - # Install greenlet by hand - just listing it in the requires file does - # not - # get it installed in the right order - self.pip_install('greenlet') - - self.pip_install('-r', self.pip_requires) - self.pip_install('-r', self.test_requires) - - def post_process(self): - self.get_distro().post_process() - - def parse_args(self, argv): - """Parses command-line arguments.""" - parser = argparse.ArgumentParser() - parser.add_argument('-n', '--no-site-packages', - action='store_true', - help="Do not inherit packages from global Python " - "install") - return parser.parse_args(argv[1:]) - - -class Distro(InstallVenv): - - def check_cmd(self, cmd): - return bool(self.run_command(['which', cmd], - check_exit_code=False).strip()) - - def install_virtualenv(self): - if self.check_cmd('virtualenv'): - return - - if self.check_cmd('easy_install'): - print 'Installing virtualenv via easy_install...', - if self.run_command(['easy_install', 'virtualenv']): - print 'Succeeded' - return - else: - print 'Failed' - - self.die('ERROR: virtualenv not found.\n\n%s development' - ' requires virtualenv, please install it using your' - ' favorite package management tool' % self.project) - - def post_process(self): - """Any distribution-specific post-processing gets done here. - - In particular, this is useful for applying patches to code inside - the venv. - """ - pass - - -class Fedora(Distro): - """This covers all Fedora-based distributions. - - Includes: Fedora, RHEL, CentOS, Scientific Linux - """ - - def check_pkg(self, pkg): - return self.run_command_with_code(['rpm', '-q', pkg], - check_exit_code=False)[1] == 0 - - def yum_install(self, pkg, **kwargs): - print "Attempting to install '%s' via yum" % pkg - self.run_command(['sudo', 'yum', 'install', '-y', pkg], **kwargs) - - def apply_patch(self, originalfile, patchfile): - self.run_command(['patch', originalfile, patchfile]) - - def install_virtualenv(self): - if self.check_cmd('virtualenv'): - return - - if not self.check_pkg('python-virtualenv'): - self.yum_install('python-virtualenv', check_exit_code=False) - - super(Fedora, self).install_virtualenv() - - def post_process(self): - """Workaround for a bug in eventlet. - - This currently affects RHEL6.1, but the fix can safely be - applied to all RHEL and Fedora distributions. - - This can be removed when the fix is applied upstream. - - Nova: https://bugs.launchpad.net/nova/+bug/884915 - Upstream: https://bitbucket.org/which_linden/eventlet/issue/89 - """ - - # Install "patch" program if it's not there - if not self.check_pkg('patch'): - self.yum_install('patch') - - # Apply the eventlet patch - self.apply_patch(os.path.join(self.venv, 'lib', self.py_version, - 'site-packages', - 'eventlet/green/subprocess.py'), - 'contrib/redhat-eventlet.patch') diff --git a/api/tools/pip-requires b/api/tools/pip-requires deleted file mode 100644 index 2b71bd97..00000000 --- a/api/tools/pip-requires +++ /dev/null @@ -1,31 +0,0 @@ -Babel -SQLAlchemy>=0.7,<=0.7.9 -anyjson -eventlet>=0.9.12 -PasteDeploy -routes -WebOb>=1.2 -wsgiref -argparse -boto -sqlalchemy-migrate>=0.7 -httplib2 -kombu -pycrypto>=2.1.0alpha1 -iso8601>=0.1.4 -amqplib - -# Note you will need gcc buildtools installed and must -# have installed libxml headers for lxml to be successfully -# installed using pip, therefore you will need to install the -# libxml2-dev and libxslt-dev Ubuntu packages. -lxml - -# For paste.util.template used in keystone.common.template -Paste - -passlib -jsonschema -python-keystoneclient>=0.2.0 - -http://tarballs.openstack.org/oslo-config/oslo-config-2013.1b4.tar.gz#egg=oslo-config diff --git a/api/tools/test-requires b/api/tools/test-requires deleted file mode 100644 index a7066f1e..00000000 --- a/api/tools/test-requires +++ /dev/null @@ -1,20 +0,0 @@ -# Packages needed for dev testing -distribute>=0.6.24 - -# Needed for testing -unittest2 -coverage -fixtures>=0.3.12 -mox -nose -nose-exclude -openstack.nose_plugin>=0.7 -nosehtmloutput>=0.0.3 -pep8==1.3.3 -sphinx>=1.1.2 -requests -testtools>=0.9.22 - -# Optional packages that should be installed when testing -xattr>=0.6.0 -pysendfile==2.0.0 \ No newline at end of file diff --git a/api/tools/with_venv.sh b/api/tools/with_venv.sh deleted file mode 100755 index c8d2940f..00000000 --- a/api/tools/with_venv.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -TOOLS=`dirname $0` -VENV=$TOOLS/../.venv -source $VENV/bin/activate && $@ diff --git a/api/tox.ini b/api/tox.ini deleted file mode 100644 index 47fc9d4d..00000000 --- a/api/tox.ini +++ /dev/null @@ -1,46 +0,0 @@ -[tox] -envlist = py27,pep8 - -[testenv] -setenv = VIRTUAL_ENV={envdir} - NOSE_WITH_OPENSTACK=1 - NOSE_OPENSTACK_COLOR=1 - NOSE_OPENSTACK_RED=0.05 - NOSE_OPENSTACK_YELLOW=0.025 - NOSE_OPENSTACK_SHOW_ELAPSED=1 -deps = -r{toxinidir}/tools/pip-requires - -r{toxinidir}/tools/test-requires -commands = nosetests - -[testenv:pep8] -deps = pep8==1.3.3 -commands = pep8 --repeat --show-source glazierapi setup.py - -[testenv:venv] -commands = {posargs} - -[testenv:cover] -commands = nosetests --cover-erase --cover-package=glazierapi --with-xcoverage - -[tox:jenkins] -downloadcache = ~/cache/pip - -[testenv:jenkins26] -basepython = python2.6 -setenv = NOSE_WITH_XUNIT=1 -deps = file://{toxinidir}/.cache.bundle - -[testenv:jenkins27] -basepython = python2.7 -setenv = NOSE_WITH_XUNIT=1 -deps = file://{toxinidir}/.cache.bundle - -[testenv:jenkinscover] -deps = file://{toxinidir}/.cache.bundle -setenv = NOSE_WITH_XUNIT=1 -commands = nosetests --cover-erase --cover-package=glazierapi --with-xcoverage - -[testenv:jenkinsvenv] -deps = file://{toxinidir}/.cache.bundle -setenv = NOSE_WITH_XUNIT=1 -commands = {posargs} diff --git a/conductor/.gitignore b/conductor/.gitignore deleted file mode 100644 index 61331307..00000000 --- a/conductor/.gitignore +++ /dev/null @@ -1,20 +0,0 @@ -#IntelJ Idea -.idea/ - -#virtualenv -.venv/ - -#Build results -build/ -dist/ -*.egg-info/ - -#Python -*.pyc - -#Translation build -*.mo -*.pot - -#SQLite Database files -*.sqlite \ No newline at end of file diff --git a/conductor/README.rst b/conductor/README.rst deleted file mode 100644 index 38cfa96f..00000000 --- a/conductor/README.rst +++ /dev/null @@ -1,8 +0,0 @@ -Glazier Conductor README -======================== -Conductor is an Glazier orchestration engine that transforms object model sent by -REST API service into a series of Heat and Glazier-Agent commands - -SEE ALSO --------- -* `Keero <http://keero.mirantis.com>`__ diff --git a/conductor/babel.cfg b/conductor/babel.cfg deleted file mode 100644 index efceab81..00000000 --- a/conductor/babel.cfg +++ /dev/null @@ -1 +0,0 @@ -[python: **.py] diff --git a/conductor/bin/conductor b/conductor/bin/conductor deleted file mode 100644 index e92f28a8..00000000 --- a/conductor/bin/conductor +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import sys - - -from conductor import config -from conductor.openstack.common import log -from conductor.openstack.common import service -from conductor.app import ConductorWorkflowService - -if __name__ == '__main__': - try: - config.parse_args() - log.setup('conductor') - launcher = service.ServiceLauncher() - launcher.launch_service(ConductorWorkflowService()) - launcher.wait() - except RuntimeError, e: - sys.stderr.write("ERROR: %s\n" % e) - sys.exit(1) diff --git a/conductor/conductor/__init__.py b/conductor/conductor/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/conductor/conductor/app.py b/conductor/conductor/app.py deleted file mode 100644 index e70f59e8..00000000 --- a/conductor/conductor/app.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import datetime -import glob -import sys -import traceback - -import anyjson -from conductor.openstack.common import service -from workflow import Workflow -from commands.dispatcher import CommandDispatcher -from openstack.common import log as logging -from config import Config -import reporting -import rabbitmq - -import windows_agent -import cloud_formation - -config = Config(sys.argv[1] if len(sys.argv) > 1 else None) - -log = logging.getLogger(__name__) - - -def task_received(task, message_id): - with rabbitmq.RmqClient() as rmqclient: - try: - log.info('Starting processing task {0}: {1}'.format( - message_id, anyjson.dumps(task))) - reporter = reporting.Reporter(rmqclient, message_id, task['id']) - - command_dispatcher = CommandDispatcher( - task['name'], rmqclient, task['token'], task['tenant_id']) - workflows = [] - for path in glob.glob("data/workflows/*.xml"): - log.debug('Loading XML {0}'.format(path)) - workflow = Workflow(path, task, command_dispatcher, config, - reporter) - workflows.append(workflow) - - while True: - try: - while True: - result = False - for workflow in workflows: - if workflow.execute(): - result = True - if not result: - break - if not command_dispatcher.execute_pending(): - break - except Exception as ex: - log.exception(ex) - break - - command_dispatcher.close() - finally: - del task['token'] - result_msg = rabbitmq.Message() - result_msg.body = task - result_msg.id = message_id - - rmqclient.send(message=result_msg, key='task-results') - log.info('Finished processing task {0}. Result = {1}'.format( - message_id, anyjson.dumps(task))) - - -class ConductorWorkflowService(service.Service): - def __init__(self): - super(ConductorWorkflowService, self).__init__() - - def start(self): - super(ConductorWorkflowService, self).start() - self.tg.add_thread(self._start_rabbitmq) - - def stop(self): - super(ConductorWorkflowService, self).stop() - - def _start_rabbitmq(self): - while True: - try: - with rabbitmq.RmqClient() as rmq: - rmq.declare('tasks', 'tasks') - rmq.declare('task-results') - with rmq.open('tasks') as subscription: - while True: - msg = subscription.get_message() - self.tg.add_thread( - task_received, msg.body, msg.id) - except Exception as ex: - log.exception(ex) - diff --git a/conductor/conductor/cloud_formation.py b/conductor/conductor/cloud_formation.py deleted file mode 100644 index 7a47e56e..00000000 --- a/conductor/conductor/cloud_formation.py +++ /dev/null @@ -1,127 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import base64 -import config -import random -import string -import time - -import xml_code_engine - - -def update_cf_stack(engine, context, body, template, - mappings, arguments, **kwargs): - command_dispatcher = context['/commandDispatcher'] - - callback = lambda result: engine.evaluate_content( - body.find('success'), context) - - command_dispatcher.execute( - name='cf', command='CreateOrUpdate', template=template, - mappings=mappings, arguments=arguments, callback=callback) - - -def delete_cf_stack(engine, context, body, **kwargs): - command_dispatcher = context['/commandDispatcher'] - - callback = lambda result: engine.evaluate_content( - body.find('success'), context) - - command_dispatcher.execute( - name='cf', command='Delete', callback=callback) - - -def prepare_user_data(context, hostname, service, unit, - template='Default', **kwargs): - settings = config.CONF.rabbitmq - - with open('data/init.ps1') as init_script_file: - with open('data/templates/agent-config/{0}.template'.format( - template)) as template_file: - init_script = init_script_file.read() - template_data = template_file.read() - - replacements = { - '%RABBITMQ_HOST%': settings.host, - '%RABBITMQ_INPUT_QUEUE%': '-'.join( - [str(context['/dataSource']['name']), - str(service), str(unit)]).lower(), - '%RESULT_QUEUE%': '-execution-results-{0}'.format( - str(context['/dataSource']['name'])).lower(), - '%RABBITMQ_USER%': settings.login, - '%RABBITMQ_PASSWORD%': settings.password, - '%RABBITMQ_VHOST%': settings.virtual_host - } - - template_data = set_config_params(template_data, replacements) - - init_script = init_script.replace( - '%WINDOWS_AGENT_CONFIG_BASE64%', - base64.b64encode(template_data)) - - init_script = init_script.replace('%INTERNAL_HOSTNAME%', hostname) - - return init_script - - -def set_config_params(template_data, replacements): - for key in replacements: - template_data = template_data.replace(key, replacements[key]) - return template_data - - -counter = 0 - - -def int2base(x, base): - digs = string.digits + string.lowercase - if x < 0: - sign = -1 - elif x == 0: - return '0' - else: - sign = 1 - x *= sign - digits = [] - while x: - digits.append(digs[x % base]) - x /= base - if sign < 0: - digits.append('-') - digits.reverse() - return ''.join(digits) - - -def generate_hostname(**kwargs): - global counter - prefix = ''.join(random.choice(string.lowercase) for _ in range(5)) - timestamp = int2base(int(time.time() * 1000), 36)[:8] - suffix = int2base(counter, 36) - counter = (counter + 1) % 1296 - return prefix + timestamp + suffix - - -xml_code_engine.XmlCodeEngine.register_function( - update_cf_stack, "update-cf-stack") - -xml_code_engine.XmlCodeEngine.register_function( - delete_cf_stack, "delete-cf-stack") - -xml_code_engine.XmlCodeEngine.register_function( - prepare_user_data, "prepare-user-data") - -xml_code_engine.XmlCodeEngine.register_function( - generate_hostname, "generate-hostname") diff --git a/conductor/conductor/commands/__init__.py b/conductor/conductor/commands/__init__.py deleted file mode 100644 index 1fa2c573..00000000 --- a/conductor/conductor/commands/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import command diff --git a/conductor/conductor/commands/cloud_formation.py b/conductor/conductor/commands/cloud_formation.py deleted file mode 100644 index 9cc7a1ca..00000000 --- a/conductor/conductor/commands/cloud_formation.py +++ /dev/null @@ -1,188 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import anyjson -import eventlet -import types -import jsonpath - -from conductor.openstack.common import log as logging -import conductor.helpers -from command import CommandBase -import conductor.config -from heatclient.client import Client -import heatclient.exc -from keystoneclient.v2_0 import client as ksclient - -log = logging.getLogger(__name__) - - -class HeatExecutor(CommandBase): - def __init__(self, stack, token, tenant_id): - self._update_pending_list = [] - self._delete_pending_list = [] - self._stack = stack - settings = conductor.config.CONF.heat - - client = ksclient.Client(endpoint=settings.auth_url) - auth_data = client.tokens.authenticate( - tenant_id=tenant_id, - token=token) - - scoped_token = auth_data.id - - heat_url = jsonpath.jsonpath( - auth_data.serviceCatalog, - "$[?(@.name == 'heat')].endpoints[0].publicURL")[0] - - self._heat_client = Client( - '1', - heat_url, - token_only=True, - token=scoped_token) - - def execute(self, command, callback, **kwargs): - log.debug('Got command {0} on stack {1}'.format(command, self._stack)) - - if command == 'CreateOrUpdate': - return self._execute_create_update( - kwargs['template'], - kwargs['mappings'], - kwargs['arguments'], - callback) - elif command == 'Delete': - return self._execute_delete(callback) - - def _execute_create_update(self, template, mappings, arguments, callback): - with open('data/templates/cf/%s.template' % template) as template_file: - template_data = template_file.read() - - template_data = conductor.helpers.transform_json( - anyjson.loads(template_data), mappings) - - self._update_pending_list.append({ - 'template': template_data, - 'arguments': arguments, - 'callback': callback - }) - - def _execute_delete(self, callback): - self._delete_pending_list.append({ - 'callback': callback - }) - - def has_pending_commands(self): - return len(self._update_pending_list) + \ - len(self._delete_pending_list) > 0 - - def execute_pending(self): - r1 = self._execute_pending_updates() - r2 = self._execute_pending_deletes() - return r1 or r2 - - def _execute_pending_updates(self): - if not len(self._update_pending_list): - return False - - template, arguments = self._get_current_template() - stack_exists = (template != {}) - - for t in self._update_pending_list: - template = conductor.helpers.merge_dicts( - template, t['template'], max_levels=2) - arguments = conductor.helpers.merge_dicts( - arguments, t['arguments'], max_levels=1) - - log.info( - 'Executing heat template {0} with arguments {1} on stack {2}' - .format(anyjson.dumps(template), arguments, self._stack)) - - if stack_exists: - self._heat_client.stacks.update( - stack_id=self._stack, - parameters=arguments, - template=template) - log.debug( - 'Waiting for the stack {0} to be update'.format(self._stack)) - self._wait_state('UPDATE_COMPLETE') - log.info('Stack {0} updated'.format(self._stack)) - else: - self._heat_client.stacks.create( - stack_name=self._stack, - parameters=arguments, - template=template) - log.debug('Waiting for the stack {0} to be create'.format( - self._stack)) - self._wait_state('CREATE_COMPLETE') - log.info('Stack {0} created'.format(self._stack)) - - pending_list = self._update_pending_list - self._update_pending_list = [] - - for item in pending_list: - item['callback'](True) - - return True - - def _execute_pending_deletes(self): - if not len(self._delete_pending_list): - return False - - log.debug('Deleting stack {0}'.format(self._stack)) - try: - self._heat_client.stacks.delete( - stack_id=self._stack) - log.debug( - 'Waiting for the stack {0} to be deleted'.format(self._stack)) - self._wait_state(['DELETE_COMPLETE', '']) - log.info('Stack {0} deleted'.format(self._stack)) - except Exception as ex: - log.exception(ex) - - pending_list = self._delete_pending_list - self._delete_pending_list = [] - - for item in pending_list: - item['callback'](True) - return True - - def _get_current_template(self): - try: - stack_info = self._heat_client.stacks.get(stack_id=self._stack) - template = self._heat_client.stacks.template( - stack_id='{0}/{1}'.format(stack_info.stack_name, stack_info.id)) - return template, stack_info.parameters - except heatclient.exc.HTTPNotFound: - return {}, {} - - def _wait_state(self, state): - if isinstance(state, types.ListType): - states = state - else: - states = [state] - - while True: - try: - status = self._heat_client.stacks.get( - stack_id=self._stack).stack_status - except heatclient.exc.HTTPNotFound: - status = '' - - if 'IN_PROGRESS' in status: - eventlet.sleep(1) - continue - if status not in states: - raise EnvironmentError() - return diff --git a/conductor/conductor/commands/command.py b/conductor/conductor/commands/command.py deleted file mode 100644 index 606c7293..00000000 --- a/conductor/conductor/commands/command.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -class CommandBase(object): - def execute(self, **kwargs): - pass - - def execute_pending(self): - return False - - def has_pending_commands(self): - return False - - def close(self): - pass diff --git a/conductor/conductor/commands/dispatcher.py b/conductor/conductor/commands/dispatcher.py deleted file mode 100644 index 37014459..00000000 --- a/conductor/conductor/commands/dispatcher.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import command -import cloud_formation -import windows_agent - - -class CommandDispatcher(command.CommandBase): - def __init__(self, environment, rmqclient, token, tenant_id): - self._command_map = { - 'cf': cloud_formation.HeatExecutor(environment, token, tenant_id), - 'agent': windows_agent.WindowsAgentExecutor( - environment, rmqclient) - } - - def execute(self, name, **kwargs): - self._command_map[name].execute(**kwargs) - - def execute_pending(self): - result = False - for command in self._command_map.values(): - result |= command.execute_pending() - - return result - - def has_pending_commands(self): - result = False - for command in self._command_map.values(): - result |= command.has_pending_commands() - - return result - - def close(self): - for t in self._command_map.values(): - t.close() diff --git a/conductor/conductor/commands/windows_agent.py b/conductor/conductor/commands/windows_agent.py deleted file mode 100644 index ea66b718..00000000 --- a/conductor/conductor/commands/windows_agent.py +++ /dev/null @@ -1,61 +0,0 @@ -import json -import uuid - -from conductor.openstack.common import log as logging -from conductor.rabbitmq import Message -import conductor.helpers -from command import CommandBase - -log = logging.getLogger(__name__) - - -class WindowsAgentExecutor(CommandBase): - def __init__(self, stack, rmqclient): - self._stack = stack - self._rmqclient = rmqclient - self._pending_list = [] - self._results_queue = '-execution-results-%s' % str(stack).lower() - rmqclient.declare(self._results_queue) - - def execute(self, template, mappings, host, service, callback): - with open('data/templates/agent/%s.template' % template) as file: - template_data = file.read() - - template_data = conductor.helpers.transform_json( - json.loads(template_data), mappings) - - id = str(uuid.uuid4()).lower() - host = ('%s-%s-%s' % (self._stack, service, host)).lower() - self._pending_list.append({ - 'id': id, - 'callback': callback - }) - - msg = Message() - msg.body = template_data - msg.id = id - self._rmqclient.declare(host) - self._rmqclient.send(message=msg, key=host) - log.info('Sending RMQ message {0} to {1} with id {2}'.format( - template_data, host, id)) - - def has_pending_commands(self): - return len(self._pending_list) > 0 - - def execute_pending(self): - if not self.has_pending_commands(): - return False - - with self._rmqclient.open(self._results_queue) as subscription: - while self.has_pending_commands(): - msg = subscription.get_message() - msg_id = msg.id.lower() - item, index = conductor.helpers.find( - lambda t: t['id'] == msg_id, self._pending_list) - if item: - self._pending_list.pop(index) - item['callback'](msg.body) - - return True - - diff --git a/conductor/conductor/config.py b/conductor/conductor/config.py deleted file mode 100644 index bb804dab..00000000 --- a/conductor/conductor/config.py +++ /dev/null @@ -1,210 +0,0 @@ -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Routines for configuring Glance -""" - -import logging -import logging.config -import logging.handlers -import os -import sys - -from oslo.config import cfg -from paste import deploy - -from conductor.version import version_info as version -from ConfigParser import SafeConfigParser - -paste_deploy_opts = [ - cfg.StrOpt('flavor'), - cfg.StrOpt('config_file'), -] - -rabbit_opts = [ - cfg.StrOpt('host', default='localhost'), - cfg.IntOpt('port', default=5672), - cfg.StrOpt('login', default='guest'), - cfg.StrOpt('password', default='guest'), - cfg.StrOpt('virtual_host', default='/'), -] - -heat_opts = [ - cfg.StrOpt('auth_url'), -] - -CONF = cfg.CONF -CONF.register_opts(paste_deploy_opts, group='paste_deploy') -CONF.register_opts(rabbit_opts, group='rabbitmq') -CONF.register_opts(heat_opts, group='heat') - - -CONF.import_opt('verbose', 'conductor.openstack.common.log') -CONF.import_opt('debug', 'conductor.openstack.common.log') -CONF.import_opt('log_dir', 'conductor.openstack.common.log') -CONF.import_opt('log_file', 'conductor.openstack.common.log') -CONF.import_opt('log_config', 'conductor.openstack.common.log') -CONF.import_opt('log_format', 'conductor.openstack.common.log') -CONF.import_opt('log_date_format', 'conductor.openstack.common.log') -CONF.import_opt('use_syslog', 'conductor.openstack.common.log') -CONF.import_opt('syslog_log_facility', 'conductor.openstack.common.log') - - -def parse_args(args=None, usage=None, default_config_files=None): - CONF(args=args, - project='conductor', - version=version.cached_version_string(), - usage=usage, - default_config_files=default_config_files) - - -def setup_logging(): - """ - Sets up the logging options for a log with supplied name - """ - - if CONF.log_config: - # Use a logging configuration file for all settings... - if os.path.exists(CONF.log_config): - logging.config.fileConfig(CONF.log_config) - return - else: - raise RuntimeError("Unable to locate specified logging " - "config file: %s" % CONF.log_config) - - root_logger = logging.root - if CONF.debug: - root_logger.setLevel(logging.DEBUG) - elif CONF.verbose: - root_logger.setLevel(logging.INFO) - else: - root_logger.setLevel(logging.WARNING) - - formatter = logging.Formatter(CONF.log_format, CONF.log_date_format) - - if CONF.use_syslog: - try: - facility = getattr(logging.handlers.SysLogHandler, - CONF.syslog_log_facility) - except AttributeError: - raise ValueError(_("Invalid syslog facility")) - - handler = logging.handlers.SysLogHandler(address='/dev/log', - facility=facility) - elif CONF.log_file: - logfile = CONF.log_file - if CONF.log_dir: - logfile = os.path.join(CONF.log_dir, logfile) - handler = logging.handlers.WatchedFileHandler(logfile) - else: - handler = logging.StreamHandler(sys.stdout) - - handler.setFormatter(formatter) - root_logger.addHandler(handler) - - -def _get_deployment_flavor(): - """ - Retrieve the paste_deploy.flavor config item, formatted appropriately - for appending to the application name. - """ - flavor = CONF.paste_deploy.flavor - return '' if not flavor else ('-' + flavor) - - -def _get_paste_config_path(): - paste_suffix = '-paste.ini' - conf_suffix = '.conf' - if CONF.config_file: - # Assume paste config is in a paste.ini file corresponding - # to the last config file - path = CONF.config_file[-1].replace(conf_suffix, paste_suffix) - else: - path = CONF.prog + '-paste.ini' - return CONF.find_file(os.path.basename(path)) - - -def _get_deployment_config_file(): - """ - Retrieve the deployment_config_file config item, formatted as an - absolute pathname. - """ - path = CONF.paste_deploy.config_file - if not path: - path = _get_paste_config_path() - if not path: - msg = "Unable to locate paste config file for %s." % CONF.prog - raise RuntimeError(msg) - return os.path.abspath(path) - - -def load_paste_app(app_name=None): - """ - Builds and returns a WSGI app from a paste config file. - - We assume the last config file specified in the supplied ConfigOpts - object is the paste config file. - - :param app_name: name of the application to load - - :raises RuntimeError when config file cannot be located or application - cannot be loaded from config file - """ - if app_name is None: - app_name = CONF.prog - - # append the deployment flavor to the application name, - # in order to identify the appropriate paste pipeline - app_name += _get_deployment_flavor() - - conf_file = _get_deployment_config_file() - - try: - logger = logging.getLogger(__name__) - logger.debug(_("Loading %(app_name)s from %(conf_file)s"), - {'conf_file': conf_file, 'app_name': app_name}) - - app = deploy.loadapp("config:%s" % conf_file, name=app_name) - - # Log the options used when starting if we're in debug mode... - if CONF.debug: - CONF.log_opt_values(logger, logging.DEBUG) - - return app - except (LookupError, ImportError), e: - msg = _("Unable to load %(app_name)s from " - "configuration file %(conf_file)s." - "\nGot: %(e)r") % locals() - logger.error(msg) - raise RuntimeError(msg) - - -class Config(object): - CONFIG_PATH = './etc/app.config' - - def __init__(self, filename=None): - self.config = SafeConfigParser() - self.config.read(filename or self.CONFIG_PATH) - - def get_setting(self, section, name, default=None): - if not self.config.has_option(section, name): - return default - return self.config.get(section, name) - - def __getitem__(self, item): - parts = item.rsplit('.', 1) - return self.get_setting( - parts[0] if len(parts) == 2 else 'DEFAULT', parts[-1]) diff --git a/conductor/conductor/function_context.py b/conductor/conductor/function_context.py deleted file mode 100644 index e210fd7b..00000000 --- a/conductor/conductor/function_context.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -class Context(object): - def __init__(self, parent=None): - self._parent = parent - self._data = None - - def _get_data(self): - if self._data is None: - self._data = {} if self._parent is None \ - else self._parent._get_data().copy() - return self._data - - def __getitem__(self, item): - context, path = self._parseContext(item) - return context._get_data().get(path) - - def __setitem__(self, key, value): - context, path = self._parseContext(key) - context._get_data()[path] = value - - def _parseContext(self, path): - context = self - index = 0 - for c in path: - if c == ':' and context._parent is not None: - context = context._parent - elif c == '/': - while context._parent is not None: - context = context._parent - else: - break - - index += 1 - - return context, path[index:] - - def assign_from(self, context, copy=False): - self._parent = context._parent - self._data = context._data - if copy and self._data is not None: - self._data = self._data.copy() - - @property - def parent(self): - return self._parent - - def __str__(self): - if self._data is not None: - return str(self._data) - if self._parent: - return str(self._parent) - return str({}) diff --git a/conductor/conductor/helpers.py b/conductor/conductor/helpers.py deleted file mode 100644 index da196560..00000000 --- a/conductor/conductor/helpers.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import types - - -def transform_json(json, mappings): - if isinstance(json, types.ListType): - return [transform_json(t, mappings) for t in json] - - if isinstance(json, types.DictionaryType): - result = {} - for key, value in json.items(): - result[transform_json(key, mappings)] = \ - transform_json(value, mappings) - return result - - if isinstance(json, types.StringTypes) and json.startswith('$'): - value = mappings.get(json[1:]) - if value is not None: - return value - - return json - - -def merge_dicts(dict1, dict2, max_levels=0): - result = {} - for key, value in dict1.items(): - result[key] = value - if key in dict2: - other_value = dict2[key] - if max_levels == 1 or not isinstance( - other_value, types.DictionaryType): - result[key] = other_value - else: - result[key] = merge_dicts( - value, other_value, - 0 if max_levels == 0 else max_levels - 1) - for key, value in dict2.items(): - if key not in result: - result[key] = value - return result - - -def find(f, seq): - """Return first item in sequence where f(item) == True.""" - index = 0 - for item in seq: - if f(item): - return item, index - index += 1 - return None, -1 diff --git a/conductor/conductor/openstack/__init__.py b/conductor/conductor/openstack/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/conductor/conductor/openstack/common/__init__.py b/conductor/conductor/openstack/common/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/conductor/conductor/openstack/common/eventlet_backdoor.py b/conductor/conductor/openstack/common/eventlet_backdoor.py deleted file mode 100644 index c0ad460f..00000000 --- a/conductor/conductor/openstack/common/eventlet_backdoor.py +++ /dev/null @@ -1,87 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (c) 2012 OpenStack Foundation. -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import gc -import pprint -import sys -import traceback - -import eventlet -import eventlet.backdoor -import greenlet -from oslo.config import cfg - -eventlet_backdoor_opts = [ - cfg.IntOpt('backdoor_port', - default=None, - help='port for eventlet backdoor to listen') -] - -CONF = cfg.CONF -CONF.register_opts(eventlet_backdoor_opts) - - -def _dont_use_this(): - print "Don't use this, just disconnect instead" - - -def _find_objects(t): - return filter(lambda o: isinstance(o, t), gc.get_objects()) - - -def _print_greenthreads(): - for i, gt in enumerate(_find_objects(greenlet.greenlet)): - print i, gt - traceback.print_stack(gt.gr_frame) - print - - -def _print_nativethreads(): - for threadId, stack in sys._current_frames().items(): - print threadId - traceback.print_stack(stack) - print - - -def initialize_if_enabled(): - backdoor_locals = { - 'exit': _dont_use_this, # So we don't exit the entire process - 'quit': _dont_use_this, # So we don't exit the entire process - 'fo': _find_objects, - 'pgt': _print_greenthreads, - 'pnt': _print_nativethreads, - } - - if CONF.backdoor_port is None: - return None - - # NOTE(johannes): The standard sys.displayhook will print the value of - # the last expression and set it to __builtin__._, which overwrites - # the __builtin__._ that gettext sets. Let's switch to using pprint - # since it won't interact poorly with gettext, and it's easier to - # read the output too. - def displayhook(val): - if val is not None: - pprint.pprint(val) - sys.displayhook = displayhook - - sock = eventlet.listen(('localhost', CONF.backdoor_port)) - port = sock.getsockname()[1] - eventlet.spawn_n(eventlet.backdoor.backdoor_server, sock, - locals=backdoor_locals) - return port diff --git a/conductor/conductor/openstack/common/exception.py b/conductor/conductor/openstack/common/exception.py deleted file mode 100644 index 5890c582..00000000 --- a/conductor/conductor/openstack/common/exception.py +++ /dev/null @@ -1,142 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Exceptions common to OpenStack projects -""" - -import logging - -from conductor.openstack.common.gettextutils import _ - -_FATAL_EXCEPTION_FORMAT_ERRORS = False - - -class Error(Exception): - def __init__(self, message=None): - super(Error, self).__init__(message) - - -class ApiError(Error): - def __init__(self, message='Unknown', code='Unknown'): - self.message = message - self.code = code - super(ApiError, self).__init__('%s: %s' % (code, message)) - - -class NotFound(Error): - pass - - -class UnknownScheme(Error): - - msg = "Unknown scheme '%s' found in URI" - - def __init__(self, scheme): - msg = self.__class__.msg % scheme - super(UnknownScheme, self).__init__(msg) - - -class BadStoreUri(Error): - - msg = "The Store URI %s was malformed. Reason: %s" - - def __init__(self, uri, reason): - msg = self.__class__.msg % (uri, reason) - super(BadStoreUri, self).__init__(msg) - - -class Duplicate(Error): - pass - - -class NotAuthorized(Error): - pass - - -class NotEmpty(Error): - pass - - -class Invalid(Error): - pass - - -class BadInputError(Exception): - """Error resulting from a client sending bad input to a server""" - pass - - -class MissingArgumentError(Error): - pass - - -class DatabaseMigrationError(Error): - pass - - -class ClientConnectionError(Exception): - """Error resulting from a client connecting to a server""" - pass - - -def wrap_exception(f): - def _wrap(*args, **kw): - try: - return f(*args, **kw) - except Exception, e: - if not isinstance(e, Error): - #exc_type, exc_value, exc_traceback = sys.exc_info() - logging.exception(_('Uncaught exception')) - #logging.error(traceback.extract_stack(exc_traceback)) - raise Error(str(e)) - raise - _wrap.func_name = f.func_name - return _wrap - - -class OpenstackException(Exception): - """ - Base Exception - - To correctly use this class, inherit from it and define - a 'message' property. That message will get printf'd - with the keyword arguments provided to the constructor. - """ - message = "An unknown exception occurred" - - def __init__(self, **kwargs): - try: - self._error_string = self.message % kwargs - - except Exception as e: - if _FATAL_EXCEPTION_FORMAT_ERRORS: - raise e - else: - # at least get the core message out if something happened - self._error_string = self.message - - def __str__(self): - return self._error_string - - -class MalformedRequestBody(OpenstackException): - message = "Malformed message body: %(reason)s" - - -class InvalidContentType(OpenstackException): - message = "Invalid content type %(content_type)s" diff --git a/conductor/conductor/openstack/common/gettextutils.py b/conductor/conductor/openstack/common/gettextutils.py deleted file mode 100644 index 3a81206c..00000000 --- a/conductor/conductor/openstack/common/gettextutils.py +++ /dev/null @@ -1,33 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2012 Red Hat, Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -gettext for openstack-common modules. - -Usual usage in an openstack.common module: - - from conductor.openstack.common.gettextutils import _ -""" - -import gettext - - -t = gettext.translation('conductor', 'locale', fallback=True) - - -def _(msg): - return t.ugettext(msg) diff --git a/conductor/conductor/openstack/common/importutils.py b/conductor/conductor/openstack/common/importutils.py deleted file mode 100644 index 3bd277f4..00000000 --- a/conductor/conductor/openstack/common/importutils.py +++ /dev/null @@ -1,67 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Import related utilities and helper functions. -""" - -import sys -import traceback - - -def import_class(import_str): - """Returns a class from a string including module and class""" - mod_str, _sep, class_str = import_str.rpartition('.') - try: - __import__(mod_str) - return getattr(sys.modules[mod_str], class_str) - except (ValueError, AttributeError): - raise ImportError('Class %s cannot be found (%s)' % - (class_str, - traceback.format_exception(*sys.exc_info()))) - - -def import_object(import_str, *args, **kwargs): - """Import a class and return an instance of it.""" - return import_class(import_str)(*args, **kwargs) - - -def import_object_ns(name_space, import_str, *args, **kwargs): - """ - Import a class and return an instance of it, first by trying - to find the class in a default namespace, then failing back to - a full path if not found in the default namespace. - """ - import_value = "%s.%s" % (name_space, import_str) - try: - return import_class(import_value)(*args, **kwargs) - except ImportError: - return import_class(import_str)(*args, **kwargs) - - -def import_module(import_str): - """Import a module.""" - __import__(import_str) - return sys.modules[import_str] - - -def try_import(import_str, default=None): - """Try to import a module and if it fails return default.""" - try: - return import_module(import_str) - except ImportError: - return default diff --git a/conductor/conductor/openstack/common/jsonutils.py b/conductor/conductor/openstack/common/jsonutils.py deleted file mode 100644 index 4d3ddd05..00000000 --- a/conductor/conductor/openstack/common/jsonutils.py +++ /dev/null @@ -1,141 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# Copyright 2011 Justin Santa Barbara -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -''' -JSON related utilities. - -This module provides a few things: - - 1) A handy function for getting an object down to something that can be - JSON serialized. See to_primitive(). - - 2) Wrappers around loads() and dumps(). The dumps() wrapper will - automatically use to_primitive() for you if needed. - - 3) This sets up anyjson to use the loads() and dumps() wrappers if anyjson - is available. -''' - - -import datetime -import functools -import inspect -import itertools -import json -import xmlrpclib - -from conductor.openstack.common import timeutils - - -def to_primitive(value, convert_instances=False, convert_datetime=True, - level=0, max_depth=3): - """Convert a complex object into primitives. - - Handy for JSON serialization. We can optionally handle instances, - but since this is a recursive function, we could have cyclical - data structures. - - To handle cyclical data structures we could track the actual objects - visited in a set, but not all objects are hashable. Instead we just - track the depth of the object inspections and don't go too deep. - - Therefore, convert_instances=True is lossy ... be aware. - - """ - nasty = [inspect.ismodule, inspect.isclass, inspect.ismethod, - inspect.isfunction, inspect.isgeneratorfunction, - inspect.isgenerator, inspect.istraceback, inspect.isframe, - inspect.iscode, inspect.isbuiltin, inspect.isroutine, - inspect.isabstract] - for test in nasty: - if test(value): - return unicode(value) - - # value of itertools.count doesn't get caught by inspects - # above and results in infinite loop when list(value) is called. - if type(value) == itertools.count: - return unicode(value) - - # FIXME(vish): Workaround for LP bug 852095. Without this workaround, - # tests that raise an exception in a mocked method that - # has a @wrap_exception with a notifier will fail. If - # we up the dependency to 0.5.4 (when it is released) we - # can remove this workaround. - if getattr(value, '__module__', None) == 'mox': - return 'mock' - - if level > max_depth: - return '?' - - # The try block may not be necessary after the class check above, - # but just in case ... - try: - recursive = functools.partial(to_primitive, - convert_instances=convert_instances, - convert_datetime=convert_datetime, - level=level, - max_depth=max_depth) - # It's not clear why xmlrpclib created their own DateTime type, but - # for our purposes, make it a datetime type which is explicitly - # handled - if isinstance(value, xmlrpclib.DateTime): - value = datetime.datetime(*tuple(value.timetuple())[:6]) - - if isinstance(value, (list, tuple)): - return [recursive(v) for v in value] - elif isinstance(value, dict): - return dict((k, recursive(v)) for k, v in value.iteritems()) - elif convert_datetime and isinstance(value, datetime.datetime): - return timeutils.strtime(value) - elif hasattr(value, 'iteritems'): - return recursive(dict(value.iteritems()), level=level + 1) - elif hasattr(value, '__iter__'): - return recursive(list(value)) - elif convert_instances and hasattr(value, '__dict__'): - # Likely an instance of something. Watch for cycles. - # Ignore class member vars. - return recursive(value.__dict__, level=level + 1) - else: - return value - except TypeError: - # Class objects are tricky since they may define something like - # __iter__ defined but it isn't callable as list(). - return unicode(value) - - -def dumps(value, default=to_primitive, **kwargs): - return json.dumps(value, default=default, **kwargs) - - -def loads(s): - return json.loads(s) - - -def load(s): - return json.load(s) - - -try: - import anyjson -except ImportError: - pass -else: - anyjson._modules.append((__name__, 'dumps', TypeError, - 'loads', ValueError, 'load')) - anyjson.force_implementation(__name__) diff --git a/conductor/conductor/openstack/common/local.py b/conductor/conductor/openstack/common/local.py deleted file mode 100644 index f1bfc824..00000000 --- a/conductor/conductor/openstack/common/local.py +++ /dev/null @@ -1,48 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Greenthread local storage of variables using weak references""" - -import weakref - -from eventlet import corolocal - - -class WeakLocal(corolocal.local): - def __getattribute__(self, attr): - rval = corolocal.local.__getattribute__(self, attr) - if rval: - # NOTE(mikal): this bit is confusing. What is stored is a weak - # reference, not the value itself. We therefore need to lookup - # the weak reference and return the inner value here. - rval = rval() - return rval - - def __setattr__(self, attr, value): - value = weakref.ref(value) - return corolocal.local.__setattr__(self, attr, value) - - -# NOTE(mikal): the name "store" should be deprecated in the future -store = WeakLocal() - -# A "weak" store uses weak references and allows an object to fall out of scope -# when it falls out of scope in the code that uses the thread local storage. A -# "strong" store will hold a reference to the object so that it never falls out -# of scope. -weak_store = WeakLocal() -strong_store = corolocal.local diff --git a/conductor/conductor/openstack/common/log.py b/conductor/conductor/openstack/common/log.py deleted file mode 100644 index d8cd9fa0..00000000 --- a/conductor/conductor/openstack/common/log.py +++ /dev/null @@ -1,543 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack Foundation. -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Openstack logging handler. - -This module adds to logging functionality by adding the option to specify -a context object when calling the various log methods. If the context object -is not specified, default formatting is used. Additionally, an instance uuid -may be passed as part of the log message, which is intended to make it easier -for admins to find messages related to a specific instance. - -It also allows setting of formatting information through conf. - -""" - -import ConfigParser -import cStringIO -import inspect -import itertools -import logging -import logging.config -import logging.handlers -import os -import stat -import sys -import traceback - -from oslo.config import cfg - -from conductor.openstack.common.gettextutils import _ -from conductor.openstack.common import jsonutils -from conductor.openstack.common import local -from conductor.openstack.common import notifier - - -_DEFAULT_LOG_FORMAT = "%(asctime)s %(levelname)8s [%(name)s] %(message)s" -_DEFAULT_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S" - -common_cli_opts = [ - cfg.BoolOpt('debug', - short='d', - default=False, - help='Print debugging output (set logging level to ' - 'DEBUG instead of default WARNING level).'), - cfg.BoolOpt('verbose', - short='v', - default=False, - help='Print more verbose output (set logging level to ' - 'INFO instead of default WARNING level).'), -] - -logging_cli_opts = [ - cfg.StrOpt('log-config', - metavar='PATH', - help='If this option is specified, the logging configuration ' - 'file specified is used and overrides any other logging ' - 'options specified. Please see the Python logging module ' - 'documentation for details on logging configuration ' - 'files.'), - cfg.StrOpt('log-format', - default=_DEFAULT_LOG_FORMAT, - metavar='FORMAT', - help='A logging.Formatter log message format string which may ' - 'use any of the available logging.LogRecord attributes. ' - 'Default: %(default)s'), - cfg.StrOpt('log-date-format', - default=_DEFAULT_LOG_DATE_FORMAT, - metavar='DATE_FORMAT', - help='Format string for %%(asctime)s in log records. ' - 'Default: %(default)s'), - cfg.StrOpt('log-file', - metavar='PATH', - deprecated_name='logfile', - help='(Optional) Name of log file to output to. ' - 'If no default is set, logging will go to stdout.'), - cfg.StrOpt('log-dir', - deprecated_name='logdir', - help='(Optional) The base directory used for relative ' - '--log-file paths'), - cfg.BoolOpt('use-syslog', - default=False, - help='Use syslog for logging.'), - cfg.StrOpt('syslog-log-facility', - default='LOG_USER', - help='syslog facility to receive log lines') -] - -generic_log_opts = [ - cfg.BoolOpt('use_stderr', - default=True, - help='Log output to standard error'), - cfg.StrOpt('logfile_mode', - default='0644', - help='Default file mode used when creating log files'), -] - -log_opts = [ - cfg.StrOpt('logging_context_format_string', - default='%(asctime)s.%(msecs)03d %(levelname)s %(name)s ' - '[%(request_id)s %(user)s %(tenant)s] %(instance)s' - '%(message)s', - help='format string to use for log messages with context'), - cfg.StrOpt('logging_default_format_string', - default='%(asctime)s.%(msecs)03d %(process)d %(levelname)s ' - '%(name)s [-] %(instance)s%(message)s', - help='format string to use for log messages without context'), - cfg.StrOpt('logging_debug_format_suffix', - default='%(funcName)s %(pathname)s:%(lineno)d', - help='data to append to log format when level is DEBUG'), - cfg.StrOpt('logging_exception_prefix', - default='%(asctime)s.%(msecs)03d %(process)d TRACE %(name)s ' - '%(instance)s', - help='prefix each line of exception output with this format'), - cfg.ListOpt('default_log_levels', - default=[ - 'amqplib=WARN', - 'sqlalchemy=WARN', - 'boto=WARN', - 'suds=INFO', - 'keystone=INFO', - 'eventlet.wsgi.server=WARN' - ], - help='list of logger=LEVEL pairs'), - cfg.BoolOpt('publish_errors', - default=False, - help='publish error events'), - cfg.BoolOpt('fatal_deprecations', - default=False, - help='make deprecations fatal'), - - # NOTE(mikal): there are two options here because sometimes we are handed - # a full instance (and could include more information), and other times we - # are just handed a UUID for the instance. - cfg.StrOpt('instance_format', - default='[instance: %(uuid)s] ', - help='If an instance is passed with the log message, format ' - 'it like this'), - cfg.StrOpt('instance_uuid_format', - default='[instance: %(uuid)s] ', - help='If an instance UUID is passed with the log message, ' - 'format it like this'), -] - -CONF = cfg.CONF -CONF.register_cli_opts(common_cli_opts) -CONF.register_cli_opts(logging_cli_opts) -CONF.register_opts(generic_log_opts) -CONF.register_opts(log_opts) - -# our new audit level -# NOTE(jkoelker) Since we synthesized an audit level, make the logging -# module aware of it so it acts like other levels. -logging.AUDIT = logging.INFO + 1 -logging.addLevelName(logging.AUDIT, 'AUDIT') - - -try: - NullHandler = logging.NullHandler -except AttributeError: # NOTE(jkoelker) NullHandler added in Python 2.7 - class NullHandler(logging.Handler): - def handle(self, record): - pass - - def emit(self, record): - pass - - def createLock(self): - self.lock = None - - -def _dictify_context(context): - if context is None: - return None - if not isinstance(context, dict) and getattr(context, 'to_dict', None): - context = context.to_dict() - return context - - -def _get_binary_name(): - return os.path.basename(inspect.stack()[-1][1]) - - -def _get_log_file_path(binary=None): - logfile = CONF.log_file - logdir = CONF.log_dir - - if logfile and not logdir: - return logfile - - if logfile and logdir: - return os.path.join(logdir, logfile) - - if logdir: - binary = binary or _get_binary_name() - return '%s.log' % (os.path.join(logdir, binary),) - - -class ContextAdapter(logging.LoggerAdapter): - warn = logging.LoggerAdapter.warning - - def __init__(self, logger, project_name, version_string): - self.logger = logger - self.project = project_name - self.version = version_string - - def audit(self, msg, *args, **kwargs): - self.log(logging.AUDIT, msg, *args, **kwargs) - - def deprecated(self, msg, *args, **kwargs): - stdmsg = _("Deprecated: %s") % msg - if CONF.fatal_deprecations: - self.critical(stdmsg, *args, **kwargs) - raise DeprecatedConfig(msg=stdmsg) - else: - self.warn(stdmsg, *args, **kwargs) - - def process(self, msg, kwargs): - if 'extra' not in kwargs: - kwargs['extra'] = {} - extra = kwargs['extra'] - - context = kwargs.pop('context', None) - if not context: - context = getattr(local.store, 'context', None) - if context: - extra.update(_dictify_context(context)) - - instance = kwargs.pop('instance', None) - instance_extra = '' - if instance: - instance_extra = CONF.instance_format % instance - else: - instance_uuid = kwargs.pop('instance_uuid', None) - if instance_uuid: - instance_extra = (CONF.instance_uuid_format - % {'uuid': instance_uuid}) - extra.update({'instance': instance_extra}) - - extra.update({"project": self.project}) - extra.update({"version": self.version}) - extra['extra'] = extra.copy() - return msg, kwargs - - -class JSONFormatter(logging.Formatter): - def __init__(self, fmt=None, datefmt=None): - # NOTE(jkoelker) we ignore the fmt argument, but its still there - # since logging.config.fileConfig passes it. - self.datefmt = datefmt - - def formatException(self, ei, strip_newlines=True): - lines = traceback.format_exception(*ei) - if strip_newlines: - lines = [itertools.ifilter( - lambda x: x, - line.rstrip().splitlines()) for line in lines] - lines = list(itertools.chain(*lines)) - return lines - - def format(self, record): - message = {'message': record.getMessage(), - 'asctime': self.formatTime(record, self.datefmt), - 'name': record.name, - 'msg': record.msg, - 'args': record.args, - 'levelname': record.levelname, - 'levelno': record.levelno, - 'pathname': record.pathname, - 'filename': record.filename, - 'module': record.module, - 'lineno': record.lineno, - 'funcname': record.funcName, - 'created': record.created, - 'msecs': record.msecs, - 'relative_created': record.relativeCreated, - 'thread': record.thread, - 'thread_name': record.threadName, - 'process_name': record.processName, - 'process': record.process, - 'traceback': None} - - if hasattr(record, 'extra'): - message['extra'] = record.extra - - if record.exc_info: - message['traceback'] = self.formatException(record.exc_info) - - return jsonutils.dumps(message) - - -class PublishErrorsHandler(logging.Handler): - def emit(self, record): - if ('conductor.openstack.common.notifier.log_notifier' in - CONF.notification_driver): - return - notifier.api.notify(None, 'error.publisher', - 'error_notification', - notifier.api.ERROR, - dict(error=record.msg)) - - -def _create_logging_excepthook(product_name): - def logging_excepthook(type, value, tb): - extra = {} - if CONF.verbose: - extra['exc_info'] = (type, value, tb) - getLogger(product_name).critical(str(value), **extra) - return logging_excepthook - - -class LogConfigError(Exception): - - message = _('Error loading logging config %(log_config)s: %(err_msg)s') - - def __init__(self, log_config, err_msg): - self.log_config = log_config - self.err_msg = err_msg - - def __str__(self): - return self.message % dict(log_config=self.log_config, - err_msg=self.err_msg) - - -def _load_log_config(log_config): - try: - logging.config.fileConfig(log_config) - except ConfigParser.Error, exc: - raise LogConfigError(log_config, str(exc)) - - -def setup(product_name): - """Setup logging.""" - if CONF.log_config: - _load_log_config(CONF.log_config) - else: - _setup_logging_from_conf() - sys.excepthook = _create_logging_excepthook(product_name) - - -def set_defaults(logging_context_format_string): - cfg.set_defaults(log_opts, - logging_context_format_string= - logging_context_format_string) - - -def _find_facility_from_conf(): - facility_names = logging.handlers.SysLogHandler.facility_names - facility = getattr(logging.handlers.SysLogHandler, - CONF.syslog_log_facility, - None) - - if facility is None and CONF.syslog_log_facility in facility_names: - facility = facility_names.get(CONF.syslog_log_facility) - - if facility is None: - valid_facilities = facility_names.keys() - consts = ['LOG_AUTH', 'LOG_AUTHPRIV', 'LOG_CRON', 'LOG_DAEMON', - 'LOG_FTP', 'LOG_KERN', 'LOG_LPR', 'LOG_MAIL', 'LOG_NEWS', - 'LOG_AUTH', 'LOG_SYSLOG', 'LOG_USER', 'LOG_UUCP', - 'LOG_LOCAL0', 'LOG_LOCAL1', 'LOG_LOCAL2', 'LOG_LOCAL3', - 'LOG_LOCAL4', 'LOG_LOCAL5', 'LOG_LOCAL6', 'LOG_LOCAL7'] - valid_facilities.extend(consts) - raise TypeError(_('syslog facility must be one of: %s') % - ', '.join("'%s'" % fac - for fac in valid_facilities)) - - return facility - - -def _setup_logging_from_conf(): - log_root = getLogger(None).logger - for handler in log_root.handlers: - log_root.removeHandler(handler) - - if CONF.use_syslog: - facility = _find_facility_from_conf() - syslog = logging.handlers.SysLogHandler(address='/dev/log', - facility=facility) - log_root.addHandler(syslog) - - logpath = _get_log_file_path() - if logpath: - filelog = logging.handlers.WatchedFileHandler(logpath) - log_root.addHandler(filelog) - - mode = int(CONF.logfile_mode, 8) - st = os.stat(logpath) - if st.st_mode != (stat.S_IFREG | mode): - os.chmod(logpath, mode) - - if CONF.use_stderr: - streamlog = ColorHandler() - log_root.addHandler(streamlog) - - elif not CONF.log_file: - # pass sys.stdout as a positional argument - # python2.6 calls the argument strm, in 2.7 it's stream - streamlog = logging.StreamHandler(sys.stdout) - log_root.addHandler(streamlog) - - if CONF.publish_errors: - log_root.addHandler(PublishErrorsHandler(logging.ERROR)) - - for handler in log_root.handlers: - datefmt = CONF.log_date_format - if CONF.log_format: - handler.setFormatter(logging.Formatter(fmt=CONF.log_format, - datefmt=datefmt)) - else: - handler.setFormatter(LegacyFormatter(datefmt=datefmt)) - - if CONF.debug: - log_root.setLevel(logging.DEBUG) - elif CONF.verbose: - log_root.setLevel(logging.INFO) - else: - log_root.setLevel(logging.WARNING) - - level = logging.NOTSET - for pair in CONF.default_log_levels: - mod, _sep, level_name = pair.partition('=') - level = logging.getLevelName(level_name) - logger = logging.getLogger(mod) - logger.setLevel(level) - for handler in log_root.handlers: - logger.addHandler(handler) - -_loggers = {} - - -def getLogger(name='unknown', version='unknown'): - if name not in _loggers: - _loggers[name] = ContextAdapter(logging.getLogger(name), - name, - version) - return _loggers[name] - - -class WritableLogger(object): - """A thin wrapper that responds to `write` and logs.""" - - def __init__(self, logger, level=logging.INFO): - self.logger = logger - self.level = level - - def write(self, msg): - self.logger.log(self.level, msg) - - -class LegacyFormatter(logging.Formatter): - """A context.RequestContext aware formatter configured through flags. - - The flags used to set format strings are: logging_context_format_string - and logging_default_format_string. You can also specify - logging_debug_format_suffix to append extra formatting if the log level is - debug. - - For information about what variables are available for the formatter see: - http://docs.python.org/library/logging.html#formatter - - """ - - def format(self, record): - """Uses contextstring if request_id is set, otherwise default.""" - # NOTE(sdague): default the fancier formating params - # to an empty string so we don't throw an exception if - # they get used - for key in ('instance', 'color'): - if key not in record.__dict__: - record.__dict__[key] = '' - - if record.__dict__.get('request_id', None): - self._fmt = CONF.logging_context_format_string - else: - self._fmt = CONF.logging_default_format_string - - if (record.levelno == logging.DEBUG and - CONF.logging_debug_format_suffix): - self._fmt += " " + CONF.logging_debug_format_suffix - - # Cache this on the record, Logger will respect our formated copy - if record.exc_info: - record.exc_text = self.formatException(record.exc_info, record) - return logging.Formatter.format(self, record) - - def formatException(self, exc_info, record=None): - """Format exception output with CONF.logging_exception_prefix.""" - if not record: - return logging.Formatter.formatException(self, exc_info) - - stringbuffer = cStringIO.StringIO() - traceback.print_exception(exc_info[0], exc_info[1], exc_info[2], - None, stringbuffer) - lines = stringbuffer.getvalue().split('\n') - stringbuffer.close() - - if CONF.logging_exception_prefix.find('%(asctime)') != -1: - record.asctime = self.formatTime(record, self.datefmt) - - formatted_lines = [] - for line in lines: - pl = CONF.logging_exception_prefix % record.__dict__ - fl = '%s%s' % (pl, line) - formatted_lines.append(fl) - return '\n'.join(formatted_lines) - - -class ColorHandler(logging.StreamHandler): - LEVEL_COLORS = { - logging.DEBUG: '\033[00;32m', # GREEN - logging.INFO: '\033[00;36m', # CYAN - logging.AUDIT: '\033[01;36m', # BOLD CYAN - logging.WARN: '\033[01;33m', # BOLD YELLOW - logging.ERROR: '\033[01;31m', # BOLD RED - logging.CRITICAL: '\033[01;31m', # BOLD RED - } - - def format(self, record): - record.color = self.LEVEL_COLORS[record.levelno] - return logging.StreamHandler.format(self, record) - - -class DeprecatedConfig(Exception): - message = _("Fatal call to deprecated config: %(msg)s") - - def __init__(self, msg): - super(Exception, self).__init__(self.message % dict(msg=msg)) diff --git a/conductor/conductor/openstack/common/loopingcall.py b/conductor/conductor/openstack/common/loopingcall.py deleted file mode 100644 index 08135f67..00000000 --- a/conductor/conductor/openstack/common/loopingcall.py +++ /dev/null @@ -1,95 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# Copyright 2011 Justin Santa Barbara -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import sys - -from eventlet import event -from eventlet import greenthread - -from conductor.openstack.common.gettextutils import _ -from conductor.openstack.common import log as logging -from conductor.openstack.common import timeutils - -LOG = logging.getLogger(__name__) - - -class LoopingCallDone(Exception): - """Exception to break out and stop a LoopingCall. - - The poll-function passed to LoopingCall can raise this exception to - break out of the loop normally. This is somewhat analogous to - StopIteration. - - An optional return-value can be included as the argument to the exception; - this return-value will be returned by LoopingCall.wait() - - """ - - def __init__(self, retvalue=True): - """:param retvalue: Value that LoopingCall.wait() should return.""" - self.retvalue = retvalue - - -class LoopingCall(object): - def __init__(self, f=None, *args, **kw): - self.args = args - self.kw = kw - self.f = f - self._running = False - - def start(self, interval, initial_delay=None): - self._running = True - done = event.Event() - - def _inner(): - if initial_delay: - greenthread.sleep(initial_delay) - - try: - while self._running: - start = timeutils.utcnow() - self.f(*self.args, **self.kw) - end = timeutils.utcnow() - if not self._running: - break - delay = interval - timeutils.delta_seconds(start, end) - if delay <= 0: - LOG.warn(_('task run outlasted interval by %s sec') % - -delay) - greenthread.sleep(delay if delay > 0 else 0) - except LoopingCallDone, e: - self.stop() - done.send(e.retvalue) - except Exception: - LOG.exception(_('in looping call')) - done.send_exception(*sys.exc_info()) - return - else: - done.send(True) - - self.done = done - - greenthread.spawn_n(_inner) - return self.done - - def stop(self): - self._running = False - - def wait(self): - return self.done.wait() diff --git a/conductor/conductor/openstack/common/notifier/__init__.py b/conductor/conductor/openstack/common/notifier/__init__.py deleted file mode 100644 index 45c3b46a..00000000 --- a/conductor/conductor/openstack/common/notifier/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright 2011 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. diff --git a/conductor/conductor/openstack/common/notifier/api.py b/conductor/conductor/openstack/common/notifier/api.py deleted file mode 100644 index d5629e81..00000000 --- a/conductor/conductor/openstack/common/notifier/api.py +++ /dev/null @@ -1,182 +0,0 @@ -# Copyright 2011 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import uuid - -from oslo.config import cfg - -from conductor.openstack.common import context -from conductor.openstack.common.gettextutils import _ -from conductor.openstack.common import importutils -from conductor.openstack.common import jsonutils -from conductor.openstack.common import log as logging -from conductor.openstack.common import timeutils - - -LOG = logging.getLogger(__name__) - -notifier_opts = [ - cfg.MultiStrOpt('notification_driver', - default=[], - help='Driver or drivers to handle sending notifications'), - cfg.StrOpt('default_notification_level', - default='INFO', - help='Default notification level for outgoing notifications'), - cfg.StrOpt('default_publisher_id', - default='$host', - help='Default publisher_id for outgoing notifications'), -] - -CONF = cfg.CONF -CONF.register_opts(notifier_opts) - -WARN = 'WARN' -INFO = 'INFO' -ERROR = 'ERROR' -CRITICAL = 'CRITICAL' -DEBUG = 'DEBUG' - -log_levels = (DEBUG, WARN, INFO, ERROR, CRITICAL) - - -class BadPriorityException(Exception): - pass - - -def notify_decorator(name, fn): - """ decorator for notify which is used from utils.monkey_patch() - - :param name: name of the function - :param function: - object of the function - :returns: function -- decorated function - - """ - def wrapped_func(*args, **kwarg): - body = {} - body['args'] = [] - body['kwarg'] = {} - for arg in args: - body['args'].append(arg) - for key in kwarg: - body['kwarg'][key] = kwarg[key] - - ctxt = context.get_context_from_function_and_args(fn, args, kwarg) - notify(ctxt, - CONF.default_publisher_id, - name, - CONF.default_notification_level, - body) - return fn(*args, **kwarg) - return wrapped_func - - -def publisher_id(service, host=None): - if not host: - host = CONF.host - return "%s.%s" % (service, host) - - -def notify(context, publisher_id, event_type, priority, payload): - """Sends a notification using the specified driver - - :param publisher_id: the source worker_type.host of the message - :param event_type: the literal type of event (ex. Instance Creation) - :param priority: patterned after the enumeration of Python logging - levels in the set (DEBUG, WARN, INFO, ERROR, CRITICAL) - :param payload: A python dictionary of attributes - - Outgoing message format includes the above parameters, and appends the - following: - - message_id - a UUID representing the id for this notification - - timestamp - the GMT timestamp the notification was sent at - - The composite message will be constructed as a dictionary of the above - attributes, which will then be sent via the transport mechanism defined - by the driver. - - Message example:: - - {'message_id': str(uuid.uuid4()), - 'publisher_id': 'compute.host1', - 'timestamp': timeutils.utcnow(), - 'priority': 'WARN', - 'event_type': 'compute.create_instance', - 'payload': {'instance_id': 12, ... }} - - """ - if priority not in log_levels: - raise BadPriorityException( - _('%s not in valid priorities') % priority) - - # Ensure everything is JSON serializable. - payload = jsonutils.to_primitive(payload, convert_instances=True) - - msg = dict(message_id=str(uuid.uuid4()), - publisher_id=publisher_id, - event_type=event_type, - priority=priority, - payload=payload, - timestamp=str(timeutils.utcnow())) - - for driver in _get_drivers(): - try: - driver.notify(context, msg) - except Exception as e: - LOG.exception(_("Problem '%(e)s' attempting to " - "send to notification system. " - "Payload=%(payload)s") - % dict(e=e, payload=payload)) - - -_drivers = None - - -def _get_drivers(): - """Instantiate, cache, and return drivers based on the CONF.""" - global _drivers - if _drivers is None: - _drivers = {} - for notification_driver in CONF.notification_driver: - add_driver(notification_driver) - - return _drivers.values() - - -def add_driver(notification_driver): - """Add a notification driver at runtime.""" - # Make sure the driver list is initialized. - _get_drivers() - if isinstance(notification_driver, basestring): - # Load and add - try: - driver = importutils.import_module(notification_driver) - _drivers[notification_driver] = driver - except ImportError: - LOG.exception(_("Failed to load notifier %s. " - "These notifications will not be sent.") % - notification_driver) - else: - # Driver is already loaded; just add the object. - _drivers[notification_driver] = notification_driver - - -def _reset_drivers(): - """Used by unit tests to reset the drivers.""" - global _drivers - _drivers = None diff --git a/conductor/conductor/openstack/common/notifier/log_notifier.py b/conductor/conductor/openstack/common/notifier/log_notifier.py deleted file mode 100644 index 9f159fa1..00000000 --- a/conductor/conductor/openstack/common/notifier/log_notifier.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2011 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo.config import cfg - -from conductor.openstack.common import jsonutils -from conductor.openstack.common import log as logging - - -CONF = cfg.CONF - - -def notify(_context, message): - """Notifies the recipient of the desired event given the model. - Log notifications using openstack's default logging system""" - - priority = message.get('priority', - CONF.default_notification_level) - priority = priority.lower() - logger = logging.getLogger( - 'conductor.openstack.common.notification.%s' % - message['event_type']) - getattr(logger, priority)(jsonutils.dumps(message)) diff --git a/conductor/conductor/openstack/common/notifier/no_op_notifier.py b/conductor/conductor/openstack/common/notifier/no_op_notifier.py deleted file mode 100644 index bc7a56ca..00000000 --- a/conductor/conductor/openstack/common/notifier/no_op_notifier.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2011 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - - -def notify(_context, message): - """Notifies the recipient of the desired event given the model""" - pass diff --git a/conductor/conductor/openstack/common/notifier/rpc_notifier.py b/conductor/conductor/openstack/common/notifier/rpc_notifier.py deleted file mode 100644 index 67d615d0..00000000 --- a/conductor/conductor/openstack/common/notifier/rpc_notifier.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright 2011 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo.config import cfg - -from conductor.openstack.common import context as req_context -from conductor.openstack.common.gettextutils import _ -from conductor.openstack.common import log as logging -from conductor.openstack.common import rpc - -LOG = logging.getLogger(__name__) - -notification_topic_opt = cfg.ListOpt( - 'notification_topics', default=['notifications', ], - help='AMQP topic used for openstack notifications') - -CONF = cfg.CONF -CONF.register_opt(notification_topic_opt) - - -def notify(context, message): - """Sends a notification via RPC""" - if not context: - context = req_context.get_admin_context() - priority = message.get('priority', - CONF.default_notification_level) - priority = priority.lower() - for topic in CONF.notification_topics: - topic = '%s.%s' % (topic, priority) - try: - rpc.notify(context, topic, message) - except Exception: - LOG.exception(_("Could not send notification to %(topic)s. " - "Payload=%(message)s"), locals()) diff --git a/conductor/conductor/openstack/common/notifier/rpc_notifier2.py b/conductor/conductor/openstack/common/notifier/rpc_notifier2.py deleted file mode 100644 index 3585e7e4..00000000 --- a/conductor/conductor/openstack/common/notifier/rpc_notifier2.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright 2011 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -'''messaging based notification driver, with message envelopes''' - -from oslo.config import cfg - -from conductor.openstack.common import context as req_context -from conductor.openstack.common.gettextutils import _ -from conductor.openstack.common import log as logging -from conductor.openstack.common import rpc - -LOG = logging.getLogger(__name__) - -notification_topic_opt = cfg.ListOpt( - 'topics', default=['notifications', ], - help='AMQP topic(s) used for openstack notifications') - -opt_group = cfg.OptGroup(name='rpc_notifier2', - title='Options for rpc_notifier2') - -CONF = cfg.CONF -CONF.register_group(opt_group) -CONF.register_opt(notification_topic_opt, opt_group) - - -def notify(context, message): - """Sends a notification via RPC""" - if not context: - context = req_context.get_admin_context() - priority = message.get('priority', - CONF.default_notification_level) - priority = priority.lower() - for topic in CONF.rpc_notifier2.topics: - topic = '%s.%s' % (topic, priority) - try: - rpc.notify(context, topic, message, envelope=True) - except Exception: - LOG.exception(_("Could not send notification to %(topic)s. " - "Payload=%(message)s"), locals()) diff --git a/conductor/conductor/openstack/common/notifier/test_notifier.py b/conductor/conductor/openstack/common/notifier/test_notifier.py deleted file mode 100644 index 96c1746b..00000000 --- a/conductor/conductor/openstack/common/notifier/test_notifier.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2011 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - - -NOTIFICATIONS = [] - - -def notify(_context, message): - """Test notifier, stores notifications in memory for unittests.""" - NOTIFICATIONS.append(message) diff --git a/conductor/conductor/openstack/common/service.py b/conductor/conductor/openstack/common/service.py deleted file mode 100644 index a31b41a8..00000000 --- a/conductor/conductor/openstack/common/service.py +++ /dev/null @@ -1,332 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# Copyright 2011 Justin Santa Barbara -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Generic Node base class for all workers that run on hosts.""" - -import errno -import os -import random -import signal -import sys -import time - -import eventlet -import logging as std_logging -from oslo.config import cfg - -from conductor.openstack.common import eventlet_backdoor -from conductor.openstack.common.gettextutils import _ -from conductor.openstack.common import importutils -from conductor.openstack.common import log as logging -from conductor.openstack.common import threadgroup - - -rpc = importutils.try_import('conductor.openstack.common.rpc') -CONF = cfg.CONF -LOG = logging.getLogger(__name__) - - -class Launcher(object): - """Launch one or more services and wait for them to complete.""" - - def __init__(self): - """Initialize the service launcher. - - :returns: None - - """ - self._services = threadgroup.ThreadGroup() - eventlet_backdoor.initialize_if_enabled() - - @staticmethod - def run_service(service): - """Start and wait for a service to finish. - - :param service: service to run and wait for. - :returns: None - - """ - service.start() - service.wait() - - def launch_service(self, service): - """Load and start the given service. - - :param service: The service you would like to start. - :returns: None - - """ - self._services.add_thread(self.run_service, service) - - def stop(self): - """Stop all services which are currently running. - - :returns: None - - """ - self._services.stop() - - def wait(self): - """Waits until all services have been stopped, and then returns. - - :returns: None - - """ - self._services.wait() - - -class SignalExit(SystemExit): - def __init__(self, signo, exccode=1): - super(SignalExit, self).__init__(exccode) - self.signo = signo - - -class ServiceLauncher(Launcher): - def _handle_signal(self, signo, frame): - # Allow the process to be killed again and die from natural causes - signal.signal(signal.SIGTERM, signal.SIG_DFL) - signal.signal(signal.SIGINT, signal.SIG_DFL) - - raise SignalExit(signo) - - def wait(self): - signal.signal(signal.SIGTERM, self._handle_signal) - signal.signal(signal.SIGINT, self._handle_signal) - - LOG.debug(_('Full set of CONF:')) - CONF.log_opt_values(LOG, std_logging.DEBUG) - - status = None - try: - super(ServiceLauncher, self).wait() - except SignalExit as exc: - signame = {signal.SIGTERM: 'SIGTERM', - signal.SIGINT: 'SIGINT'}[exc.signo] - LOG.info(_('Caught %s, exiting'), signame) - status = exc.code - except SystemExit as exc: - status = exc.code - finally: - if rpc: - rpc.cleanup() - self.stop() - return status - - -class ServiceWrapper(object): - def __init__(self, service, workers): - self.service = service - self.workers = workers - self.children = set() - self.forktimes = [] - - -class ProcessLauncher(object): - def __init__(self): - self.children = {} - self.sigcaught = None - self.running = True - rfd, self.writepipe = os.pipe() - self.readpipe = eventlet.greenio.GreenPipe(rfd, 'r') - - signal.signal(signal.SIGTERM, self._handle_signal) - signal.signal(signal.SIGINT, self._handle_signal) - - def _handle_signal(self, signo, frame): - self.sigcaught = signo - self.running = False - - # Allow the process to be killed again and die from natural causes - signal.signal(signal.SIGTERM, signal.SIG_DFL) - signal.signal(signal.SIGINT, signal.SIG_DFL) - - def _pipe_watcher(self): - # This will block until the write end is closed when the parent - # dies unexpectedly - self.readpipe.read() - - LOG.info(_('Parent process has died unexpectedly, exiting')) - - sys.exit(1) - - def _child_process(self, service): - # Setup child signal handlers differently - def _sigterm(*args): - signal.signal(signal.SIGTERM, signal.SIG_DFL) - raise SignalExit(signal.SIGTERM) - - signal.signal(signal.SIGTERM, _sigterm) - # Block SIGINT and let the parent send us a SIGTERM - signal.signal(signal.SIGINT, signal.SIG_IGN) - - # Reopen the eventlet hub to make sure we don't share an epoll - # fd with parent and/or siblings, which would be bad - eventlet.hubs.use_hub() - - # Close write to ensure only parent has it open - os.close(self.writepipe) - # Create greenthread to watch for parent to close pipe - eventlet.spawn_n(self._pipe_watcher) - - # Reseed random number generator - random.seed() - - launcher = Launcher() - launcher.run_service(service) - - def _start_child(self, wrap): - if len(wrap.forktimes) > wrap.workers: - # Limit ourselves to one process a second (over the period of - # number of workers * 1 second). This will allow workers to - # start up quickly but ensure we don't fork off children that - # die instantly too quickly. - if time.time() - wrap.forktimes[0] < wrap.workers: - LOG.info(_('Forking too fast, sleeping')) - time.sleep(1) - - wrap.forktimes.pop(0) - - wrap.forktimes.append(time.time()) - - pid = os.fork() - if pid == 0: - # NOTE(johannes): All exceptions are caught to ensure this - # doesn't fallback into the loop spawning children. It would - # be bad for a child to spawn more children. - status = 0 - try: - self._child_process(wrap.service) - except SignalExit as exc: - signame = {signal.SIGTERM: 'SIGTERM', - signal.SIGINT: 'SIGINT'}[exc.signo] - LOG.info(_('Caught %s, exiting'), signame) - status = exc.code - except SystemExit as exc: - status = exc.code - except BaseException: - LOG.exception(_('Unhandled exception')) - status = 2 - finally: - wrap.service.stop() - - os._exit(status) - - LOG.info(_('Started child %d'), pid) - - wrap.children.add(pid) - self.children[pid] = wrap - - return pid - - def launch_service(self, service, workers=1): - wrap = ServiceWrapper(service, workers) - - LOG.info(_('Starting %d workers'), wrap.workers) - while self.running and len(wrap.children) < wrap.workers: - self._start_child(wrap) - - def _wait_child(self): - try: - # Don't block if no child processes have exited - pid, status = os.waitpid(0, os.WNOHANG) - if not pid: - return None - except OSError as exc: - if exc.errno not in (errno.EINTR, errno.ECHILD): - raise - return None - - if os.WIFSIGNALED(status): - sig = os.WTERMSIG(status) - LOG.info(_('Child %(pid)d killed by signal %(sig)d'), - dict(pid=pid, sig=sig)) - else: - code = os.WEXITSTATUS(status) - LOG.info(_('Child %(pid)s exited with status %(code)d'), - dict(pid=pid, code=code)) - - if pid not in self.children: - LOG.warning(_('pid %d not in child list'), pid) - return None - - wrap = self.children.pop(pid) - wrap.children.remove(pid) - return wrap - - def wait(self): - """Loop waiting on children to die and respawning as necessary""" - - LOG.debug(_('Full set of CONF:')) - CONF.log_opt_values(LOG, std_logging.DEBUG) - - while self.running: - wrap = self._wait_child() - if not wrap: - # Yield to other threads if no children have exited - # Sleep for a short time to avoid excessive CPU usage - # (see bug #1095346) - eventlet.greenthread.sleep(.01) - continue - - while self.running and len(wrap.children) < wrap.workers: - self._start_child(wrap) - - if self.sigcaught: - signame = {signal.SIGTERM: 'SIGTERM', - signal.SIGINT: 'SIGINT'}[self.sigcaught] - LOG.info(_('Caught %s, stopping children'), signame) - - for pid in self.children: - try: - os.kill(pid, signal.SIGTERM) - except OSError as exc: - if exc.errno != errno.ESRCH: - raise - - # Wait for children to die - if self.children: - LOG.info(_('Waiting on %d children to exit'), len(self.children)) - while self.children: - self._wait_child() - - -class Service(object): - """Service object for binaries running on hosts.""" - - def __init__(self, threads=1000): - self.tg = threadgroup.ThreadGroup(threads) - - def start(self): - pass - - def stop(self): - self.tg.stop() - - def wait(self): - self.tg.wait() - - -def launch(service, workers=None): - if workers: - launcher = ProcessLauncher() - launcher.launch_service(service, workers=workers) - else: - launcher = ServiceLauncher() - launcher.launch_service(service) - return launcher diff --git a/conductor/conductor/openstack/common/setup.py b/conductor/conductor/openstack/common/setup.py deleted file mode 100644 index dec74fd0..00000000 --- a/conductor/conductor/openstack/common/setup.py +++ /dev/null @@ -1,367 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack Foundation. -# Copyright 2012-2013 Hewlett-Packard Development Company, L.P. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Utilities with minimum-depends for use in setup.py -""" - -import email -import os -import re -import subprocess -import sys - -from setuptools.command import sdist - - -def parse_mailmap(mailmap='.mailmap'): - mapping = {} - if os.path.exists(mailmap): - with open(mailmap, 'r') as fp: - for l in fp: - try: - canonical_email, alias = re.match( - r'[^#]*?(<.+>).*(<.+>).*', l).groups() - except AttributeError: - continue - mapping[alias] = canonical_email - return mapping - - -def _parse_git_mailmap(git_dir, mailmap='.mailmap'): - mailmap = os.path.join(os.path.dirname(git_dir), mailmap) - return parse_mailmap(mailmap) - - -def canonicalize_emails(changelog, mapping): - """Takes in a string and an email alias mapping and replaces all - instances of the aliases in the string with their real email. - """ - for alias, email_address in mapping.iteritems(): - changelog = changelog.replace(alias, email_address) - return changelog - - -# Get requirements from the first file that exists -def get_reqs_from_files(requirements_files): - for requirements_file in requirements_files: - if os.path.exists(requirements_file): - with open(requirements_file, 'r') as fil: - return fil.read().split('\n') - return [] - - -def parse_requirements(requirements_files=['requirements.txt', - 'tools/pip-requires']): - requirements = [] - for line in get_reqs_from_files(requirements_files): - # For the requirements list, we need to inject only the portion - # after egg= so that distutils knows the package it's looking for - # such as: - # -e git://github.com/openstack/nova/master#egg=nova - if re.match(r'\s*-e\s+', line): - requirements.append(re.sub(r'\s*-e\s+.*#egg=(.*)$', r'\1', - line)) - # such as: - # http://github.com/openstack/nova/zipball/master#egg=nova - elif re.match(r'\s*https?:', line): - requirements.append(re.sub(r'\s*https?:.*#egg=(.*)$', r'\1', - line)) - # -f lines are for index locations, and don't get used here - elif re.match(r'\s*-f\s+', line): - pass - # argparse is part of the standard library starting with 2.7 - # adding it to the requirements list screws distro installs - elif line == 'argparse' and sys.version_info >= (2, 7): - pass - else: - requirements.append(line) - - return requirements - - -def parse_dependency_links(requirements_files=['requirements.txt', - 'tools/pip-requires']): - dependency_links = [] - # dependency_links inject alternate locations to find packages listed - # in requirements - for line in get_reqs_from_files(requirements_files): - # skip comments and blank lines - if re.match(r'(\s*#)|(\s*$)', line): - continue - # lines with -e or -f need the whole line, minus the flag - if re.match(r'\s*-[ef]\s+', line): - dependency_links.append(re.sub(r'\s*-[ef]\s+', '', line)) - # lines that are only urls can go in unmolested - elif re.match(r'\s*https?:', line): - dependency_links.append(line) - return dependency_links - - -def _run_shell_command(cmd, throw_on_error=False): - if os.name == 'nt': - output = subprocess.Popen(["cmd.exe", "/C", cmd], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - else: - output = subprocess.Popen(["/bin/sh", "-c", cmd], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - out = output.communicate() - if output.returncode and throw_on_error: - raise Exception("%s returned %d" % cmd, output.returncode) - if len(out) == 0: - return None - if len(out[0].strip()) == 0: - return None - return out[0].strip() - - -def _get_git_directory(): - parent_dir = os.path.dirname(__file__) - while True: - git_dir = os.path.join(parent_dir, '.git') - if os.path.exists(git_dir): - return git_dir - parent_dir, child = os.path.split(parent_dir) - if not child: # reached to root dir - return None - - -def write_git_changelog(): - """Write a changelog based on the git changelog.""" - new_changelog = 'ChangeLog' - git_dir = _get_git_directory() - if not os.getenv('SKIP_WRITE_GIT_CHANGELOG'): - if git_dir: - git_log_cmd = 'git --git-dir=%s log' % git_dir - changelog = _run_shell_command(git_log_cmd) - mailmap = _parse_git_mailmap(git_dir) - with open(new_changelog, "w") as changelog_file: - changelog_file.write(canonicalize_emails(changelog, mailmap)) - else: - open(new_changelog, 'w').close() - - -def generate_authors(): - """Create AUTHORS file using git commits.""" - jenkins_email = 'jenkins@review.(openstack|stackforge).org' - old_authors = 'AUTHORS.in' - new_authors = 'AUTHORS' - git_dir = _get_git_directory() - if not os.getenv('SKIP_GENERATE_AUTHORS'): - if git_dir: - # don't include jenkins email address in AUTHORS file - git_log_cmd = ("git --git-dir=" + git_dir + - " log --format='%aN <%aE>' | sort -u | " - "egrep -v '" + jenkins_email + "'") - changelog = _run_shell_command(git_log_cmd) - signed_cmd = ("git log --git-dir=" + git_dir + - " | grep -i Co-authored-by: | sort -u") - signed_entries = _run_shell_command(signed_cmd) - if signed_entries: - new_entries = "\n".join( - [signed.split(":", 1)[1].strip() - for signed in signed_entries.split("\n") if signed]) - changelog = "\n".join((changelog, new_entries)) - mailmap = _parse_git_mailmap(git_dir) - with open(new_authors, 'w') as new_authors_fh: - new_authors_fh.write(canonicalize_emails(changelog, mailmap)) - if os.path.exists(old_authors): - with open(old_authors, "r") as old_authors_fh: - new_authors_fh.write('\n' + old_authors_fh.read()) - else: - open(new_authors, 'w').close() - - -_rst_template = """%(heading)s -%(underline)s - -.. automodule:: %(module)s - :members: - :undoc-members: - :show-inheritance: -""" - - -def get_cmdclass(): - """Return dict of commands to run from setup.py.""" - - cmdclass = dict() - - def _find_modules(arg, dirname, files): - for filename in files: - if filename.endswith('.py') and filename != '__init__.py': - arg["%s.%s" % (dirname.replace('/', '.'), - filename[:-3])] = True - - class LocalSDist(sdist.sdist): - """Builds the ChangeLog and Authors files from VC first.""" - - def run(self): - write_git_changelog() - generate_authors() - # sdist.sdist is an old style class, can't use super() - sdist.sdist.run(self) - - cmdclass['sdist'] = LocalSDist - - # If Sphinx is installed on the box running setup.py, - # enable setup.py to build the documentation, otherwise, - # just ignore it - try: - from sphinx.setup_command import BuildDoc - - class LocalBuildDoc(BuildDoc): - - builders = ['html', 'man'] - - def generate_autoindex(self): - print "**Autodocumenting from %s" % os.path.abspath(os.curdir) - modules = {} - option_dict = self.distribution.get_option_dict('build_sphinx') - source_dir = os.path.join(option_dict['source_dir'][1], 'api') - if not os.path.exists(source_dir): - os.makedirs(source_dir) - for pkg in self.distribution.packages: - if '.' not in pkg: - os.path.walk(pkg, _find_modules, modules) - module_list = modules.keys() - module_list.sort() - autoindex_filename = os.path.join(source_dir, 'autoindex.rst') - with open(autoindex_filename, 'w') as autoindex: - autoindex.write(""".. toctree:: - :maxdepth: 1 - -""") - for module in module_list: - output_filename = os.path.join(source_dir, - "%s.rst" % module) - heading = "The :mod:`%s` Module" % module - underline = "=" * len(heading) - values = dict(module=module, heading=heading, - underline=underline) - - print "Generating %s" % output_filename - with open(output_filename, 'w') as output_file: - output_file.write(_rst_template % values) - autoindex.write(" %s.rst\n" % module) - - def run(self): - if not os.getenv('SPHINX_DEBUG'): - self.generate_autoindex() - - for builder in self.builders: - self.builder = builder - self.finalize_options() - self.project = self.distribution.get_name() - self.version = self.distribution.get_version() - self.release = self.distribution.get_version() - BuildDoc.run(self) - - class LocalBuildLatex(LocalBuildDoc): - builders = ['latex'] - - cmdclass['build_sphinx'] = LocalBuildDoc - cmdclass['build_sphinx_latex'] = LocalBuildLatex - except ImportError: - pass - - return cmdclass - - -def _get_revno(git_dir): - """Return the number of commits since the most recent tag. - - We use git-describe to find this out, but if there are no - tags then we fall back to counting commits since the beginning - of time. - """ - describe = _run_shell_command( - "git --git-dir=%s describe --always" % git_dir) - if "-" in describe: - return describe.rsplit("-", 2)[-2] - - # no tags found - revlist = _run_shell_command( - "git --git-dir=%s rev-list --abbrev-commit HEAD" % git_dir) - return len(revlist.splitlines()) - - -def _get_version_from_git(pre_version): - """Return a version which is equal to the tag that's on the current - revision if there is one, or tag plus number of additional revisions - if the current revision has no tag.""" - - git_dir = _get_git_directory() - if git_dir: - if pre_version: - try: - return _run_shell_command( - "git --git-dir=" + git_dir + " describe --exact-match", - throw_on_error=True).replace('-', '.') - except Exception: - sha = _run_shell_command( - "git --git-dir=" + git_dir + " log -n1 --pretty=format:%h") - return "%s.a%s.g%s" % (pre_version, _get_revno(git_dir), sha) - else: - return _run_shell_command( - "git --git-dir=" + git_dir + " describe --always").replace( - '-', '.') - return None - - -def _get_version_from_pkg_info(package_name): - """Get the version from PKG-INFO file if we can.""" - try: - pkg_info_file = open('PKG-INFO', 'r') - except (IOError, OSError): - return None - try: - pkg_info = email.message_from_file(pkg_info_file) - except email.MessageError: - return None - # Check to make sure we're in our own dir - if pkg_info.get('Name', None) != package_name: - return None - return pkg_info.get('Version', None) - - -def get_version(package_name, pre_version=None): - """Get the version of the project. First, try getting it from PKG-INFO, if - it exists. If it does, that means we're in a distribution tarball or that - install has happened. Otherwise, if there is no PKG-INFO file, pull the - version from git. - - We do not support setup.py version sanity in git archive tarballs, nor do - we support packagers directly sucking our git repo into theirs. We expect - that a source tarball be made from our git repo - or that if someone wants - to make a source tarball from a fork of our repo with additional tags in it - that they understand and desire the results of doing that. - """ - version = os.environ.get("OSLO_PACKAGE_VERSION", None) - if version: - return version - version = _get_version_from_pkg_info(package_name) - if version: - return version - version = _get_version_from_git(pre_version) - if version: - return version - raise Exception("Versioning for this project requires either an sdist" - " tarball, or access to an upstream git repository.") diff --git a/conductor/conductor/openstack/common/sslutils.py b/conductor/conductor/openstack/common/sslutils.py deleted file mode 100644 index 6ccbac87..00000000 --- a/conductor/conductor/openstack/common/sslutils.py +++ /dev/null @@ -1,80 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 IBM -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import os -import ssl - -from oslo.config import cfg - -from conductor.openstack.common.gettextutils import _ - - -ssl_opts = [ - cfg.StrOpt('ca_file', - default=None, - help="CA certificate file to use to verify " - "connecting clients"), - cfg.StrOpt('cert_file', - default=None, - help="Certificate file to use when starting " - "the server securely"), - cfg.StrOpt('key_file', - default=None, - help="Private key file to use when starting " - "the server securely"), -] - - -CONF = cfg.CONF -CONF.register_opts(ssl_opts, "ssl") - - -def is_enabled(): - cert_file = CONF.ssl.cert_file - key_file = CONF.ssl.key_file - ca_file = CONF.ssl.ca_file - use_ssl = cert_file or key_file - - if cert_file and not os.path.exists(cert_file): - raise RuntimeError(_("Unable to find cert_file : %s") % cert_file) - - if ca_file and not os.path.exists(ca_file): - raise RuntimeError(_("Unable to find ca_file : %s") % ca_file) - - if key_file and not os.path.exists(key_file): - raise RuntimeError(_("Unable to find key_file : %s") % key_file) - - if use_ssl and (not cert_file or not key_file): - raise RuntimeError(_("When running server in SSL mode, you must " - "specify both a cert_file and key_file " - "option value in your configuration file")) - - return use_ssl - - -def wrap(sock): - ssl_kwargs = { - 'server_side': True, - 'certfile': CONF.ssl.cert_file, - 'keyfile': CONF.ssl.key_file, - 'cert_reqs': ssl.CERT_NONE, - } - - if CONF.ssl.ca_file: - ssl_kwargs['ca_certs'] = CONF.ssl.ca_file - ssl_kwargs['cert_reqs'] = ssl.CERT_REQUIRED - - return ssl.wrap_socket(sock, **ssl_kwargs) diff --git a/conductor/conductor/openstack/common/threadgroup.py b/conductor/conductor/openstack/common/threadgroup.py deleted file mode 100644 index 5c986aa9..00000000 --- a/conductor/conductor/openstack/common/threadgroup.py +++ /dev/null @@ -1,114 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2012 Red Hat, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from eventlet import greenlet -from eventlet import greenpool -from eventlet import greenthread - -from conductor.openstack.common import log as logging -from conductor.openstack.common import loopingcall - - -LOG = logging.getLogger(__name__) - - -def _thread_done(gt, *args, **kwargs): - """ Callback function to be passed to GreenThread.link() when we spawn() - Calls the :class:`ThreadGroup` to notify if. - - """ - kwargs['group'].thread_done(kwargs['thread']) - - -class Thread(object): - """ Wrapper around a greenthread, that holds a reference to the - :class:`ThreadGroup`. The Thread will notify the :class:`ThreadGroup` when - it has done so it can be removed from the threads list. - """ - def __init__(self, thread, group): - self.thread = thread - self.thread.link(_thread_done, group=group, thread=self) - - def stop(self): - self.thread.kill() - - def wait(self): - return self.thread.wait() - - -class ThreadGroup(object): - """ The point of the ThreadGroup classis to: - - * keep track of timers and greenthreads (making it easier to stop them - when need be). - * provide an easy API to add timers. - """ - def __init__(self, thread_pool_size=10): - self.pool = greenpool.GreenPool(thread_pool_size) - self.threads = [] - self.timers = [] - - def add_timer(self, interval, callback, initial_delay=None, - *args, **kwargs): - pulse = loopingcall.LoopingCall(callback, *args, **kwargs) - pulse.start(interval=interval, - initial_delay=initial_delay) - self.timers.append(pulse) - - def add_thread(self, callback, *args, **kwargs): - gt = self.pool.spawn(callback, *args, **kwargs) - th = Thread(gt, self) - self.threads.append(th) - - def thread_done(self, thread): - self.threads.remove(thread) - - def stop(self): - current = greenthread.getcurrent() - for x in self.threads: - if x is current: - # don't kill the current thread. - continue - try: - x.stop() - except Exception as ex: - LOG.exception(ex) - - for x in self.timers: - try: - x.stop() - except Exception as ex: - LOG.exception(ex) - self.timers = [] - - def wait(self): - for x in self.timers: - try: - x.wait() - except greenlet.GreenletExit: - pass - except Exception as ex: - LOG.exception(ex) - current = greenthread.getcurrent() - for x in self.threads: - if x is current: - continue - try: - x.wait() - except greenlet.GreenletExit: - pass - except Exception as ex: - LOG.exception(ex) diff --git a/conductor/conductor/openstack/common/timeutils.py b/conductor/conductor/openstack/common/timeutils.py deleted file mode 100644 index 60943659..00000000 --- a/conductor/conductor/openstack/common/timeutils.py +++ /dev/null @@ -1,186 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Time related utilities and helper functions. -""" - -import calendar -import datetime - -import iso8601 - - -# ISO 8601 extended time format with microseconds -_ISO8601_TIME_FORMAT_SUBSECOND = '%Y-%m-%dT%H:%M:%S.%f' -_ISO8601_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S' -PERFECT_TIME_FORMAT = _ISO8601_TIME_FORMAT_SUBSECOND - - -def isotime(at=None, subsecond=False): - """Stringify time in ISO 8601 format""" - if not at: - at = utcnow() - st = at.strftime(_ISO8601_TIME_FORMAT - if not subsecond - else _ISO8601_TIME_FORMAT_SUBSECOND) - tz = at.tzinfo.tzname(None) if at.tzinfo else 'UTC' - st += ('Z' if tz == 'UTC' else tz) - return st - - -def parse_isotime(timestr): - """Parse time from ISO 8601 format""" - try: - return iso8601.parse_date(timestr) - except iso8601.ParseError as e: - raise ValueError(e.message) - except TypeError as e: - raise ValueError(e.message) - - -def strtime(at=None, fmt=PERFECT_TIME_FORMAT): - """Returns formatted utcnow.""" - if not at: - at = utcnow() - return at.strftime(fmt) - - -def parse_strtime(timestr, fmt=PERFECT_TIME_FORMAT): - """Turn a formatted time back into a datetime.""" - return datetime.datetime.strptime(timestr, fmt) - - -def normalize_time(timestamp): - """Normalize time in arbitrary timezone to UTC naive object""" - offset = timestamp.utcoffset() - if offset is None: - return timestamp - return timestamp.replace(tzinfo=None) - offset - - -def is_older_than(before, seconds): - """Return True if before is older than seconds.""" - if isinstance(before, basestring): - before = parse_strtime(before).replace(tzinfo=None) - return utcnow() - before > datetime.timedelta(seconds=seconds) - - -def is_newer_than(after, seconds): - """Return True if after is newer than seconds.""" - if isinstance(after, basestring): - after = parse_strtime(after).replace(tzinfo=None) - return after - utcnow() > datetime.timedelta(seconds=seconds) - - -def utcnow_ts(): - """Timestamp version of our utcnow function.""" - return calendar.timegm(utcnow().timetuple()) - - -def utcnow(): - """Overridable version of utils.utcnow.""" - if utcnow.override_time: - try: - return utcnow.override_time.pop(0) - except AttributeError: - return utcnow.override_time - return datetime.datetime.utcnow() - - -def iso8601_from_timestamp(timestamp): - """Returns a iso8601 formated date from timestamp""" - return isotime(datetime.datetime.utcfromtimestamp(timestamp)) - - -utcnow.override_time = None - - -def set_time_override(override_time=datetime.datetime.utcnow()): - """ - Override utils.utcnow to return a constant time or a list thereof, - one at a time. - """ - utcnow.override_time = override_time - - -def advance_time_delta(timedelta): - """Advance overridden time using a datetime.timedelta.""" - assert(not utcnow.override_time is None) - try: - for dt in utcnow.override_time: - dt += timedelta - except TypeError: - utcnow.override_time += timedelta - - -def advance_time_seconds(seconds): - """Advance overridden time by seconds.""" - advance_time_delta(datetime.timedelta(0, seconds)) - - -def clear_time_override(): - """Remove the overridden time.""" - utcnow.override_time = None - - -def marshall_now(now=None): - """Make an rpc-safe datetime with microseconds. - - Note: tzinfo is stripped, but not required for relative times.""" - if not now: - now = utcnow() - return dict(day=now.day, month=now.month, year=now.year, hour=now.hour, - minute=now.minute, second=now.second, - microsecond=now.microsecond) - - -def unmarshall_time(tyme): - """Unmarshall a datetime dict.""" - return datetime.datetime(day=tyme['day'], - month=tyme['month'], - year=tyme['year'], - hour=tyme['hour'], - minute=tyme['minute'], - second=tyme['second'], - microsecond=tyme['microsecond']) - - -def delta_seconds(before, after): - """ - Compute the difference in seconds between two date, time, or - datetime objects (as a float, to microsecond resolution). - """ - delta = after - before - try: - return delta.total_seconds() - except AttributeError: - return ((delta.days * 24 * 3600) + delta.seconds + - float(delta.microseconds) / (10 ** 6)) - - -def is_soon(dt, window): - """ - Determines if time is going to happen in the next window seconds. - - :params dt: the time - :params window: minimum seconds to remain to consider the time not soon - - :return: True if expiration is within the given duration - """ - soon = (utcnow() + datetime.timedelta(seconds=window)) - return normalize_time(dt) <= soon diff --git a/conductor/conductor/openstack/common/uuidutils.py b/conductor/conductor/openstack/common/uuidutils.py deleted file mode 100644 index 7608acb9..00000000 --- a/conductor/conductor/openstack/common/uuidutils.py +++ /dev/null @@ -1,39 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (c) 2012 Intel Corporation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -UUID related utilities and helper functions. -""" - -import uuid - - -def generate_uuid(): - return str(uuid.uuid4()) - - -def is_uuid_like(val): - """Returns validation of a value as a UUID. - - For our purposes, a UUID is a canonical form string: - aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa - - """ - try: - return str(uuid.UUID(val)) == val - except (TypeError, ValueError, AttributeError): - return False diff --git a/conductor/conductor/openstack/common/version.py b/conductor/conductor/openstack/common/version.py deleted file mode 100644 index 080a89e2..00000000 --- a/conductor/conductor/openstack/common/version.py +++ /dev/null @@ -1,94 +0,0 @@ - -# Copyright 2012 OpenStack Foundation -# Copyright 2012-2013 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Utilities for consuming the version from pkg_resources. -""" - -import pkg_resources - - -class VersionInfo(object): - - def __init__(self, package): - """Object that understands versioning for a package - :param package: name of the python package, such as glance, or - python-glanceclient - """ - self.package = package - self.release = None - self.version = None - self._cached_version = None - - def __str__(self): - """Make the VersionInfo object behave like a string.""" - return self.version_string() - - def __repr__(self): - """Include the name.""" - return "VersionInfo(%s:%s)" % (self.package, self.version_string()) - - def _get_version_from_pkg_resources(self): - """Get the version of the package from the pkg_resources record - associated with the package.""" - try: - requirement = pkg_resources.Requirement.parse(self.package) - provider = pkg_resources.get_provider(requirement) - return provider.version - except pkg_resources.DistributionNotFound: - # The most likely cause for this is running tests in a tree - # produced from a tarball where the package itself has not been - # installed into anything. Revert to setup-time logic. - from conductor.openstack.common import setup - return setup.get_version(self.package) - - def release_string(self): - """Return the full version of the package including suffixes indicating - VCS status. - """ - if self.release is None: - self.release = self._get_version_from_pkg_resources() - - return self.release - - def version_string(self): - """Return the short version minus any alpha/beta tags.""" - if self.version is None: - parts = [] - for part in self.release_string().split('.'): - if part[0].isdigit(): - parts.append(part) - else: - break - self.version = ".".join(parts) - - return self.version - - # Compatibility functions - canonical_version_string = version_string - version_string_with_vcs = release_string - - def cached_version_string(self, prefix=""): - """Generate an object which will expand in a string context to - the results of version_string(). We do this so that don't - call into pkg_resources every time we start up a program when - passing version information into the CONF constructor, but - rather only do the calculation when and if a version is requested - """ - if not self._cached_version: - self._cached_version = "%s%s" % (prefix, - self.version_string()) - return self._cached_version diff --git a/conductor/conductor/openstack/common/wsgi.py b/conductor/conductor/openstack/common/wsgi.py deleted file mode 100644 index 9df31881..00000000 --- a/conductor/conductor/openstack/common/wsgi.py +++ /dev/null @@ -1,797 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Utility methods for working with WSGI servers.""" - -import eventlet -eventlet.patcher.monkey_patch(all=False, socket=True) - -import datetime -import errno -import socket -import sys -import time - -import eventlet.wsgi -from oslo.config import cfg -import routes -import routes.middleware -import webob.dec -import webob.exc -from xml.dom import minidom -from xml.parsers import expat - -from conductor.openstack.common import exception -from conductor.openstack.common.gettextutils import _ -from conductor.openstack.common import jsonutils -from conductor.openstack.common import log as logging -from conductor.openstack.common import service -from conductor.openstack.common import sslutils -from conductor.openstack.common import xmlutils - -socket_opts = [ - cfg.IntOpt('backlog', - default=4096, - help="Number of backlog requests to configure the socket with"), - cfg.IntOpt('tcp_keepidle', - default=600, - help="Sets the value of TCP_KEEPIDLE in seconds for each " - "server socket. Not supported on OS X."), -] - -CONF = cfg.CONF -CONF.register_opts(socket_opts) - -LOG = logging.getLogger(__name__) - - -def run_server(application, port, **kwargs): - """Run a WSGI server with the given application.""" - sock = eventlet.listen(('0.0.0.0', port)) - eventlet.wsgi.server(sock, application, **kwargs) - - -class Service(service.Service): - """ - Provides a Service API for wsgi servers. - - This gives us the ability to launch wsgi servers with the - Launcher classes in service.py. - """ - - def __init__(self, application, port, - host='0.0.0.0', backlog=4096, threads=1000): - self.application = application - self._port = port - self._host = host - self._backlog = backlog if backlog else CONF.backlog - super(Service, self).__init__(threads) - - def _get_socket(self, host, port, backlog): - # TODO(dims): eventlet's green dns/socket module does not actually - # support IPv6 in getaddrinfo(). We need to get around this in the - # future or monitor upstream for a fix - info = socket.getaddrinfo(host, - port, - socket.AF_UNSPEC, - socket.SOCK_STREAM)[0] - family = info[0] - bind_addr = info[-1] - - sock = None - retry_until = time.time() + 30 - while not sock and time.time() < retry_until: - try: - sock = eventlet.listen(bind_addr, - backlog=backlog, - family=family) - if sslutils.is_enabled(): - sock = sslutils.wrap(sock) - - except socket.error, err: - if err.args[0] != errno.EADDRINUSE: - raise - eventlet.sleep(0.1) - if not sock: - raise RuntimeError(_("Could not bind to %(host)s:%(port)s " - "after trying for 30 seconds") % - {'host': host, 'port': port}) - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - # sockets can hang around forever without keepalive - sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) - - # This option isn't available in the OS X version of eventlet - if hasattr(socket, 'TCP_KEEPIDLE'): - sock.setsockopt(socket.IPPROTO_TCP, - socket.TCP_KEEPIDLE, - CONF.tcp_keepidle) - - return sock - - def start(self): - """Start serving this service using the provided server instance. - - :returns: None - - """ - super(Service, self).start() - self._socket = self._get_socket(self._host, self._port, self._backlog) - self.tg.add_thread(self._run, self.application, self._socket) - - @property - def backlog(self): - return self._backlog - - @property - def host(self): - return self._socket.getsockname()[0] if self._socket else self._host - - @property - def port(self): - return self._socket.getsockname()[1] if self._socket else self._port - - def stop(self): - """Stop serving this API. - - :returns: None - - """ - super(Service, self).stop() - - def _run(self, application, socket): - """Start a WSGI server in a new green thread.""" - logger = logging.getLogger('eventlet.wsgi') - eventlet.wsgi.server(socket, - application, - custom_pool=self.tg.pool, - log=logging.WritableLogger(logger)) - - -class Middleware(object): - """ - Base WSGI middleware wrapper. These classes require an application to be - initialized that will be called next. By default the middleware will - simply call its wrapped app, or you can override __call__ to customize its - behavior. - """ - - def __init__(self, application): - self.application = application - - def process_request(self, req): - """ - Called on each request. - - If this returns None, the next application down the stack will be - executed. If it returns a response then that response will be returned - and execution will stop here. - """ - return None - - def process_response(self, response): - """Do whatever you'd like to the response.""" - return response - - @webob.dec.wsgify - def __call__(self, req): - response = self.process_request(req) - if response: - return response - response = req.get_response(self.application) - return self.process_response(response) - - -class Debug(Middleware): - """ - Helper class that can be inserted into any WSGI application chain - to get information about the request and response. - """ - - @webob.dec.wsgify - def __call__(self, req): - print ("*" * 40) + " REQUEST ENVIRON" - for key, value in req.environ.items(): - print key, "=", value - print - resp = req.get_response(self.application) - - print ("*" * 40) + " RESPONSE HEADERS" - for (key, value) in resp.headers.iteritems(): - print key, "=", value - print - - resp.app_iter = self.print_generator(resp.app_iter) - - return resp - - @staticmethod - def print_generator(app_iter): - """ - Iterator that prints the contents of a wrapper string iterator - when iterated. - """ - print ("*" * 40) + " BODY" - for part in app_iter: - sys.stdout.write(part) - sys.stdout.flush() - yield part - print - - -class Router(object): - - """ - WSGI middleware that maps incoming requests to WSGI apps. - """ - - def __init__(self, mapper): - """ - Create a router for the given routes.Mapper. - - Each route in `mapper` must specify a 'controller', which is a - WSGI app to call. You'll probably want to specify an 'action' as - well and have your controller be a wsgi.Controller, who will route - the request to the action method. - - Examples: - mapper = routes.Mapper() - sc = ServerController() - - # Explicit mapping of one route to a controller+action - mapper.connect(None, "/svrlist", controller=sc, action="list") - - # Actions are all implicitly defined - mapper.resource("server", "servers", controller=sc) - - # Pointing to an arbitrary WSGI app. You can specify the - # {path_info:.*} parameter so the target app can be handed just that - # section of the URL. - mapper.connect(None, "/v1.0/{path_info:.*}", controller=BlogApp()) - """ - self.map = mapper - self._router = routes.middleware.RoutesMiddleware(self._dispatch, - self.map) - - @webob.dec.wsgify - def __call__(self, req): - """ - Route the incoming request to a controller based on self.map. - If no match, return a 404. - """ - return self._router - - @staticmethod - @webob.dec.wsgify - def _dispatch(req): - """ - Called by self._router after matching the incoming request to a route - and putting the information into req.environ. Either returns 404 - or the routed WSGI app's response. - """ - match = req.environ['wsgiorg.routing_args'][1] - if not match: - return webob.exc.HTTPNotFound() - app = match['controller'] - return app - - -class Request(webob.Request): - """Add some Openstack API-specific logic to the base webob.Request.""" - - default_request_content_types = ('application/json', 'application/xml') - default_accept_types = ('application/json', 'application/xml') - default_accept_type = 'application/json' - - def best_match_content_type(self, supported_content_types=None): - """Determine the requested response content-type. - - Based on the query extension then the Accept header. - Defaults to default_accept_type if we don't find a preference - - """ - supported_content_types = (supported_content_types or - self.default_accept_types) - - parts = self.path.rsplit('.', 1) - if len(parts) > 1: - ctype = 'application/{0}'.format(parts[1]) - if ctype in supported_content_types: - return ctype - - bm = self.accept.best_match(supported_content_types) - return bm or self.default_accept_type - - def get_content_type(self, allowed_content_types=None): - """Determine content type of the request body. - - Does not do any body introspection, only checks header - - """ - if "Content-Type" not in self.headers: - return None - - content_type = self.content_type - allowed_content_types = (allowed_content_types or - self.default_request_content_types) - - if content_type not in allowed_content_types: - raise exception.InvalidContentType(content_type=content_type) - return content_type - - -class Resource(object): - """ - WSGI app that handles (de)serialization and controller dispatch. - - Reads routing information supplied by RoutesMiddleware and calls - the requested action method upon its deserializer, controller, - and serializer. Those three objects may implement any of the basic - controller action methods (create, update, show, index, delete) - along with any that may be specified in the api router. A 'default' - method may also be implemented to be used in place of any - non-implemented actions. Deserializer methods must accept a request - argument and return a dictionary. Controller methods must accept a - request argument. Additionally, they must also accept keyword - arguments that represent the keys returned by the Deserializer. They - may raise a webob.exc exception or return a dict, which will be - serialized by requested content type. - """ - def __init__(self, controller, deserializer=None, serializer=None): - """ - :param controller: object that implement methods created by routes lib - :param deserializer: object that supports webob request deserialization - through controller-like actions - :param serializer: object that supports webob response serialization - through controller-like actions - """ - self.controller = controller - self.serializer = serializer or ResponseSerializer() - self.deserializer = deserializer or RequestDeserializer() - - @webob.dec.wsgify(RequestClass=Request) - def __call__(self, request): - """WSGI method that controls (de)serialization and method dispatch.""" - - try: - action, action_args, accept = self.deserialize_request(request) - except exception.InvalidContentType: - msg = _("Unsupported Content-Type") - return webob.exc.HTTPUnsupportedMediaType(explanation=msg) - except exception.MalformedRequestBody: - msg = _("Malformed request body") - return webob.exc.HTTPBadRequest(explanation=msg) - - action_result = self.execute_action(action, request, **action_args) - try: - return self.serialize_response(action, action_result, accept) - # return unserializable result (typically a webob exc) - except Exception: - return action_result - - def deserialize_request(self, request): - return self.deserializer.deserialize(request) - - def serialize_response(self, action, action_result, accept): - return self.serializer.serialize(action_result, accept, action) - - def execute_action(self, action, request, **action_args): - return self.dispatch(self.controller, action, request, **action_args) - - def dispatch(self, obj, action, *args, **kwargs): - """Find action-specific method on self and call it.""" - try: - method = getattr(obj, action) - except AttributeError: - method = getattr(obj, 'default') - - return method(*args, **kwargs) - - def get_action_args(self, request_environment): - """Parse dictionary created by routes library.""" - try: - args = request_environment['wsgiorg.routing_args'][1].copy() - except Exception: - return {} - - try: - del args['controller'] - except KeyError: - pass - - try: - del args['format'] - except KeyError: - pass - - return args - - -class ActionDispatcher(object): - """Maps method name to local methods through action name.""" - - def dispatch(self, *args, **kwargs): - """Find and call local method.""" - action = kwargs.pop('action', 'default') - action_method = getattr(self, str(action), self.default) - return action_method(*args, **kwargs) - - def default(self, data): - raise NotImplementedError() - - -class DictSerializer(ActionDispatcher): - """Default request body serialization""" - - def serialize(self, data, action='default'): - return self.dispatch(data, action=action) - - def default(self, data): - return "" - - -class JSONDictSerializer(DictSerializer): - """Default JSON request body serialization""" - - def default(self, data): - def sanitizer(obj): - if isinstance(obj, datetime.datetime): - _dtime = obj - datetime.timedelta(microseconds=obj.microsecond) - return _dtime.isoformat() - return unicode(obj) - return jsonutils.dumps(data, default=sanitizer) - - -class XMLDictSerializer(DictSerializer): - - def __init__(self, metadata=None, xmlns=None): - """ - :param metadata: information needed to deserialize xml into - a dictionary. - :param xmlns: XML namespace to include with serialized xml - """ - super(XMLDictSerializer, self).__init__() - self.metadata = metadata or {} - self.xmlns = xmlns - - def default(self, data): - # We expect data to contain a single key which is the XML root. - root_key = data.keys()[0] - doc = minidom.Document() - node = self._to_xml_node(doc, self.metadata, root_key, data[root_key]) - - return self.to_xml_string(node) - - def to_xml_string(self, node, has_atom=False): - self._add_xmlns(node, has_atom) - return node.toprettyxml(indent=' ', encoding='UTF-8') - - #NOTE (ameade): the has_atom should be removed after all of the - # xml serializers and view builders have been updated to the current - # spec that required all responses include the xmlns:atom, the has_atom - # flag is to prevent current tests from breaking - def _add_xmlns(self, node, has_atom=False): - if self.xmlns is not None: - node.setAttribute('xmlns', self.xmlns) - if has_atom: - node.setAttribute('xmlns:atom', "http://www.w3.org/2005/Atom") - - def _to_xml_node(self, doc, metadata, nodename, data): - """Recursive method to convert data members to XML nodes.""" - result = doc.createElement(nodename) - - # Set the xml namespace if one is specified - # TODO(justinsb): We could also use prefixes on the keys - xmlns = metadata.get('xmlns', None) - if xmlns: - result.setAttribute('xmlns', xmlns) - - #TODO(bcwaldon): accomplish this without a type-check - if type(data) is list: - collections = metadata.get('list_collections', {}) - if nodename in collections: - metadata = collections[nodename] - for item in data: - node = doc.createElement(metadata['item_name']) - node.setAttribute(metadata['item_key'], str(item)) - result.appendChild(node) - return result - singular = metadata.get('plurals', {}).get(nodename, None) - if singular is None: - if nodename.endswith('s'): - singular = nodename[:-1] - else: - singular = 'item' - for item in data: - node = self._to_xml_node(doc, metadata, singular, item) - result.appendChild(node) - #TODO(bcwaldon): accomplish this without a type-check - elif type(data) is dict: - collections = metadata.get('dict_collections', {}) - if nodename in collections: - metadata = collections[nodename] - for k, v in data.items(): - node = doc.createElement(metadata['item_name']) - node.setAttribute(metadata['item_key'], str(k)) - text = doc.createTextNode(str(v)) - node.appendChild(text) - result.appendChild(node) - return result - attrs = metadata.get('attributes', {}).get(nodename, {}) - for k, v in data.items(): - if k in attrs: - result.setAttribute(k, str(v)) - else: - node = self._to_xml_node(doc, metadata, k, v) - result.appendChild(node) - else: - # Type is atom - node = doc.createTextNode(str(data)) - result.appendChild(node) - return result - - def _create_link_nodes(self, xml_doc, links): - link_nodes = [] - for link in links: - link_node = xml_doc.createElement('atom:link') - link_node.setAttribute('rel', link['rel']) - link_node.setAttribute('href', link['href']) - if 'type' in link: - link_node.setAttribute('type', link['type']) - link_nodes.append(link_node) - return link_nodes - - -class ResponseHeadersSerializer(ActionDispatcher): - """Default response headers serialization""" - - def serialize(self, response, data, action): - self.dispatch(response, data, action=action) - - def default(self, response, data): - response.status_int = 200 - - -class ResponseSerializer(object): - """Encode the necessary pieces into a response object""" - - def __init__(self, body_serializers=None, headers_serializer=None): - self.body_serializers = { - 'application/xml': XMLDictSerializer(), - 'application/json': JSONDictSerializer(), - } - self.body_serializers.update(body_serializers or {}) - - self.headers_serializer = (headers_serializer or - ResponseHeadersSerializer()) - - def serialize(self, response_data, content_type, action='default'): - """Serialize a dict into a string and wrap in a wsgi.Request object. - - :param response_data: dict produced by the Controller - :param content_type: expected mimetype of serialized response body - - """ - response = webob.Response() - self.serialize_headers(response, response_data, action) - self.serialize_body(response, response_data, content_type, action) - return response - - def serialize_headers(self, response, data, action): - self.headers_serializer.serialize(response, data, action) - - def serialize_body(self, response, data, content_type, action): - response.headers['Content-Type'] = content_type - if data is not None: - serializer = self.get_body_serializer(content_type) - response.body = serializer.serialize(data, action) - - def get_body_serializer(self, content_type): - try: - return self.body_serializers[content_type] - except (KeyError, TypeError): - raise exception.InvalidContentType(content_type=content_type) - - -class RequestHeadersDeserializer(ActionDispatcher): - """Default request headers deserializer""" - - def deserialize(self, request, action): - return self.dispatch(request, action=action) - - def default(self, request): - return {} - - -class RequestDeserializer(object): - """Break up a Request object into more useful pieces.""" - - def __init__(self, body_deserializers=None, headers_deserializer=None, - supported_content_types=None): - - self.supported_content_types = supported_content_types - - self.body_deserializers = { - 'application/xml': XMLDeserializer(), - 'application/json': JSONDeserializer(), - } - self.body_deserializers.update(body_deserializers or {}) - - self.headers_deserializer = (headers_deserializer or - RequestHeadersDeserializer()) - - def deserialize(self, request): - """Extract necessary pieces of the request. - - :param request: Request object - :returns: tuple of (expected controller action name, dictionary of - keyword arguments to pass to the controller, the expected - content type of the response) - - """ - action_args = self.get_action_args(request.environ) - action = action_args.pop('action', None) - - action_args.update(self.deserialize_headers(request, action)) - action_args.update(self.deserialize_body(request, action)) - - accept = self.get_expected_content_type(request) - - return (action, action_args, accept) - - def deserialize_headers(self, request, action): - return self.headers_deserializer.deserialize(request, action) - - def deserialize_body(self, request, action): - if not len(request.body) > 0: - LOG.debug(_("Empty body provided in request")) - return {} - - try: - content_type = request.get_content_type() - except exception.InvalidContentType: - LOG.debug(_("Unrecognized Content-Type provided in request")) - raise - - if content_type is None: - LOG.debug(_("No Content-Type provided in request")) - return {} - - try: - deserializer = self.get_body_deserializer(content_type) - except exception.InvalidContentType: - LOG.debug(_("Unable to deserialize body as provided Content-Type")) - raise - - return deserializer.deserialize(request.body, action) - - def get_body_deserializer(self, content_type): - try: - return self.body_deserializers[content_type] - except (KeyError, TypeError): - raise exception.InvalidContentType(content_type=content_type) - - def get_expected_content_type(self, request): - return request.best_match_content_type(self.supported_content_types) - - def get_action_args(self, request_environment): - """Parse dictionary created by routes library.""" - try: - args = request_environment['wsgiorg.routing_args'][1].copy() - except Exception: - return {} - - try: - del args['controller'] - except KeyError: - pass - - try: - del args['format'] - except KeyError: - pass - - return args - - -class TextDeserializer(ActionDispatcher): - """Default request body deserialization""" - - def deserialize(self, datastring, action='default'): - return self.dispatch(datastring, action=action) - - def default(self, datastring): - return {} - - -class JSONDeserializer(TextDeserializer): - - def _from_json(self, datastring): - try: - return jsonutils.loads(datastring) - except ValueError: - msg = _("cannot understand JSON") - raise exception.MalformedRequestBody(reason=msg) - - def default(self, datastring): - return {'body': self._from_json(datastring)} - - -class XMLDeserializer(TextDeserializer): - - def __init__(self, metadata=None): - """ - :param metadata: information needed to deserialize xml into - a dictionary. - """ - super(XMLDeserializer, self).__init__() - self.metadata = metadata or {} - - def _from_xml(self, datastring): - plurals = set(self.metadata.get('plurals', {})) - - try: - node = xmlutils.safe_minidom_parse_string(datastring).childNodes[0] - return {node.nodeName: self._from_xml_node(node, plurals)} - except expat.ExpatError: - msg = _("cannot understand XML") - raise exception.MalformedRequestBody(reason=msg) - - def _from_xml_node(self, node, listnames): - """Convert a minidom node to a simple Python type. - - :param listnames: list of XML node names whose subnodes should - be considered list items. - - """ - - if len(node.childNodes) == 1 and node.childNodes[0].nodeType == 3: - return node.childNodes[0].nodeValue - elif node.nodeName in listnames: - return [self._from_xml_node(n, listnames) for n in node.childNodes] - else: - result = dict() - for attr in node.attributes.keys(): - result[attr] = node.attributes[attr].nodeValue - for child in node.childNodes: - if child.nodeType != node.TEXT_NODE: - result[child.nodeName] = self._from_xml_node(child, - listnames) - return result - - def find_first_child_named(self, parent, name): - """Search a nodes children for the first child with a given name""" - for node in parent.childNodes: - if node.nodeName == name: - return node - return None - - def find_children_named(self, parent, name): - """Return all of a nodes children who have the given name""" - for node in parent.childNodes: - if node.nodeName == name: - yield node - - def extract_text(self, node): - """Get the text field contained by the given node""" - if len(node.childNodes) == 1: - child = node.childNodes[0] - if child.nodeType == child.TEXT_NODE: - return child.nodeValue - return "" - - def default(self, datastring): - return {'body': self._from_xml(datastring)} diff --git a/conductor/conductor/openstack/common/xmlutils.py b/conductor/conductor/openstack/common/xmlutils.py deleted file mode 100644 index 33700485..00000000 --- a/conductor/conductor/openstack/common/xmlutils.py +++ /dev/null @@ -1,74 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 IBM -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from xml.dom import minidom -from xml.parsers import expat -from xml import sax -from xml.sax import expatreader - - -class ProtectedExpatParser(expatreader.ExpatParser): - """An expat parser which disables DTD's and entities by default.""" - - def __init__(self, forbid_dtd=True, forbid_entities=True, - *args, **kwargs): - # Python 2.x old style class - expatreader.ExpatParser.__init__(self, *args, **kwargs) - self.forbid_dtd = forbid_dtd - self.forbid_entities = forbid_entities - - def start_doctype_decl(self, name, sysid, pubid, has_internal_subset): - raise ValueError("Inline DTD forbidden") - - def entity_decl(self, entityName, is_parameter_entity, value, base, - systemId, publicId, notationName): - raise ValueError("<!ENTITY> entity declaration forbidden") - - def unparsed_entity_decl(self, name, base, sysid, pubid, notation_name): - # expat 1.2 - raise ValueError("<!ENTITY> unparsed entity forbidden") - - def external_entity_ref(self, context, base, systemId, publicId): - raise ValueError("<!ENTITY> external entity forbidden") - - def notation_decl(self, name, base, sysid, pubid): - raise ValueError("<!ENTITY> notation forbidden") - - def reset(self): - expatreader.ExpatParser.reset(self) - if self.forbid_dtd: - self._parser.StartDoctypeDeclHandler = self.start_doctype_decl - self._parser.EndDoctypeDeclHandler = None - if self.forbid_entities: - self._parser.EntityDeclHandler = self.entity_decl - self._parser.UnparsedEntityDeclHandler = self.unparsed_entity_decl - self._parser.ExternalEntityRefHandler = self.external_entity_ref - self._parser.NotationDeclHandler = self.notation_decl - try: - self._parser.SkippedEntityHandler = None - except AttributeError: - # some pyexpat versions do not support SkippedEntity - pass - - -def safe_minidom_parse_string(xml_string): - """Parse an XML string using minidom safely. - - """ - try: - return minidom.parseString(xml_string, parser=ProtectedExpatParser()) - except sax.SAXParseException: - raise expat.ExpatError() diff --git a/conductor/conductor/rabbitmq.py b/conductor/conductor/rabbitmq.py deleted file mode 100644 index f9d5d4c6..00000000 --- a/conductor/conductor/rabbitmq.py +++ /dev/null @@ -1,141 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from eventlet import patcher -puka = patcher.import_patched('puka') -#import puka -import anyjson -import config - - -class RmqClient(object): - def __init__(self): - settings = config.CONF.rabbitmq - self._client = puka.Client('amqp://{0}:{1}@{2}:{3}/{4}'.format( - settings.login, - settings.password, - settings.host, - settings.port, - settings.virtual_host - )) - self._connected = False - - def __enter__(self): - self.connect() - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.close() - return False - - def connect(self): - if not self._connected: - promise = self._client.connect() - self._client.wait(promise, timeout=10000) - self._connected = True - - def close(self): - if self._connected: - self._client.close() - self._connected = False - - def declare(self, queue, exchange=None): - promise = self._client.queue_declare(str(queue), durable=True) - self._client.wait(promise) - - if exchange: - promise = self._client.exchange_declare(str(exchange), durable=True) - self._client.wait(promise) - promise = self._client.queue_bind( - str(queue), str(exchange), routing_key=str(queue)) - self._client.wait(promise) - - def send(self, message, key, exchange='', timeout=None): - if not self._connected: - raise RuntimeError('Not connected to RabbitMQ') - - headers = { 'message_id': message.id } - - promise = self._client.basic_publish( - exchange=str(exchange), - routing_key=str(key), - body=anyjson.dumps(message.body), - headers=headers) - self._client.wait(promise, timeout=timeout) - - def open(self, queue): - if not self._connected: - raise RuntimeError('Not connected to RabbitMQ') - - return Subscription(self._client, queue) - - -class Subscription(object): - def __init__(self, client, queue): - self._client = client - self._queue = queue - self._promise = None - self._lastMessage = None - - def __enter__(self): - self._promise = self._client.basic_consume( - queue=self._queue, - prefetch_count=1) - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self._ack_last() - promise = self._client.basic_cancel(self._promise) - self._client.wait(promise) - return False - - def _ack_last(self): - if self._lastMessage: - self._client.basic_ack(self._lastMessage) - self._lastMessage = None - - def get_message(self, timeout=None): - if not self._promise: - raise RuntimeError( - "Subscription object must be used within 'with' block") - self._ack_last() - self._lastMessage = self._client.wait(self._promise, timeout=timeout) - msg = Message() - msg.body = anyjson.loads(self._lastMessage['body']) - msg.id = self._lastMessage['headers'].get('message_id') - return msg - - -class Message(object): - def __init__(self): - self._body = {} - self._id = '' - - @property - def body(self): - return self._body - - @body.setter - def body(self, value): - self._body = value - - @property - def id(self): - return self._id - - @id.setter - def id(self, value): - self._id = value or '' - diff --git a/conductor/conductor/reporting.py b/conductor/conductor/reporting.py deleted file mode 100644 index d9d5fdf9..00000000 --- a/conductor/conductor/reporting.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import xml_code_engine -import rabbitmq - - -class Reporter(object): - def __init__(self, rmqclient, task_id, environment_id): - self._rmqclient = rmqclient - self._task_id = task_id - self._environment_id = environment_id - rmqclient.declare('task-reports') - - def _report_func(self, id, entity, text, **kwargs): - body = { - 'id': id, - 'entity': entity, - 'text': text, - 'environment_id': self._environment_id - } - - msg = rabbitmq.Message() - msg.body = body - msg.id = self._task_id - - self._rmqclient.send( - message=msg, - key='task-reports') - - -def _report_func(context, id, entity, text, **kwargs): - reporter = context['/reporter'] - return reporter._report_func(id, entity, text, **kwargs) - -xml_code_engine.XmlCodeEngine.register_function(_report_func, "report") diff --git a/conductor/conductor/version.py b/conductor/conductor/version.py deleted file mode 100644 index f17cad4c..00000000 --- a/conductor/conductor/version.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - - -from conductor.openstack.common import version as common_version - -version_info = common_version.VersionInfo('conductor') diff --git a/conductor/conductor/windows_agent.py b/conductor/conductor/windows_agent.py deleted file mode 100644 index 7470228c..00000000 --- a/conductor/conductor/windows_agent.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import xml_code_engine - -from openstack.common import log as logging -log = logging.getLogger(__name__) - - -def send_command(engine, context, body, template, service, host, mappings=None, - result=None, **kwargs): - if not mappings: - mappings = {} - command_dispatcher = context['/commandDispatcher'] - - def callback(result_value): - log.info( - 'Received result from {2} for {0}: {1}'.format( - template, result_value, host)) - if result is not None: - context[result] = result_value['Result'] - - success_handler = body.find('success') - if success_handler is not None: - engine.evaluate_content(success_handler, context) - - command_dispatcher.execute( - name='agent', template=template, mappings=mappings, - host=host, service=service, callback=callback) - - -xml_code_engine.XmlCodeEngine.register_function(send_command, "send-command") \ No newline at end of file diff --git a/conductor/conductor/workflow.py b/conductor/conductor/workflow.py deleted file mode 100644 index 773a7863..00000000 --- a/conductor/conductor/workflow.py +++ /dev/null @@ -1,190 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import jsonpath -import re -import types - -import function_context -import xml_code_engine - - -class Workflow(object): - def __init__(self, filename, data, command_dispatcher, config, reporter): - self._data = data - self._engine = xml_code_engine.XmlCodeEngine() - with open(filename) as xml: - self._engine.load(xml) - self._command_dispatcher = command_dispatcher - self._config = config - self._reporter = reporter - - def execute(self): - context = function_context.Context() - context['/dataSource'] = self._data - context['/commandDispatcher'] = self._command_dispatcher - context['/config'] = self._config - context['/reporter'] = self._reporter - return self._engine.execute(context) - - @staticmethod - def _get_path(obj, path, create_non_existing=False): - current = obj - for part in path: - if isinstance(current, types.ListType): - current = current[int(part)] - elif isinstance(current, types.DictionaryType): - if part not in current: - if create_non_existing: - current[part] = {} - else: - return None - current = current[part] - else: - raise ValueError() - - return current - - @staticmethod - def _set_path(obj, path, value): - current = Workflow._get_path(obj, path[:-1], True) - if isinstance(current, types.ListType): - current[int(path[-1])] = value - elif isinstance(current, types.DictionaryType): - current[path[-1]] = value - else: - raise ValueError() - - @staticmethod - def _get_relative_position(path, context): - position = context['__dataSource_currentPosition'] or [] - - index = 0 - for c in path: - if c == ':': - if len(position) > 0: - position = position[:-1] - elif c == '/': - position = [] - else: - break - - index += 1 - - return position, path[index:] - - @staticmethod - def _correct_position(path, context): - position, suffix = Workflow._get_relative_position(path, context) - - if not suffix: - return position - else: - return position + suffix.split('.') - - @staticmethod - def _select_func(context, path='', source=None, **kwargs): - - if path.startswith('##'): - config = context['/config'] - return config[path[2:]] - elif path.startswith('#'): - return context[path[1:]] - - if source is not None: - return Workflow._get_path( - context[source], path.split('.')) - else: - return Workflow._get_path( - context['/dataSource'], - Workflow._correct_position(path, context)) - - @staticmethod - def _set_func(path, context, body, engine, target=None, **kwargs): - body_data = engine.evaluate_content(body, context) - - if path.startswith('##'): - raise RuntimeError('Cannot modify config from XML-code') - elif path.startswith('#'): - context[':' + path[1:]] = body_data - return - - if target: - data = context[target] - position = path.split('.') - if Workflow._get_path(data, position) != body_data: - Workflow._set_path(data, position, body_data) - context['/hasSideEffects'] = True - - else: - data = context['/dataSource'] - new_position = Workflow._correct_position(path, context) - if Workflow._get_path(data, new_position) != body_data: - Workflow._set_path(data, new_position, body_data) - context['/hasSideEffects'] = True - - @staticmethod - def _rule_func(match, context, body, engine, limit=0, name=None, **kwargs): - position = context['__dataSource_currentPosition'] or [] - - position, match = Workflow._get_relative_position(match, context) - data = Workflow._get_path(context['/dataSource'], position) - match = re.sub(r'@\.([\w.]+)', - r"Workflow._get_path(@, '\1'.split('.'))", match) - match = match.replace('$.', '$[*].') - selected = jsonpath.jsonpath([data], match, 'IPATH') or [] - index = 0 - for found_match in selected: - if 0 < int(limit) <= index: - break - index += 1 - new_position = position + found_match[1:] - context['__dataSource_currentPosition'] = new_position - context['__dataSource_currentObj'] = Workflow._get_path( - context['/dataSource'], new_position) - for element in body: - if element.tag == 'empty': - continue - engine.evaluate(element, context) - if element.tag == 'rule' and context['/hasSideEffects']: - break - if not index: - empty_handler = body.find('empty') - if empty_handler is not None: - - engine.evaluate_content(empty_handler, context) - - - @staticmethod - def _workflow_func(context, body, engine, **kwargs): - context['/hasSideEffects'] = False - for element in body: - engine.evaluate(element, context) - if element.tag == 'rule' and context['/hasSideEffects']: - return True - return False - - -xml_code_engine.XmlCodeEngine.register_function( - Workflow._rule_func, 'rule') - -xml_code_engine.XmlCodeEngine.register_function( - Workflow._workflow_func, 'workflow') - -xml_code_engine.XmlCodeEngine.register_function( - Workflow._set_func, 'set') - -xml_code_engine.XmlCodeEngine.register_function( - Workflow._select_func, 'select') diff --git a/conductor/conductor/xml_code_engine.py b/conductor/conductor/xml_code_engine.py deleted file mode 100644 index 8661ead4..00000000 --- a/conductor/conductor/xml_code_engine.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import xml.etree.ElementTree as etree -import types - -import function_context - - -class XmlCodeEngine(object): - _functionMap = {} - - def __init__(self): - self._document = None - - def load(self, file_obj): - self._document = etree.parse(file_obj) - - @staticmethod - def register_function(func, name): - XmlCodeEngine._functionMap[name] = func - - def _execute_function(self, name, element, parent_context): - if name == 'parameter': - return None - - if name not in self._functionMap: - raise KeyError('Unknown function %s' % name) - - definition = self._functionMap[name] - context = function_context.Context(parent_context) - args = {'engine': self, 'body': element, 'context': context} - - for key, value in element.items(): - args[key] = value - - for parameter in element.findall('parameter'): - args[parameter.get('name')] = self.evaluate_content( - parameter, context) - - return definition(**args) - - def evaluate(self, element, parent_context): - return self._execute_function(element.tag, element, parent_context) - - def evaluate_content(self, element, context): - parts = [element.text or ''] - do_strip = False - for sub_element in element: - if sub_element.tag == 'parameter': - continue - do_strip = True - parts.append(self._execute_function( - sub_element.tag, sub_element, context)) - parts.append(sub_element.tail or '') - - result = [] - - for t in parts: - if not isinstance(t, types.StringTypes): - result.append(t) - - return_value = result - if len(result) == 0: - return_value = ''.join(parts) - if do_strip: - return_value = return_value.strip() - elif len(result) == 1: - return_value = result[0] - - return return_value - - def execute(self, parent_context=None): - root = self._document.getroot() - return self.evaluate(root, parent_context) - - -def _dict_func(engine, body, context, **kwargs): - result = {} - for item in body: - key = item.get('name') - value = engine.evaluate_content(item, context) - result[key] = value - return result - - -def _array_func(engine, body, context, **kwargs): - result = [] - for item in body: - result.append(engine.evaluate(item, context)) - return result - - -def _text_func(engine, body, context, **kwargs): - return str(engine.evaluate_content(body, context)) - - -def _int_func(engine, body, context, **kwargs): - return int(engine.evaluate_content(body, context)) - - -def _function_func(engine, body, context, **kwargs): - return lambda: engine.evaluate_content(body, context) - - -def _null_func(**kwargs): - return None - - -def _true_func(**kwargs): - return True - - -def _false_func(**kwargs): - return False - - -XmlCodeEngine.register_function(_dict_func, "map") -XmlCodeEngine.register_function(_array_func, "list") -XmlCodeEngine.register_function(_text_func, "text") -XmlCodeEngine.register_function(_int_func, "int") -XmlCodeEngine.register_function(_function_func, "function") -XmlCodeEngine.register_function(_null_func, "null") -XmlCodeEngine.register_function(_true_func, "true") -XmlCodeEngine.register_function(_false_func, "false") diff --git a/conductor/data/init.ps1 b/conductor/data/init.ps1 deleted file mode 100644 index 0e6cb212..00000000 --- a/conductor/data/init.ps1 +++ /dev/null @@ -1,33 +0,0 @@ -#ps1 - -$WindowsAgentConfigBase64 = '%WINDOWS_AGENT_CONFIG_BASE64%' -$WindowsAgentConfigFile = "C:\Keero\Agent\WindowsAgent.exe.config" - -$NewComputerName = '%INTERNAL_HOSTNAME%' - -$RestartRequired = $false - -Import-Module CoreFunctions - -Write-Log "Updating Keero Windows Agent." -Stop-Service "Keero Agent" -Backup-File $WindowsAgentConfigFile -Remove-Item $WindowsAgentConfigFile -Force -ConvertFrom-Base64String -Base64String $WindowsAgentConfigBase64 -Path $WindowsAgentConfigFile -Exec sc.exe 'config','"Keero Agent"','start=','delayed-auto' -Write-Log "Service has been updated." - -Write-Log "Renaming computer ..." -Rename-Computer -NewName $NewComputerName | Out-Null -Write-Log "New name assigned, restart required." -$RestartRequired = $true - - -Write-Log 'All done!' -if ( $RestartRequired ) { - Write-Log "Restarting computer ..." - Restart-Computer -Force -} -else { - Start-Service 'Keero Agent' -} diff --git a/conductor/data/templates/agent-config/Default.template b/conductor/data/templates/agent-config/Default.template deleted file mode 100644 index 36287273..00000000 --- a/conductor/data/templates/agent-config/Default.template +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<configuration> - <configSections> - <section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog"/> - </configSections> - <startup> - <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> - </startup> - <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <targets> - <target name="file" xsi:type="File" fileName="${basedir}/log.txt" - layout="${date} ${level}: &lt;${logger:shortName=true}&gt; ${message} ${exception:format=tostring}"/> - </targets> - - <rules> - <logger name="*" minlevel="Debug" writeTo="file" /> - </rules> - </nlog> - <appSettings> - <add key="rabbitmq.host" value="%RABBITMQ_HOST%"/> - <add key="rabbitmq.user" value="%RABBITMQ_USER%"/> - <add key="rabbitmq.password" value="%RABBITMQ_PASSWORD%"/> - <add key="rabbitmq.vhost" value="%RABBITMQ_VHOST%"/> - <add key="rabbitmq.inputQueue" value="%RABBITMQ_INPUT_QUEUE%"/> - <add key="rabbitmq.resultExchange" value=""/> - <add key="rabbitmq.resultRoutingKey" value="%RESULT_QUEUE%"/> - <add key="rabbitmq.durableMessages" value="true"/> - - </appSettings> -</configuration> \ No newline at end of file diff --git a/conductor/data/templates/agent/AskDnsIp.template b/conductor/data/templates/agent/AskDnsIp.template deleted file mode 100644 index a9f6ee38..00000000 --- a/conductor/data/templates/agent/AskDnsIp.template +++ /dev/null @@ -1,12 +0,0 @@ -{ - "Scripts": [ - "ZnVuY3Rpb24gR2V0LURuc0xpc3RlbmluZ0lwQWRkcmVzcyB7DQogICAgSW1wb3J0LU1vZHVsZSBEbnNTZXJ2ZXINCiAgICAoR2V0LUROU1NlcnZlciAtQ29tcHV0ZXJOYW1lIGxvY2FsaG9zdCkuU2VydmVyU2V0dGluZy5MaXN0ZW5pbmdJcEFkZHJlc3MgfA0KICAgICAgICBXaGVyZS1PYmplY3QgeyAkXyAtbWF0Y2ggIlxkezEsM31cLlxkezEsM31cLlxkezEsM31cLlxkezEsM30iIH0NCn0NCg==" - ], - "Commands": [ - { - "Name": "Get-DnsListeningIpAddress", - "Arguments": {} - } - ], - "RebootOnCompletion": 0 -} \ No newline at end of file diff --git a/conductor/data/templates/agent/CreatePrimaryDC.template b/conductor/data/templates/agent/CreatePrimaryDC.template deleted file mode 100644 index f3b68679..00000000 --- a/conductor/data/templates/agent/CreatePrimaryDC.template +++ /dev/null @@ -1,21 +0,0 @@ -{ - "Scripts": [ - "RnVuY3Rpb24gU2V0LUxvY2FsVXNlclBhc3N3b3JkIHsKICAgIHBhcmFtICgKICAgICAgICBbU3RyaW5nXSAkVXNlck5hbWUsCiAgICAgICAgW1N0cmluZ10gJFBhc3N3b3JkLAogICAgICAgIFtTd2l0Y2hdICRGb3JjZQogICAgKQogICAgCiAgICB0cmFwIHsgU3RvcC1FeGVjdXRpb24gJF8gfQogICAgCiAgICBpZiAoKEdldC1XbWlPYmplY3QgV2luMzJfVXNlckFjY291bnQgLUZpbHRlciAiTG9jYWxBY2NvdW50ID0gJ1RydWUnIEFORCBOYW1lPSckVXNlck5hbWUnIikgLWVxICRudWxsKSB7CiAgICAgICAgdGhyb3cgIlVuYWJsZSB0byBmaW5kIGxvY2FsIHVzZXIgYWNjb3VudCAnJFVzZXJOYW1lJyIKICAgIH0KICAgIAogICAgaWYgKCRGb3JjZSkgewogICAgICAgIFdyaXRlLUxvZyAiQ2hhbmdpbmcgcGFzc3dvcmQgZm9yIHVzZXIgJyRVc2VyTmFtZScgdG8gJyoqKioqJyIgIyA6KQogICAgICAgIChbQURTSV0gIldpbk5UOi8vLi8kVXNlck5hbWUiKS5TZXRQYXNzd29yZCgkUGFzc3dvcmQpCiAgICB9CiAgICBlbHNlIHsKICAgICAgICBXcml0ZS1Mb2dXYXJuaW5nICJZb3UgYXJlIHRyeWluZyB0byBjaGFuZ2UgcGFzc3dvcmQgZm9yIHVzZXIgJyRVc2VyTmFtZScuIFRvIGRvIHRoaXMgcGxlYXNlIHJ1biB0aGUgY29tbWFuZCBhZ2FpbiB3aXRoIC1Gb3JjZSBwYXJhbWV0ZXIuIgogICAgICAgICRVc2VyQWNjb3VudAogICAgfQp9CgoKCkZ1bmN0aW9uIEluc3RhbGwtUm9sZVByaW1hcnlEb21haW5Db250cm9sbGVyCnsKPCMKLlNZTk9QU0lTCkNvbmZpZ3VyZSBub2RlJ3MgbmV0d29yayBhZGFwdGVycy4KQ3JlYXRlIGZpcnN0IGRvbWFpbiBjb250cm9sbGVyIGluIHRoZSBmb3Jlc3QuCgouRVhBTVBMRQpQUz4gSW5zdGFsbC1Sb2xlUHJpbWFyeURvbWFpbkNvbnRyb2xsZXIgLURvbWFpbk5hbWUgYWNtZS5sb2NhbCAtU2FmZU1vZGVQYXNzd29yZCAiUEBzc3cwcmQiCgpJbnN0YWxsIEROUyBhbmQgQUREUywgY3JlYXRlIGZvcmVzdCBhbmQgZG9tYWluICdhY21lLmxvY2FsJy4KU2V0IERDIHJlY292ZXJ5IG1vZGUgcGFzc3dvcmQgdG8gJ1BAc3N3MHJkJy4KIz4KCQoJcGFyYW0KCSgKCQlbU3RyaW5nXQoJCSMgTmV3IGRvbWFpbiBuYW1lLgoJCSREb21haW5OYW1lLAoJCQoJCVtTdHJpbmddCgkJIyBEb21haW4gY29udHJvbGxlciByZWNvdmVyeSBtb2RlIHBhc3N3b3JkLgoJCSRTYWZlTW9kZVBhc3N3b3JkCgkpCgoJdHJhcCB7IFN0b3AtRXhlY3V0aW9uICRfIH0KCiAgICAgICAgIyBBZGQgcmVxdWlyZWQgd2luZG93cyBmZWF0dXJlcwoJQWRkLVdpbmRvd3NGZWF0dXJlV3JhcHBlciBgCgkJLU5hbWUgIkROUyIsIkFELURvbWFpbi1TZXJ2aWNlcyIsIlJTQVQtREZTLU1nbXQtQ29uIiBgCgkJLUluY2x1ZGVNYW5hZ2VtZW50VG9vbHMgYAogICAgICAgIC1Ob3RpZnlSZXN0YXJ0CgoKCVdyaXRlLUxvZyAiQ3JlYXRpbmcgZmlyc3QgZG9tYWluIGNvbnRyb2xsZXIgLi4uIgoJCQoJJFNNQVAgPSBDb252ZXJ0VG8tU2VjdXJlU3RyaW5nIC1TdHJpbmcgJFNhZmVNb2RlUGFzc3dvcmQgLUFzUGxhaW5UZXh0IC1Gb3JjZQoJCQoJSW5zdGFsbC1BRERTRm9yZXN0IGAKCQktRG9tYWluTmFtZSAkRG9tYWluTmFtZSBgCgkJLVNhZmVNb2RlQWRtaW5pc3RyYXRvclBhc3N3b3JkICRTTUFQIGAKCQktRG9tYWluTW9kZSBEZWZhdWx0IGAKCQktRm9yZXN0TW9kZSBEZWZhdWx0IGAKCQktTm9SZWJvb3RPbkNvbXBsZXRpb24gYAoJCS1Gb3JjZSBgCgkJLUVycm9yQWN0aW9uIFN0b3AgfCBPdXQtTnVsbAoKCVdyaXRlLUxvZyAiV2FpdGluZyBmb3IgcmVib290IC4uLiIJCQojCVN0b3AtRXhlY3V0aW9uIC1FeGl0Q29kZSAzMDEwIC1FeGl0U3RyaW5nICJDb21wdXRlciBtdXN0IGJlIHJlc3RhcnRlZCB0byBmaW5pc2ggZG9tYWluIGNvbnRyb2xsZXIgcHJvbW90aW9uLiIKIwlXcml0ZS1Mb2cgIlJlc3RhcmluZyBjb21wdXRlciAuLi4iCiMJUmVzdGFydC1Db21wdXRlciAtRm9yY2UKfQo=" - ], - "Commands": [ - { - "Name": "Import-Module", - "Arguments": { - "Name": "CoreFunctions" - } - }, - { - "Name": "Install-RolePrimaryDomainController", - "Arguments": { - "DomainName": "$domain", - "SafeModePassword": "$recoveryPassword" - } - } - ], - "RebootOnCompletion": 1 -} \ No newline at end of file diff --git a/conductor/data/templates/agent/CreateSecondaryDC.template b/conductor/data/templates/agent/CreateSecondaryDC.template deleted file mode 100644 index a5ad7f45..00000000 --- a/conductor/data/templates/agent/CreateSecondaryDC.template +++ /dev/null @@ -1,23 +0,0 @@ -{ - "Scripts": [ - "RnVuY3Rpb24gSW5zdGFsbC1Sb2xlU2Vjb25kYXJ5RG9tYWluQ29udHJvbGxlcg0Kew0KPCMNCi5TWU5PUFNJUw0KSW5zdGFsbCBhZGRpdGlvbmFsIChzZWNvbmRhcnkpIGRvbWFpbiBjb250cm9sbGVyLg0KDQojPg0KCXBhcmFtDQoJKA0KCQlbU3RyaW5nXQ0KCQkjIERvbWFpbiBuYW1lIHRvIGpvaW4gdG8uDQoJCSREb21haW5OYW1lLA0KCQkNCgkJW1N0cmluZ10NCgkJIyBEb21haW4gdXNlciB3aG8gaXMgYWxsb3dlZCB0byBqb2luIGNvbXB1dGVyIHRvIGRvbWFpbi4NCgkJJFVzZXJOYW1lLA0KCQkNCgkJW1N0cmluZ10NCgkJIyBVc2VyJ3MgcGFzc3dvcmQuDQoJCSRQYXNzd29yZCwNCgkJDQoJCVtTdHJpbmddDQoJCSMgRG9tYWluIGNvbnRyb2xsZXIgcmVjb3ZlcnkgbW9kZSBwYXNzd29yZC4NCgkJJFNhZmVNb2RlUGFzc3dvcmQNCgkpDQoNCgl0cmFwIHsgU3RvcC1FeGVjdXRpb24gJF8gfQ0KCQ0KCSRDcmVkZW50aWFsID0gTmV3LUNyZWRlbnRpYWwgLVVzZXJOYW1lICIkRG9tYWluTmFtZVwkVXNlck5hbWUiIC1QYXNzd29yZCAkUGFzc3dvcmQNCgkJDQoJIyBBZGQgcmVxdWlyZWQgd2luZG93cyBmZWF0dXJlcw0KCUFkZC1XaW5kb3dzRmVhdHVyZVdyYXBwZXIgYA0KCQktTmFtZSAiRE5TIiwiQUQtRG9tYWluLVNlcnZpY2VzIiwiUlNBVC1ERlMtTWdtdC1Db24iIGANCgkJLUluY2x1ZGVNYW5hZ2VtZW50VG9vbHMgYA0KICAgICAgICAgICAgICAgIC1Ob3RpZnlSZXN0YXJ0DQoJCQ0KCQ0KICAgICAgICBXcml0ZS1Mb2cgIkFkZGluZyBzZWNvbmRhcnkgZG9tYWluIGNvbnRyb2xsZXIgLi4uIg0KICAgIA0KCSRTTUFQID0gQ29udmVydFRvLVNlY3VyZVN0cmluZyAtU3RyaW5nICRTYWZlTW9kZVBhc3N3b3JkIC1Bc1BsYWluVGV4dCAtRm9yY2UNCg0KCUluc3RhbGwtQUREU0RvbWFpbkNvbnRyb2xsZXIgYA0KCQktRG9tYWluTmFtZSAkRG9tYWluTmFtZSBgDQoJCS1TYWZlTW9kZUFkbWluaXN0cmF0b3JQYXNzd29yZCAkU01BUCBgDQoJCS1DcmVkZW50aWFsICRDcmVkZW50aWFsIGANCgkJLU5vUmVib290T25Db21wbGV0aW9uIGANCgkJLUZvcmNlIGANCgkJLUVycm9yQWN0aW9uIFN0b3AgfCBPdXQtTnVsbA0KDQoJV3JpdGUtTG9nICJXYWl0aW5nIGZvciByZXN0YXJ0IC4uLiINCiMJU3RvcC1FeGVjdXRpb24gLUV4aXRDb2RlIDMwMTAgLUV4aXRTdHJpbmcgIkNvbXB1dGVyIG11c3QgYmUgcmVzdGFydGVkIHRvIGZpbmlzaCBkb21haW4gY29udHJvbGxlciBwcm9tb3Rpb24uIg0KIwlXcml0ZS1Mb2cgIlJlc3RhcnRpbmcgY29tcHV0ZXIgLi4uIg0KIwlSZXN0YXJ0LUNvbXB1dGVyIC1Gb3JjZQ0KfQ0K" - ], - "Commands": [ - { - "Name": "Import-Module", - "Arguments": { - "Name": "CoreFunctions" - } - }, - { - "Name": "Install-RoleSecondaryDomainController", - "Arguments": { - "DomainName": "$domain", - "UserName": "Administrator", - "Password": "$domainPassword", - "SafeModePassword": "$recoveryPassword" - } - } - ], - "RebootOnCompletion": 1 -} \ No newline at end of file diff --git a/conductor/data/templates/agent/InstallIIS.template b/conductor/data/templates/agent/InstallIIS.template deleted file mode 100644 index baeaec15..00000000 --- a/conductor/data/templates/agent/InstallIIS.template +++ /dev/null @@ -1,12 +0,0 @@ -{ - "Scripts": [ - "ZnVuY3Rpb24gSW5zdGFsbC1XZWJTZXJ2ZXIgew0KICAgIEltcG9ydC1Nb2R1bGUgU2VydmVyTWFuYWdlcg0KICAgIEluc3RhbGwtV2luZG93c0ZlYXR1cmUgV2ViLVNlcnZlciAtSW5jbHVkZU1hbmFnZW1lbnRUb29scw0KfQ0K" - ], - "Commands": [ - { - "Name": "Install-WebServer", - "Arguments": {} - } - ], - "RebootOnCompletion": 0 -} \ No newline at end of file diff --git a/conductor/data/templates/agent/JoinDomain.template b/conductor/data/templates/agent/JoinDomain.template deleted file mode 100644 index 85e4b9ca..00000000 --- a/conductor/data/templates/agent/JoinDomain.template +++ /dev/null @@ -1,27 +0,0 @@ -{ - "Scripts": [], - "Commands": [ - { - "Name": "Import-Module", - "Arguments": { - "Name": "CoreFunctions" - } - }, - { - "Name": "Set-NetworkAdapterConfiguration", - "Arguments": { - "FirstAvailable": true, - "DNSServer": "$dnsIp" - } - }, - { - "Name": "Join-Domain", - "Arguments": { - "DomainName": "$domain", - "Username": "Administrator", - "Password": "$domainPassword" - } - } - ], - "RebootOnCompletion": 1 -} \ No newline at end of file diff --git a/conductor/data/templates/agent/SetPassword.template b/conductor/data/templates/agent/SetPassword.template deleted file mode 100644 index 1cc9dcc9..00000000 --- a/conductor/data/templates/agent/SetPassword.template +++ /dev/null @@ -1,22 +0,0 @@ -{ - "Scripts": [ - "RnVuY3Rpb24gU2V0LUxvY2FsVXNlclBhc3N3b3JkIHsNCiAgICBwYXJhbSAoDQogICAgICAgIFtTdHJpbmddICRVc2VyTmFtZSwNCiAgICAgICAgW1N0cmluZ10gJFBhc3N3b3JkLA0KICAgICAgICBbU3dpdGNoXSAkRm9yY2UNCiAgICApDQogICAgDQogICAgdHJhcCB7IFN0b3AtRXhlY3V0aW9uICRfIH0NCiAgICANCiAgICBpZiAoKEdldC1XbWlPYmplY3QgV2luMzJfVXNlckFjY291bnQgLUZpbHRlciAiTG9jYWxBY2NvdW50ID0gJ1RydWUnIEFORCBOYW1lPSckVXNlck5hbWUnIikgLWVxICRudWxsKSB7DQogICAgICAgIHRocm93ICJVbmFibGUgdG8gZmluZCBsb2NhbCB1c2VyIGFjY291bnQgJyRVc2VyTmFtZSciDQogICAgfQ0KICAgIA0KICAgIGlmICgkRm9yY2UpIHsNCiAgICAgICAgV3JpdGUtTG9nICJDaGFuZ2luZyBwYXNzd29yZCBmb3IgdXNlciAnJFVzZXJOYW1lJyB0byAnKioqKionIiAjIDopDQogICAgICAgIChbQURTSV0gIldpbk5UOi8vLi8kVXNlck5hbWUiKS5TZXRQYXNzd29yZCgkUGFzc3dvcmQpDQogICAgfQ0KICAgIGVsc2Ugew0KICAgICAgICBXcml0ZS1Mb2dXYXJuaW5nICJZb3UgYXJlIHRyeWluZyB0byBjaGFuZ2UgcGFzc3dvcmQgZm9yIHVzZXIgJyRVc2VyTmFtZScuIFRvIGRvIHRoaXMgcGxlYXNlIHJ1biB0aGUgY29tbWFuZCBhZ2FpbiB3aXRoIC1Gb3JjZSBwYXJhbWV0ZXIuIg0KICAgICAgICAkVXNlckFjY291bnQNCiAgICB9DQp9DQoNCg0KDQpGdW5jdGlvbiBJbnN0YWxsLVJvbGVQcmltYXJ5RG9tYWluQ29udHJvbGxlcg0Kew0KPCMNCi5TWU5PUFNJUw0KQ29uZmlndXJlIG5vZGUncyBuZXR3b3JrIGFkYXB0ZXJzLg0KQ3JlYXRlIGZpcnN0IGRvbWFpbiBjb250cm9sbGVyIGluIHRoZSBmb3Jlc3QuDQoNCi5FWEFNUExFDQpQUz4gSW5zdGFsbC1Sb2xlUHJpbWFyeURvbWFpbkNvbnRyb2xsZXIgLURvbWFpbk5hbWUgYWNtZS5sb2NhbCAtU2FmZU1vZGVQYXNzd29yZCAiUEBzc3cwcmQiDQoNCkluc3RhbGwgRE5TIGFuZCBBRERTLCBjcmVhdGUgZm9yZXN0IGFuZCBkb21haW4gJ2FjbWUubG9jYWwnLg0KU2V0IERDIHJlY292ZXJ5IG1vZGUgcGFzc3dvcmQgdG8gJ1BAc3N3MHJkJy4NCiM+DQoJDQoJcGFyYW0NCgkoDQoJCVtTdHJpbmddDQoJCSMgTmV3IGRvbWFpbiBuYW1lLg0KCQkkRG9tYWluTmFtZSwNCgkJDQoJCVtTdHJpbmddDQoJCSMgRG9tYWluIGNvbnRyb2xsZXIgcmVjb3ZlcnkgbW9kZSBwYXNzd29yZC4NCgkJJFNhZmVNb2RlUGFzc3dvcmQNCgkpDQoNCgl0cmFwIHsgU3RvcC1FeGVjdXRpb24gJF8gfQ0KDQogICAgICAgICMgQWRkIHJlcXVpcmVkIHdpbmRvd3MgZmVhdHVyZXMNCglBZGQtV2luZG93c0ZlYXR1cmVXcmFwcGVyIGANCgkJLU5hbWUgIkROUyIsIkFELURvbWFpbi1TZXJ2aWNlcyIsIlJTQVQtREZTLU1nbXQtQ29uIiBgDQoJCS1JbmNsdWRlTWFuYWdlbWVudFRvb2xzIGANCiAgICAgICAgLU5vdGlmeVJlc3RhcnQNCg0KDQoJV3JpdGUtTG9nICJDcmVhdGluZyBmaXJzdCBkb21haW4gY29udHJvbGxlciAuLi4iDQoJCQ0KCSRTTUFQID0gQ29udmVydFRvLVNlY3VyZVN0cmluZyAtU3RyaW5nICRTYWZlTW9kZVBhc3N3b3JkIC1Bc1BsYWluVGV4dCAtRm9yY2UNCgkJDQoJSW5zdGFsbC1BRERTRm9yZXN0IGANCgkJLURvbWFpbk5hbWUgJERvbWFpbk5hbWUgYA0KCQktU2FmZU1vZGVBZG1pbmlzdHJhdG9yUGFzc3dvcmQgJFNNQVAgYA0KCQktRG9tYWluTW9kZSBEZWZhdWx0IGANCgkJLUZvcmVzdE1vZGUgRGVmYXVsdCBgDQoJCS1Ob1JlYm9vdE9uQ29tcGxldGlvbiBgDQoJCS1Gb3JjZSBgDQoJCS1FcnJvckFjdGlvbiBTdG9wIHwgT3V0LU51bGwNCg0KCVdyaXRlLUhvc3QgIldhaXRpbmcgZm9yIHJlYm9vdCAuLi4iCQkNCiMJU3RvcC1FeGVjdXRpb24gLUV4aXRDb2RlIDMwMTAgLUV4aXRTdHJpbmcgIkNvbXB1dGVyIG11c3QgYmUgcmVzdGFydGVkIHRvIGZpbmlzaCBkb21haW4gY29udHJvbGxlciBwcm9tb3Rpb24uIg0KIwlXcml0ZS1Mb2cgIlJlc3RhcmluZyBjb21wdXRlciAuLi4iDQojCVJlc3RhcnQtQ29tcHV0ZXIgLUZvcmNlDQp9DQo=" - ], - "Commands": [ - { - "Name": "Import-Module", - "Arguments": { - "Name": "CoreFunctions" - } - }, - { - "Name": "Set-LocalUserPassword", - "Arguments": { - "UserName": "Administrator", - "Password": "$adminPassword", - "Force": true - } - } - ], - "RebootOnCompletion": 0 -} \ No newline at end of file diff --git a/conductor/data/templates/cf/Windows.template b/conductor/data/templates/cf/Windows.template deleted file mode 100644 index e14069ff..00000000 --- a/conductor/data/templates/cf/Windows.template +++ /dev/null @@ -1,62 +0,0 @@ -{ - "AWSTemplateFormatVersion" : "2010-09-09", - - "Description" : "", - - "Parameters" : { - "KeyName" : { - "Description" : "Name of an existing Amazon EC2 key pair for RDP access", - "Type" : "String", - "Default" : "keero_key" - }, - "InstanceType" : { - "Description" : "Amazon EC2 instance type", - "Type" : "String", - "Default" : "m1.medium", - "AllowedValues" : [ "m1.small", "m1.medium", "m1.large" ] - }, - "ImageName" : { - "Description" : "Image name", - "Type" : "String", - "Default" : "ws-2012-full-agent", - "AllowedValues" : [ "ws-2012-full", "ws-2012-core", "ws-2012-full-agent" ] - } - }, - - "Resources" : { - "IAMUser" : { - "Type" : "AWS::IAM::User", - "Properties" : { - "Path": "/", - "Policies": [{ - "PolicyName": "root", - "PolicyDocument": { "Statement":[{ - "Effect": "Allow", - "Action": "CloudFormation:DescribeStackResource", - "Resource": "*" - }]} - }] - } - }, - - "IAMUserAccessKey" : { - "Type" : "AWS::IAM::AccessKey", - "Properties" : { - "UserName" : {"Ref": "IAMUser"} - } - }, - - "$instanceName": { - "Type" : "AWS::EC2::Instance", - "Properties": { - "InstanceType" : { "Ref" : "InstanceType" }, - "ImageId" : { "Ref" : "ImageName" }, - "KeyName" : { "Ref" : "KeyName" }, - "UserData": "$userData" - } - } - }, - - "Outputs" : { - } -} \ No newline at end of file diff --git a/conductor/data/workflows/AD.xml b/conductor/data/workflows/AD.xml deleted file mode 100644 index 0433e4d0..00000000 --- a/conductor/data/workflows/AD.xml +++ /dev/null @@ -1,218 +0,0 @@ -<workflow> - <rule match="$.services.activeDirectories[?(@.domain)].units[?(not @.isMaster)]"> - <set path="domain"> - <select path="::domain"/> - </set> - </rule> - - <rule match="$.services.activeDirectories[*].units[?(@.state.hostname and not @.state.instanceName)]"> - <report entity="unit"> - <parameter name="id"><select path="id"/></parameter> - <parameter name="text">Creating instance <select path="name"/></parameter> - </report> - <update-cf-stack template="Windows"> - <parameter name="mappings"> - <map> - <mapping name="instanceName"><select path="::name"/>-<select path="name"/></mapping> - <mapping name="userData"> - <prepare-user-data> - <parameter name="hostname"><select path="state.hostname"/></parameter> - <parameter name="unit"><select path="name"/></parameter> - <parameter name="service"><select path="::id"/></parameter> - </prepare-user-data> - </mapping> - </map> - </parameter> - <parameter name="arguments"> - <map> - <argument name="KeyName">keero-keys</argument> - <argument name="InstanceType">m1.medium</argument> - <argument name="ImageName">ws-2012-full</argument> - </map> - </parameter> - - <success> - <set path="state.instanceName"><select path="name"/></set> - <report entity="unit"> - <parameter name="id"><select path="id"/></parameter> - <parameter name="text">Instance <select path="name"/> created</parameter> - </report> - </success> - </update-cf-stack> - </rule> - - <rule match="$.services.activeDirectories[*].units[?(@.state.instanceName and @.adminPassword and @.adminPassword != @.state.adminPassword)]"> - <send-command template="SetPassword"> - <parameter name="host"> - <select path="name"/> - </parameter> - <parameter name="service"> - <select path="::id"/> - </parameter> - <parameter name="mappings"> - <map> - <mapping name="adminPassword"> - <select path="adminPassword"/> - </mapping> - </map> - </parameter> - <success> - <set path="state.adminPassword"> - <select path="adminPassword"/> - </set> - </success> - </send-command> - </rule> - - <rule match="$.services.activeDirectories[?(@.adminPassword and @.adminPassword != @.state.domainAdminPassword)].units[?(@.state.instanceName and @.isMaster)]"> - <send-command template="SetPassword"> - <parameter name="host"> - <select path="name"/> - </parameter> - <parameter name="service"> - <select path="::id"/> - </parameter> - <parameter name="mappings"> - <map> - <mapping name="adminPassword"> - <select path="::adminPassword"/> - </mapping> - </map> - </parameter> - <success> - <set path="::state.domainAdminPassword"> - <select path="::adminPassword"/> - </set> - </success> - </send-command> - </rule> - - <rule match="$.services.activeDirectories[?(@.state.primaryDc is None)].units[?(@.state.instanceName and @.isMaster)]"> - <report entity="unit"> - <parameter name="id"><select path="id"/></parameter> - <parameter name="text">Creating Primary Domain Controller on unit <select path="name"/></parameter> - </report> - <send-command template="CreatePrimaryDC"> - <parameter name="host"> - <select path="name"/> - </parameter> - <parameter name="service"> - <select path="::id"/> - </parameter> - <parameter name="mappings"> - <map> - <mapping name="domain"> - <select path="::domain"/> - </mapping> - <mapping name="recoveryPassword"> - <select path="recoveryPassword"/> - </mapping> - </map> - </parameter> - <success> - <set path="::state.primaryDc"><select path="name"/></set> - <report entity="unit"> - <parameter name="id"><select path="id"/></parameter> - <parameter name="text">Primary Domain Controller created</parameter> - </report> - </success> - </send-command> - </rule> - - <rule match="$.services.activeDirectories[?(@.state.primaryDc and not @.state.primaryDcIp)].units[?(@.state.instanceName and @.isMaster)]"> - <send-command template="AskDnsIp" result="ip"> - <parameter name="host"> - <select path="name"/> - </parameter> - <parameter name="service"> - <select path="::id"/> - </parameter> - <success> - <set path="::state.primaryDcIp"> - <select source="ip" path="0.Result.0"/> - </set> - </success> - </send-command> - </rule> - - <rule match="$..units[?(@.state.instanceName and @.domain and @.domain != @.state.domain)]"> - <set path="#unit"> - <select/> - </set> - <set path="#service"> - <select path="::"/> - </set> - <rule> - <parameter name="match">/$.services.activeDirectories[?(@.domain == '<select path="domain"/>' and @.state.primaryDcIp)]</parameter> - - <send-command template="JoinDomain"> - <parameter name="host"> - <select path="name" source="unit"/> - </parameter> - <parameter name="service"> - <select path="id" source="service"/> - </parameter> - <parameter name="mappings"> - <map> - <mapping name="domain"> - <select path="domain"/> - </mapping> - <mapping name="domainPassword"> - <select path="adminPassword"/> - </mapping> - <mapping name="dnsIp"> - <select path="state.primaryDcIp"/> - </mapping> - </map> - </parameter> - - <success> - <set path="state.domain" target="unit"> - <select path="domain"/> - </set> - <report entity="unit"> - <parameter name="id"><select path="id" source="unit"/></parameter> - <parameter name="text">Unit <select path="name" source="unit"/> has joined domain <select path="domain"/></parameter> - </report> - </success> - </send-command> - </rule> - </rule> - - - <rule match="$.services.activeDirectories[*].units[?(@.state.domain and not @.isMaster and not @.state.installed)]"> - <report entity="unit"> - <parameter name="id"><select path="id"/></parameter> - <parameter name="text">Creating Secondary Domain Controller on unit <select path="name"/></parameter> - </report> - <send-command template="CreateSecondaryDC"> - <parameter name="host"> - <select path="name"/> - </parameter> - <parameter name="service"> - <select path="::id"/> - </parameter> - <parameter name="mappings"> - <map> - <mapping name="recoveryPassword"> - <select path="recoveryPassword"/> - </mapping> - <mapping name="domainPassword"> - <select path="::adminPassword"/> - </mapping> - </map> - </parameter> - <success> - <set path="state.installed"><true/></set> - <report entity="unit"> - <parameter name="id"><select path="id"/></parameter> - <parameter name="text">Secondary Domain Controller created</parameter> - </report> - <report entity="service"> - <parameter name="id"><select path="::id"/></parameter> - <parameter name="text">Domain <select path="::domain"/> created</parameter> - </report> - </success> - </send-command> - </rule> -</workflow> \ No newline at end of file diff --git a/conductor/data/workflows/Common.xml b/conductor/data/workflows/Common.xml deleted file mode 100644 index 01d13259..00000000 --- a/conductor/data/workflows/Common.xml +++ /dev/null @@ -1,19 +0,0 @@ -<workflow> - - <rule match="$.services[*][*].units[?(@.state.hostname is None)]"> - <set path="state.hostname"><generate-hostname/></set> - </rule> - - <rule match="$[?(not @.state.deleted)]"> - <rule match="$.services[*][*].units[*]"> - <empty> - <delete-cf-stack> - <success> - <set path="/state.deleted"><true/></set> - </success> - </delete-cf-stack> - </empty> - </rule> - </rule> - -</workflow> \ No newline at end of file diff --git a/conductor/data/workflows/IIS.xml b/conductor/data/workflows/IIS.xml deleted file mode 100644 index 136358b4..00000000 --- a/conductor/data/workflows/IIS.xml +++ /dev/null @@ -1,65 +0,0 @@ -<workflow> - <rule match="$.services.webServers[?(@.domain)].units[*]"> - <set path="domain"> - <select path="::domain"/> - </set> - </rule> - - <rule match="$.services.webServers[*].units[?(@.state.hostname and not @.state.instanceName)]"> - <report entity="unit"> - <parameter name="id"><select path="id"/></parameter> - <parameter name="text">Creating instance <select path="name"/></parameter> - </report> - <update-cf-stack template="Windows"> - <parameter name="mappings"> - <map> - <mapping name="instanceName"><select path="::name"/>-<select path="name"/></mapping> - <mapping name="userData"> - <prepare-user-data> - <parameter name="hostname"><select path="state.hostname"/></parameter> - <parameter name="unit"><select path="name"/></parameter> - <parameter name="service"><select path="::id"/></parameter> - </prepare-user-data> - </mapping> - </map> - </parameter> - <parameter name="arguments"> - <map> - <argument name="KeyName">keero-keys</argument> - <argument name="InstanceType">m1.medium</argument> - <argument name="ImageName">ws-2012-full</argument> - </map> - </parameter> - - <success> - <set path="state.instanceName"><select path="name"/></set> - <report entity="unit"> - <parameter name="id"><select path="id"/></parameter> - <parameter name="text">Instance <select path="name"/> created</parameter> - </report> - </success> - </update-cf-stack> - </rule> - - <rule match="$.services.webServers[*].units[?(@.state.instanceName and not @.state.iisInstalled)]"> - <report entity="unit"> - <parameter name="id"><select path="id"/></parameter> - <parameter name="text">Creating IIS Web Server on unit <select path="name"/></parameter> - </report> - <send-command template="InstallIIS"> - <parameter name="host"> - <select path="name"/> - </parameter> - <parameter name="service"> - <select path="::id"/> - </parameter> - <success> - <set path="state.iisInstalled"><true/></set> - <report entity="unit"> - <parameter name="id"><select path="id"/></parameter> - <parameter name="text">IIS <select path="name"/> has started</parameter> - </report> - </success> - </send-command> - </rule> -</workflow> \ No newline at end of file diff --git a/conductor/doc/source/_static/basic.css b/conductor/doc/source/_static/basic.css deleted file mode 100644 index d909ce37..00000000 --- a/conductor/doc/source/_static/basic.css +++ /dev/null @@ -1,416 +0,0 @@ -/** - * Sphinx stylesheet -- basic theme - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - -/* -- main layout ----------------------------------------------------------- */ - -div.clearer { - clear: both; -} - -/* -- relbar ---------------------------------------------------------------- */ - -div.related { - width: 100%; - font-size: 90%; -} - -div.related h3 { - display: none; -} - -div.related ul { - margin: 0; - padding: 0 0 0 10px; - list-style: none; -} - -div.related li { - display: inline; -} - -div.related li.right { - float: right; - margin-right: 5px; -} - -/* -- sidebar --------------------------------------------------------------- */ - -div.sphinxsidebarwrapper { - padding: 10px 5px 0 10px; -} - -div.sphinxsidebar { - float: left; - width: 230px; - margin-left: -100%; - font-size: 90%; -} - -div.sphinxsidebar ul { - list-style: none; -} - -div.sphinxsidebar ul ul, -div.sphinxsidebar ul.want-points { - margin-left: 20px; - list-style: square; -} - -div.sphinxsidebar ul ul { - margin-top: 0; - margin-bottom: 0; -} - -div.sphinxsidebar form { - margin-top: 10px; -} - -div.sphinxsidebar input { - border: 1px solid #98dbcc; - font-family: sans-serif; - font-size: 1em; -} - -img { - border: 0; -} - -/* -- search page ----------------------------------------------------------- */ - -ul.search { - margin: 10px 0 0 20px; - padding: 0; -} - -ul.search li { - padding: 5px 0 5px 20px; - background-image: url(file.png); - background-repeat: no-repeat; - background-position: 0 7px; -} - -ul.search li a { - font-weight: bold; -} - -ul.search li div.context { - color: #888; - margin: 2px 0 0 30px; - text-align: left; -} - -ul.keywordmatches li.goodmatch a { - font-weight: bold; -} - -/* -- index page ------------------------------------------------------------ */ - -table.contentstable { - width: 90%; -} - -table.contentstable p.biglink { - line-height: 150%; -} - -a.biglink { - font-size: 1.3em; -} - -span.linkdescr { - font-style: italic; - padding-top: 5px; - font-size: 90%; -} - -/* -- general index --------------------------------------------------------- */ - -table.indextable td { - text-align: left; - vertical-align: top; -} - -table.indextable dl, table.indextable dd { - margin-top: 0; - margin-bottom: 0; -} - -table.indextable tr.pcap { - height: 10px; -} - -table.indextable tr.cap { - margin-top: 10px; - background-color: #f2f2f2; -} - -img.toggler { - margin-right: 3px; - margin-top: 3px; - cursor: pointer; -} - -/* -- general body styles --------------------------------------------------- */ - -a.headerlink { - visibility: hidden; -} - -h1:hover > a.headerlink, -h2:hover > a.headerlink, -h3:hover > a.headerlink, -h4:hover > a.headerlink, -h5:hover > a.headerlink, -h6:hover > a.headerlink, -dt:hover > a.headerlink { - visibility: visible; -} - -div.body p.caption { - text-align: inherit; -} - -div.body td { - text-align: left; -} - -.field-list ul { - padding-left: 1em; -} - -.first { -} - -p.rubric { - margin-top: 30px; - font-weight: bold; -} - -/* -- sidebars -------------------------------------------------------------- */ - -div.sidebar { - margin: 0 0 0.5em 1em; - border: 1px solid #ddb; - padding: 7px 7px 0 7px; - background-color: #ffe; - width: 40%; - float: right; -} - -p.sidebar-title { - font-weight: bold; -} - -/* -- topics ---------------------------------------------------------------- */ - -div.topic { - border: 1px solid #ccc; - padding: 7px 7px 0 7px; - margin: 10px 0 10px 0; -} - -p.topic-title { - font-size: 1.1em; - font-weight: bold; - margin-top: 10px; -} - -/* -- admonitions ----------------------------------------------------------- */ - -div.admonition { - margin-top: 10px; - margin-bottom: 10px; - padding: 7px; -} - -div.admonition dt { - font-weight: bold; -} - -div.admonition dl { - margin-bottom: 0; -} - -p.admonition-title { - margin: 0px 10px 5px 0px; - font-weight: bold; -} - -div.body p.centered { - text-align: center; - margin-top: 25px; -} - -/* -- tables ---------------------------------------------------------------- */ - -table.docutils { - border: 0; - border-collapse: collapse; -} - -table.docutils td, table.docutils th { - padding: 1px 8px 1px 0; - border-top: 0; - border-left: 0; - border-right: 0; - border-bottom: 1px solid #aaa; -} - -table.field-list td, table.field-list th { - border: 0 !important; -} - -table.footnote td, table.footnote th { - border: 0 !important; -} - -th { - text-align: left; - padding-right: 5px; -} - -/* -- other body styles ----------------------------------------------------- */ - -dl { - margin-bottom: 15px; -} - -dd p { - margin-top: 0px; -} - -dd ul, dd table { - margin-bottom: 10px; -} - -dd { - margin-top: 3px; - margin-bottom: 10px; - margin-left: 30px; -} - -dt:target, .highlight { - background-color: #fbe54e; -} - -dl.glossary dt { - font-weight: bold; - font-size: 1.1em; -} - -.field-list ul { - margin: 0; - padding-left: 1em; -} - -.field-list p { - margin: 0; -} - -.refcount { - color: #060; -} - -.optional { - font-size: 1.3em; -} - -.versionmodified { - font-style: italic; -} - -.system-message { - background-color: #fda; - padding: 5px; - border: 3px solid red; -} - -.footnote:target { - background-color: #ffa -} - -.line-block { - display: block; - margin-top: 1em; - margin-bottom: 1em; -} - -.line-block .line-block { - margin-top: 0; - margin-bottom: 0; - margin-left: 1.5em; -} - -/* -- code displays --------------------------------------------------------- */ - -pre { - overflow: auto; -} - -td.linenos pre { - padding: 5px 0px; - border: 0; - background-color: transparent; - color: #aaa; -} - -table.highlighttable { - margin-left: 0.5em; -} - -table.highlighttable td { - padding: 0 0.5em 0 0.5em; -} - -tt.descname { - background-color: transparent; - font-weight: bold; - font-size: 1.2em; -} - -tt.descclassname { - background-color: transparent; -} - -tt.xref, a tt { - background-color: transparent; - font-weight: bold; -} - -h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { - background-color: transparent; -} - -/* -- math display ---------------------------------------------------------- */ - -img.math { - vertical-align: middle; -} - -div.body div.math p { - text-align: center; -} - -span.eqno { - float: right; -} - -/* -- printout stylesheet --------------------------------------------------- */ - -@media print { - div.document, - div.documentwrapper, - div.bodywrapper { - margin: 0 !important; - width: 100%; - } - - div.sphinxsidebar, - div.related, - div.footer, - #top-link { - display: none; - } -} diff --git a/conductor/doc/source/_static/default.css b/conductor/doc/source/_static/default.css deleted file mode 100644 index c8091ecb..00000000 --- a/conductor/doc/source/_static/default.css +++ /dev/null @@ -1,230 +0,0 @@ -/** - * Sphinx stylesheet -- default theme - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - -@import url("basic.css"); - -/* -- page layout ----------------------------------------------------------- */ - -body { - font-family: sans-serif; - font-size: 100%; - background-color: #11303d; - color: #000; - margin: 0; - padding: 0; -} - -div.document { - background-color: #1c4e63; -} - -div.documentwrapper { - float: left; - width: 100%; -} - -div.bodywrapper { - margin: 0 0 0 230px; -} - -div.body { - background-color: #ffffff; - color: #000000; - padding: 0 20px 30px 20px; -} - -div.footer { - color: #ffffff; - width: 100%; - padding: 9px 0 9px 0; - text-align: center; - font-size: 75%; -} - -div.footer a { - color: #ffffff; - text-decoration: underline; -} - -div.related { - background-color: #133f52; - line-height: 30px; - color: #ffffff; -} - -div.related a { - color: #ffffff; -} - -div.sphinxsidebar { -} - -div.sphinxsidebar h3 { - font-family: 'Trebuchet MS', sans-serif; - color: #ffffff; - font-size: 1.4em; - font-weight: normal; - margin: 0; - padding: 0; -} - -div.sphinxsidebar h3 a { - color: #ffffff; -} - -div.sphinxsidebar h4 { - font-family: 'Trebuchet MS', sans-serif; - color: #ffffff; - font-size: 1.3em; - font-weight: normal; - margin: 5px 0 0 0; - padding: 0; -} - -div.sphinxsidebar p { - color: #ffffff; -} - -div.sphinxsidebar p.topless { - margin: 5px 10px 10px 10px; -} - -div.sphinxsidebar ul { - margin: 10px; - padding: 0; - color: #ffffff; -} - -div.sphinxsidebar a { - color: #98dbcc; -} - -div.sphinxsidebar input { - border: 1px solid #98dbcc; - font-family: sans-serif; - font-size: 1em; -} - -/* -- body styles ----------------------------------------------------------- */ - -a { - color: #355f7c; - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - -div.body p, div.body dd, div.body li { - text-align: left; - line-height: 130%; -} - -div.body h1, -div.body h2, -div.body h3, -div.body h4, -div.body h5, -div.body h6 { - font-family: 'Trebuchet MS', sans-serif; - background-color: #f2f2f2; - font-weight: normal; - color: #20435c; - border-bottom: 1px solid #ccc; - margin: 20px -20px 10px -20px; - padding: 3px 0 3px 10px; -} - -div.body h1 { margin-top: 0; font-size: 200%; } -div.body h2 { font-size: 160%; } -div.body h3 { font-size: 140%; } -div.body h4 { font-size: 120%; } -div.body h5 { font-size: 110%; } -div.body h6 { font-size: 100%; } - -a.headerlink { - color: #c60f0f; - font-size: 0.8em; - padding: 0 4px 0 4px; - text-decoration: none; -} - -a.headerlink:hover { - background-color: #c60f0f; - color: white; -} - -div.body p, div.body dd, div.body li { - text-align: left; - line-height: 130%; -} - -div.admonition p.admonition-title + p { - display: inline; -} - -div.admonition p { - margin-bottom: 5px; -} - -div.admonition pre { - margin-bottom: 5px; -} - -div.admonition ul, div.admonition ol { - margin-bottom: 5px; -} - -div.note { - background-color: #eee; - border: 1px solid #ccc; -} - -div.seealso { - background-color: #ffc; - border: 1px solid #ff6; -} - -div.topic { - background-color: #eee; -} - -div.warning { - background-color: #ffe4e4; - border: 1px solid #f66; -} - -p.admonition-title { - display: inline; -} - -p.admonition-title:after { - content: ":"; -} - -pre { - padding: 5px; - background-color: #eeffcc; - color: #333333; - line-height: 120%; - border: 1px solid #ac9; - border-left: none; - border-right: none; -} - -tt { - background-color: #ecf0f3; - padding: 0 1px 0 1px; - font-size: 0.95em; -} - -.warning tt { - background: #efc2c2; -} - -.note tt { - background: #d6d6d6; -} diff --git a/conductor/doc/source/_static/header-line.gif b/conductor/doc/source/_static/header-line.gif deleted file mode 100644 index 3601730e03488b7b5f92dc992d23ad753357c167..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48 zcmZ?wbhEHbWMg1uXkcVG`smgF|Nj+#vM@3*Ff!;c00Bsbfr-7RpY8O^Kn4bD08FwB Aga7~l diff --git a/conductor/doc/source/_static/header_bg.jpg b/conductor/doc/source/_static/header_bg.jpg deleted file mode 100644 index f788c41c26481728fa4329c17c87bde36001adc1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3738 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 diff --git a/conductor/doc/source/_static/jquery.tweet.js b/conductor/doc/source/_static/jquery.tweet.js deleted file mode 100644 index 79bf0bdb..00000000 --- a/conductor/doc/source/_static/jquery.tweet.js +++ /dev/null @@ -1,154 +0,0 @@ -(function($) { - - $.fn.tweet = function(o){ - var s = { - username: ["seaofclouds"], // [string] required, unless you want to display our tweets. :) it can be an array, just do ["username1","username2","etc"] - list: null, //[string] optional name of list belonging to username - avatar_size: null, // [integer] height and width of avatar if displayed (48px max) - count: 3, // [integer] how many tweets to display? - intro_text: null, // [string] do you want text BEFORE your your tweets? - outro_text: null, // [string] do you want text AFTER your tweets? - join_text: null, // [string] optional text in between date and tweet, try setting to "auto" - auto_join_text_default: "i said,", // [string] auto text for non verb: "i said" bullocks - auto_join_text_ed: "i", // [string] auto text for past tense: "i" surfed - auto_join_text_ing: "i am", // [string] auto tense for present tense: "i was" surfing - auto_join_text_reply: "i replied to", // [string] auto tense for replies: "i replied to" @someone "with" - auto_join_text_url: "i was looking at", // [string] auto tense for urls: "i was looking at" http:... - loading_text: null, // [string] optional loading text, displayed while tweets load - query: null // [string] optional search query - }; - - if(o) $.extend(s, o); - - $.fn.extend({ - linkUrl: function() { - var returning = []; - var regexp = /((ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?)/gi; - this.each(function() { - returning.push(this.replace(regexp,"<a href=\"$1\">$1</a>")); - }); - return $(returning); - }, - linkUser: function() { - var returning = []; - var regexp = /[\@]+([A-Za-z0-9-_]+)/gi; - this.each(function() { - returning.push(this.replace(regexp,"<a href=\"http://twitter.com/$1\">@$1</a>")); - }); - return $(returning); - }, - linkHash: function() { - var returning = []; - var regexp = / [\#]+([A-Za-z0-9-_]+)/gi; - this.each(function() { - returning.push(this.replace(regexp, ' <a href="http://search.twitter.com/search?q=&tag=$1&lang=all&from='+s.username.join("%2BOR%2B")+'">#$1</a>')); - }); - return $(returning); - }, - capAwesome: function() { - var returning = []; - this.each(function() { - returning.push(this.replace(/\b(awesome)\b/gi, '<span class="awesome">$1</span>')); - }); - return $(returning); - }, - capEpic: function() { - var returning = []; - this.each(function() { - returning.push(this.replace(/\b(epic)\b/gi, '<span class="epic">$1</span>')); - }); - return $(returning); - }, - makeHeart: function() { - var returning = []; - this.each(function() { - returning.push(this.replace(/(&lt;)+[3]/gi, "<tt class='heart'>&#x2665;</tt>")); - }); - return $(returning); - } - }); - - function relative_time(time_value) { - var parsed_date = Date.parse(time_value); - var relative_to = (arguments.length > 1) ? arguments[1] : new Date(); - var delta = parseInt((relative_to.getTime() - parsed_date) / 1000); - var pluralize = function (singular, n) { - return '' + n + ' ' + singular + (n == 1 ? '' : 's'); - }; - if(delta < 60) { - return 'less than a minute ago'; - } else if(delta < (45*60)) { - return 'about ' + pluralize("minute", parseInt(delta / 60)) + ' ago'; - } else if(delta < (24*60*60)) { - return 'about ' + pluralize("hour", parseInt(delta / 3600)) + ' ago'; - } else { - return 'about ' + pluralize("day", parseInt(delta / 86400)) + ' ago'; - } - } - - function build_url() { - var proto = ('https:' == document.location.protocol ? 'https:' : 'http:'); - if (s.list) { - return proto+"//api.twitter.com/1/"+s.username[0]+"/lists/"+s.list+"/statuses.json?per_page="+s.count+"&callback=?"; - } else if (s.query == null && s.username.length == 1) { - return proto+'//twitter.com/status/user_timeline/'+s.username[0]+'.json?count='+s.count+'&callback=?'; - } else { - var query = (s.query || 'from:'+s.username.join('%20OR%20from:')); - return proto+'//search.twitter.com/search.json?&q='+query+'&rpp='+s.count+'&callback=?'; - } - } - - return this.each(function(){ - var list = $('<ul class="tweet_list">').appendTo(this); - var intro = '<p class="tweet_intro">'+s.intro_text+'</p>'; - var outro = '<p class="tweet_outro">'+s.outro_text+'</p>'; - var loading = $('<p class="loading">'+s.loading_text+'</p>'); - - if(typeof(s.username) == "string"){ - s.username = [s.username]; - } - - if (s.loading_text) $(this).append(loading); - $.getJSON(build_url(), function(data){ - if (s.loading_text) loading.remove(); - if (s.intro_text) list.before(intro); - $.each((data.results || data), function(i,item){ - // auto join text based on verb tense and content - if (s.join_text == "auto") { - if (item.text.match(/^(@([A-Za-z0-9-_]+)) .*/i)) { - var join_text = s.auto_join_text_reply; - } else if (item.text.match(/(^\w+:\/\/[A-Za-z0-9-_]+\.[A-Za-z0-9-_:%&\?\/.=]+) .*/i)) { - var join_text = s.auto_join_text_url; - } else if (item.text.match(/^((\w+ed)|just) .*/im)) { - var join_text = s.auto_join_text_ed; - } else if (item.text.match(/^(\w*ing) .*/i)) { - var join_text = s.auto_join_text_ing; - } else { - var join_text = s.auto_join_text_default; - } - } else { - var join_text = s.join_text; - }; - - var from_user = item.from_user || item.user.screen_name; - var profile_image_url = item.profile_image_url || item.user.profile_image_url; - var join_template = '<span class="tweet_join"> '+join_text+' </span>'; - var join = ((s.join_text) ? join_template : ' '); - var avatar_template = '<a class="tweet_avatar" href="http://twitter.com/'+from_user+'"><img src="'+profile_image_url+'" height="'+s.avatar_size+'" width="'+s.avatar_size+'" alt="'+from_user+'\'s avatar" title="'+from_user+'\'s avatar" border="0"/></a>'; - var avatar = (s.avatar_size ? avatar_template : ''); - var date = '<a href="http://twitter.com/'+from_user+'/statuses/'+item.id+'" title="view tweet on twitter">'+relative_time(item.created_at)+'</a>'; - var text = '<span class="tweet_text">' +$([item.text]).linkUrl().linkUser().linkHash().makeHeart().capAwesome().capEpic()[0]+ '</span>'; - - // until we create a template option, arrange the items below to alter a tweet's display. - list.append('<li>' + avatar + date + join + text + '</li>'); - - list.children('li:first').addClass('tweet_first'); - list.children('li:odd').addClass('tweet_even'); - list.children('li:even').addClass('tweet_odd'); - }); - if (s.outro_text) list.after(outro); - }); - - }); - }; -})(jQuery); \ No newline at end of file diff --git a/conductor/doc/source/_static/nature.css b/conductor/doc/source/_static/nature.css deleted file mode 100644 index a98bd420..00000000 --- a/conductor/doc/source/_static/nature.css +++ /dev/null @@ -1,245 +0,0 @@ -/* - * nature.css_t - * ~~~~~~~~~~~~ - * - * Sphinx stylesheet -- nature theme. - * - * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -@import url("basic.css"); - -/* -- page layout ----------------------------------------------------------- */ - -body { - font-family: Arial, sans-serif; - font-size: 100%; - background-color: #111; - color: #555; - margin: 0; - padding: 0; -} - -div.documentwrapper { - float: left; - width: 100%; -} - -div.bodywrapper { - margin: 0 0 0 {{ theme_sidebarwidth|toint }}px; -} - -hr { - border: 1px solid #B1B4B6; -} - -div.document { - background-color: #eee; -} - -div.body { - background-color: #ffffff; - color: #3E4349; - padding: 0 30px 30px 30px; - font-size: 0.9em; -} - -div.footer { - color: #555; - width: 100%; - padding: 13px 0; - text-align: center; - font-size: 75%; -} - -div.footer a { - color: #444; - text-decoration: underline; -} - -div.related { - background-color: #6BA81E; - line-height: 32px; - color: #fff; - text-shadow: 0px 1px 0 #444; - font-size: 0.9em; -} - -div.related a { - color: #E2F3CC; -} - -div.sphinxsidebar { - font-size: 0.75em; - line-height: 1.5em; -} - -div.sphinxsidebarwrapper{ - padding: 20px 0; -} - -div.sphinxsidebar h3, -div.sphinxsidebar h4 { - font-family: Arial, sans-serif; - color: #222; - font-size: 1.2em; - font-weight: normal; - margin: 0; - padding: 5px 10px; - background-color: #ddd; - text-shadow: 1px 1px 0 white -} - -div.sphinxsidebar h4{ - font-size: 1.1em; -} - -div.sphinxsidebar h3 a { - color: #444; -} - - -div.sphinxsidebar p { - color: #888; - padding: 5px 20px; -} - -div.sphinxsidebar p.topless { -} - -div.sphinxsidebar ul { - margin: 10px 20px; - padding: 0; - color: #000; -} - -div.sphinxsidebar a { - color: #444; -} - -div.sphinxsidebar input { - border: 1px solid #ccc; - font-family: sans-serif; - font-size: 1em; -} - -div.sphinxsidebar input[type=text]{ - margin-left: 20px; -} - -/* -- body styles ----------------------------------------------------------- */ - -a { - color: #005B81; - text-decoration: none; -} - -a:hover { - color: #E32E00; - text-decoration: underline; -} - -div.body h1, -div.body h2, -div.body h3, -div.body h4, -div.body h5, -div.body h6 { - font-family: Arial, sans-serif; - background-color: #BED4EB; - font-weight: normal; - color: #212224; - margin: 30px 0px 10px 0px; - padding: 5px 0 5px 10px; - text-shadow: 0px 1px 0 white -} - -div.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; } -div.body h2 { font-size: 150%; background-color: #C8D5E3; } -div.body h3 { font-size: 120%; background-color: #D8DEE3; } -div.body h4 { font-size: 110%; background-color: #D8DEE3; } -div.body h5 { font-size: 100%; background-color: #D8DEE3; } -div.body h6 { font-size: 100%; background-color: #D8DEE3; } - -a.headerlink { - color: #c60f0f; - font-size: 0.8em; - padding: 0 4px 0 4px; - text-decoration: none; -} - -a.headerlink:hover { - background-color: #c60f0f; - color: white; -} - -div.body p, div.body dd, div.body li { - line-height: 1.5em; -} - -div.admonition p.admonition-title + p { - display: inline; -} - -div.highlight{ - background-color: white; -} - -div.note { - background-color: #eee; - border: 1px solid #ccc; -} - -div.seealso { - background-color: #ffc; - border: 1px solid #ff6; -} - -div.topic { - background-color: #eee; -} - -div.warning { - background-color: #ffe4e4; - border: 1px solid #f66; -} - -p.admonition-title { - display: inline; -} - -p.admonition-title:after { - content: ":"; -} - -pre { - padding: 10px; - background-color: White; - color: #222; - line-height: 1.2em; - border: 1px solid #C6C9CB; - font-size: 1.1em; - margin: 1.5em 0 1.5em 0; - -webkit-box-shadow: 1px 1px 1px #d8d8d8; - -moz-box-shadow: 1px 1px 1px #d8d8d8; -} - -tt { - background-color: #ecf0f3; - color: #222; - /* padding: 1px 2px; */ - font-size: 1.1em; - font-family: monospace; -} - -.viewcode-back { - font-family: Arial, sans-serif; -} - -div.viewcode-block:target { - background-color: #f4debf; - border-top: 1px solid #ac9; - border-bottom: 1px solid #ac9; -} diff --git a/conductor/doc/source/_static/openstack_logo.png b/conductor/doc/source/_static/openstack_logo.png deleted file mode 100644 index 146faec5cfe3773824f4caf39e4480e4974d10df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3670 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 diff --git a/conductor/doc/source/_static/tweaks.css b/conductor/doc/source/_static/tweaks.css deleted file mode 100644 index 3f3fb3f0..00000000 --- a/conductor/doc/source/_static/tweaks.css +++ /dev/null @@ -1,94 +0,0 @@ -body { - background: #fff url(../_static/header_bg.jpg) top left no-repeat; -} - -#header { - width: 950px; - margin: 0 auto; - height: 102px; -} - -#header h1#logo { - background: url(../_static/openstack_logo.png) top left no-repeat; - display: block; - float: left; - text-indent: -9999px; - width: 175px; - height: 55px; -} - -#navigation { - background: url(../_static/header-line.gif) repeat-x 0 bottom; - display: block; - float: left; - margin: 27px 0 0 25px; - padding: 0; -} - -#navigation li{ - float: left; - display: block; - margin-right: 25px; -} - -#navigation li a { - display: block; - font-weight: normal; - text-decoration: none; - background-position: 50% 0; - padding: 20px 0 5px; - color: #353535; - font-size: 14px; -} - -#navigation li a.current, #navigation li a.section { - border-bottom: 3px solid #cf2f19; - color: #cf2f19; -} - -div.related { - background-color: #cde2f8; - border: 1px solid #b0d3f8; -} - -div.related a { - color: #4078ba; - text-shadow: none; -} - -div.sphinxsidebarwrapper { - padding-top: 0; -} - -pre { - color: #555; -} - -div.documentwrapper h1, div.documentwrapper h2, div.documentwrapper h3, div.documentwrapper h4, div.documentwrapper h5, div.documentwrapper h6 { - font-family: 'PT Sans', sans-serif !important; - color: #264D69; - border-bottom: 1px dotted #C5E2EA; - padding: 0; - background: none; - padding-bottom: 5px; -} - -div.documentwrapper h3 { - color: #CF2F19; -} - -a.headerlink { - color: #fff !important; - margin-left: 5px; - background: #CF2F19 !important; -} - -div.body { - margin-top: -25px; - margin-left: 230px; -} - -div.document { - width: 960px; - margin: 0 auto; -} \ No newline at end of file diff --git a/conductor/doc/source/_templates/.placeholder b/conductor/doc/source/_templates/.placeholder deleted file mode 100644 index e69de29b..00000000 diff --git a/conductor/doc/source/_theme/layout.html b/conductor/doc/source/_theme/layout.html deleted file mode 100644 index 750b7822..00000000 --- a/conductor/doc/source/_theme/layout.html +++ /dev/null @@ -1,83 +0,0 @@ -{% extends "basic/layout.html" %} -{% set css_files = css_files + ['_static/tweaks.css'] %} -{% set script_files = script_files + ['_static/jquery.tweet.js'] %} - -{%- macro sidebar() %} - {%- if not embedded %}{% if not theme_nosidebar|tobool %} - <div class="sphinxsidebar"> - <div class="sphinxsidebarwrapper"> - {%- block sidebarlogo %} - {%- if logo %} - <p class="logo"><a href="{{ pathto(master_doc) }}"> - <img class="logo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/> - </a></p> - {%- endif %} - {%- endblock %} - {%- block sidebartoc %} - {%- if display_toc %} - <h3><a href="{{ pathto(master_doc) }}">{{ _('Table Of Contents') }}</a></h3> - {{ toc }} - {%- endif %} - {%- endblock %} - {%- block sidebarrel %} - {%- if prev %} - <h4>{{ _('Previous topic') }}</h4> - <p class="topless"><a href="{{ prev.link|e }}" - title="{{ _('previous chapter') }}">{{ prev.title }}</a></p> - {%- endif %} - {%- if next %} - <h4>{{ _('Next topic') }}</h4> - <p class="topless"><a href="{{ next.link|e }}" - title="{{ _('next chapter') }}">{{ next.title }}</a></p> - {%- endif %} - {%- endblock %} - {%- block sidebarsourcelink %} - {%- if show_source and has_source and sourcename %} - <h3>{{ _('This Page') }}</h3> - <ul class="this-page-menu"> - <li><a href="{{ pathto('_sources/' + sourcename, true)|e }}" - rel="nofollow">{{ _('Show Source') }}</a></li> - </ul> - {%- endif %} - {%- endblock %} - {%- if customsidebar %} - {% include customsidebar %} - {%- endif %} - {%- block sidebarsearch %} - {%- if pagename != "search" %} - <div id="searchbox" style="display: none"> - <h3>{{ _('Quick search') }}</h3> - <form class="search" action="{{ pathto('search') }}" method="get"> - <input type="text" name="q" size="18" /> - <input type="submit" value="{{ _('Go') }}" /> - <input type="hidden" name="check_keywords" value="yes" /> - <input type="hidden" name="area" value="default" /> - </form> - <p class="searchtip" style="font-size: 90%"> - {{ _('Enter search terms or a module, class or function name.') }} - </p> - </div> - <script type="text/javascript">$('#searchbox').show(0);</script> - {%- endif %} - {%- endblock %} - </div> - </div> - {%- endif %}{% endif %} -{%- endmacro %} - -{% block relbar1 %}{% endblock relbar1 %} - -{% block header %} - <div id="header"> - <h1 id="logo"><a href="http://www.openstack.org/">OpenStack</a></h1> - <ul id="navigation"> - <li><a href="http://www.openstack.org/" title="Go to the Home page" class="link">Home</a></li> - <li><a href="http://www.openstack.org/projects/" title="Go to the OpenStack Projects page">Projects</a></li> - <li><a href="http://www.openstack.org/user-stories/" title="Go to the User Stories page" class="link">User Stories</a></li> - <li><a href="http://www.openstack.org/community/" title="Go to the Community page" class="link">Community</a></li> - <li><a href="http://www.openstack.org/blog/" title="Go to the OpenStack Blog">Blog</a></li> - <li><a href="http://wiki.openstack.org/" title="Go to the OpenStack Wiki">Wiki</a></li> - <li><a href="http://docs.openstack.org/" title="Go to OpenStack Documentation" class="current">Documentation</a></li> - </ul> - </div> -{% endblock %} \ No newline at end of file diff --git a/conductor/doc/source/_theme/theme.conf b/conductor/doc/source/_theme/theme.conf deleted file mode 100644 index 1cc40044..00000000 --- a/conductor/doc/source/_theme/theme.conf +++ /dev/null @@ -1,4 +0,0 @@ -[theme] -inherit = basic -stylesheet = nature.css -pygments_style = tango diff --git a/conductor/doc/source/conf.py b/conductor/doc/source/conf.py deleted file mode 100644 index e9b38f91..00000000 --- a/conductor/doc/source/conf.py +++ /dev/null @@ -1,242 +0,0 @@ - -# -*- coding: utf-8 -*- -# Copyright (c) 2010 OpenStack Foundation. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# -# Conductor documentation build configuration file, created by -# sphinx-quickstart on Tue February 28 13:50:15 2013. -# -# This file is execfile()'d with the current directory set to its containing -# dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import os -import sys - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path = [os.path.abspath('../../conductor'), - os.path.abspath('../..'), - os.path.abspath('../../bin') - ] + sys.path - -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.coverage', - 'sphinx.ext.ifconfig', - 'sphinx.ext.intersphinx', - 'sphinx.ext.pngmath', - 'sphinx.ext.graphviz'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = [] -if os.getenv('HUDSON_PUBLISH_DOCS'): - templates_path = ['_ga', '_templates'] -else: - templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'Conductor' -copyright = u'2013, Mirantis, Inc.' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -from conductor.version import version_info as conductor_version -# The full version, including alpha/beta/rc tags. -release = conductor_version.version_string_with_vcs() -# The short X.Y version. -version = conductor_version.canonical_version_string() - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of documents that shouldn't be included in the build. -#unused_docs = [] - -# List of directories, relative to source directory, that shouldn't be searched -# for source files. -exclude_trees = ['api'] - -# The reST default role (for this markup: `text`) to use for all documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -show_authors = True - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -modindex_common_prefix = ['portas.'] - -# -- Options for man page output -------------------------------------------- - -# Grouping the document tree for man pages. -# List of tuples 'sourcefile', 'target', u'title', u'Authors name', 'manual' - -man_pages = [ - ('man/conductor', 'conductor', u'Conductor Orchestrator', - [u'Mirantis, Inc.'], 1) -] - - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. Major themes that come with -# Sphinx are currently 'default' and 'sphinxdoc'. -html_theme_path = ["."] -html_theme = '_theme' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = ['_theme'] - -# The name for this set of Sphinx documents. If None, it defaults to -# "<project> v<release> documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' -git_cmd = "git log --pretty=format:'%ad, commit %h' --date=local -n1" -html_last_updated_fmt = os.popen(git_cmd).read() - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -html_use_modindex = False - -# If false, no index is generated. -html_use_index = False - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a <link> tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = '' - -# Output file base name for HTML help builder. -htmlhelp_basename = 'conductordoc' - - -# -- Options for LaTeX output ------------------------------------------------ - -# The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' - -# The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, -# documentclass [howto/manual]). -latex_documents = [ - ('index', 'Conductor.tex', u'Conductor Documentation', - u'Keero Team', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# Additional stuff for the LaTeX preamble. -#latex_preamble = '' - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_use_modindex = True - -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'python': ('http://docs.python.org/', None)} diff --git a/conductor/doc/source/index.rst b/conductor/doc/source/index.rst deleted file mode 100644 index ea1b15b3..00000000 --- a/conductor/doc/source/index.rst +++ /dev/null @@ -1,72 +0,0 @@ -.. - Copyright 2010 OpenStack Foundation - All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); you may - not use this file except in compliance with the License. You may obtain - a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - License for the specific language governing permissions and limitations - under the License. - -======================================================= -Welcome to Conductor, the Glazier orchestration engine! -======================================================= - -Conductor is an Glazier orchestration engine that transforms object model sent by -REST API service into a series of Heat and Glazier-Agent commands - -This document describes Conductor for contributors of the project. - -This documentation is generated by the Sphinx toolkit and lives in the source -tree. - -Installation Guide -================== -Install -------- - - Check out sources to some directory (<home>/glazier): - - smelikyan@work:~/git clone ssh://<user>@gerrit.mirantis.com:29418/keero/keero.git - - Install Conductor: - - smelikyan@work:~/cd glazier/conductor && sudo python setup.py install - -Configure ---------- - - Open configuration file for editing: - - smelikyan@work:~/cd glazier/conductor/etc && nano conductor.conf - - Configure according to you environment: - [DEFAULT] - log_file = logs/conductor.log - debug=True - verbose=True - - [heat] - auth_url = http://localhost:5000/v2.0 - - [rabbitmq] - # this must be IP or hostname accessible from instances (VMs) - host = YOUR.REAL.IP.HERE - port = 5672 - virtual_host = glazier - login = glazier - password = glazier - -Run ----- - -Run Conductor and supply valid configuration file: - -smelikyan@work:~/cd glazier/conductor && conductor --config-file=./glazier/conductor/etc/conductor.conf - diff --git a/conductor/etc/conductor-paste.ini b/conductor/etc/conductor-paste.ini deleted file mode 100644 index e69de29b..00000000 diff --git a/conductor/etc/conductor.conf b/conductor/etc/conductor.conf deleted file mode 100644 index 60f3202f..00000000 --- a/conductor/etc/conductor.conf +++ /dev/null @@ -1,14 +0,0 @@ -[DEFAULT] -log_file = logs/conductor.log -debug=True -verbose=True - -[heat] -auth_url = http://172.18.124.101:5000/v2.0 - -[rabbitmq] -host = 172.18.124.101 -port = 5672 -virtual_host = keero -login = keero -password = keero \ No newline at end of file diff --git a/conductor/logs/.gitignore b/conductor/logs/.gitignore deleted file mode 100644 index 44c5ea8f..00000000 --- a/conductor/logs/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# Ignore everything in this directory -* -# Except this file -!.gitignore \ No newline at end of file diff --git a/conductor/openstack-common.conf b/conductor/openstack-common.conf deleted file mode 100644 index 04377376..00000000 --- a/conductor/openstack-common.conf +++ /dev/null @@ -1,7 +0,0 @@ -[DEFAULT] - -# The list of modules to copy from openstack-common -modules=setup,wsgi,config,exception,gettextutils,importutils,jsonutils,log,xmlutils,sslutils,service,notifier,local,install_venv_common,version,timeutils,eventlet_backdoor,threadgroup,loopingcall,uuidutils - -# The base module to hold the copy of openstack.common -base=conductor \ No newline at end of file diff --git a/conductor/run_tests.sh b/conductor/run_tests.sh deleted file mode 100755 index ba2c9e01..00000000 --- a/conductor/run_tests.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash - -function usage { - echo "Usage: $0 [OPTION]..." - echo "Run python-portasclient's test suite(s)" - echo "" - echo " -p, --pep8 Just run pep8" - echo " -h, --help Print this usage message" - echo "" - echo "This script is deprecated and currently retained for compatibility." - echo 'You can run the full test suite for multiple environments by running "tox".' - echo 'You can run tests for only python 2.7 by running "tox -e py27", or run only' - echo 'the pep8 tests with "tox -e pep8".' - exit -} - -command -v tox > /dev/null 2>&1 -if [ $? -ne 0 ]; then - echo 'This script requires "tox" to run.' - echo 'You can install it with "pip install tox".' - exit 1; -fi - -just_pep8=0 - -function process_option { - case "$1" in - -h|--help) usage;; - -p|--pep8) let just_pep8=1;; - esac -} - -for arg in "$@"; do - process_option $arg -done - -if [ $just_pep8 -eq 1 ]; then - tox -e pep8 - exit -fi - -tox -e py27 $toxargs 2>&1 | tee run_tests.err.log || exit -if [ ${PIPESTATUS[0]} -ne 0 ]; then - exit ${PIPESTATUS[0]} -fi - -if [ -z "$toxargs" ]; then - tox -e pep8 -fi diff --git a/conductor/setup.cfg b/conductor/setup.cfg deleted file mode 100644 index 6e6f6554..00000000 --- a/conductor/setup.cfg +++ /dev/null @@ -1,33 +0,0 @@ -[build_sphinx] -all_files = 1 -build-dir = doc/build -source-dir = doc/source - -[egg_info] -tag_build = -tag_date = 0 -tag_svn_revision = 0 - -[compile_catalog] -directory = conductor/locale -domain = conductor - -[update_catalog] -domain = conductor -output_dir = conductor/locale -input_file = conductor/locale/conductor.pot - -[extract_messages] -keywords = _ gettext ngettext l_ lazy_gettext -mapping_file = babel.cfg -output_file = conductor/locale/conductor.pot - -[nosetests] -# NOTE(jkoelker) To run the test suite under nose install the following -# coverage http://pypi.python.org/pypi/coverage -# tissue http://pypi.python.org/pypi/tissue (pep8 checker) -# openstack-nose https://github.com/jkoelker/openstack-nose -verbosity=2 -cover-package = conductor -cover-html = true -cover-erase = true \ No newline at end of file diff --git a/conductor/setup.py b/conductor/setup.py deleted file mode 100644 index fb9da8cd..00000000 --- a/conductor/setup.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/python -# Copyright (c) 2010 OpenStack, LLC. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import setuptools - -from conductor.openstack.common import setup - -requires = setup.parse_requirements() -depend_links = setup.parse_dependency_links() -project = 'conductor' - -setuptools.setup( - name=project, - version=setup.get_version(project, '2013.1'), - description='The Conductor is orchestration engine server', - license='Apache License (2.0)', - author='Mirantis, Inc.', - author_email='openstack@lists.launchpad.net', - url='http://conductor.openstack.org/', - packages=setuptools.find_packages(exclude=['bin']), - test_suite='nose.collector', - cmdclass=setup.get_cmdclass(), - include_package_data=True, - install_requires=requires, - dependency_links=depend_links, - classifiers=[ - 'Development Status :: 4 - Beta', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: POSIX :: Linux', - 'Programming Language :: Python :: 2.7', - 'Environment :: No Input/Output (Daemon)', - 'Environment :: OpenStack', - ], - scripts=['bin/conductor'], - py_modules=[] -) diff --git a/conductor/test.json b/conductor/test.json deleted file mode 100644 index c2815d50..00000000 --- a/conductor/test.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "name": "MyDataCenterx", - "id": "adc6d143f9584d10808c7ef4d07e4802", - "token": "MIINIQYJKoZIhvcNAQcCoIINEjCCDQ4CAQExCTAHBgUrDgMCGjCCC-oGCSqGSIb3DQEHAaCCC+sEggvneyJhY2Nlc3MiOiB7InRva2VuIjogeyJpc3N1ZWRfYXQiOiAiMjAxMy0wMy0yNlQwNjo0NTozNy4zOTI0MDAiLCAiZXhwaXJlcyI6ICIyMDEzLTAzLTI3VDA2OjQ1OjM3WiIsICJpZCI6ICJwbGFjZWhvbGRlciIsICJ0ZW5hbnQiOiB7ImRlc2NyaXB0aW9uIjogbnVsbCwgImVuYWJsZWQiOiB0cnVlLCAiaWQiOiAiMTZlYjc4Y2JiNjg4NDU5YzgzMDhkODk2NzhiY2VmNTAiLCAibmFtZSI6ICJhZG1pbiJ9fSwgInNlcnZpY2VDYXRhbG9nIjogW3siZW5kcG9pbnRzIjogW3siYWRtaW5VUkwiOiAiaHR0cDovLzE3Mi4xOC4xMjQuMTAxOjg3NzQvdjIvMTZlYjc4Y2JiNjg4NDU5YzgzMDhkODk2NzhiY2VmNTAiLCAicmVnaW9uIjogIlJlZ2lvbk9uZSIsICJpbnRlcm5hbFVSTCI6ICJodHRwOi8vMTcyLjE4LjEyNC4xMDE6ODc3NC92Mi8xNmViNzhjYmI2ODg0NTljODMwOGQ4OTY3OGJjZWY1MCIsICJpZCI6ICIwNGFlNjM2ZTdhYzc0NmJjYjExM2EwYzI5NDYzMzgzMCIsICJwdWJsaWNVUkwiOiAiaHR0cDovLzE3Mi4xOC4xMjQuMTAxOjg3NzQvdjIvMTZlYjc4Y2JiNjg4NDU5YzgzMDhkODk2NzhiY2VmNTAifV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUiOiAiY29tcHV0ZSIsICJuYW1lIjogIm5vdmEifSwgeyJlbmRwb2ludHMiOiBbeyJhZG1pblVSTCI6ICJodHRwOi8vMTcyLjE4LjEyNC4xMDE6MzMzMyIsICJyZWdpb24iOiAiUmVnaW9uT25lIiwgImludGVybmFsVVJMIjogImh0dHA6Ly8xNzIuMTguMTI0LjEwMTozMzMzIiwgImlkIjogIjA5MmJkMjMyMGU5ZDRlYWY4ZDBlZjEzNDhjOGU3NTJjIiwgInB1YmxpY1VSTCI6ICJodHRwOi8vMTcyLjE4LjEyNC4xMDE6MzMzMyJ9XSwgImVuZHBvaW50c19saW5rcyI6IFtdLCAidHlwZSI6ICJzMyIsICJuYW1lIjogInMzIn0sIHsiZW5kcG9pbnRzIjogW3siYWRtaW5VUkwiOiAiaHR0cDovLzE3Mi4xOC4xMjQuMTAxOjkyOTIiLCAicmVnaW9uIjogIlJlZ2lvbk9uZSIsICJpbnRlcm5hbFVSTCI6ICJodHRwOi8vMTcyLjE4LjEyNC4xMDE6OTI5MiIsICJpZCI6ICI1ZWUzNjdjYzRhNjY0YmQzYTYyNmI2MjBkMzFhYzcwYyIsICJwdWJsaWNVUkwiOiAiaHR0cDovLzE3Mi4xOC4xMjQuMTAxOjkyOTIifV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUiOiAiaW1hZ2UiLCAibmFtZSI6ICJnbGFuY2UifSwgeyJlbmRwb2ludHMiOiBbeyJhZG1pblVSTCI6ICJodHRwOi8vMTcyLjE4LjEyNC4xMDE6ODAwMC92MSIsICJyZWdpb24iOiAiUmVnaW9uT25lIiwgImludGVybmFsVVJMIjogImh0dHA6Ly8xNzIuMTguMTI0LjEwMTo4MDAwL3YxIiwgImlkIjogIjM3MzMzYmQwNDkxOTQzY2FiNWEyZGM5N2I5YWQzYjE2IiwgInB1YmxpY1VSTCI6ICJodHRwOi8vMTcyLjE4LjEyNC4xMDE6ODAwMC92MSJ9XSwgImVuZHBvaW50c19saW5rcyI6IFtdLCAidHlwZSI6ICJjbG91ZGZvcm1hdGlvbiIsICJuYW1lIjogImhlYXQtY2ZuIn0sIHsiZW5kcG9pbnRzIjogW3siYWRtaW5VUkwiOiAiaHR0cDovLzE3Mi4xOC4xMjQuMTAxOjg3NzYvdjEvMTZlYjc4Y2JiNjg4NDU5YzgzMDhkODk2NzhiY2VmNTAiLCAicmVnaW9uIjogIlJlZ2lvbk9uZSIsICJpbnRlcm5hbFVSTCI6ICJodHRwOi8vMTcyLjE4LjEyNC4xMDE6ODc3Ni92MS8xNmViNzhjYmI2ODg0NTljODMwOGQ4OTY3OGJjZWY1MCIsICJpZCI6ICI4NTgwYjMzOTAxZWU0YTUyOWI0OGMyMzU0ZjFiMWNhZSIsICJwdWJsaWNVUkwiOiAiaHR0cDovLzE3Mi4xOC4xMjQuMTAxOjg3NzYvdjEvMTZlYjc4Y2JiNjg4NDU5YzgzMDhkODk2NzhiY2VmNTAifV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUiOiAidm9sdW1lIiwgIm5hbWUiOiAiY2luZGVyIn0sIHsiZW5kcG9pbnRzIjogW3siYWRtaW5VUkwiOiAiaHR0cDovLzE3Mi4xOC4xMjQuMTAxOjg3NzMvc2VydmljZXMvQWRtaW4iLCAicmVnaW9uIjogIlJlZ2lvbk9uZSIsICJpbnRlcm5hbFVSTCI6ICJodHRwOi8vMTcyLjE4LjEyNC4xMDE6ODc3My9zZXJ2aWNlcy9DbG91ZCIsICJpZCI6ICIwYTViOTIyNTNiZjg0NTAwYTA4OWY1N2VkMmYzZDY3NSIsICJwdWJsaWNVUkwiOiAiaHR0cDovLzE3Mi4xOC4xMjQuMTAxOjg3NzMvc2VydmljZXMvQ2xvdWQifV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUiOiAiZWMyIiwgIm5hbWUiOiAiZWMyIn0sIHsiZW5kcG9pbnRzIjogW3siYWRtaW5VUkwiOiAiaHR0cDovLzE3Mi4xOC4xMjQuMTAxOjgwMDQvdjEvMTZlYjc4Y2JiNjg4NDU5YzgzMDhkODk2NzhiY2VmNTAiLCAicmVnaW9uIjogIlJlZ2lvbk9uZSIsICJpbnRlcm5hbFVSTCI6ICJodHRwOi8vMTcyLjE4LjEyNC4xMDE6ODAwNC92MS8xNmViNzhjYmI2ODg0NTljODMwOGQ4OTY3OGJjZWY1MCIsICJpZCI6ICJhMjRjMGY1ZmUzMmQ0ZDU5YWEwMTk1Mzg3OGFlMDQwNyIsICJwdWJsaWNVUkwiOiAiaHR0cDovLzE3Mi4xOC4xMjQuMTAxOjgwMDQvdjEvMTZlYjc4Y2JiNjg4NDU5YzgzMDhkODk2NzhiY2VmNTAifV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUiOiAib3JjaGVzdHJhdGlvbiIsICJuYW1lIjogImhlYXQifSwgeyJlbmRwb2ludHMiOiBbeyJhZG1pblVSTCI6ICJodHRwOi8vMTcyLjE4LjEyNC4xMDE6MzUzNTcvdjIuMCIsICJyZWdpb24iOiAiUmVnaW9uT25lIiwgImludGVybmFsVVJMIjogImh0dHA6Ly8xNzIuMTguMTI0LjEwMTo1MDAwL3YyLjAiLCAiaWQiOiAiNGM4M2VlYjk3MDA5NDg3M2FiNjg3NjUzNWJlZjgxZWEiLCAicHVibGljVVJMIjogImh0dHA6Ly8xNzIuMTguMTI0LjEwMTo1MDAwL3YyLjAifV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUiOiAiaWRlbnRpdHkiLCAibmFtZSI6ICJrZXlzdG9uZSJ9XSwgInVzZXIiOiB7InVzZXJuYW1lIjogImFkbWluIiwgInJvbGVzX2xpbmtzIjogW10sICJpZCI6ICJmMmNkZWM4NTQ2MmQ0N2UzODQ5ZTZmMzE3NGRhMTk4NSIsICJyb2xlcyI6IFt7Im5hbWUiOiAiYWRtaW4ifV0sICJuYW1lIjogImFkbWluIn0sICJtZXRhZGF0YSI6IHsiaXNfYWRtaW4iOiAwLCAicm9sZXMiOiBbIjc4N2JlODdjMGFkMjQ3ODJiNTQ4NWU5NjNhZjllNzllIl19fX0xgf8wgfwCAQEwXDBXMQswCQYDVQQGEwJVUzEOMAwGA1UECBMFVW5zZXQxDjAMBgNVBAcTBVVuc2V0MQ4wDAYDVQQKEwVVbnNldDEYMBYGA1UEAxMPd3d3LmV4YW1wbGUuY29tAgEBMAcGBSsOAwIaMA0GCSqGSIb3DQEBAQUABIGAURfgqd8iZ-UWZTta2pyKzXBXm9nmdzlOY-TN8526LWH4jrU1uuimAZKSjZUCwmnaSvoXHLlP6CSGvNUJWDDu6YFNmDfmatVqFrTij4EFGruExmtUxmhbQOnAyhKqIxHFg2t3VKEB2tVhLGSzoSH1dM2+j0-I0JgOLWIStVFEF5A=", - "services": { - "activeDirectories": [ - { - "id": "9571747991184642B95F430A014616F9", - "domain": "acme.loc", - "adminPassword": "SuperP@ssw0rd!", - "units": [ - { - "id": "273c9183b6e74c9c9db7fdd532c5eb25", - "name": "dc01", - "isMaster": true, - "recoveryPassword": "2SuperP@ssw0rd2" - }, - { - "id": "377c6f16d17a416791f80724dab360c6", - "name": "dc02", - "isMaster": false, - "adminPassword": "SuperP@ssw0rd", - "recoveryPassword": "2SuperP@ssw0rd2" - } - ] - } - ], - "webServers": [ - { - "id": "e9657ceef84a4e669e31795040080262", - "domain": "acme.loc", - "units": [ - { - "id": "e6f9cfd07ced48fba64e6bd9e65aba64", - "name": "iis01", - "adminPassword": "SuperP@ssw0rd" - } - ] - } - ] - } -} diff --git a/conductor/tests/__init__.py b/conductor/tests/__init__.py deleted file mode 100644 index e3f3e8d4..00000000 --- a/conductor/tests/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/conductor/tests/conductor/__init__.py b/conductor/tests/conductor/__init__.py deleted file mode 100644 index e3f3e8d4..00000000 --- a/conductor/tests/conductor/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/conductor/tests/conductor/test_methods.py b/conductor/tests/conductor/test_methods.py deleted file mode 100644 index 68c4edeb..00000000 --- a/conductor/tests/conductor/test_methods.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import unittest -from conductor.app import ConductorWorkflowService -import conductor.rabbitmq as rabbitmq -from conductor.workflow import Workflow -import conductor.xml_code_engine as engine - -class TestMethodsAndClasses(unittest.TestCase): - - def test_init_service_class(self): - con = ConductorWorkflowService() - - con.start() - con.stop() diff --git a/conductor/tests/conductor/test_with_fake_service.py b/conductor/tests/conductor/test_with_fake_service.py deleted file mode 100644 index ca380b1c..00000000 --- a/conductor/tests/conductor/test_with_fake_service.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import unittest -from conductor.app import ConductorWorkflowService -from conductor.openstack.common import service - -class TestMethodsAndClasses(unittest.TestCase): - - def test_init_service_class(self): - launcher = service.ServiceLauncher() - con = ConductorWorkflowService() - launcher.launch_service(con) - diff --git a/conductor/tests/soapui.log b/conductor/tests/soapui.log deleted file mode 100644 index e69de29b..00000000 diff --git a/conductor/tools/install_venv.py b/conductor/tools/install_venv.py deleted file mode 100644 index c3b81718..00000000 --- a/conductor/tools/install_venv.py +++ /dev/null @@ -1,154 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Copyright 2010 OpenStack LLC. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Installation script for Glance's development virtualenv -""" - -import os -import subprocess -import sys - - -ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) -VENV = os.path.join(ROOT, '.venv') -PIP_REQUIRES = os.path.join(ROOT, 'tools', 'pip-requires') -TEST_REQUIRES = os.path.join(ROOT, 'tools', 'test-requires') - - -def die(message, *args): - print >> sys.stderr, message % args - sys.exit(1) - - -def run_command(cmd, redirect_output=True, check_exit_code=True): - """ - Runs a command in an out-of-process shell, returning the - output of that command. Working directory is ROOT. - """ - if redirect_output: - stdout = subprocess.PIPE - else: - stdout = None - - proc = subprocess.Popen(cmd, cwd=ROOT, stdout=stdout) - output = proc.communicate()[0] - if check_exit_code and proc.returncode != 0: - die('Command "%s" failed.\n%s', ' '.join(cmd), output) - return output - - -HAS_EASY_INSTALL = bool(run_command(['which', 'easy_install'], - check_exit_code=False).strip()) -HAS_VIRTUALENV = bool(run_command(['which', 'virtualenv'], - check_exit_code=False).strip()) - - -def check_dependencies(): - """Make sure virtualenv is in the path.""" - - if not HAS_VIRTUALENV: - print 'not found.' - # Try installing it via easy_install... - if HAS_EASY_INSTALL: - print 'Installing virtualenv via easy_install...', - if not run_command(['which', 'easy_install']): - die('ERROR: virtualenv not found.\n\n' - 'Balancer development requires virtualenv, please install' - ' it using your favorite package management tool') - print 'done.' - print 'done.' - - -def create_virtualenv(venv=VENV): - """ - Creates the virtual environment and installs PIP only into the - virtual environment - """ - print 'Creating venv...', - run_command(['virtualenv', '-q', '--no-site-packages', VENV]) - print 'done.' - print 'Installing pip in virtualenv...', - if not run_command(['tools/with_venv.sh', 'easy_install', - 'pip>1.0']).strip(): - die("Failed to install pip.") - print 'done.' - - -def pip_install(*args): - run_command(['tools/with_venv.sh', - 'pip', 'install', '--upgrade'] + list(args), - redirect_output=False) - - -def install_dependencies(venv=VENV): - print 'Installing dependencies with pip (this can take a while)...' - - pip_install('pip') - - pip_install('-r', PIP_REQUIRES) - pip_install('-r', TEST_REQUIRES) - - # Tell the virtual env how to "import glance" - py_ver = _detect_python_version(venv) - pthfile = os.path.join(venv, "lib", py_ver, - "site-packages", "balancer.pth") - f = open(pthfile, 'w') - f.write("%s\n" % ROOT) - - -def _detect_python_version(venv): - lib_dir = os.path.join(venv, "lib") - for pathname in os.listdir(lib_dir): - if pathname.startswith('python'): - return pathname - raise Exception('Unable to detect Python version') - - -def print_help(): - help = """ - Glance development environment setup is complete. - - Glance development uses virtualenv to track and manage Python dependencies - while in development and testing. - - To activate the Glance virtualenv for the extent of your current shell session - you can run: - - $ source .venv/bin/activate - - Or, if you prefer, you can run commands in the virtualenv on a case by case - basis by running: - - $ tools/with_venv.sh <your command> - - Also, make test will automatically use the virtualenv. - """ - print help - - -def main(argv): - check_dependencies() - create_virtualenv() - install_dependencies() - print_help() - -if __name__ == '__main__': - main(sys.argv) diff --git a/conductor/tools/install_venv_common.py b/conductor/tools/install_venv_common.py deleted file mode 100644 index 41306564..00000000 --- a/conductor/tools/install_venv_common.py +++ /dev/null @@ -1,220 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 OpenStack Foundation -# Copyright 2013 IBM Corp. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Provides methods needed by installation script for OpenStack development -virtual environments. - -Synced in from openstack-common -""" - -import argparse -import os -import subprocess -import sys - - -class InstallVenv(object): - - def __init__(self, root, venv, pip_requires, test_requires, py_version, - project): - self.root = root - self.venv = venv - self.pip_requires = pip_requires - self.test_requires = test_requires - self.py_version = py_version - self.project = project - - def die(self, message, *args): - print >> sys.stderr, message % args - sys.exit(1) - - def check_python_version(self): - if sys.version_info < (2, 6): - self.die("Need Python Version >= 2.6") - - def run_command_with_code(self, cmd, redirect_output=True, - check_exit_code=True): - """Runs a command in an out-of-process shell. - - Returns the output of that command. Working directory is self.root. - """ - if redirect_output: - stdout = subprocess.PIPE - else: - stdout = None - - proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout) - output = proc.communicate()[0] - if check_exit_code and proc.returncode != 0: - self.die('Command "%s" failed.\n%s', ' '.join(cmd), output) - return (output, proc.returncode) - - def run_command(self, cmd, redirect_output=True, check_exit_code=True): - return self.run_command_with_code(cmd, redirect_output, - check_exit_code)[0] - - def get_distro(self): - if (os.path.exists('/etc/fedora-release') or - os.path.exists('/etc/redhat-release')): - return Fedora(self.root, self.venv, self.pip_requires, - self.test_requires, self.py_version, self.project) - else: - return Distro(self.root, self.venv, self.pip_requires, - self.test_requires, self.py_version, self.project) - - def check_dependencies(self): - self.get_distro().install_virtualenv() - - def create_virtualenv(self, no_site_packages=True): - """Creates the virtual environment and installs PIP. - - Creates the virtual environment and installs PIP only into the - virtual environment. - """ - if not os.path.isdir(self.venv): - print 'Creating venv...', - if no_site_packages: - self.run_command(['virtualenv', '-q', '--no-site-packages', - self.venv]) - else: - self.run_command(['virtualenv', '-q', self.venv]) - print 'done.' - print 'Installing pip in venv...', - if not self.run_command(['tools/with_venv.sh', 'easy_install', - 'pip>1.0']).strip(): - self.die("Failed to install pip.") - print 'done.' - else: - print "venv already exists..." - pass - - def pip_install(self, *args): - self.run_command(['tools/with_venv.sh', - 'pip', 'install', '--upgrade'] + list(args), - redirect_output=False) - - def install_dependencies(self): - print 'Installing dependencies with pip (this can take a while)...' - - # First things first, make sure our venv has the latest pip and - # distribute. - # NOTE: we keep pip at version 1.1 since the most recent version causes - # the .venv creation to fail. See: - # https://bugs.launchpad.net/nova/+bug/1047120 - self.pip_install('pip==1.1') - self.pip_install('distribute') - - # Install greenlet by hand - just listing it in the requires file does - # not - # get it installed in the right order - self.pip_install('greenlet') - - self.pip_install('-r', self.pip_requires) - self.pip_install('-r', self.test_requires) - - def post_process(self): - self.get_distro().post_process() - - def parse_args(self, argv): - """Parses command-line arguments.""" - parser = argparse.ArgumentParser() - parser.add_argument('-n', '--no-site-packages', - action='store_true', - help="Do not inherit packages from global Python " - "install") - return parser.parse_args(argv[1:]) - - -class Distro(InstallVenv): - - def check_cmd(self, cmd): - return bool(self.run_command(['which', cmd], - check_exit_code=False).strip()) - - def install_virtualenv(self): - if self.check_cmd('virtualenv'): - return - - if self.check_cmd('easy_install'): - print 'Installing virtualenv via easy_install...', - if self.run_command(['easy_install', 'virtualenv']): - print 'Succeeded' - return - else: - print 'Failed' - - self.die('ERROR: virtualenv not found.\n\n%s development' - ' requires virtualenv, please install it using your' - ' favorite package management tool' % self.project) - - def post_process(self): - """Any distribution-specific post-processing gets done here. - - In particular, this is useful for applying patches to code inside - the venv. - """ - pass - - -class Fedora(Distro): - """This covers all Fedora-based distributions. - - Includes: Fedora, RHEL, CentOS, Scientific Linux - """ - - def check_pkg(self, pkg): - return self.run_command_with_code(['rpm', '-q', pkg], - check_exit_code=False)[1] == 0 - - def yum_install(self, pkg, **kwargs): - print "Attempting to install '%s' via yum" % pkg - self.run_command(['sudo', 'yum', 'install', '-y', pkg], **kwargs) - - def apply_patch(self, originalfile, patchfile): - self.run_command(['patch', '-N', originalfile, patchfile], - check_exit_code=False) - - def install_virtualenv(self): - if self.check_cmd('virtualenv'): - return - - if not self.check_pkg('python-virtualenv'): - self.yum_install('python-virtualenv', check_exit_code=False) - - super(Fedora, self).install_virtualenv() - - def post_process(self): - """Workaround for a bug in eventlet. - - This currently affects RHEL6.1, but the fix can safely be - applied to all RHEL and Fedora distributions. - - This can be removed when the fix is applied upstream. - - Nova: https://bugs.launchpad.net/nova/+bug/884915 - Upstream: https://bitbucket.org/which_linden/eventlet/issue/89 - """ - - # Install "patch" program if it's not there - if not self.check_pkg('patch'): - self.yum_install('patch') - - # Apply the eventlet patch - self.apply_patch(os.path.join(self.venv, 'lib', self.py_version, - 'site-packages', - 'eventlet/green/subprocess.py'), - 'contrib/redhat-eventlet.patch') diff --git a/conductor/tools/pip-requires b/conductor/tools/pip-requires deleted file mode 100644 index ac910b04..00000000 --- a/conductor/tools/pip-requires +++ /dev/null @@ -1,10 +0,0 @@ -anyjson -eventlet>=0.9.12 -jsonpath -puka -Paste -PasteDeploy -iso8601>=0.1.4 -python-heatclient - -http://tarballs.openstack.org/oslo-config/oslo-config-2013.1b4.tar.gz#egg=oslo-config diff --git a/conductor/tools/test-requires b/conductor/tools/test-requires deleted file mode 100644 index d69cfd4c..00000000 --- a/conductor/tools/test-requires +++ /dev/null @@ -1,8 +0,0 @@ -unittest2 -mock==0.8.0 -nose -nose-exclude -nosexcover -#openstack.nose_plugin -pep8==1.0.1 -sphinx>=1.1.2 diff --git a/conductor/tools/with_venv.sh b/conductor/tools/with_venv.sh deleted file mode 100755 index ae91bbcb..00000000 --- a/conductor/tools/with_venv.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -TOOLS=`dirname $0` -VENV=$TOOLS/../.venv -source $VENV/bin/activate && $@ diff --git a/conductor/tox.ini b/conductor/tox.ini deleted file mode 100644 index 9033100e..00000000 --- a/conductor/tox.ini +++ /dev/null @@ -1,46 +0,0 @@ -[tox] -envlist = py27,pep8 - -[testenv] -setenv = VIRTUAL_ENV={envdir} - NOSE_WITH_OPENSTACK=1 - NOSE_OPENSTACK_COLOR=1 - NOSE_OPENSTACK_RED=0.05 - NOSE_OPENSTACK_YELLOW=0.025 - NOSE_OPENSTACK_SHOW_ELAPSED=1 -deps = -r{toxinidir}/tools/pip-requires - -r{toxinidir}/tools/test-requires -commands = nosetests - -[testenv:pep8] -deps = pep8==1.3.3 -commands = pep8 --repeat --show-source conductor setup.py - -[testenv:venv] -commands = {posargs} - -[testenv:cover] -commands = nosetests --cover-erase --cover-package=conductor --with-xcoverage - -[tox:jenkins] -downloadcache = ~/cache/pip - -[testenv:jenkins26] -basepython = python2.6 -setenv = NOSE_WITH_XUNIT=1 -deps = file://{toxinidir}/.cache.bundle - -[testenv:jenkins27] -basepython = python2.7 -setenv = NOSE_WITH_XUNIT=1 -deps = file://{toxinidir}/.cache.bundle - -[testenv:jenkinscover] -deps = file://{toxinidir}/.cache.bundle -setenv = NOSE_WITH_XUNIT=1 -commands = nosetests --cover-erase --cover-package=conductor --with-xcoverage - -[testenv:jenkinsvenv] -deps = file://{toxinidir}/.cache.bundle -setenv = NOSE_WITH_XUNIT=1 -commands = {posargs} diff --git a/dashboard/.gitignore b/dashboard/.gitignore deleted file mode 100644 index 78cafe90..00000000 --- a/dashboard/.gitignore +++ /dev/null @@ -1,19 +0,0 @@ -*.pyc -*.swp -.environment_version -.selenium_log -.coverage* -.noseids -.venv -coverage.xml -pep8.txt -pylint.txt -reports -tabula/local/local_settings.py -/static/ -docs/build/ -docs/source/sourcecode -build -dist -#Autogenerated Documentation -doc/source/api \ No newline at end of file diff --git a/dashboard/README.rst b/dashboard/README.rst deleted file mode 100644 index 150c1b4a..00000000 --- a/dashboard/README.rst +++ /dev/null @@ -1,7 +0,0 @@ -Keero Tabula README -===================== -Tabula is a project that provides Web UI to Keero Project. - -SEE ALSO --------- -* `Keero <http://keero.mirantis.com>`__ diff --git a/dashboard/bin/less/lessc b/dashboard/bin/less/lessc deleted file mode 100755 index 30ae3520..00000000 --- a/dashboard/bin/less/lessc +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/env node - -var path = require('path'), - fs = require('fs'), - sys = require('util'), - os = require('os'); - -var less = require('../lib/less'); -var args = process.argv.slice(1); -var options = { - compress: false, - yuicompress: false, - optimization: 1, - silent: false, - paths: [], - color: true, - strictImports: false -}; - -args = args.filter(function (arg) { - var match; - - if (match = arg.match(/^-I(.+)$/)) { - options.paths.push(match[1]); - return false; - } - - if (match = arg.match(/^--?([a-z][0-9a-z-]*)(?:=([^\s]+))?$/i)) { arg = match[1] } - else { return arg } - - switch (arg) { - case 'v': - case 'version': - sys.puts("lessc " + less.version.join('.') + " (LESS Compiler) [JavaScript]"); - process.exit(0); - case 'verbose': - options.verbose = true; - break; - case 's': - case 'silent': - options.silent = true; - break; - case 'strict-imports': - options.strictImports = true; - break; - case 'h': - case 'help': - sys.puts("usage: lessc source [destination]"); - process.exit(0); - case 'x': - case 'compress': - options.compress = true; - break; - case 'yui-compress': - options.yuicompress = true; - break; - case 'no-color': - options.color = false; - break; - case 'include-path': - options.paths = match[2].split(os.type().match(/Windows/) ? ';' : ':') - .map(function(p) { - if (p) { - return path.resolve(process.cwd(), p); - } - }); - break; - case 'O0': options.optimization = 0; break; - case 'O1': options.optimization = 1; break; - case 'O2': options.optimization = 2; break; - } -}); - -var input = args[1]; -if (input && input != '-') { - input = path.resolve(process.cwd(), input); -} -var output = args[2]; -if (output) { - output = path.resolve(process.cwd(), output); -} - -var css, fd, tree; - -if (! input) { - sys.puts("lessc: no input files"); - process.exit(1); -} - -var parseLessFile = function (e, data) { - if (e) { - sys.puts("lessc: " + e.message); - process.exit(1); - } - - new(less.Parser)({ - paths: [path.dirname(input)].concat(options.paths), - optimization: options.optimization, - filename: input, - strictImports: options.strictImports - }).parse(data, function (err, tree) { - if (err) { - less.writeError(err, options); - process.exit(1); - } else { - try { - css = tree.toCSS({ - compress: options.compress, - yuicompress: options.yuicompress - }); - if (output) { - fd = fs.openSync(output, "w"); - fs.writeSync(fd, css, 0, "utf8"); - } else { - sys.print(css); - } - } catch (e) { - less.writeError(e, options); - process.exit(2); - } - } - }); -}; - -if (input != '-') { - fs.readFile(input, 'utf-8', parseLessFile); -} else { - process.stdin.resume(); - process.stdin.setEncoding('utf8'); - - var buffer = ''; - process.stdin.on('data', function(data) { - buffer += data; - }); - - process.stdin.on('end', function() { - parseLessFile(false, buffer); - }); -} diff --git a/dashboard/bin/lib/less/browser.js b/dashboard/bin/lib/less/browser.js deleted file mode 100644 index cab913be..00000000 --- a/dashboard/bin/lib/less/browser.js +++ /dev/null @@ -1,380 +0,0 @@ -// -// browser.js - client-side engine -// - -var isFileProtocol = (location.protocol === 'file:' || - location.protocol === 'chrome:' || - location.protocol === 'chrome-extension:' || - location.protocol === 'resource:'); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - location.port.length > 0 || - isFileProtocol ? 'development' - : 'production'); - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -// -// Watch mode -// -less.watch = function () { return this.watchMode = true }; -less.unwatch = function () { return this.watchMode = false }; - -if (less.env === 'development') { - less.optimization = 0; - - if (/!watch/.test(location.hash)) { - less.watch(); - } - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (e, root, _, sheet, env) { - if (root) { - createCSS(root.toCSS(), sheet, env.lastModified); - } - }); - } - }, less.poll); -} else { - less.optimization = 3; -} - -var cache; - -try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; -} catch (_) { - cache = null; -} - -// -// Get all <link> tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); -var typePattern = /^text\/(x-)?less$/; - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - - -less.refresh = function (reload) { - var startTime, endTime; - startTime = endTime = new(Date); - - loadStyleSheets(function (e, root, _, sheet, env) { - if (env.local) { - log("loading " + sheet.href + " from cache."); - } else { - log("parsed " + sheet.href + " successfully."); - createCSS(root.toCSS(), sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms'); - (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms'); - endTime = new(Date); - }, reload); - - loadStyles(); -}; -less.refreshStyles = loadStyles; - -less.refresh(less.env === 'development'); - -function loadStyles() { - var styles = document.getElementsByTagName('style'); - for (var i = 0; i < styles.length; i++) { - if (styles[i].type.match(typePattern)) { - new(less.Parser)().parse(styles[i].innerHTML || '', function (e, tree) { - var css = tree.toCSS(); - var style = styles[i]; - style.type = 'text/css'; - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - style.innerHTML = css; - } - }); - } - } -} - -function loadStyleSheets(callback, reload) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1)); - } -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - var url = window.location.href.replace(/[#?].*$/, ''); - var href = sheet.href.replace(/\?.*$/, ''); - var css = cache && cache.getItem(href); - var timestamp = cache && cache.getItem(href + ':timestamp'); - var styles = { css: css, timestamp: timestamp }; - - // Stylesheets in IE don't always return the full path - if (! /^(https?|file):/.test(href)) { - if (href.charAt(0) == "/") { - href = window.location.protocol + "//" + window.location.host + href; - } else { - href = url.slice(0, url.lastIndexOf('/') + 1) + href; - } - } - var filename = href.match(/([^\/]+)$/)[1]; - - xhr(sheet.href, sheet.type, function (data, lastModified) { - if (!reload && styles && lastModified && - (new(Date)(lastModified).valueOf() === - new(Date)(styles.timestamp).valueOf())) { - // Use local copy - createCSS(styles.css, sheet); - callback(null, null, data, sheet, { local: true, remaining: remaining }); - } else { - // Use remote copy (re-parse) - try { - new(less.Parser)({ - optimization: less.optimization, - paths: [href.replace(/[\w\.-]+$/, '')], - mime: sheet.type, - filename: filename - }).parse(data, function (e, root) { - if (e) { return error(e, href) } - try { - callback(e, root, data, sheet, { local: false, lastModified: lastModified, remaining: remaining }); - removeNode(document.getElementById('less-error-message:' + extractId(href))); - } catch (e) { - error(e, href); - } - }); - } catch (e) { - error(e, href); - } - } - }, function (status, url) { - throw new(Error)("Couldn't load " + url + " (" + status + ")"); - }); -} - -function extractId(href) { - return href.replace(/^[a-z]+:\/\/?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\?.*$/, '' ) // Remove query - .replace(/\.[^\.\/]+$/, '' ) // Remove file extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function createCSS(styles, sheet, lastModified) { - var css; - - // Strip the query-string - var href = sheet.href ? sheet.href.replace(/\?.*$/, '') : ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If the stylesheet doesn't exist, create a new node - if ((css = document.getElementById(id)) === null) { - css = document.createElement('style'); - css.type = 'text/css'; - css.media = sheet.media || 'screen'; - css.id = id; - document.getElementsByTagName('head')[0].appendChild(css); - } - - if (css.styleSheet) { // IE - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } else { - (function (node) { - if (css.childNodes.length > 0) { - if (css.firstChild.nodeValue !== node.nodeValue) { - css.replaceChild(node, css.firstChild); - } - } else { - css.appendChild(node); - } - })(document.createTextNode(styles)); - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.'); - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } -} - -function xhr(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? false : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - if (isFileProtocol) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest) { - return new(XMLHttpRequest); - } else { - try { - return new(ActiveXObject)("MSXML2.XMLHTTP.3.0"); - } catch (e) { - log("browser doesn't support AJAX."); - return null; - } - } -} - -function removeNode(node) { - return node && node.parentNode.removeChild(node); -} - -function log(str) { - if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) } -} - -function error(e, href) { - var id = 'less-error-message:' + extractId(href); - var template = '<li><label>{line}</label><pre class="{class}">{content}</pre></li>'; - var elem = document.createElement('div'), timer, content, error = []; - var filename = e.filename || href; - - elem.id = id; - elem.className = "less-error-message"; - - content = '<h3>' + (e.message || 'There is an error in your .less file') + - '</h3>' + '<p>in <a href="' + filename + '">' + filename + "</a> "; - - var errorline = function (e, i, classname) { - if (e.extract[i]) { - error.push(template.replace(/\{line\}/, parseInt(e.line) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.stack) { - content += '<br/>' + e.stack.split('\n').slice(1).join('<br/>'); - } else if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':</p>' + - '<ul>' + error.join('') + '</ul>'; - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #dd6666;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.line {', - 'color: #ff0000;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} - diff --git a/dashboard/bin/lib/less/colors.js b/dashboard/bin/lib/less/colors.js deleted file mode 100644 index ed4c2838..00000000 --- a/dashboard/bin/lib/less/colors.js +++ /dev/null @@ -1,152 +0,0 @@ -(function (tree) { - tree.colors = { - 'aliceblue':'#f0f8ff', - 'antiquewhite':'#faebd7', - 'aqua':'#00ffff', - 'aquamarine':'#7fffd4', - 'azure':'#f0ffff', - 'beige':'#f5f5dc', - 'bisque':'#ffe4c4', - 'black':'#000000', - 'blanchedalmond':'#ffebcd', - 'blue':'#0000ff', - 'blueviolet':'#8a2be2', - 'brown':'#a52a2a', - 'burlywood':'#deb887', - 'cadetblue':'#5f9ea0', - 'chartreuse':'#7fff00', - 'chocolate':'#d2691e', - 'coral':'#ff7f50', - 'cornflowerblue':'#6495ed', - 'cornsilk':'#fff8dc', - 'crimson':'#dc143c', - 'cyan':'#00ffff', - 'darkblue':'#00008b', - 'darkcyan':'#008b8b', - 'darkgoldenrod':'#b8860b', - 'darkgray':'#a9a9a9', - 'darkgrey':'#a9a9a9', - 'darkgreen':'#006400', - 'darkkhaki':'#bdb76b', - 'darkmagenta':'#8b008b', - 'darkolivegreen':'#556b2f', - 'darkorange':'#ff8c00', - 'darkorchid':'#9932cc', - 'darkred':'#8b0000', - 'darksalmon':'#e9967a', - 'darkseagreen':'#8fbc8f', - 'darkslateblue':'#483d8b', - 'darkslategray':'#2f4f4f', - 'darkslategrey':'#2f4f4f', - 'darkturquoise':'#00ced1', - 'darkviolet':'#9400d3', - 'deeppink':'#ff1493', - 'deepskyblue':'#00bfff', - 'dimgray':'#696969', - 'dimgrey':'#696969', - 'dodgerblue':'#1e90ff', - 'firebrick':'#b22222', - 'floralwhite':'#fffaf0', - 'forestgreen':'#228b22', - 'fuchsia':'#ff00ff', - 'gainsboro':'#dcdcdc', - 'ghostwhite':'#f8f8ff', - 'gold':'#ffd700', - 'goldenrod':'#daa520', - 'gray':'#808080', - 'grey':'#808080', - 'green':'#008000', - 'greenyellow':'#adff2f', - 'honeydew':'#f0fff0', - 'hotpink':'#ff69b4', - 'indianred':'#cd5c5c', - 'indigo':'#4b0082', - 'ivory':'#fffff0', - 'khaki':'#f0e68c', - 'lavender':'#e6e6fa', - 'lavenderblush':'#fff0f5', - 'lawngreen':'#7cfc00', - 'lemonchiffon':'#fffacd', - 'lightblue':'#add8e6', - 'lightcoral':'#f08080', - 'lightcyan':'#e0ffff', - 'lightgoldenrodyellow':'#fafad2', - 'lightgray':'#d3d3d3', - 'lightgrey':'#d3d3d3', - 'lightgreen':'#90ee90', - 'lightpink':'#ffb6c1', - 'lightsalmon':'#ffa07a', - 'lightseagreen':'#20b2aa', - 'lightskyblue':'#87cefa', - 'lightslategray':'#778899', - 'lightslategrey':'#778899', - 'lightsteelblue':'#b0c4de', - 'lightyellow':'#ffffe0', - 'lime':'#00ff00', - 'limegreen':'#32cd32', - 'linen':'#faf0e6', - 'magenta':'#ff00ff', - 'maroon':'#800000', - 'mediumaquamarine':'#66cdaa', - 'mediumblue':'#0000cd', - 'mediumorchid':'#ba55d3', - 'mediumpurple':'#9370d8', - 'mediumseagreen':'#3cb371', - 'mediumslateblue':'#7b68ee', - 'mediumspringgreen':'#00fa9a', - 'mediumturquoise':'#48d1cc', - 'mediumvioletred':'#c71585', - 'midnightblue':'#191970', - 'mintcream':'#f5fffa', - 'mistyrose':'#ffe4e1', - 'moccasin':'#ffe4b5', - 'navajowhite':'#ffdead', - 'navy':'#000080', - 'oldlace':'#fdf5e6', - 'olive':'#808000', - 'olivedrab':'#6b8e23', - 'orange':'#ffa500', - 'orangered':'#ff4500', - 'orchid':'#da70d6', - 'palegoldenrod':'#eee8aa', - 'palegreen':'#98fb98', - 'paleturquoise':'#afeeee', - 'palevioletred':'#d87093', - 'papayawhip':'#ffefd5', - 'peachpuff':'#ffdab9', - 'peru':'#cd853f', - 'pink':'#ffc0cb', - 'plum':'#dda0dd', - 'powderblue':'#b0e0e6', - 'purple':'#800080', - 'red':'#ff0000', - 'rosybrown':'#bc8f8f', - 'royalblue':'#4169e1', - 'saddlebrown':'#8b4513', - 'salmon':'#fa8072', - 'sandybrown':'#f4a460', - 'seagreen':'#2e8b57', - 'seashell':'#fff5ee', - 'sienna':'#a0522d', - 'silver':'#c0c0c0', - 'skyblue':'#87ceeb', - 'slateblue':'#6a5acd', - 'slategray':'#708090', - 'slategrey':'#708090', - 'snow':'#fffafa', - 'springgreen':'#00ff7f', - 'steelblue':'#4682b4', - 'tan':'#d2b48c', - 'teal':'#008080', - 'thistle':'#d8bfd8', - 'tomato':'#ff6347', - 'transparent':'rgba(0,0,0,0)', - 'turquoise':'#40e0d0', - 'violet':'#ee82ee', - 'wheat':'#f5deb3', - 'white':'#ffffff', - 'whitesmoke':'#f5f5f5', - 'yellow':'#ffff00', - 'yellowgreen':'#9acd32' - }; -})(require('./tree')); diff --git a/dashboard/bin/lib/less/cssmin.js b/dashboard/bin/lib/less/cssmin.js deleted file mode 100644 index 427de71c..00000000 --- a/dashboard/bin/lib/less/cssmin.js +++ /dev/null @@ -1,355 +0,0 @@ -/** - * cssmin.js - * Author: Stoyan Stefanov - http://phpied.com/ - * This is a JavaScript port of the CSS minification tool - * distributed with YUICompressor, itself a port - * of the cssmin utility by Isaac Schlueter - http://foohack.com/ - * Permission is hereby granted to use the JavaScript version under the same - * conditions as the YUICompressor (original YUICompressor note below). - */ - -/* -* YUI Compressor -* http://developer.yahoo.com/yui/compressor/ -* Author: Julien Lecomte - http://www.julienlecomte.net/ -* Copyright (c) 2011 Yahoo! Inc. All rights reserved. -* The copyrights embodied in the content of this file are licensed -* by Yahoo! Inc. under the BSD (revised) open source license. -*/ -var YAHOO = YAHOO || {}; -YAHOO.compressor = YAHOO.compressor || {}; - -/** - * Utility method to replace all data urls with tokens before we start - * compressing, to avoid performance issues running some of the subsequent - * regexes against large strings chunks. - * - * @private - * @method _extractDataUrls - * @param {String} css The input css - * @param {Array} The global array of tokens to preserve - * @returns String The processed css - */ -YAHOO.compressor._extractDataUrls = function (css, preservedTokens) { - - // Leave data urls alone to increase parse performance. - var maxIndex = css.length - 1, - appendIndex = 0, - startIndex, - endIndex, - terminator, - foundTerminator, - sb = [], - m, - preserver, - token, - pattern = /url\(\s*(["']?)data\:/g; - - // Since we need to account for non-base64 data urls, we need to handle - // ' and ) being part of the data string. Hence switching to indexOf, - // to determine whether or not we have matching string terminators and - // handling sb appends directly, instead of using matcher.append* methods. - - while ((m = pattern.exec(css)) !== null) { - - startIndex = m.index + 4; // "url(".length() - terminator = m[1]; // ', " or empty (not quoted) - - if (terminator.length === 0) { - terminator = ")"; - } - - foundTerminator = false; - - endIndex = pattern.lastIndex - 1; - - while(foundTerminator === false && endIndex+1 <= maxIndex) { - endIndex = css.indexOf(terminator, endIndex + 1); - - // endIndex == 0 doesn't really apply here - if ((endIndex > 0) && (css.charAt(endIndex - 1) !== '\\')) { - foundTerminator = true; - if (")" != terminator) { - endIndex = css.indexOf(")", endIndex); - } - } - } - - // Enough searching, start moving stuff over to the buffer - sb.push(css.substring(appendIndex, m.index)); - - if (foundTerminator) { - token = css.substring(startIndex, endIndex); - token = token.replace(/\s+/g, ""); - preservedTokens.push(token); - - preserver = "url(___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.length - 1) + "___)"; - sb.push(preserver); - - appendIndex = endIndex + 1; - } else { - // No end terminator found, re-add the whole match. Should we throw/warn here? - sb.push(css.substring(m.index, pattern.lastIndex)); - appendIndex = pattern.lastIndex; - } - } - - sb.push(css.substring(appendIndex)); - - return sb.join(""); -}; - -/** - * Utility method to compress hex color values of the form #AABBCC to #ABC. - * - * DOES NOT compress CSS ID selectors which match the above pattern (which would break things). - * e.g. #AddressForm { ... } - * - * DOES NOT compress IE filters, which have hex color values (which would break things). - * e.g. filter: chroma(color="#FFFFFF"); - * - * DOES NOT compress invalid hex values. - * e.g. background-color: #aabbccdd - * - * @private - * @method _compressHexColors - * @param {String} css The input css - * @returns String The processed css - */ -YAHOO.compressor._compressHexColors = function(css) { - - // Look for hex colors inside { ... } (to avoid IDs) and which don't have a =, or a " in front of them (to avoid filters) - var pattern = /(\=\s*?["']?)?#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])(\}|[^0-9a-f{][^{]*?\})/gi, - m, - index = 0, - isFilter, - sb = []; - - while ((m = pattern.exec(css)) !== null) { - - sb.push(css.substring(index, m.index)); - - isFilter = m[1]; - - if (isFilter) { - // Restore, maintain case, otherwise filter will break - sb.push(m[1] + "#" + (m[2] + m[3] + m[4] + m[5] + m[6] + m[7])); - } else { - if (m[2].toLowerCase() == m[3].toLowerCase() && - m[4].toLowerCase() == m[5].toLowerCase() && - m[6].toLowerCase() == m[7].toLowerCase()) { - - // Compress. - sb.push("#" + (m[3] + m[5] + m[7]).toLowerCase()); - } else { - // Non compressible color, restore but lower case. - sb.push("#" + (m[2] + m[3] + m[4] + m[5] + m[6] + m[7]).toLowerCase()); - } - } - - index = pattern.lastIndex = pattern.lastIndex - m[8].length; - } - - sb.push(css.substring(index)); - - return sb.join(""); -}; - -YAHOO.compressor.cssmin = function (css, linebreakpos) { - - var startIndex = 0, - endIndex = 0, - i = 0, max = 0, - preservedTokens = [], - comments = [], - token = '', - totallen = css.length, - placeholder = ''; - - css = this._extractDataUrls(css, preservedTokens); - - // collect all comment blocks... - while ((startIndex = css.indexOf("/*", startIndex)) >= 0) { - endIndex = css.indexOf("*/", startIndex + 2); - if (endIndex < 0) { - endIndex = totallen; - } - token = css.slice(startIndex + 2, endIndex); - comments.push(token); - css = css.slice(0, startIndex + 2) + "___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" + (comments.length - 1) + "___" + css.slice(endIndex); - startIndex += 2; - } - - // preserve strings so their content doesn't get accidentally minified - css = css.replace(/("([^\\"]|\\.|\\)*")|('([^\\']|\\.|\\)*')/g, function (match) { - var i, max, quote = match.substring(0, 1); - - match = match.slice(1, -1); - - // maybe the string contains a comment-like substring? - // one, maybe more? put'em back then - if (match.indexOf("___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_") >= 0) { - for (i = 0, max = comments.length; i < max; i = i + 1) { - match = match.replace("___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" + i + "___", comments[i]); - } - } - - // minify alpha opacity in filter strings - match = match.replace(/progid:DXImageTransform\.Microsoft\.Alpha\(Opacity=/gi, "alpha(opacity="); - - preservedTokens.push(match); - return quote + "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.length - 1) + "___" + quote; - }); - - // strings are safe, now wrestle the comments - for (i = 0, max = comments.length; i < max; i = i + 1) { - - token = comments[i]; - placeholder = "___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" + i + "___"; - - // ! in the first position of the comment means preserve - // so push to the preserved tokens keeping the ! - if (token.charAt(0) === "!") { - preservedTokens.push(token); - css = css.replace(placeholder, "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.length - 1) + "___"); - continue; - } - - // \ in the last position looks like hack for Mac/IE5 - // shorten that to /*\*/ and the next one to /**/ - if (token.charAt(token.length - 1) === "\\") { - preservedTokens.push("\\"); - css = css.replace(placeholder, "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.length - 1) + "___"); - i = i + 1; // attn: advancing the loop - preservedTokens.push(""); - css = css.replace("___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" + i + "___", "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.length - 1) + "___"); - continue; - } - - // keep empty comments after child selectors (IE7 hack) - // e.g. html >/**/ body - if (token.length === 0) { - startIndex = css.indexOf(placeholder); - if (startIndex > 2) { - if (css.charAt(startIndex - 3) === '>') { - preservedTokens.push(""); - css = css.replace(placeholder, "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.length - 1) + "___"); - } - } - } - - // in all other cases kill the comment - css = css.replace("/*" + placeholder + "*/", ""); - } - - - // Normalize all whitespace strings to single spaces. Easier to work with that way. - css = css.replace(/\s+/g, " "); - - // Remove the spaces before the things that should not have spaces before them. - // But, be careful not to turn "p :link {...}" into "p:link{...}" - // Swap out any pseudo-class colons with the token, and then swap back. - css = css.replace(/(^|\})(([^\{:])+:)+([^\{]*\{)/g, function (m) { - return m.replace(":", "___YUICSSMIN_PSEUDOCLASSCOLON___"); - }); - css = css.replace(/\s+([!{};:>+\(\)\],])/g, '$1'); - css = css.replace(/___YUICSSMIN_PSEUDOCLASSCOLON___/g, ":"); - - // retain space for special IE6 cases - css = css.replace(/:first-(line|letter)(\{|,)/g, ":first-$1 $2"); - - // no space after the end of a preserved comment - css = css.replace(/\*\/ /g, '*/'); - - - // If there is a @charset, then only allow one, and push to the top of the file. - css = css.replace(/^(.*)(@charset "[^"]*";)/gi, '$2$1'); - css = css.replace(/^(\s*@charset [^;]+;\s*)+/gi, '$1'); - - // Put the space back in some cases, to support stuff like - // @media screen and (-webkit-min-device-pixel-ratio:0){ - css = css.replace(/\band\(/gi, "and ("); - - - // Remove the spaces after the things that should not have spaces after them. - css = css.replace(/([!{}:;>+\(\[,])\s+/g, '$1'); - - // remove unnecessary semicolons - css = css.replace(/;+\}/g, "}"); - - // Replace 0(px,em,%) with 0. - css = css.replace(/([\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)/gi, "$1$2"); - - // Replace 0 0 0 0; with 0. - css = css.replace(/:0 0 0 0(;|\})/g, ":0$1"); - css = css.replace(/:0 0 0(;|\})/g, ":0$1"); - css = css.replace(/:0 0(;|\})/g, ":0$1"); - - // Replace background-position:0; with background-position:0 0; - // same for transform-origin - css = css.replace(/(background-position|transform-origin|webkit-transform-origin|moz-transform-origin|o-transform-origin|ms-transform-origin):0(;|\})/gi, function(all, prop, tail) { - return prop.toLowerCase() + ":0 0" + tail; - }); - - // Replace 0.6 to .6, but only when preceded by : or a white-space - css = css.replace(/(:|\s)0+\.(\d+)/g, "$1.$2"); - - // Shorten colors from rgb(51,102,153) to #336699 - // This makes it more likely that it'll get further compressed in the next step. - css = css.replace(/rgb\s*\(\s*([0-9,\s]+)\s*\)/gi, function () { - var i, rgbcolors = arguments[1].split(','); - for (i = 0; i < rgbcolors.length; i = i + 1) { - rgbcolors[i] = parseInt(rgbcolors[i], 10).toString(16); - if (rgbcolors[i].length === 1) { - rgbcolors[i] = '0' + rgbcolors[i]; - } - } - return '#' + rgbcolors.join(''); - }); - - // Shorten colors from #AABBCC to #ABC. - css = this._compressHexColors(css); - - // border: none -> border:0 - css = css.replace(/(border|border-top|border-right|border-bottom|border-right|outline|background):none(;|\})/gi, function(all, prop, tail) { - return prop.toLowerCase() + ":0" + tail; - }); - - // shorter opacity IE filter - css = css.replace(/progid:DXImageTransform\.Microsoft\.Alpha\(Opacity=/gi, "alpha(opacity="); - - // Remove empty rules. - css = css.replace(/[^\};\{\/]+\{\}/g, ""); - - if (linebreakpos >= 0) { - // Some source control tools don't like it when files containing lines longer - // than, say 8000 characters, are checked in. The linebreak option is used in - // that case to split long lines after a specific column. - startIndex = 0; - i = 0; - while (i < css.length) { - i = i + 1; - if (css[i - 1] === '}' && i - startIndex > linebreakpos) { - css = css.slice(0, i) + '\n' + css.slice(i); - startIndex = i; - } - } - } - - // Replace multiple semi-colons in a row by a single one - // See SF bug #1980989 - css = css.replace(/;;+/g, ";"); - - // restore preserved comments and strings - for (i = 0, max = preservedTokens.length; i < max; i = i + 1) { - css = css.replace("___YUICSSMIN_PRESERVED_TOKEN_" + i + "___", preservedTokens[i]); - } - - // Trim the final string (for any leading or trailing white spaces) - css = css.replace(/^\s+|\s+$/g, ""); - - return css; - -}; - -exports.compressor = YAHOO.compressor; diff --git a/dashboard/bin/lib/less/functions.js b/dashboard/bin/lib/less/functions.js deleted file mode 100644 index 6eb34bac..00000000 --- a/dashboard/bin/lib/less/functions.js +++ /dev/null @@ -1,228 +0,0 @@ -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return number(c) }), - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - h = (number(h) % 360) / 360; - s = number(s); l = number(l); a = number(a); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - - function hue(h) { - h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h); - if (h * 6 < 1) return m1 + (m2 - m1) * h * 6; - else if (h * 2 < 1) return m2; - else if (h * 3 < 2) return m1 + (m2 - m1) * (2/3 - h) * 6; - else return m1; - } - }, - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - saturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - round: function (n) { - return this._math('round', n); - }, - ceil: function (n) { - return this._math('ceil', n); - }, - floor: function (n) { - return this._math('floor', n); - }, - _math: function (fn, n) { - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(Math[fn](number(n)), n.unit); - } else if (typeof(n) === 'number') { - return Math[fn](n); - } else { - throw { type: "Argument", message: "argument must be a number" }; - } - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - return new(tree.Color)(n.value.slice(1)); - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return (n instanceof tree.Dimension) && n.unit === 'px' ? tree.True : tree.False; - }, - ispercentage: function (n) { - return (n instanceof tree.Dimension) && n.unit === '%' ? tree.True : tree.False; - }, - isem: function (n) { - return (n instanceof tree.Dimension) && n.unit === 'em' ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - } -}; - -function hsla(hsla) { - return tree.functions.hsla(hsla.h, hsla.s, hsla.l, hsla.a); -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit == '%' ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -})(require('./tree')); diff --git a/dashboard/bin/lib/less/index.js b/dashboard/bin/lib/less/index.js deleted file mode 100644 index a11fa998..00000000 --- a/dashboard/bin/lib/less/index.js +++ /dev/null @@ -1,148 +0,0 @@ -var path = require('path'), - sys = require('util'), - fs = require('fs'); - -var less = { - version: [1, 3, 0], - Parser: require('./parser').Parser, - importer: require('./parser').importer, - tree: require('./tree'), - render: function (input, options, callback) { - options = options || {}; - - if (typeof(options) === 'function') { - callback = options, options = {}; - } - - var parser = new(less.Parser)(options), - ee; - - if (callback) { - parser.parse(input, function (e, root) { - callback(e, root && root.toCSS && root.toCSS(options)); - }); - } else { - ee = new(require('events').EventEmitter); - - process.nextTick(function () { - parser.parse(input, function (e, root) { - if (e) { ee.emit('error', e) } - else { ee.emit('success', root.toCSS(options)) } - }); - }); - return ee; - } - }, - writeError: function (ctx, options) { - options = options || {}; - - var message = ""; - var extract = ctx.extract; - var error = []; - var stylize = options.color ? less.stylize : function (str) { return str }; - - if (options.silent) { return } - - if (ctx.stack) { return sys.error(stylize(ctx.stack, 'red')) } - - if (!ctx.hasOwnProperty('index')) { - return sys.error(ctx.stack || ctx.message); - } - - if (typeof(extract[0]) === 'string') { - error.push(stylize((ctx.line - 1) + ' ' + extract[0], 'grey')); - } - - if (extract[1]) { - error.push(ctx.line + ' ' + extract[1].slice(0, ctx.column) - + stylize(stylize(stylize(extract[1][ctx.column], 'bold') - + extract[1].slice(ctx.column + 1), 'red'), 'inverse')); - } - - if (typeof(extract[2]) === 'string') { - error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey')); - } - error = error.join('\n') + '\033[0m\n'; - - message += stylize(ctx.type + 'Error: ' + ctx.message, 'red'); - ctx.filename && (message += stylize(' in ', 'red') + ctx.filename + - stylize(':' + ctx.line + ':' + ctx.column, 'grey')); - - sys.error(message, error); - - if (ctx.callLine) { - sys.error(stylize('from ', 'red') + (ctx.filename || '')); - sys.error(stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract); - } - } -}; - -['color', 'directive', 'operation', 'dimension', - 'keyword', 'variable', 'ruleset', 'element', - 'selector', 'quoted', 'expression', 'rule', - 'call', 'url', 'alpha', 'import', - 'mixin', 'comment', 'anonymous', 'value', - 'javascript', 'assignment', 'condition', 'paren', - 'media' -].forEach(function (n) { - require('./tree/' + n); -}); - -less.Parser.importer = function (file, paths, callback, env) { - var pathname; - - // TODO: Undo this at some point, - // or use different approach. - paths.unshift('.'); - - for (var i = 0; i < paths.length; i++) { - try { - pathname = path.join(paths[i], file); - fs.statSync(pathname); - break; - } catch (e) { - pathname = null; - } - } - - if (pathname) { - fs.readFile(pathname, 'utf-8', function(e, data) { - if (e) return callback(e); - - new(less.Parser)({ - paths: [path.dirname(pathname)].concat(paths), - filename: pathname - }).parse(data, function (e, root) { - callback(e, root, data); - }); - }); - } else { - if (typeof(env.errback) === "function") { - env.errback(file, paths, callback); - } else { - callback({ type: 'File', message: "'" + file + "' wasn't found.\n" }); - } - } -} - -require('./functions'); -require('./colors'); - -for (var k in less) { exports[k] = less[k] } - -// Stylize a string -function stylize(str, style) { - var styles = { - 'bold' : [1, 22], - 'inverse' : [7, 27], - 'underline' : [4, 24], - 'yellow' : [33, 39], - 'green' : [32, 39], - 'red' : [31, 39], - 'grey' : [90, 39] - }; - return '\033[' + styles[style][0] + 'm' + str + - '\033[' + styles[style][1] + 'm'; -} -less.stylize = stylize; - diff --git a/dashboard/bin/lib/less/parser.js b/dashboard/bin/lib/less/parser.js deleted file mode 100644 index d732e1b1..00000000 --- a/dashboard/bin/lib/less/parser.js +++ /dev/null @@ -1,1334 +0,0 @@ -var less, tree; - -if (typeof environment === "object" && ({}).toString.call(environment) === "[object Environment]") { - // Rhino - // Details on how to detect Rhino: https://github.com/ringo/ringojs/issues/88 - if (typeof(window) === 'undefined') { less = {} } - else { less = window.less = {} } - tree = less.tree = {}; - less.mode = 'rhino'; -} else if (typeof(window) === 'undefined') { - // Node.js - less = exports, - tree = require('./tree'); - less.mode = 'node'; -} else { - // Browser - if (typeof(window.less) === 'undefined') { window.less = {} } - less = window.less, - tree = window.less.tree = {}; - less.mode = 'browser'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `current` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // index of current chunk, in `input` - parser; - - var that = this; - - // This function is called after all files - // have been imported through `@import`. - var finish = function () {}; - - var imports = this.imports = { - paths: env && env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: {}, // Holds the imported parse trees - contents: {}, // Holds the imported file contents - mime: env && env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, callback) { - var that = this; - this.queue.push(path); - - // - // Import a file asynchronously - // - less.Parser.importer(path, this.paths, function (e, root, contents) { - that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue - - var imported = path in that.files; - - that.files[path] = root; // Store the root - that.contents[path] = contents; - - if (e && !that.error) { that.error = e } - - callback(e, root, imported); - - if (that.queue.length === 0) { finish() } // Call `finish` if we're done importing - }, env); - } - }; - - function save() { temp = chunks[j], memo = i, current = i } - function restore() { chunks[j] = temp, i = memo, current = i } - - function sync() { - if (i > current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, args, length, c, index, endIndex, k, mem; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // and consume any extra white-space characters (' ' || '\n') - // which come after that. The reason for this is that LeSS's - // grammar is mostly white-space insensitive. - // - if (match) { - mem = i += length; - endIndex = i + chunks[j].length - length; - - while (i < endIndex) { - c = input.charCodeAt(i); - if (! (c === 32 || c === 10 || c === 9)) { break } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - function expect(arg, msg) { - var result = $(arg); - if (! result) { - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } else { - return result; - } - } - - function error(msg, type) { - throw { index: i, type: type || 'Syntax', message: msg }; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - if (tok.test(chunks[j])) { - return true; - } else { - return false; - } - } - } - - function basename(pathname) { - if (less.mode === 'node') { - return require('path').basename(pathname); - } else { - return pathname.match(/[^\/]+$/)[0]; - } - } - - function getInput(e, env) { - if (e.filename && env.filename && (e.filename !== env.filename)) { - return parser.imports.contents[basename(e.filename)]; - } else { - return input; - } - } - - function getLocation(index, input) { - for (var n = index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } - - return { line: typeof(index) === 'number' ? (input.slice(0, index).match(/\n/g) || "").length : null, - column: column }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = e.call && (getLocation(e.call, input).line + 1); - this.callExtract = lines[getLocation(e.call, input).line]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - this.env.filename = this.env.filename || null; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, start, end, zone, line, lines, buff = [], c, error = null; - - i = j = current = furthest = 0; - input = str.replace(/\r\n/g, '\n'); - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /[^"'`\{\}\/\(\)\\]+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - string = /"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`\\\r\n]|\\.)*)`/g, - level = 0, - match, - chunk = chunks[0], - inParam; - - for (var i = 0, c, cc; i < input.length; i++) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = string.lastIndex = i; - - if (match = string.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - c = input.charAt(i); - } - } - - if (!inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - c = input.charAt(i); - } - } - } - } - - switch (c) { - case '{': if (! inParam) { level ++; chunk.push(c); break } - case '}': if (! inParam) { level --; chunk.push(c); chunks[++j] = chunk = []; break } - case '(': if (! inParam) { inParam = true; chunk.push(c); break } - case ')': if ( inParam) { inParam = false; chunk.push(c); break } - default: chunk.push(c); - } - } - if (level > 0) { - error = new(LessError)({ - index: i, - type: 'Parse', - message: "missing closing `}`", - filename: env.filename - }, env); - } - - return chunks.map(function (c) { return c.join('') });; - })([[]]); - - if (error) { - return callback(error); - } - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - var line, lines, column; - - return function (options, variables) { - var frames = [], importError; - - options = options || {}; - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, 0); - }); - frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var css = evaluate.call(this, { frames: frames }) - .toCSS([], { compress: options.compress || false }); - } catch (e) { - throw new(LessError)(e, env); - } - - if ((importError = parser.imports.error)) { // Check if there was an error during importing - if (importError instanceof LessError) throw importError; - else throw new(LessError)(importError, env); - } - - if (options.yuicompress && less.mode === 'node') { - return require('./cssmin').compressor.cssmin(css); - } else if (options.compress) { - return css.replace(/(\s)+/g, "$1"); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } - - error = { - type: "Parse", - message: "Syntax Error on line " + line, - index: i, - filename: env.filename, - line: line, - column: column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - if (this.imports.queue.length > 0) { - finish = function () { callback(error, root) }; - } else { - callback(error, root); - } - }, - - // - // Here in, the parsing rules/functions - // - // The basic structure of the syntax tree generated is as follows: - // - // Ruleset -> Rule -> Value -> Expression -> Entity - // - // Here's some LESS code: - // - // .class { - // color: #fff; - // border: 1px solid #000; - // width: @w + 4px; - // > .child {...} - // } - // - // And here's what the parse tree might look like: - // - // Ruleset (Selector '.class', [ - // Rule ("color", Value ([Expression [Color #fff]])) - // Rule ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]])) - // Rule ("width", Value ([Expression [Operation "+" [Variable "@w"][Dimension 4px]]])) - // Ruleset (Selector [Element '>', '.child'], [...]) - // ]) - // - // In general, most rules will try to parse a token with the `$()` function, and if the return - // value is truly, will return a new node, of the relevant type. Sometimes, we need to check - // first, before parsing, that's when we use `peek()`. - // - parsers: { - // - // The `primary` rule is the *entry* and *exit* point of the parser. - // The rules here can appear at any level of the parse tree. - // - // The recursive nature of the grammar is an interplay between the `block` - // rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule, - // as represented by this simplified grammar: - // - // primary → (ruleset | rule)+ - // ruleset → selector+ block - // block → '{' primary '}' - // - // Only at one point is the primary rule not called from the - // block rule: at the root level. - // - primary: function () { - var node, root = []; - - while ((node = $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') return; - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment); - } - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) { - if (tree.colors.hasOwnProperty(k)) { - // detect named color - return new(tree.Color)(tree.colors[k].slice(1)); - } else { - return new(tree.Keyword)(k); - } - } - }, - - // - // A function call - // - // rgb(255, 0, 255) - // - // We also try to catch IE's `alpha()`, but let the `alpha` parser - // deal with the details. - // - // The arguments are parsed with the `entities.arguments` parser. - // - call: function () { - var name, args, index = i; - - if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) return; - - name = name[1].toLowerCase(); - - if (name === 'url') { return null } - else { i += name.length } - - if (name === 'alpha') { return $(this.alpha) } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) return; - - if (name) { return new(tree.Call)(name, args, index, env.filename) } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.entities.assignment) || $(this.expression)) { - args.push(arg); - if (! $(',')) { break } - } - return args; - }, - literal: function () { - return $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - if ((key = $(/^\w+(?=\s?=)/i)) && $('=') && (value = $(this.entity))) { - return new(tree.Assignment)(key, value); - } - }, - - // - // Parse url() tokens - // - // We use a specific rule for urls, because they don't really behave like - // standard function calls. The difference is that the argument doesn't have - // to be enclosed within a string, so it can't be parsed as an Expression. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; - value = $(this.entities.quoted) || $(this.entities.variable) || - $(this.entities.dataURI) || $(/^[-\w%@$\/.&=:;#+?~]+/) || ""; - - expect(')'); - - return new(tree.URL)((value.value || value.data || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), imports.paths); - }, - - dataURI: function () { - var obj; - - if ($(/^data:/)) { - obj = {}; - obj.mime = $(/^[^\/]+\/[^,;)]+/) || ''; - obj.charset = $(/^;\s*charset=[^,;)]+/) || ''; - obj.base64 = $(/^;\s*base64/) || ''; - obj.data = $(/^,\s*[^)]+/); - - if (obj.data) { return obj } - } - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.filename); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - if ((c > 57 || c < 45) || c === 47) return; - - if (value = $(/^(-?\d*\.?\d+)(px|%|em|rem|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn|dpi)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '`') { return } - - e && $('~'); - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } - }, - - // - // A font size/line-height shorthand - // - // small/12px - // - // We need to peek first, or we'll match on keywords and dimensions - // - shorthand: function () { - var a, b; - - if (! peek(/^[@\w.%-]+\/[@\w.-]+/)) return; - - if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) { - return new(tree.Shorthand)(a, b); - } - }, - - // - // Mixins - // - mixin: { - // - // A Mixin call, with an optional argument list - // - // #mixins > .square(#fff); - // .rounded(4px, black); - // .button; - // - // The `while` loop is there because mixins can be - // namespaced, but we only support the child and descendant - // selector for now. - // - call: function () { - var elements = [], e, c, args = [], arg, index = i, s = input.charAt(i), name, value, important = false; - - if (s !== '.' && s !== '#') { return } - - while (e = $(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)) { - elements.push(new(tree.Element)(c, e, i)); - c = $('>'); - } - if ($('(')) { - while (arg = $(this.expression)) { - value = arg; - name = null; - - // Variable - if (arg.value.length == 1) { - var val = arg.value[0]; - if (val instanceof tree.Variable) { - if ($(':')) { - if (value = $(this.expression)) { - name = val.name; - } else { - throw new(Error)("Expected value"); - } - } - } - } - - args.push({ name: name, value: value }); - - if (! $(',')) { break } - } - if (! $(')')) throw new(Error)("Expected )"); - } - - if ($(this.important)) { - important = true; - } - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index, env.filename, important); - } - }, - - // - // A Mixin definition, with a list of parameters - // - // .rounded (@radius: 2px, @color) { - // ... - // } - // - // Until we have a finer grained state-machine, we have to - // do a look-ahead, to make sure we don't have a mixin call. - // See the `rule` function for more information. - // - // We start by matching `.rounded (`, and then proceed on to - // the argument list, which has optional default values. - // We store the parameters in `params`, with a `value` key, - // if there is a value, such as in the case of `@radius`. - // - // Once we've got our params list, and a closing `)`, we parse - // the `{...}` block. - // - definition: function () { - var name, params = [], match, ruleset, param, value, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*(;|})/)) return; - - save(); - - if (match = $(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)) { - name = match[1]; - - do { - if (input.charAt(i) === '.' && $(/^\.{3}/)) { - variadic = true; - break; - } else if (param = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword)) { - // Variable - if (param instanceof tree.Variable) { - if ($(':')) { - value = expect(this.expression, 'expected expression'); - params.push({ name: param.name, value: value }); - } else if ($(/^\.{3}/)) { - params.push({ name: param.name, variadic: true }); - variadic = true; - break; - } else { - params.push({ name: param.name }); - } - } else { - params.push({ value: param }); - } - } else { - break; - } - } while ($(',')) - - expect(')'); - - if ($(/^when/)) { // Guard - cond = expect(this.conditions, 'expected condition'); - } - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) || $(this.entities.javascript) || - $(this.comment); - }, - - // - // A Rule terminator. Note that we use `peek()` to check for '}', - // because the `block` rule will be expecting it, but we still need to make sure - // it's there, if ';' was ommitted. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^\(opacity=/i)) return; - if (value = $(/^\d+/) || $(this.entities.variable)) { - expect(')'); - return new(tree.Alpha)(value); - } - }, - - // - // A Selector Element - // - // div - // + h1 - // #socks - // input[type="text"] - // - // Elements are the building blocks for Selectors, - // they are made out of a `Combinator` (see combinator rule), - // and an element name, such as a tag a class, or `*`. - // - element: function () { - var e, t, c, v; - - c = $(this.combinator); - e = $(/^(?:\d+\.\d+|\d+)%/) || $(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/) || - $('*') || $(this.attribute) || $(/^\([^)@]+\)/); - - if (! e) { - $('(') && (v = $(this.entities.variable)) && $(')') && (e = new(tree.Paren)(v)); - } - - if (e) { return new(tree.Element)(c, e, i) } - - if (c.value && c.value.charAt(0) === '&') { - return new(tree.Element)(c, null, i); - } - }, - - // - // Combinators combine elements together, in a Selector. - // - // Because our parser isn't white-space sensitive, special care - // has to be taken, when parsing the descendant combinator, ` `, - // as it's an empty space. We have to check the previous character - // in the input, to see if it's a ` ` character. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var match, c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~') { - i++; - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)(c); - } else if (c === '&') { - match = '&'; - i++; - if(input.charAt(i) === ' ') { - match = '& '; - } - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)(match); - } else if (input.charAt(i - 1) === ' ') { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function () { - var sel, e, elements = [], c, match; - - if ($('(')) { - sel = $(this.entity); - expect(')'); - return new(tree.Selector)([new(tree.Element)('', sel, i)]); - } - - while (e = $(this.element)) { - c = input.charAt(i); - elements.push(e) - if (c === '{' || c === '}' || c === ';' || c === ',') { break } - } - - if (elements.length > 0) { return new(tree.Selector)(elements) } - }, - tag: function () { - return $(/^[a-zA-Z][a-zA-Z-]*[0-9]?/) || $('*'); - }, - attribute: function () { - var attr = '', key, val, op; - - if (! $('[')) return; - - if (key = $(/^[a-zA-Z-]+/) || $(this.entities.quoted)) { - if ((op = $(/^[|~*$^]?=/)) && - (val = $(this.entities.quoted) || $(/^[\w-]+/))) { - attr = [key, op, val.toCSS ? val.toCSS() : val].join(''); - } else { attr = key } - } - - if (! $(']')) return; - - if (attr) { return "[" + attr + "]" } - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, match; - save(); - - while (s = $(this.selector)) { - selectors.push(s); - $(this.comment); - if (! $(',')) { break } - $(this.comment); - } - - if (selectors.length > 0 && (rules = $(this.block))) { - return new(tree.Ruleset)(selectors, rules, env.strictImports); - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function () { - var name, value, c = input.charAt(i), important, match; - save(); - - if (c === '.' || c === '#' || c === '&') { return } - - if (name = $(this.variable) || $(this.property)) { - if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) { - i += match[0].length - 1; - value = new(tree.Anonymous)(match[1]); - } else if (name === "font") { - value = $(this.font); - } else { - value = $(this.value); - } - important = $(this.important); - - if (value && $(this.end)) { - return new(tree.Rule)(name, value, important, memo); - } else { - furthest = i; - restore(); - } - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, importing is done differently: - // In the browser, it's an XHR request, in Node, it would be a - // file-system operation. The function used for importing is - // stored in `import`, which we pass to the Import constructor. - // - "import": function () { - var path, features, index = i; - var dir = $(/^@import(?:-(once))?\s+/); - - if (dir && (path = $(this.entities.quoted) || $(this.entities.url))) { - features = $(this.mediaFeatures); - if ($(';')) { - return new(tree.Import)(path, imports, features, (dir[1] === 'once'), index); - } - } - }, - - mediaFeature: function () { - var e, p, nodes = []; - - do { - if (e = $(this.entities.keyword)) { - nodes.push(e); - } else if ($('(')) { - p = $(this.property); - e = $(this.entity); - if ($(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, i, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var e, features = []; - - do { - if (e = $(this.mediaFeature)) { - features.push(e); - if (! $(',')) { break } - } else if (e = $(this.entities.variable)) { - features.push(e); - if (! $(',')) { break } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules; - - if ($(/^@media/)) { - features = $(this.mediaFeatures); - - if (rules = $(this.block)) { - return new(tree.Media)(rules, features); - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, types, e, nodes; - - if (input.charAt(i) !== '@') return; - - if (value = $(this['import']) || $(this.media)) { - return value; - } else if (name = $(/^@page|@keyframes/) || $(/^@(?:-webkit-|-moz-|-o-|-ms-)[a-z0-9-]+/)) { - types = ($(/^[^{]+/) || '').trim(); - if (rules = $(this.block)) { - return new(tree.Directive)(name + " " + types, rules); - } - } else if (name = $(/^@[-a-z]+/)) { - if (name === '@font-face') { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules); - } - } else if ((value = $(this.entity)) && $(';')) { - return new(tree.Directive)(name, value); - } - } - }, - font: function () { - var value = [], expression = [], weight, shorthand, font, e; - - while (e = $(this.shorthand) || $(this.entity)) { - expression.push(e); - } - value.push(new(tree.Expression)(expression)); - - if ($(',')) { - while (e = $(this.expression)) { - value.push(e); - if (! $(',')) { break } - } - } - return new(tree.Value)(value); - }, - - // - // A Value is a comma-delimited list of Expressions - // - // font-family: Baskerville, Georgia, serif; - // - // In a Rule, a Value represents everything after the `:`, - // and before the `;`. - // - value: function () { - var e, expressions = [], important; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var e; - - if ($('(') && (e = $(this.expression)) && $(')')) { - return e; - } - }, - multiplication: function () { - var m, a, op, operation; - if (m = $(this.operand)) { - while (!peek(/^\/\*/) && (op = ($('/') || $('*'))) && (a = $(this.operand))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation; - if (m = $(this.multiplication)) { - while ((op = $(/^[-+]\s+/) || (input.charAt(i - 1) != ' ' && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - if (a = $(this.condition)) { - while ($(',') && (b = $(this.condition))) { - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var a, b, c, op, index = i, negate = false; - - if ($(/^not/)) { negate = true } - expect('('); - if (a = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - if (op = $(/^(?:>=|=<|[<=>])/)) { - if (b = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expect(')'); - return $(/^and/) ? new(tree.Condition)('and', c, $(this.condition)) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o]) - : o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = [], d; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[-a-z_0-9]+)\s*:/)) { - return name[1]; - } - } - } - }; -}; - -if (less.mode === 'browser' || less.mode === 'rhino') { - // - // Used by `@import` directives - // - less.Parser.importer = function (path, paths, callback, env) { - if (!/^([a-z]+:)?\//.test(path) && paths.length > 0) { - path = paths[0] + path; - } - // We pass `true` as 3rd argument, to force the reload of the import. - // This is so we can get the syntax tree as opposed to just the CSS output, - // as we need this to evaluate the current stylesheet. - loadStyleSheet({ href: path, title: path, type: env.mime }, function (e) { - if (e && typeof(env.errback) === "function") { - env.errback.call(null, path, paths, callback, env); - } else { - callback.apply(null, arguments); - } - }, true); - }; -} - diff --git a/dashboard/bin/lib/less/rhino.js b/dashboard/bin/lib/less/rhino.js deleted file mode 100644 index a2c5662f..00000000 --- a/dashboard/bin/lib/less/rhino.js +++ /dev/null @@ -1,62 +0,0 @@ -var name; - -function loadStyleSheet(sheet, callback, reload, remaining) { - var sheetName = name.slice(0, name.lastIndexOf('/') + 1) + sheet.href; - var input = readFile(sheetName); - var parser = new less.Parser({ - paths: [sheet.href.replace(/[\w\.-]+$/, '')] - }); - parser.parse(input, function (e, root) { - if (e) { - print("Error: " + e); - quit(1); - } - callback(root, sheet, { local: false, lastModified: 0, remaining: remaining }); - }); - - // callback({}, sheet, { local: true, remaining: remaining }); -} - -function writeFile(filename, content) { - var fstream = new java.io.FileWriter(filename); - var out = new java.io.BufferedWriter(fstream); - out.write(content); - out.close(); -} - -// Command line integration via Rhino -(function (args) { - name = args[0]; - var output = args[1]; - - if (!name) { - print('No files present in the fileset; Check your pattern match in build.xml'); - quit(1); - } - path = name.split("/");path.pop();path=path.join("/") - - var input = readFile(name); - - if (!input) { - print('lesscss: couldn\'t open file ' + name); - quit(1); - } - - var result; - var parser = new less.Parser(); - parser.parse(input, function (e, root) { - if (e) { - quit(1); - } else { - result = root.toCSS(); - if (output) { - writeFile(output, result); - print("Written to " + output); - } else { - print(result); - } - quit(0); - } - }); - print("done"); -}(arguments)); diff --git a/dashboard/bin/lib/less/tree.js b/dashboard/bin/lib/less/tree.js deleted file mode 100644 index 24ecd712..00000000 --- a/dashboard/bin/lib/less/tree.js +++ /dev/null @@ -1,17 +0,0 @@ -(function (tree) { - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } - } - return null; -}; -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -})(require('./tree')); diff --git a/dashboard/bin/lib/less/tree/alpha.js b/dashboard/bin/lib/less/tree/alpha.js deleted file mode 100644 index 139ae920..00000000 --- a/dashboard/bin/lib/less/tree/alpha.js +++ /dev/null @@ -1,17 +0,0 @@ -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - toCSS: function () { - return "alpha(opacity=" + - (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - } -}; - -})(require('../tree')); diff --git a/dashboard/bin/lib/less/tree/anonymous.js b/dashboard/bin/lib/less/tree/anonymous.js deleted file mode 100644 index 460c9ec7..00000000 --- a/dashboard/bin/lib/less/tree/anonymous.js +++ /dev/null @@ -1,13 +0,0 @@ -(function (tree) { - -tree.Anonymous = function (string) { - this.value = string.value || string; -}; -tree.Anonymous.prototype = { - toCSS: function () { - return this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); diff --git a/dashboard/bin/lib/less/tree/assignment.js b/dashboard/bin/lib/less/tree/assignment.js deleted file mode 100644 index 70ce6e2f..00000000 --- a/dashboard/bin/lib/less/tree/assignment.js +++ /dev/null @@ -1,17 +0,0 @@ -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - toCSS: function () { - return this.key + '=' + (this.value.toCSS ? this.value.toCSS() : this.value); - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - } -}; - -})(require('../tree')); \ No newline at end of file diff --git a/dashboard/bin/lib/less/tree/call.js b/dashboard/bin/lib/less/tree/call.js deleted file mode 100644 index c1465dd4..00000000 --- a/dashboard/bin/lib/less/tree/call.js +++ /dev/null @@ -1,48 +0,0 @@ -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, filename) { - this.name = name; - this.args = args; - this.index = index; - this.filename = filename; -}; -tree.Call.prototype = { - // - // When evaluating a function call, - // we either find the function in `tree.functions` [1], - // in which case we call it, passing the evaluated arguments, - // or we simply print it out as it appeared originally [2]. - // - // The *functions.js* file contains the built-in functions. - // - // The reason why we evaluate the arguments, is in the case where - // we try to pass a variable to a function, like: `saturate(@color)`. - // The function should receive the value, not the variable. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env) }); - - if (this.name in tree.functions) { // 1. - try { - return tree.functions[this.name].apply(tree.functions, args); - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.filename }; - } - } else { // 2. - return new(tree.Anonymous)(this.name + - "(" + args.map(function (a) { return a.toCSS() }).join(', ') + ")"); - } - }, - - toCSS: function (env) { - return this.eval(env).toCSS(); - } -}; - -})(require('../tree')); diff --git a/dashboard/bin/lib/less/tree/color.js b/dashboard/bin/lib/less/tree/color.js deleted file mode 100644 index 37ce1781..00000000 --- a/dashboard/bin/lib/less/tree/color.js +++ /dev/null @@ -1,101 +0,0 @@ -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; -tree.Color.prototype = { - eval: function () { return this }, - - // - // If we have some transparency, the only way to represent it - // is via `rgba`. Otherwise, we use the hex representation, - // which has better compatibility with older browsers. - // Values are capped between `0` and `255`, rounded and zero-padded. - // - toCSS: function () { - if (this.alpha < 1.0) { - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(', ') + ")"; - } else { - return '#' + this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } - }, - - // - // Operations have to be done per-channel, if not, - // channels will spill onto each other. Once we have - // our result, in the form of an integer triplet, - // we create a new Color node to hold the result. - // - operate: function (op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { h: h * 360, s: s, l: l, a: a }; - }, - toARGB: function () { - var argb = [Math.round(this.alpha * 255)].concat(this.rgb); - return '#' + argb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } -}; - - -})(require('../tree')); diff --git a/dashboard/bin/lib/less/tree/comment.js b/dashboard/bin/lib/less/tree/comment.js deleted file mode 100644 index f4a33840..00000000 --- a/dashboard/bin/lib/less/tree/comment.js +++ /dev/null @@ -1,14 +0,0 @@ -(function (tree) { - -tree.Comment = function (value, silent) { - this.value = value; - this.silent = !!silent; -}; -tree.Comment.prototype = { - toCSS: function (env) { - return env.compress ? '' : this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); diff --git a/dashboard/bin/lib/less/tree/condition.js b/dashboard/bin/lib/less/tree/condition.js deleted file mode 100644 index 6b79dc96..00000000 --- a/dashboard/bin/lib/less/tree/condition.js +++ /dev/null @@ -1,42 +0,0 @@ -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype.eval = function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - var result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<'; - case 0: return op === '=' || op === '>=' || op === '=<'; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; -}; - -})(require('../tree')); diff --git a/dashboard/bin/lib/less/tree/dimension.js b/dashboard/bin/lib/less/tree/dimension.js deleted file mode 100644 index 9a6fce3d..00000000 --- a/dashboard/bin/lib/less/tree/dimension.js +++ /dev/null @@ -1,49 +0,0 @@ -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = unit || null; -}; - -tree.Dimension.prototype = { - eval: function () { return this }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - toCSS: function () { - var css = this.value + this.unit; - return css; - }, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2em` will yield `3px`. - // In the future, we could implement some unit - // conversions such that `100cm + 10mm` would yield - // `101cm`. - operate: function (op, other) { - return new(tree.Dimension) - (tree.operate(op, this.value, other.value), - this.unit || other.unit); - }, - - // TODO: Perform unit conversion before comparing - compare: function (other) { - if (other instanceof tree.Dimension) { - if (other.value > this.value) { - return -1; - } else if (other.value < this.value) { - return 1; - } else { - return 0; - } - } else { - return -1; - } - } -}; - -})(require('../tree')); diff --git a/dashboard/bin/lib/less/tree/directive.js b/dashboard/bin/lib/less/tree/directive.js deleted file mode 100644 index 27538332..00000000 --- a/dashboard/bin/lib/less/tree/directive.js +++ /dev/null @@ -1,35 +0,0 @@ -(function (tree) { - -tree.Directive = function (name, value, features) { - this.name = name; - - if (Array.isArray(value)) { - this.ruleset = new(tree.Ruleset)([], value); - this.ruleset.allowImports = true; - } else { - this.value = value; - } -}; -tree.Directive.prototype = { - toCSS: function (ctx, env) { - if (this.ruleset) { - this.ruleset.root = true; - return this.name + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - } else { - return this.name + ' ' + this.value.toCSS() + ';\n'; - } - }, - eval: function (env) { - env.frames.unshift(this); - this.ruleset = this.ruleset && this.ruleset.eval(env); - env.frames.shift(); - return this; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) } -}; - -})(require('../tree')); diff --git a/dashboard/bin/lib/less/tree/element.js b/dashboard/bin/lib/less/tree/element.js deleted file mode 100644 index 14b08d2e..00000000 --- a/dashboard/bin/lib/less/tree/element.js +++ /dev/null @@ -1,52 +0,0 @@ -(function (tree) { - -tree.Element = function (combinator, value, index) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; -}; -tree.Element.prototype.eval = function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index); -}; -tree.Element.prototype.toCSS = function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value == '' && this.combinator.value.charAt(0) == '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else if (value === '& ') { - this.value = '& '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype.toCSS = function (env) { - return { - '' : '', - ' ' : ' ', - '&' : '', - '& ' : ' ', - ':' : ' :', - '+' : env.compress ? '+' : ' + ', - '~' : env.compress ? '~' : ' ~ ', - '>' : env.compress ? '>' : ' > ' - }[this.value]; -}; - -})(require('../tree')); diff --git a/dashboard/bin/lib/less/tree/expression.js b/dashboard/bin/lib/less/tree/expression.js deleted file mode 100644 index fbfa9c5b..00000000 --- a/dashboard/bin/lib/less/tree/expression.js +++ /dev/null @@ -1,23 +0,0 @@ -(function (tree) { - -tree.Expression = function (value) { this.value = value }; -tree.Expression.prototype = { - eval: function (env) { - if (this.value.length > 1) { - return new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return this; - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS ? e.toCSS(env) : ''; - }).join(' '); - } -}; - -})(require('../tree')); diff --git a/dashboard/bin/lib/less/tree/import.js b/dashboard/bin/lib/less/tree/import.js deleted file mode 100644 index 7a977def..00000000 --- a/dashboard/bin/lib/less/tree/import.js +++ /dev/null @@ -1,83 +0,0 @@ -(function (tree) { -// -// CSS @import node -// -// The general strategy here is that we don't want to wait -// for the parsing to be completed, before we start importing -// the file. That's because in the context of a browser, -// most of the time will be spent waiting for the server to respond. -// -// On creation, we push the import path to our import queue, though -// `import,push`, we also pass it a callback, which it'll call once -// the file has been fetched, and parsed. -// -tree.Import = function (path, imports, features, once, index) { - var that = this; - - this.once = once; - this.index = index; - this._path = path; - this.features = features && new(tree.Value)(features); - - // The '.less' extension is optional - if (path instanceof tree.Quoted) { - this.path = /\.(le?|c)ss(\?.*)?$/.test(path.value) ? path.value : path.value + '.less'; - } else { - this.path = path.value.value || path.value; - } - - this.css = /css(\?.*)?$/.test(this.path); - - // Only pre-compile .less files - if (! this.css) { - imports.push(this.path, function (e, root, imported) { - if (e) { e.index = index } - if (imported && that.once) that.skip = imported; - that.root = root || new(tree.Ruleset)([], []); - }); - } -}; - -// -// The actual import node doesn't return anything, when converted to CSS. -// The reason is that it's used at the evaluation stage, so that the rules -// it imports can be treated like any other rules. -// -// In `eval`, we make sure all Import nodes get evaluated, recursively, so -// we end up with a flat structure, which can easily be imported in the parent -// ruleset. -// -tree.Import.prototype = { - toCSS: function (env) { - var features = this.features ? ' ' + this.features.toCSS(env) : ''; - - if (this.css) { - return "@import " + this._path.toCSS() + features + ';\n'; - } else { - return ""; - } - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) return []; - - if (this.css) { - return this; - } else { - ruleset = new(tree.Ruleset)([], this.root.rules.slice(0)); - - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype - .splice - .apply(ruleset.rules, - [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); diff --git a/dashboard/bin/lib/less/tree/javascript.js b/dashboard/bin/lib/less/tree/javascript.js deleted file mode 100644 index 772a31dd..00000000 --- a/dashboard/bin/lib/less/tree/javascript.js +++ /dev/null @@ -1,51 +0,0 @@ -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - diff --git a/dashboard/bin/lib/less/tree/keyword.js b/dashboard/bin/lib/less/tree/keyword.js deleted file mode 100644 index 701b79e5..00000000 --- a/dashboard/bin/lib/less/tree/keyword.js +++ /dev/null @@ -1,19 +0,0 @@ -(function (tree) { - -tree.Keyword = function (value) { this.value = value }; -tree.Keyword.prototype = { - eval: function () { return this }, - toCSS: function () { return this.value }, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); diff --git a/dashboard/bin/lib/less/tree/media.js b/dashboard/bin/lib/less/tree/media.js deleted file mode 100644 index 2b7b26e5..00000000 --- a/dashboard/bin/lib/less/tree/media.js +++ /dev/null @@ -1,114 +0,0 @@ -(function (tree) { - -tree.Media = function (value, features) { - var el = new(tree.Element)('&', null, 0), - selectors = [new(tree.Selector)([el])]; - - this.features = new(tree.Value)(features); - this.ruleset = new(tree.Ruleset)(selectors, value); - this.ruleset.allowImports = true; -}; -tree.Media.prototype = { - toCSS: function (ctx, env) { - var features = this.features.toCSS(env); - - this.ruleset.root = (ctx.length === 0 || ctx[0].multiMedia); - return '@media ' + features + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - }, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var blockIndex = env.mediaBlocks.length; - env.mediaPath.push(this); - env.mediaBlocks.push(this); - - var media = new(tree.Media)([], []); - media.features = this.features.eval(env); - - env.frames.unshift(this.ruleset); - media.ruleset = this.ruleset.eval(env); - env.frames.shift(); - - env.mediaBlocks[blockIndex] = media; - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env) - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var el = new(tree.Element)('&', null, 0); - var selectors = [new(tree.Selector)([el])]; - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [value]; - } - - // Trace all permutations to generate the resulting media-query. - // - // (a, b and c) with nested (d, e) -> - // a and d - // a and e - // b and c and d - // b and c and e - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - } -}; - -})(require('../tree')); diff --git a/dashboard/bin/lib/less/tree/mixin.js b/dashboard/bin/lib/less/tree/mixin.js deleted file mode 100644 index b441bf3b..00000000 --- a/dashboard/bin/lib/less/tree/mixin.js +++ /dev/null @@ -1,146 +0,0 @@ -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, filename, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; - this.filename = filename; - this.important = important; -}; -tree.mixin.Call.prototype = { - eval: function (env) { - var mixins, args, rules = [], match = false; - - for (var i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - for (var m = 0; m < mixins.length; m++) { - if (mixins[m].match(args, env)) { - try { - Array.prototype.push.apply( - rules, mixins[m].eval(env, this.arguments, this.important).rules); - match = true; - } catch (e) { - throw { message: e.message, index: this.index, filename: this.filename, stack: e.stack }; - } - } - } - if (match) { - return rules; - } else { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - this.arguments.map(function (a) { - return a.toCSS(); - }).join(', ') + ")`", - index: this.index, filename: this.filename }; - } - } - } - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.filename }; - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - toCSS: function () { return "" }, - variable: function (name) { return this.parent.variable.call(this, name) }, - variables: function () { return this.parent.variables.call(this) }, - find: function () { return this.parent.find.apply(this, arguments) }, - rulesets: function () { return this.parent.rulesets.apply(this) }, - - evalParams: function (env, args) { - var frame = new(tree.Ruleset)(null, []), varargs, arg; - - for (var i = 0, val, name; i < this.params.length; i++) { - arg = args && args[i] - - if (arg && arg.name) { - frame.rules.unshift(new(tree.Rule)(arg.name, arg.value.eval(env))); - args.splice(i, 1); - i--; - continue; - } - - if (name = this.params[i].name) { - if (this.params[i].variadic && args) { - varargs = []; - for (var j = i; j < args.length; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.rules.unshift(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else if (val = (arg && arg.value) || this.params[i].value) { - frame.rules.unshift(new(tree.Rule)(name, val.eval(env))); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - } - } - return frame; - }, - eval: function (env, args, important) { - var frame = this.evalParams(env, args), context, _arguments = [], rules, start; - - for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) { - _arguments.push((args[i] && args[i].value) || this.params[i].value); - } - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = important ? - this.rules.map(function (r) { - return new(tree.Rule)(r.name, r.value, '!important', r.index); - }) : this.rules.slice(0); - - return new(tree.Ruleset)(null, rules).eval({ - frames: [this, frame].concat(this.frames, env.frames) - }); - }, - match: function (args, env) { - var argsLength = (args && args.length) || 0, len, frame; - - if (! this.variadic) { - if (argsLength < this.required) { return false } - if (argsLength > this.params.length) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } - } - - if (this.condition && !this.condition.eval({ - frames: [this.evalParams(env, args)].concat(env.frames) - })) { return false } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); diff --git a/dashboard/bin/lib/less/tree/operation.js b/dashboard/bin/lib/less/tree/operation.js deleted file mode 100644 index 1ce22fb0..00000000 --- a/dashboard/bin/lib/less/tree/operation.js +++ /dev/null @@ -1,32 +0,0 @@ -(function (tree) { - -tree.Operation = function (op, operands) { - this.op = op.trim(); - this.operands = operands; -}; -tree.Operation.prototype.eval = function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { name: "OperationError", - message: "Can't substract or divide a color from a number" }; - } - } - return a.operate(this.op, b); -}; - -tree.operate = function (op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); diff --git a/dashboard/bin/lib/less/tree/paren.js b/dashboard/bin/lib/less/tree/paren.js deleted file mode 100644 index 384a43c7..00000000 --- a/dashboard/bin/lib/less/tree/paren.js +++ /dev/null @@ -1,16 +0,0 @@ - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - toCSS: function (env) { - return '(' + this.value.toCSS(env) + ')'; - }, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); diff --git a/dashboard/bin/lib/less/tree/quoted.js b/dashboard/bin/lib/less/tree/quoted.js deleted file mode 100644 index 794bf4ce..00000000 --- a/dashboard/bin/lib/less/tree/quoted.js +++ /dev/null @@ -1,29 +0,0 @@ -(function (tree) { - -tree.Quoted = function (str, content, escaped, i) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = i; -}; -tree.Quoted.prototype = { - toCSS: function () { - if (this.escaped) { - return this.value; - } else { - return this.quote + this.value + this.quote; - } - }, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index).eval(env); - return ('value' in v) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index); - } -}; - -})(require('../tree')); diff --git a/dashboard/bin/lib/less/tree/rule.js b/dashboard/bin/lib/less/tree/rule.js deleted file mode 100644 index 9e4e54a3..00000000 --- a/dashboard/bin/lib/less/tree/rule.js +++ /dev/null @@ -1,42 +0,0 @@ -(function (tree) { - -tree.Rule = function (name, value, important, index, inline) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.index = index; - this.inline = inline || false; - - if (name.charAt(0) === '@') { - this.variable = true; - } else { this.variable = false } -}; -tree.Rule.prototype.toCSS = function (env) { - if (this.variable) { return "" } - else { - return this.name + (env.compress ? ':' : ': ') + - this.value.toCSS(env) + - this.important + (this.inline ? "" : ";"); - } -}; - -tree.Rule.prototype.eval = function (context) { - return new(tree.Rule)(this.name, - this.value.eval(context), - this.important, - this.index, this.inline); -}; - -tree.Shorthand = function (a, b) { - this.a = a; - this.b = b; -}; - -tree.Shorthand.prototype = { - toCSS: function (env) { - return this.a.toCSS(env) + "/" + this.b.toCSS(env); - }, - eval: function () { return this } -}; - -})(require('../tree')); diff --git a/dashboard/bin/lib/less/tree/ruleset.js b/dashboard/bin/lib/less/tree/ruleset.js deleted file mode 100644 index 3100cc35..00000000 --- a/dashboard/bin/lib/less/tree/ruleset.js +++ /dev/null @@ -1,225 +0,0 @@ -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - eval: function (env) { - var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env) }); - var ruleset = new(tree.Ruleset)(selectors, this.rules.slice(0), this.strictImports); - - ruleset.root = this.root; - ruleset.allowImports = this.allowImports; - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - - // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - - return ruleset; - }, - match: function (args) { - return !args || args.length === 0; - }, - variables: function () { - if (this._variables) { return this._variables } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (this._rulesets) { return this._rulesets } - else { - return this._rulesets = this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - } - }, - find: function (selector, self) { - self = self || this; - var rules = [], rule, match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key] } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > rule.selectors[j].elements.length) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - // - // Entry point for code generation - // - // `context` holds an array of arrays. - // - toCSS: function (context, env) { - var css = [], // The CSS output - rules = [], // node.Rule instances - _rules = [], // - rulesets = [], // node.Ruleset instances - paths = [], // Current selectors - selector, // The fully rendered selector - rule; - - if (! this.root) { - if (context.length === 0) { - paths = this.selectors.map(function (s) { return [s] }); - } else { - this.joinSelectors(paths, context, this.selectors); - } - } - - // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if (rule.rules || (rule instanceof tree.Directive) || (rule instanceof tree.Media)) { - rulesets.push(rule.toCSS(paths, env)); - } else if (rule instanceof tree.Comment) { - if (!rule.silent) { - if (this.root) { - rulesets.push(rule.toCSS(env)); - } else { - rules.push(rule.toCSS(env)); - } - } - } else { - if (rule.toCSS && !rule.variable) { - rules.push(rule.toCSS(env)); - } else if (rule.value && !rule.variable) { - rules.push(rule.value.toString()); - } - } - } - - rulesets = rulesets.join(''); - - // If this is the root node, we don't render - // a selector, or {}. - // Otherwise, only output if this ruleset has rules. - if (this.root) { - css.push(rules.join(env.compress ? '' : '\n')); - } else { - if (rules.length > 0) { - selector = paths.map(function (p) { - return p.map(function (s) { - return s.toCSS(env); - }).join('').trim(); - }).join(env.compress ? ',' : ',\n'); - - // Remove duplicates - for (var i = rules.length - 1; i >= 0; i--) { - if (_rules.indexOf(rules[i]) === -1) { - _rules.unshift(rules[i]); - } - } - rules = _rules; - - css.push(selector, - (env.compress ? '{' : ' {\n ') + - rules.join(env.compress ? '' : '\n ') + - (env.compress ? '}' : '\n}\n')); - } - } - css.push(rulesets); - - return css.join('') + (env.compress ? '\n' : ''); - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - var before = [], after = [], beforeElements = [], - afterElements = [], hasParentSelector = false, el; - - for (var i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.combinator.value.charAt(0) === '&') { - hasParentSelector = true; - } - if (hasParentSelector) afterElements.push(el); - else beforeElements.push(el); - } - - if (! hasParentSelector) { - afterElements = beforeElements; - beforeElements = []; - } - - if (beforeElements.length > 0) { - before.push(new(tree.Selector)(beforeElements)); - } - - if (afterElements.length > 0) { - after.push(new(tree.Selector)(afterElements)); - } - - for (var c = 0; c < context.length; c++) { - paths.push(before.concat(context[c]).concat(after)); - } - } -}; -})(require('../tree')); diff --git a/dashboard/bin/lib/less/tree/selector.js b/dashboard/bin/lib/less/tree/selector.js deleted file mode 100644 index 65abbb69..00000000 --- a/dashboard/bin/lib/less/tree/selector.js +++ /dev/null @@ -1,42 +0,0 @@ -(function (tree) { - -tree.Selector = function (elements) { - this.elements = elements; - if (this.elements[0].combinator.value === "") { - this.elements[0].combinator.value = ' '; - } -}; -tree.Selector.prototype.match = function (other) { - var len = this.elements.length, - olen = other.elements.length, - max = Math.min(len, olen); - - if (len < olen) { - return false; - } else { - for (var i = 0; i < max; i++) { - if (this.elements[i].value !== other.elements[i].value) { - return false; - } - } - } - return true; -}; -tree.Selector.prototype.eval = function (env) { - return new(tree.Selector)(this.elements.map(function (e) { - return e.eval(env); - })); -}; -tree.Selector.prototype.toCSS = function (env) { - if (this._css) { return this._css } - - return this._css = this.elements.map(function (e) { - if (typeof(e) === 'string') { - return ' ' + e.trim(); - } else { - return e.toCSS(env); - } - }).join(''); -}; - -})(require('../tree')); diff --git a/dashboard/bin/lib/less/tree/url.js b/dashboard/bin/lib/less/tree/url.js deleted file mode 100644 index 0caec345..00000000 --- a/dashboard/bin/lib/less/tree/url.js +++ /dev/null @@ -1,25 +0,0 @@ -(function (tree) { - -tree.URL = function (val, paths) { - if (val.data) { - this.attrs = val; - } else { - // Add the base path if the URL is relative and we are in the browser - if (typeof(window) !== 'undefined' && !/^(?:https?:\/\/|file:\/\/|data:|\/)/.test(val.value) && paths.length > 0) { - val.value = paths[0] + (val.value.charAt(0) === '/' ? val.value.slice(1) : val.value); - } - this.value = val; - this.paths = paths; - } -}; -tree.URL.prototype = { - toCSS: function () { - return "url(" + (this.attrs ? 'data:' + this.attrs.mime + this.attrs.charset + this.attrs.base64 + this.attrs.data - : this.value.toCSS()) + ")"; - }, - eval: function (ctx) { - return this.attrs ? this : new(tree.URL)(this.value.eval(ctx), this.paths); - } -}; - -})(require('../tree')); diff --git a/dashboard/bin/lib/less/tree/value.js b/dashboard/bin/lib/less/tree/value.js deleted file mode 100644 index 3c1eb29a..00000000 --- a/dashboard/bin/lib/less/tree/value.js +++ /dev/null @@ -1,24 +0,0 @@ -(function (tree) { - -tree.Value = function (value) { - this.value = value; - this.is = 'value'; -}; -tree.Value.prototype = { - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(env.compress ? ',' : ', '); - } -}; - -})(require('../tree')); diff --git a/dashboard/bin/lib/less/tree/variable.js b/dashboard/bin/lib/less/tree/variable.js deleted file mode 100644 index ee557e1d..00000000 --- a/dashboard/bin/lib/less/tree/variable.js +++ /dev/null @@ -1,26 +0,0 @@ -(function (tree) { - -tree.Variable = function (name, index, file) { this.name = name, this.index = index, this.file = file }; -tree.Variable.prototype = { - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') == 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { return variable } - else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.file, - index: this.index }; - } - } -}; - -})(require('../tree')); diff --git a/dashboard/doc/source/_static/.placeholder b/dashboard/doc/source/_static/.placeholder deleted file mode 100755 index e69de29b..00000000 diff --git a/dashboard/doc/source/_templates/.placeholder b/dashboard/doc/source/_templates/.placeholder deleted file mode 100755 index e69de29b..00000000 diff --git a/dashboard/doc/source/_theme/theme.conf b/dashboard/doc/source/_theme/theme.conf deleted file mode 100755 index e8748699..00000000 --- a/dashboard/doc/source/_theme/theme.conf +++ /dev/null @@ -1,2 +0,0 @@ -[theme] -inherit = default diff --git a/dashboard/doc/source/conf.py b/dashboard/doc/source/conf.py deleted file mode 100644 index be34f745..00000000 --- a/dashboard/doc/source/conf.py +++ /dev/null @@ -1,237 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2010 OpenStack Foundation. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# -# Portas documentation build configuration file, created by -# sphinx-quickstart on Tue February 28 13:50:15 2013. -# -# This file is execfile()'d with the current directory set to its containing -# dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import os -import sys - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path = [os.path.abspath('../../tabula'), - os.path.abspath('../..')] + sys.path - -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', - 'sphinx.ext.intersphinx', - 'sphinx.ext.coverage', - 'sphinx.ext.pngmath', - 'sphinx.ext.ifconfig', - 'sphinx.ext.graphviz'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = [] -if os.getenv('HUDSON_PUBLISH_DOCS'): - templates_path = ['_ga', '_templates'] -else: - templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'Tabula' -copyright = u'2013, Mirantis, Inc' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -from tabula.version import version_info as tabula_version -# The full version, including alpha/beta/rc tags. -release = tabula_version.version_string_with_vcs() -# The short X.Y version. -version = tabula_version.canonical_version_string() - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of documents that shouldn't be included in the build. -#unused_docs = [] - -# List of directories, relative to source directory, that shouldn't be searched -# for source files. -exclude_trees = ['api'] - -# The reST default role (for this markup: `text`) to use for all documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -show_authors = True - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -modindex_common_prefix = ['tabula.'] - -# -- Options for man page output -------------------------------------------- - -# Grouping the document tree for man pages. -# List of tuples 'sourcefile', 'target', u'title', u'Authors name', 'manual' - -man_pages = [] - - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. Major themes that come with -# Sphinx are currently 'default' and 'sphinxdoc'. -html_theme_path = ["."] -html_theme = '_theme' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = ['_theme'] - -# The name for this set of Sphinx documents. If None, it defaults to -# "<project> v<release> documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' -git_cmd = "git log --pretty=format:'%ad, commit %h' --date=local -n1" -html_last_updated_fmt = os.popen(git_cmd).read() - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -html_use_modindex = False - -# If false, no index is generated. -html_use_index = False - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a <link> tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = '' - -# Output file base name for HTML help builder. -htmlhelp_basename = 'tabuladoc' - - -# -- Options for LaTeX output ------------------------------------------------ - -# The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' - -# The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, -# documentclass [howto/manual]). -latex_documents = [ - ('index', 'Tabula.tex', u'Tabula Documentation', - u'Keero Team', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# Additional stuff for the LaTeX preamble. -#latex_preamble = '' - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_use_modindex = True - -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'python': ('http://docs.python.org/', None)} diff --git a/dashboard/doc/source/index.rst b/dashboard/doc/source/index.rst deleted file mode 100644 index 0de7d53d..00000000 --- a/dashboard/doc/source/index.rst +++ /dev/null @@ -1,65 +0,0 @@ -.. - Copyright 2010 OpenStack Foundation - All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); you may - not use this file except in compliance with the License. You may obtain - a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - License for the specific language governing permissions and limitations - under the License. - -============================================== -Welcome to Tabula, the Keero Project Web UI! -============================================== - -Tabula is a project that provides Web UI to Keero Project. - -This document describes Tabula for contributors of the project, and assumes -that you are already familiar with Portas from an `end-user perspective`_. - -.. _`end-user perspective`: http://keero.mirantis.com/ - -This documentation is generated by the Sphinx toolkit and lives in the source -tree. - -Installation Guide -================== -Install -------- -1. Check out sources to some directory (<home>/keero):: - - user@work:~/$ git clone ssh://<user>@gerrit.mirantis.com:29418/keero/keero.git - -2. Install virtualenv:: - - user@work:~/$ cd keero/tabula && sudo python ./tools/install_venv.py - -Configure ---------- -1. Copy configuration file from template:: - - user@work:~/$ cp keero/tabula/tabula/local/local_settings.py.example keero/tabula/tabula/local/local_settings.py - -2. Open configuration file for editing:: - - user@work:~/$ cd keero/tabula/tabula/local/ && nano local_settings.py - -2. Configure according to you environment:: - - ... - SECRET_KEY = 'some_random_value' - ... - OPENSTACK_HOST = "localhost" - ... - -Run ----- -Run Tabula in virtualenv:: - - user@work:~/$ cd keero/tabula && sudo ./tools/with_venv.sh python manage.py runserver 0.0.0.0:8080 diff --git a/dashboard/glazierdashboard/__init__.py b/dashboard/glazierdashboard/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/dashboard/glazierdashboard/local/__init__.py b/dashboard/glazierdashboard/local/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/dashboard/glazierdashboard/local/local_settings.py.example b/dashboard/glazierdashboard/local/local_settings.py.example deleted file mode 100644 index d841e6a2..00000000 --- a/dashboard/glazierdashboard/local/local_settings.py.example +++ /dev/null @@ -1,147 +0,0 @@ -import os - -from django.utils.translation import ugettext_lazy as _ - -DEBUG = True -TEMPLATE_DEBUG = DEBUG - -# Set SSL proxy settings: -# For Django 1.4+ pass this header from the proxy after terminating the SSL, -# and don't forget to strip it from the client's request. -# For more information see: -# https://docs.djangoproject.com/en/1.4/ref/settings/#secure-proxy-ssl-header -# SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTOCOL', 'https') - -# Specify a regular expression to validate user passwords. -# HORIZON_CONFIG = { -# "password_validator": { -# "regex": '.*', -# "help_text": _("Your password does not meet the requirements.") -# }, -# 'help_url': "http://docs.openstack.org" -# } - -LOCAL_PATH = os.path.dirname(os.path.abspath(__file__)) - -# Set custom secret key: -# You can either set it to a specific value or you can let horizion generate a -# default secret key that is unique on this machine, e.i. regardless of the -# amount of Python WSGI workers (if used behind Apache+mod_wsgi): However, there -# may be situations where you would want to set this explicitly, e.g. when -# multiple dashboard instances are distributed on different machines (usually -# behind a load-balancer). Either you have to make sure that a session gets all -# requests routed to the same dashboard instance or you set the same SECRET_KEY -# for all of them. -# from horizon.utils import secret_key -# SECRET_KEY = secret_key.generate_or_read_from_file(os.path.join(LOCAL_PATH, '.secret_key_store')) - -# We recommend you use memcached for development; otherwise after every reload -# of the django development server, you will have to login again. To use -# memcached set CACHE_BACKED to something like 'memcached://127.0.0.1:11211/' -CACHE_BACKEND = 'locmem://' - -# Send email to the console by default -EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' -# Or send them to /dev/null -#EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend' - -# Configure these for your outgoing email host -# EMAIL_HOST = 'smtp.my-company.com' -# EMAIL_PORT = 25 -# EMAIL_HOST_USER = 'djangomail' -# EMAIL_HOST_PASSWORD = 'top-secret!' - -# For multiple regions uncomment this configuration, and add (endpoint, title). -# AVAILABLE_REGIONS = [ -# ('http://cluster1.example.com:5000/v2.0', 'cluster1'), -# ('http://cluster2.example.com:5000/v2.0', 'cluster2'), -# ] - -OPENSTACK_HOST = "127.0.0.1" -OPENSTACK_KEYSTONE_URL = "http://%s:5000/v2.0" % OPENSTACK_HOST -OPENSTACK_KEYSTONE_DEFAULT_ROLE = "Member" - -# Disable SSL certificate checks (useful for self-signed certificates): -# OPENSTACK_SSL_NO_VERIFY = True - -# The OPENSTACK_KEYSTONE_BACKEND settings can be used to identify the -# capabilities of the auth backend for Keystone. -# If Keystone has been configured to use LDAP as the auth backend then set -# can_edit_user to False and name to 'ldap'. -# -# TODO(tres): Remove these once Keystone has an API to identify auth backend. -OPENSTACK_KEYSTONE_BACKEND = { - 'name': 'native', - 'can_edit_user': True -} - -OPENSTACK_HYPERVISOR_FEATURES = { - 'can_set_mount_point': True -} - -# OPENSTACK_ENDPOINT_TYPE specifies the endpoint type to use for the endpoints -# in the Keystone service catalog. Use this setting when Horizon is running -# external to the OpenStack environment. The default is 'internalURL'. -#OPENSTACK_ENDPOINT_TYPE = "publicURL" - -# The number of objects (Swift containers/objects or images) to display -# on a single page before providing a paging element (a "more" link) -# to paginate results. -API_RESULT_LIMIT = 1000 -API_RESULT_PAGE_SIZE = 20 - -# The timezone of the server. This should correspond with the timezone -# of your entire OpenStack installation, and hopefully be in UTC. -TIME_ZONE = "UTC" - -LOGGING = { - 'version': 1, - # When set to True this will disable all logging except - # for loggers specified in this configuration dictionary. Note that - # if nothing is specified here and disable_existing_loggers is True, - # django.db.backends will still log unless it is disabled explicitly. - 'disable_existing_loggers': False, - 'handlers': { - 'null': { - 'level': 'DEBUG', - 'class': 'django.utils.log.NullHandler', - }, - 'console': { - # Set the level to "DEBUG" for verbose output logging. - 'level': 'INFO', - 'class': 'logging.StreamHandler', - }, - }, - 'loggers': { - # Logging from django.db.backends is VERY verbose, send to null - # by default. - 'django.db.backends': { - 'handlers': ['null'], - 'propagate': False, - }, - 'horizon': { - 'handlers': ['console'], - 'propagate': False, - }, - 'openstack_dashboard': { - 'handlers': ['console'], - 'propagate': False, - }, - 'novaclient': { - 'handlers': ['console'], - 'propagate': False, - }, - 'keystoneclient': { - 'handlers': ['console'], - 'propagate': False, - }, - 'glanceclient': { - 'handlers': ['console'], - 'propagate': False, - }, - 'nose.plugins.manager': { - 'handlers': ['console'], - 'propagate': False, - } - } -} diff --git a/dashboard/glazierdashboard/models.py b/dashboard/glazierdashboard/models.py deleted file mode 100644 index 1b3d5f9e..00000000 --- a/dashboard/glazierdashboard/models.py +++ /dev/null @@ -1,3 +0,0 @@ -""" -Stub file to work around django bug: https://code.djangoproject.com/ticket/7198 -""" diff --git a/dashboard/glazierdashboard/openstack/__init__.py b/dashboard/glazierdashboard/openstack/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/dashboard/glazierdashboard/openstack/common/__init__.py b/dashboard/glazierdashboard/openstack/common/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/dashboard/glazierdashboard/openstack/common/importutils.py b/dashboard/glazierdashboard/openstack/common/importutils.py deleted file mode 100644 index 3bd277f4..00000000 --- a/dashboard/glazierdashboard/openstack/common/importutils.py +++ /dev/null @@ -1,67 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Import related utilities and helper functions. -""" - -import sys -import traceback - - -def import_class(import_str): - """Returns a class from a string including module and class""" - mod_str, _sep, class_str = import_str.rpartition('.') - try: - __import__(mod_str) - return getattr(sys.modules[mod_str], class_str) - except (ValueError, AttributeError): - raise ImportError('Class %s cannot be found (%s)' % - (class_str, - traceback.format_exception(*sys.exc_info()))) - - -def import_object(import_str, *args, **kwargs): - """Import a class and return an instance of it.""" - return import_class(import_str)(*args, **kwargs) - - -def import_object_ns(name_space, import_str, *args, **kwargs): - """ - Import a class and return an instance of it, first by trying - to find the class in a default namespace, then failing back to - a full path if not found in the default namespace. - """ - import_value = "%s.%s" % (name_space, import_str) - try: - return import_class(import_value)(*args, **kwargs) - except ImportError: - return import_class(import_str)(*args, **kwargs) - - -def import_module(import_str): - """Import a module.""" - __import__(import_str) - return sys.modules[import_str] - - -def try_import(import_str, default=None): - """Try to import a module and if it fails return default.""" - try: - return import_module(import_str) - except ImportError: - return default diff --git a/dashboard/glazierdashboard/openstack/common/setup.py b/dashboard/glazierdashboard/openstack/common/setup.py deleted file mode 100644 index dec74fd0..00000000 --- a/dashboard/glazierdashboard/openstack/common/setup.py +++ /dev/null @@ -1,367 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack Foundation. -# Copyright 2012-2013 Hewlett-Packard Development Company, L.P. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Utilities with minimum-depends for use in setup.py -""" - -import email -import os -import re -import subprocess -import sys - -from setuptools.command import sdist - - -def parse_mailmap(mailmap='.mailmap'): - mapping = {} - if os.path.exists(mailmap): - with open(mailmap, 'r') as fp: - for l in fp: - try: - canonical_email, alias = re.match( - r'[^#]*?(<.+>).*(<.+>).*', l).groups() - except AttributeError: - continue - mapping[alias] = canonical_email - return mapping - - -def _parse_git_mailmap(git_dir, mailmap='.mailmap'): - mailmap = os.path.join(os.path.dirname(git_dir), mailmap) - return parse_mailmap(mailmap) - - -def canonicalize_emails(changelog, mapping): - """Takes in a string and an email alias mapping and replaces all - instances of the aliases in the string with their real email. - """ - for alias, email_address in mapping.iteritems(): - changelog = changelog.replace(alias, email_address) - return changelog - - -# Get requirements from the first file that exists -def get_reqs_from_files(requirements_files): - for requirements_file in requirements_files: - if os.path.exists(requirements_file): - with open(requirements_file, 'r') as fil: - return fil.read().split('\n') - return [] - - -def parse_requirements(requirements_files=['requirements.txt', - 'tools/pip-requires']): - requirements = [] - for line in get_reqs_from_files(requirements_files): - # For the requirements list, we need to inject only the portion - # after egg= so that distutils knows the package it's looking for - # such as: - # -e git://github.com/openstack/nova/master#egg=nova - if re.match(r'\s*-e\s+', line): - requirements.append(re.sub(r'\s*-e\s+.*#egg=(.*)$', r'\1', - line)) - # such as: - # http://github.com/openstack/nova/zipball/master#egg=nova - elif re.match(r'\s*https?:', line): - requirements.append(re.sub(r'\s*https?:.*#egg=(.*)$', r'\1', - line)) - # -f lines are for index locations, and don't get used here - elif re.match(r'\s*-f\s+', line): - pass - # argparse is part of the standard library starting with 2.7 - # adding it to the requirements list screws distro installs - elif line == 'argparse' and sys.version_info >= (2, 7): - pass - else: - requirements.append(line) - - return requirements - - -def parse_dependency_links(requirements_files=['requirements.txt', - 'tools/pip-requires']): - dependency_links = [] - # dependency_links inject alternate locations to find packages listed - # in requirements - for line in get_reqs_from_files(requirements_files): - # skip comments and blank lines - if re.match(r'(\s*#)|(\s*$)', line): - continue - # lines with -e or -f need the whole line, minus the flag - if re.match(r'\s*-[ef]\s+', line): - dependency_links.append(re.sub(r'\s*-[ef]\s+', '', line)) - # lines that are only urls can go in unmolested - elif re.match(r'\s*https?:', line): - dependency_links.append(line) - return dependency_links - - -def _run_shell_command(cmd, throw_on_error=False): - if os.name == 'nt': - output = subprocess.Popen(["cmd.exe", "/C", cmd], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - else: - output = subprocess.Popen(["/bin/sh", "-c", cmd], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - out = output.communicate() - if output.returncode and throw_on_error: - raise Exception("%s returned %d" % cmd, output.returncode) - if len(out) == 0: - return None - if len(out[0].strip()) == 0: - return None - return out[0].strip() - - -def _get_git_directory(): - parent_dir = os.path.dirname(__file__) - while True: - git_dir = os.path.join(parent_dir, '.git') - if os.path.exists(git_dir): - return git_dir - parent_dir, child = os.path.split(parent_dir) - if not child: # reached to root dir - return None - - -def write_git_changelog(): - """Write a changelog based on the git changelog.""" - new_changelog = 'ChangeLog' - git_dir = _get_git_directory() - if not os.getenv('SKIP_WRITE_GIT_CHANGELOG'): - if git_dir: - git_log_cmd = 'git --git-dir=%s log' % git_dir - changelog = _run_shell_command(git_log_cmd) - mailmap = _parse_git_mailmap(git_dir) - with open(new_changelog, "w") as changelog_file: - changelog_file.write(canonicalize_emails(changelog, mailmap)) - else: - open(new_changelog, 'w').close() - - -def generate_authors(): - """Create AUTHORS file using git commits.""" - jenkins_email = 'jenkins@review.(openstack|stackforge).org' - old_authors = 'AUTHORS.in' - new_authors = 'AUTHORS' - git_dir = _get_git_directory() - if not os.getenv('SKIP_GENERATE_AUTHORS'): - if git_dir: - # don't include jenkins email address in AUTHORS file - git_log_cmd = ("git --git-dir=" + git_dir + - " log --format='%aN <%aE>' | sort -u | " - "egrep -v '" + jenkins_email + "'") - changelog = _run_shell_command(git_log_cmd) - signed_cmd = ("git log --git-dir=" + git_dir + - " | grep -i Co-authored-by: | sort -u") - signed_entries = _run_shell_command(signed_cmd) - if signed_entries: - new_entries = "\n".join( - [signed.split(":", 1)[1].strip() - for signed in signed_entries.split("\n") if signed]) - changelog = "\n".join((changelog, new_entries)) - mailmap = _parse_git_mailmap(git_dir) - with open(new_authors, 'w') as new_authors_fh: - new_authors_fh.write(canonicalize_emails(changelog, mailmap)) - if os.path.exists(old_authors): - with open(old_authors, "r") as old_authors_fh: - new_authors_fh.write('\n' + old_authors_fh.read()) - else: - open(new_authors, 'w').close() - - -_rst_template = """%(heading)s -%(underline)s - -.. automodule:: %(module)s - :members: - :undoc-members: - :show-inheritance: -""" - - -def get_cmdclass(): - """Return dict of commands to run from setup.py.""" - - cmdclass = dict() - - def _find_modules(arg, dirname, files): - for filename in files: - if filename.endswith('.py') and filename != '__init__.py': - arg["%s.%s" % (dirname.replace('/', '.'), - filename[:-3])] = True - - class LocalSDist(sdist.sdist): - """Builds the ChangeLog and Authors files from VC first.""" - - def run(self): - write_git_changelog() - generate_authors() - # sdist.sdist is an old style class, can't use super() - sdist.sdist.run(self) - - cmdclass['sdist'] = LocalSDist - - # If Sphinx is installed on the box running setup.py, - # enable setup.py to build the documentation, otherwise, - # just ignore it - try: - from sphinx.setup_command import BuildDoc - - class LocalBuildDoc(BuildDoc): - - builders = ['html', 'man'] - - def generate_autoindex(self): - print "**Autodocumenting from %s" % os.path.abspath(os.curdir) - modules = {} - option_dict = self.distribution.get_option_dict('build_sphinx') - source_dir = os.path.join(option_dict['source_dir'][1], 'api') - if not os.path.exists(source_dir): - os.makedirs(source_dir) - for pkg in self.distribution.packages: - if '.' not in pkg: - os.path.walk(pkg, _find_modules, modules) - module_list = modules.keys() - module_list.sort() - autoindex_filename = os.path.join(source_dir, 'autoindex.rst') - with open(autoindex_filename, 'w') as autoindex: - autoindex.write(""".. toctree:: - :maxdepth: 1 - -""") - for module in module_list: - output_filename = os.path.join(source_dir, - "%s.rst" % module) - heading = "The :mod:`%s` Module" % module - underline = "=" * len(heading) - values = dict(module=module, heading=heading, - underline=underline) - - print "Generating %s" % output_filename - with open(output_filename, 'w') as output_file: - output_file.write(_rst_template % values) - autoindex.write(" %s.rst\n" % module) - - def run(self): - if not os.getenv('SPHINX_DEBUG'): - self.generate_autoindex() - - for builder in self.builders: - self.builder = builder - self.finalize_options() - self.project = self.distribution.get_name() - self.version = self.distribution.get_version() - self.release = self.distribution.get_version() - BuildDoc.run(self) - - class LocalBuildLatex(LocalBuildDoc): - builders = ['latex'] - - cmdclass['build_sphinx'] = LocalBuildDoc - cmdclass['build_sphinx_latex'] = LocalBuildLatex - except ImportError: - pass - - return cmdclass - - -def _get_revno(git_dir): - """Return the number of commits since the most recent tag. - - We use git-describe to find this out, but if there are no - tags then we fall back to counting commits since the beginning - of time. - """ - describe = _run_shell_command( - "git --git-dir=%s describe --always" % git_dir) - if "-" in describe: - return describe.rsplit("-", 2)[-2] - - # no tags found - revlist = _run_shell_command( - "git --git-dir=%s rev-list --abbrev-commit HEAD" % git_dir) - return len(revlist.splitlines()) - - -def _get_version_from_git(pre_version): - """Return a version which is equal to the tag that's on the current - revision if there is one, or tag plus number of additional revisions - if the current revision has no tag.""" - - git_dir = _get_git_directory() - if git_dir: - if pre_version: - try: - return _run_shell_command( - "git --git-dir=" + git_dir + " describe --exact-match", - throw_on_error=True).replace('-', '.') - except Exception: - sha = _run_shell_command( - "git --git-dir=" + git_dir + " log -n1 --pretty=format:%h") - return "%s.a%s.g%s" % (pre_version, _get_revno(git_dir), sha) - else: - return _run_shell_command( - "git --git-dir=" + git_dir + " describe --always").replace( - '-', '.') - return None - - -def _get_version_from_pkg_info(package_name): - """Get the version from PKG-INFO file if we can.""" - try: - pkg_info_file = open('PKG-INFO', 'r') - except (IOError, OSError): - return None - try: - pkg_info = email.message_from_file(pkg_info_file) - except email.MessageError: - return None - # Check to make sure we're in our own dir - if pkg_info.get('Name', None) != package_name: - return None - return pkg_info.get('Version', None) - - -def get_version(package_name, pre_version=None): - """Get the version of the project. First, try getting it from PKG-INFO, if - it exists. If it does, that means we're in a distribution tarball or that - install has happened. Otherwise, if there is no PKG-INFO file, pull the - version from git. - - We do not support setup.py version sanity in git archive tarballs, nor do - we support packagers directly sucking our git repo into theirs. We expect - that a source tarball be made from our git repo - or that if someone wants - to make a source tarball from a fork of our repo with additional tags in it - that they understand and desire the results of doing that. - """ - version = os.environ.get("OSLO_PACKAGE_VERSION", None) - if version: - return version - version = _get_version_from_pkg_info(package_name) - if version: - return version - version = _get_version_from_git(pre_version) - if version: - return version - raise Exception("Versioning for this project requires either an sdist" - " tarball, or access to an upstream git repository.") diff --git a/dashboard/glazierdashboard/openstack/common/version.py b/dashboard/glazierdashboard/openstack/common/version.py deleted file mode 100644 index b2d4a79b..00000000 --- a/dashboard/glazierdashboard/openstack/common/version.py +++ /dev/null @@ -1,94 +0,0 @@ - -# Copyright 2012 OpenStack Foundation -# Copyright 2012-2013 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Utilities for consuming the version from pkg_resources. -""" - -import pkg_resources - - -class VersionInfo(object): - - def __init__(self, package): - """Object that understands versioning for a package - :param package: name of the python package, such as glance, or - python-glanceclient - """ - self.package = package - self.release = None - self.version = None - self._cached_version = None - - def __str__(self): - """Make the VersionInfo object behave like a string.""" - return self.version_string() - - def __repr__(self): - """Include the name.""" - return "VersionInfo(%s:%s)" % (self.package, self.version_string()) - - def _get_version_from_pkg_resources(self): - """Get the version of the package from the pkg_resources record - associated with the package.""" - try: - requirement = pkg_resources.Requirement.parse(self.package) - provider = pkg_resources.get_provider(requirement) - return provider.version - except pkg_resources.DistributionNotFound: - # The most likely cause for this is running tests in a tree - # produced from a tarball where the package itself has not been - # installed into anything. Revert to setup-time logic. - from tabula.openstack.common import setup - return setup.get_version(self.package) - - def release_string(self): - """Return the full version of the package including suffixes indicating - VCS status. - """ - if self.release is None: - self.release = self._get_version_from_pkg_resources() - - return self.release - - def version_string(self): - """Return the short version minus any alpha/beta tags.""" - if self.version is None: - parts = [] - for part in self.release_string().split('.'): - if part[0].isdigit(): - parts.append(part) - else: - break - self.version = ".".join(parts) - - return self.version - - # Compatibility functions - canonical_version_string = version_string - version_string_with_vcs = release_string - - def cached_version_string(self, prefix=""): - """Generate an object which will expand in a string context to - the results of version_string(). We do this so that don't - call into pkg_resources every time we start up a program when - passing version information into the CONF constructor, but - rather only do the calculation when and if a version is requested - """ - if not self._cached_version: - self._cached_version = "%s%s" % (prefix, - self.version_string()) - return self._cached_version diff --git a/dashboard/glazierdashboard/settings.py b/dashboard/glazierdashboard/settings.py deleted file mode 100644 index e2bb24a2..00000000 --- a/dashboard/glazierdashboard/settings.py +++ /dev/null @@ -1,148 +0,0 @@ -import logging -import os -import sys - -from openstack_dashboard import exceptions - -ROOT_PATH = os.path.dirname(os.path.abspath(__file__)) -BIN_DIR = os.path.abspath(os.path.join(ROOT_PATH, '..', 'bin')) - -if ROOT_PATH not in sys.path: - sys.path.append(ROOT_PATH) - -DEBUG = False -TEMPLATE_DEBUG = DEBUG - -SITE_BRANDING = 'OpenStack Dashboard' - -LOGIN_URL = '/auth/login/' -LOGOUT_URL = '/auth/logout/' -# LOGIN_REDIRECT_URL can be used as an alternative for -# HORIZON_CONFIG.user_home, if user_home is not set. -# Do not set it to '/home/', as this will cause circular redirect loop -LOGIN_REDIRECT_URL = '/' - -MEDIA_ROOT = os.path.abspath(os.path.join(ROOT_PATH, '..', 'media')) -MEDIA_URL = '/media/' -STATIC_ROOT = os.path.abspath(os.path.join(ROOT_PATH, '..', 'static')) -STATIC_URL = '/static/' -ADMIN_MEDIA_PREFIX = '/static/admin/' - -ROOT_URLCONF = 'openstack_dashboard.urls' - -HORIZON_CONFIG = { - 'dashboards': ('project', 'admin', 'settings',), - 'default_dashboard': 'project', - 'user_home': 'openstack_dashboard.views.get_user_home', - 'ajax_queue_limit': 10, - 'help_url': "http://docs.openstack.org", - 'exceptions': {'recoverable': exceptions.RECOVERABLE, - 'not_found': exceptions.NOT_FOUND, - 'unauthorized': exceptions.UNAUTHORIZED}, - 'customization_module': 'tabula.overrides' -} - - -MIDDLEWARE_CLASSES = ( - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'horizon.middleware.HorizonMiddleware', - 'django.middleware.doc.XViewMiddleware', - 'django.middleware.locale.LocaleMiddleware', -) - -TEMPLATE_CONTEXT_PROCESSORS = ( - 'django.core.context_processors.debug', - 'django.core.context_processors.i18n', - 'django.core.context_processors.request', - 'django.core.context_processors.media', - 'django.core.context_processors.static', - 'django.contrib.messages.context_processors.messages', - 'horizon.context_processors.horizon', -) - -TEMPLATE_LOADERS = ( - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', - 'horizon.loaders.TemplateLoader' -) - -TEMPLATE_DIRS = ( - os.path.join(ROOT_PATH, 'templates'), -) - -STATICFILES_FINDERS = ( - 'compressor.finders.CompressorFinder', - 'django.contrib.staticfiles.finders.AppDirectoriesFinder', -) - -less_binary = os.path.join(BIN_DIR, 'less', 'lessc') -COMPRESS_PRECOMPILERS = ( - ('text/less', (less_binary + ' {infile} {outfile}')), -) - -COMPRESS_CSS_FILTERS = ( - 'compressor.filters.css_default.CssAbsoluteFilter', -) - -COMPRESS_ENABLED = True -COMPRESS_OUTPUT_DIR = 'windc' -COMPRESS_CSS_HASHING_METHOD = 'hash' -COMPRESS_PARSER = 'compressor.parser.HtmlParser' - -INSTALLED_APPS = ( - 'openstack_dashboard', - 'django.contrib.contenttypes', - 'django.contrib.auth', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'django.contrib.humanize', - 'compressor', - 'horizon', - 'openstack_dashboard.dashboards.project', - 'openstack_dashboard.dashboards.admin', - 'openstack_dashboard.dashboards.settings', - 'openstack_auth' -) - -TEST_RUNNER = 'django_nose.NoseTestSuiteRunner' -AUTHENTICATION_BACKENDS = ('openstack_auth.backend.KeystoneBackend',) -MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage' - -SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' -SESSION_COOKIE_HTTPONLY = True -SESSION_EXPIRE_AT_BROWSER_CLOSE = True -SESSION_COOKIE_SECURE = False - -gettext_noop = lambda s: s -LANGUAGES = ( - ('en', gettext_noop('English')), - ('it', gettext_noop('Italiano')), - ('es', gettext_noop('Spanish')), - ('fr', gettext_noop('French')), - ('ja', gettext_noop('Japanese')), - ('pt', gettext_noop('Portuguese')), - ('pl', gettext_noop('Polish')), - ('zh-cn', gettext_noop('Simplified Chinese')), - ('zh-tw', gettext_noop('Traditional Chinese')), -) -LANGUAGE_CODE = 'en' -USE_I18N = True -USE_L10N = True -USE_TZ = True - -OPENSTACK_KEYSTONE_DEFAULT_ROLE = 'Member' - -DEFAULT_EXCEPTION_REPORTER_FILTER = 'horizon.exceptions.HorizonReporterFilter' - -try: - from local.local_settings import * -except ImportError: - logging.warning("No local_settings file found.") - -if DEBUG: - logging.basicConfig(level=logging.DEBUG) diff --git a/dashboard/glazierdashboard/tabula/__init__.py b/dashboard/glazierdashboard/tabula/__init__.py deleted file mode 100644 index eb97e8c1..00000000 --- a/dashboard/glazierdashboard/tabula/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. \ No newline at end of file diff --git a/dashboard/glazierdashboard/tabula/api.py b/dashboard/glazierdashboard/tabula/api.py deleted file mode 100644 index 03130d0e..00000000 --- a/dashboard/glazierdashboard/tabula/api.py +++ /dev/null @@ -1,211 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging - -from glazierclient.v1.client import Client as glazier_client - -log = logging.getLogger(__name__) - - -def glazierclient(request): - url = "http://127.0.0.1:8082" - log.debug('glazierclient connection created using token "%s" and url "%s"' - % (request.user.token, url)) - return glazier_client(endpoint=url, token=request.user.token.token['id']) - - -def environment_create(request, parameters): - env = glazierclient(request).environments.create(parameters.get('name', '')) - log.debug('Environment::Create {0}'.format(env)) - return env - - -def environment_delete(request, environment_id): - result = glazierclient(request).environments.delete(environment_id) - log.debug('Environment::Delete Id:{0}'.format(environment_id)) - return result - - -def environment_get(request, environment_id): - env = glazierclient(request).environments.get(environment_id) - log.debug('Environment::Get {0}'.format(env)) - return env - - -def environments_list(request): - log.debug('Environment::List') - return glazierclient(request).environments.list() - - -def environment_deploy(request, environment_id): - sessions = glazierclient(request).sessions.list(environment_id) - for session in sessions: - if session.state == 'open': - session_id = session.id - if not session_id: - return "Sorry, nothing to deploy." - log.debug('Obtained session with Id: {0}'.format(session_id)) - result = glazierclient(request).sessions.deploy(environment_id, session_id) - log.debug('Environment with Id: {0} deployed in session ' - 'with Id: {1}'.format(environment_id, session_id)) - return result - - -def service_create(request, environment_id, parameters): - session_id = None - sessions = glazierclient(request).sessions.list(environment_id) - - for s in sessions: - if s.state == 'open': - session_id = s.id - else: - glazierclient(request).sessions.delete(environment_id, s.id) - - if session_id is None: - session_id = glazierclient(request).sessions.configure(environment_id).id - - if parameters['service_type'] == 'Active Directory': - service = glazierclient(request)\ - .activeDirectories\ - .create(environment_id, session_id, parameters) - else: - service = glazierclient(request)\ - .webServers.create(environment_id, session_id, parameters) - - log.debug('Service::Create {0}'.format(service)) - return service - - -def get_time(obj): - return obj.updated - - -def services_list(request, environment_id): - services = [] - session_id = None - sessions = glazierclient(request).sessions.list(environment_id) - for s in sessions: - session_id = s.id - - if session_id: - services = glazierclient(request).activeDirectories.\ - list(environment_id, session_id) - services += glazierclient(request).webServers.\ - list(environment_id, session_id) - - for i in range(len(services)): - reports = glazierclient(request).sessions. \ - reports(environment_id, session_id, - services[i].id) - - for report in reports: - services[i].operation = report.text - - log.debug('Service::List') - return services - - -def get_active_directories(request, environment_id): - services = [] - session_id = None - sessions = glazierclient(request).sessions.list(environment_id) - - for s in sessions: - session_id = s.id - - if session_id: - services = glazierclient(request)\ - .activeDirectories\ - .list(environment_id, session_id) - - log.debug('Service::Active Directories::List') - return services - - -def service_get(request, environment_id, service_id): - services = services_list(request, environment_id) - - for service in services: - if service.id == service_id: - log.debug('Service::Get {0}'.format(service)) - return service - - -def get_data_center_id_for_service(request, service_id): - environments = environments_list(request) - - for environment in environments: - services = services_list(request, environment.id) - for service in services: - if service.id == service_id: - return environment.id - - -def get_service_datails(request, service_id): - environments = environments_list(request) - services = [] - for environment in environments: - services += services_list(request, environment.id) - - for service in services: - if service.id == service_id: - return service - - -def get_status_message_for_service(request, service_id): - environment_id = get_data_center_id_for_service(request, service_id) - session_id = None - sessions = glazierclient(request).sessions.list(environment_id) - - for s in sessions: - session_id = s.id - - if session_id: - reports = glazierclient(request).sessions.\ - reports(environment_id, session_id, service_id) - - result = 'Initialization.... \n' - for report in reports: - result += ' ' + str(report.text) + '\n' - - return result - - -def service_delete(request, environment_id, service_id): - log.debug('Service::Remove EnvId: {0} ' - 'SrvId: {1}'.format(environment_id, service_id)) - - services = services_list(request, environment_id) - - session_id = None - sessions = glazierclient(request).sessions.list(environment_id) - for session in sessions: - if session.state == 'open': - session_id = session.id - - if session_id is None: - raise Exception("Sorry, you can not delete this service now.") - - for service in services: - if service.id is service_id: - if service.type is 'Active Directory': - glazierclient(request).activeDirectories.delete(environment_id, - session_id, - service_id) - elif service.type is 'IIS': - glazierclient(request).webServers.delete(environment_id, - session_id, - service_id) diff --git a/dashboard/glazierdashboard/tabula/forms.py b/dashboard/glazierdashboard/tabula/forms.py deleted file mode 100644 index 515488d9..00000000 --- a/dashboard/glazierdashboard/tabula/forms.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging -import string - -from django import forms -from django.utils.translation import ugettext_lazy as _ -import re -from glazierdashboard.tabula import api - -log = logging.getLogger(__name__) - - -class PasswordField(forms.CharField): - # Setup the Field - def __init__(self, label, *args, **kwargs): - super(PasswordField, self).__init__(min_length=7, required=True, - label=label, - widget=forms.PasswordInput( - render_value=False), - *args, **kwargs) - - def clean(self, value): - - # Setup Our Lists of Characters and Numbers - characters = list(string.letters) - special_characters = '!@#$%^&*()_+|\/.,~?><:{}' - numbers = [str(i) for i in range(10)] - - # Assume False until Proven Otherwise - numCheck = False - charCheck = False - specCharCheck = False - - # Loop until we Match - for char in value: - if not charCheck: - if char in characters: - charCheck = True - if not specCharCheck: - if char in special_characters: - specCharCheck = True - if not numCheck: - if char in numbers: - numCheck = True - if numCheck and charCheck and specCharCheck: - break - - if not numCheck or not charCheck or not specCharCheck: - raise forms.ValidationError(u'Your password must include at least \ - one letter, at least one number and \ - at least one special character.') - - return super(PasswordField, self).clean(value) - - -class WizardFormServiceType(forms.Form): - service = forms.ChoiceField(label=_('Service Type'), - choices=[ - ('Active Directory', 'Active Directory'), - ('IIS', 'Internet Information Services') - ]) - - -class WizardFormConfiguration(forms.Form): - 'The functions for this class will dynamically create in views.py' - pass - - -class WizardFormADConfiguration(forms.Form): - dc_name = forms.CharField(label=_('Domain Name'), - required=True) - - dc_count = forms.IntegerField(label=_('Instances Count'), - required=True, - min_value=1, - max_value=100, - initial=1) - - adm_password = PasswordField(_('Administrator password')) - - recovery_password = PasswordField(_('Recovery password')) - - def __init__(self, request, *args, **kwargs): - super(WizardFormADConfiguration, self).__init__(*args, **kwargs) - - -class WizardFormIISConfiguration(forms.Form): - iis_name = forms.CharField(label=_('IIS Server Name'), - required=True) - - adm_password = PasswordField(_('Administrator password')) - - iis_domain = forms.ChoiceField(label=_('Member of the Domain'), - required=False) - - def __init__(self, request, *args, **kwargs): - super(WizardFormIISConfiguration, self).__init__(*args, **kwargs) - - link = request.__dict__['META']['HTTP_REFERER'] - environment_id = re.search('tabula/(\S+)', link).group(0)[7:-1] - - domains = api.get_active_directories(request, environment_id) - - self.fields['iis_domain'].choices = [("", "")] + \ - [(domain.name, domain.name) - for domain in domains] diff --git a/dashboard/glazierdashboard/tabula/overrides.py b/dashboard/glazierdashboard/tabula/overrides.py deleted file mode 100644 index 849eaefc..00000000 --- a/dashboard/glazierdashboard/tabula/overrides.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import horizon - -from panel import tabula - -project = horizon.get_dashboard('project') -project.register(tabula) diff --git a/dashboard/glazierdashboard/tabula/panel.py b/dashboard/glazierdashboard/tabula/panel.py deleted file mode 100644 index 6acae86b..00000000 --- a/dashboard/glazierdashboard/tabula/panel.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import horizon -from django.utils.translation import ugettext_lazy as _ -from openstack_dashboard.dashboards.project import dashboard - - -class tabula(horizon.Panel): - name = _("Environments") - slug = 'tabula' - - -dashboard.Project.register(tabula) diff --git a/dashboard/glazierdashboard/tabula/tables.py b/dashboard/glazierdashboard/tabula/tables.py deleted file mode 100644 index 94eabd3b..00000000 --- a/dashboard/glazierdashboard/tabula/tables.py +++ /dev/null @@ -1,193 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging -import re - -from django.utils.translation import ugettext_lazy as _ -from horizon import messages -from horizon import tables -from glazierdashboard.tabula import api - - -LOG = logging.getLogger(__name__) - - -class CreateService(tables.LinkAction): - name = 'CreateService' - verbose_name = _('Create Service') - url = 'horizon:project:tabula:create' - classes = ('btn-launch', 'ajax-modal') - - def allowed(self, request, datum): - return True - - def action(self, request, service): - api.service_create(request, service) - - -class CreateEnvironment(tables.LinkAction): - name = 'CreateEnvironment' - verbose_name = _('Create Environment') - url = 'horizon:project:tabula:create_dc' - classes = ('btn-launch', 'ajax-modal') - - def allowed(self, request, datum): - return True - - def action(self, request, environment): - api.environment_create(request, environment) - - -class DeleteEnvironment(tables.BatchAction): - name = 'delete' - action_present = _('Delete') - action_past = _('Delete') - data_type_singular = _('Environment') - data_type_plural = _('Environment') - classes = ('btn-danger', 'btn-terminate') - - def allowed(self, request, datum): - return True - - def action(self, request, environment_id): - api.environment_delete(request, environment_id) - - -class DeleteService(tables.BatchAction): - name = 'delete' - action_present = _('Delete') - action_past = _('Delete') - data_type_singular = _('Service') - data_type_plural = _('Service') - classes = ('btn-danger', 'btn-terminate') - - def allowed(self, request, datum): - return True - - def action(self, request, service_id): - link = request.__dict__['META']['HTTP_REFERER'] - datacenter_id = re.search('tabula/(\S+)', link).group(0)[7:-1] - - try: - api.service_delete(request, datacenter_id, service_id) - except: - messages.error(request, _('Sorry, you can not delete this ' - 'service right now.')) - - -class DeployEnvironment(tables.BatchAction): - name = 'deploy' - action_present = _('Deploy') - action_past = _('Deploy') - data_type_singular = _('Environment') - data_type_plural = _('Environment') - classes = 'btn-launch' - - def allowed(self, request, datum): - return True - - def action(self, request, environment_id): - return api.environment_deploy(request, environment_id) - - -class ShowEnvironmentServices(tables.LinkAction): - name = 'edit' - verbose_name = _('Services') - url = 'horizon:project:tabula:services' - - def allowed(self, request, instance): - return True - - -class UpdateEnvironmentRow(tables.Row): - ajax = True - - def get_data(self, request, environment_id): - return api.environment_get(request, environment_id) - - -class UpdateServiceRow(tables.Row): - ajax = True - - def get_data(self, request, service_id): - - link = request.__dict__['META']['HTTP_REFERER'] - environment_id = re.search('tabula/(\S+)', link).group(0)[77:-1] - - service = api.service_get(request, environment_id, service_id) - - return service - - -STATUS_DISPLAY_CHOICES = ( - ('draft', 'Ready to deploy'), - ('pending', 'Wait for configuration'), - ('inprogress', 'Deploy in progress'), - ('finished', 'Active') -) - - -class EnvironmentsTable(tables.DataTable): - STATUS_CHOICES = ( - (None, True), - ('Ready to deploy', True), - ('Active', True) - ) - - name = tables.Column('name', - link=('horizon:project:tabula:services'), - verbose_name=_('Name')) - - status = tables.Column('status', verbose_name=_('Status'), - status=True, - status_choices=STATUS_CHOICES, - display_choices=STATUS_DISPLAY_CHOICES) - - class Meta: - name = 'tabula' - verbose_name = _('Environments') - row_class = UpdateEnvironmentRow - status_columns = ['status'] - table_actions = (CreateEnvironment,) - row_actions = (ShowEnvironmentServices, DeleteEnvironment, - DeployEnvironment) - - -class ServicesTable(tables.DataTable): - STATUS_CHOICES = ( - (None, True), - ('Ready to deploy', True), - ('Active', True) - ) - - name = tables.Column('name', verbose_name=_('Name'), - link=('horizon:project:tabula:service_details')) - - _type = tables.Column('service_type', verbose_name=_('Type')) - - status = tables.Column('status', verbose_name=_('Status'), - status=True, - status_choices=STATUS_CHOICES, - display_choices=STATUS_DISPLAY_CHOICES) - - operation = tables.Column('operation', verbose_name=_('Operation')) - - class Meta: - name = 'services' - verbose_name = _('Services') - row_class = UpdateServiceRow - status_columns = ['status'] - table_actions = (CreateService,) diff --git a/dashboard/glazierdashboard/tabula/tabs.py b/dashboard/glazierdashboard/tabula/tabs.py deleted file mode 100644 index a42683d0..00000000 --- a/dashboard/glazierdashboard/tabula/tabs.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from django.utils.translation import ugettext_lazy as _ - -from horizon import exceptions -from horizon import tabs -import logging - -from glazierdashboard.tabula import api - - -LOG = logging.getLogger(__name__) - - -class OverviewTab(tabs.Tab): - name = _("Service") - slug = "_service" - template_name = '_services.html' - - def get_context_data(self, request): - data = self.tab_group.kwargs['service'] - - return {"service_name": data.name, - "service_status": data.status, - "service_type": data.service_type, - "service_domain": data.domain} - - -class LogsTab(tabs.Tab): - name = _("Logs") - slug = "_logs" - template_name = '_service_logs.html' - - def get_context_data(self, request): - data = self.tab_group.kwargs['service'] - - reports = api.get_status_message_for_service(request, data.id) - - return {"reports": reports} - - -class ServicesTabs(tabs.TabGroup): - slug = "services_details" - tabs = (OverviewTab, LogsTab) - sticky = True diff --git a/dashboard/glazierdashboard/tabula/urls.py b/dashboard/glazierdashboard/tabula/urls.py deleted file mode 100644 index 7e273fb6..00000000 --- a/dashboard/glazierdashboard/tabula/urls.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from django.conf.urls.defaults import patterns, url - -from .views import IndexView, Services, CreateEnvironmentView, DetailServiceView -from .views import Wizard -from .forms import WizardFormServiceType, WizardFormConfiguration - -VIEW_MOD = 'openstack_dashboard.dashboards.project.tabula.views' - -urlpatterns = patterns(VIEW_MOD, - url(r'^$', IndexView.as_view(), name='index'), - url(r'^create$', - Wizard.as_view([WizardFormServiceType, - WizardFormConfiguration]), - name='create'), - url(r'^create_dc$', CreateEnvironmentView.as_view(), - name='create_dc'), - url(r'^(?P<environment_id>[^/]+)/$', - Services.as_view(), name='services'), - url(r'^(?P<service_id>[^/]+)/details$', - DetailServiceView.as_view(), - name='service_details')) diff --git a/dashboard/glazierdashboard/tabula/views.py b/dashboard/glazierdashboard/tabula/views.py deleted file mode 100644 index 9e4308e2..00000000 --- a/dashboard/glazierdashboard/tabula/views.py +++ /dev/null @@ -1,204 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging -import re - -from django.views import generic -from django.core.urlresolvers import reverse -from django.utils.translation import ugettext_lazy as _ -from django.contrib.formtools.wizard.views import SessionWizardView - -from horizon import exceptions -from horizon import tabs -from horizon import tables -from horizon import workflows -from horizon.forms.views import ModalFormMixin - -from glazierdashboard.tabula import api - -from .tables import EnvironmentsTable, ServicesTable -from .workflows import CreateEnvironment -from .tabs import ServicesTabs -from .forms import (WizardFormADConfiguration, WizardFormIISConfiguration) - -from horizon import messages - -from django.http import HttpResponseRedirect - -LOG = logging.getLogger(__name__) - - -class Wizard(ModalFormMixin, SessionWizardView, generic.FormView): - template_name = 'services_tabs.html' - - def done(self, form_list, **kwargs): - link = self.request.__dict__['META']['HTTP_REFERER'] - environment_id = re.search('tabula/(\S+)', link).group(0)[7:-1] - - url = "/project/tabula/%s/" % environment_id - - service_type = form_list[0].data.get('0-service', '') - parameters = {'service_type': service_type} - data = form_list[1].data - if service_type == 'Active Directory': - parameters['configuration'] = 'standalone' - parameters['name'] = str(data.get('1-dc_name', 'noname')) - parameters['domain'] = parameters['name'] # Fix Me in orchestrator - parameters['adminPassword'] = str(data.get('1-adm_password', '')) - dc_count = int(data.get('1-dc_count', 1)) - recovery_password = str(data.get('1-recovery_password', '')) - parameters['units'] = [] - parameters['units'].append({'isMaster': True, - 'recoveryPassword': recovery_password, - 'location': 'west-dc'}) - for dc in range(dc_count - 1): - parameters['units'].append({ - 'isMaster': False, - 'recoveryPassword': recovery_password, - 'location': 'west-dc' - }) - - elif service_type == 'IIS': - password = data.get('1-adm_password', '') - parameters['name'] = str(data.get('1-iis_name', 'noname')) - parameters['credentials'] = {'username': 'Administrator', - 'password': password} - parameters['domain'] = str(data.get('1-iis_domain', '')) - password = form_list[1].data.get('1-adm_password', '') - domain = form_list[1].data.get('1-iis_domain', '') - dc_user = form_list[1].data.get('1-domain_user_name', '') - dc_pass = form_list[1].data.get('1-domain_user_password', '') - parameters['name'] = str(form_list[1].data.get('1-iis_name', - 'noname')) - parameters['domain'] = parameters['name'] - parameters['credentials'] = {'username': 'Administrator', - 'password': password} - parameters['domain'] = str(domain) - parameters['location'] = 'west-dc' - - parameters['units'] = [] - parameters['units'].append({'id': '1', - 'endpoint': [{'host': '10.0.0.1'}], - 'location': 'west-dc'}) - - service = api.service_create(self.request, environment_id, parameters) - - message = "The %s service successfully created." % service_type - messages.success(self.request, message) - return HttpResponseRedirect(url) - - def get_form(self, step=None, data=None, files=None): - - form = super(Wizard, self).get_form(step, data, files) - if data: - self.service_type = data.get('0-service', '') - if self.service_type == 'Active Directory': - self.form_list['1'] = WizardFormADConfiguration - elif self.service_type == 'IIS': - self.form_list['1'] = WizardFormIISConfiguration - - return form - - def get_form_kwargs(self, step=None): - return {'request': self.request} if step == u'1' else {} - - def get_form_step_data(self, form): - LOG.debug(form.data) - return form.data - - def get_context_data(self, form, **kwargs): - context = super(Wizard, self).get_context_data(form=form, **kwargs) - if self.steps.index > 0: - context.update({'service_type': self.service_type}) - return context - - -class IndexView(tables.DataTableView): - table_class = EnvironmentsTable - template_name = 'index.html' - - def get_data(self): - try: - environments = api.environments_list(self.request) - except: - environments = [] - exceptions.handle(self.request, - _('Unable to retrieve environments list.')) - return environments - - -class Services(tables.DataTableView): - table_class = ServicesTable - template_name = 'services.html' - - def get_context_data(self, **kwargs): - context = super(Services, self).get_context_data(**kwargs) - context['environment_name'] = self.environment_name - return context - - def get_data(self): - try: - self.environment_id = self.kwargs['environment_id'] - environment = api.environment_get(self.request, self.environment_id) - self.environment_name = environment.name - services = api.services_list(self.request, self.environment_id) - except: - services = [] - exceptions.handle(self.request, - _('Unable to retrieve list of services for ' - 'environment "%s".') % self.environment_name) - self._services = services - return self._services - - -class DetailServiceView(tabs.TabView): - tab_group_class = ServicesTabs - template_name = 'service_details.html' - - def get_context_data(self, **kwargs): - context = super(DetailServiceView, self).get_context_data(**kwargs) - context["service"] = self.get_data() - context["service_name"] = self.get_data().name - return context - - def get_data(self): - if not hasattr(self, "_service"): - try: - service_id = self.kwargs['service_id'] - service = api.get_service_datails(self.request, service_id) - except: - redirect = reverse('horizon:project:tabula:index') - exceptions.handle(self.request, - _('Unable to retrieve details for ' - 'service "%s".') % service_id, - redirect=redirect) - self._service = service - return self._service - - def get_tabs(self, request, *args, **kwargs): - service = self.get_data() - return self.tab_group_class(request, service=service, **kwargs) - - -class CreateEnvironmentView(workflows.WorkflowView): - workflow_class = CreateEnvironment - template_name = 'create_dc.html' - - def get_initial(self): - initial = super(CreateEnvironmentView, self).get_initial() - initial['project_id'] = self.request.user.tenant_id - initial['user_id'] = self.request.user.id - return initial diff --git a/dashboard/glazierdashboard/tabula/workflows.py b/dashboard/glazierdashboard/tabula/workflows.py deleted file mode 100644 index 44701dd6..00000000 --- a/dashboard/glazierdashboard/tabula/workflows.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import json -import logging -import re - -from django.utils.text import normalize_newlines -from django.utils.translation import ugettext as _ - -from horizon import exceptions -from horizon import forms -from horizon import workflows - -from glazierdashboard.tabula import api - - -LOG = logging.getLogger(__name__) - - -class SelectProjectUserAction(workflows.Action): - project_id = forms.ChoiceField(label=_("Project")) - user_id = forms.ChoiceField(label=_("User")) - - def __init__(self, request, *args, **kwargs): - super(SelectProjectUserAction, self).__init__(request, *args, **kwargs) - # Set our project choices - projects = [(tenant.id, tenant.name) - for tenant in request.user.authorized_tenants] - self.fields['project_id'].choices = projects - - # Set our user options - users = [(request.user.id, request.user.username)] - self.fields['user_id'].choices = users - - class Meta: - name = _("Project & User") - # Unusable permission so this is always hidden. However, we - # keep this step in the workflow for validation/verification purposes. - permissions = ("!",) - - -class SelectProjectUser(workflows.Step): - action_class = SelectProjectUserAction - - -class ConfigureEnvironmentAction(workflows.Action): - name = forms.CharField(label=_("Environment Name"), required=True) - - class Meta: - name = _("Environment") - help_text_template = "_data_center_help.html" - - -class ConfigureEnvironment(workflows.Step): - action_class = ConfigureEnvironmentAction - contibutes = ('name',) - - def contribute(self, data, context): - if data: - context['name'] = data.get('name', '') - return context - - -class CreateEnvironment(workflows.Workflow): - slug = "create" - name = _("Create Environment") - finalize_button_name = _("Create") - success_message = _('Created environment "%s".') - failure_message = _('Unable to create environment "%s".') - success_url = "horizon:project:tabula:index" - default_steps = (SelectProjectUser, ConfigureEnvironment) - - def format_status_message(self, message): - name = self.context.get('name', 'noname') - return message % name - - def handle(self, request, context): - try: - api.environment_create(request, context) - return True - except: - exceptions.handle(request) - return False diff --git a/dashboard/glazierdashboard/templates/_data_center_help.html b/dashboard/glazierdashboard/templates/_data_center_help.html deleted file mode 100644 index 68ffe5a4..00000000 --- a/dashboard/glazierdashboard/templates/_data_center_help.html +++ /dev/null @@ -1,2 +0,0 @@ -{% load i18n %} -<p>{% blocktrans %}Data Center is an instance with different services.{% endblocktrans %}</p> \ No newline at end of file diff --git a/dashboard/glazierdashboard/templates/_dc_help.html b/dashboard/glazierdashboard/templates/_dc_help.html deleted file mode 100644 index 5e29e2d2..00000000 --- a/dashboard/glazierdashboard/templates/_dc_help.html +++ /dev/null @@ -1,3 +0,0 @@ -{% load i18n %} -<p>{% blocktrans %}You can deploy few Active Directory services with one domain name.{% endblocktrans %}</p> -<p>{% blocktrans %}The DNS service will automatically created for each Active Directory.{% endblocktrans %}</p> \ No newline at end of file diff --git a/dashboard/glazierdashboard/templates/_iis_help.html b/dashboard/glazierdashboard/templates/_iis_help.html deleted file mode 100644 index e8004c88..00000000 --- a/dashboard/glazierdashboard/templates/_iis_help.html +++ /dev/null @@ -1,2 +0,0 @@ -{% load i18n %} -<p>{% blocktrans %}You can deploy few Internet Information Services in one domain.{% endblocktrans %}</p> \ No newline at end of file diff --git a/dashboard/glazierdashboard/templates/_service_logs.html b/dashboard/glazierdashboard/templates/_service_logs.html deleted file mode 100644 index dda752a0..00000000 --- a/dashboard/glazierdashboard/templates/_service_logs.html +++ /dev/null @@ -1,7 +0,0 @@ -{% load i18n %} -<div class="clearfix"> - <h3 class="pull-left">{% trans "Service Logs" %}</h3> -</div> -<pre class="logs"> - {{ reports }} -</pre> \ No newline at end of file diff --git a/dashboard/glazierdashboard/templates/_services.html b/dashboard/glazierdashboard/templates/_services.html deleted file mode 100644 index ee5006f6..00000000 --- a/dashboard/glazierdashboard/templates/_services.html +++ /dev/null @@ -1,16 +0,0 @@ -{% load i18n %} - -<div class="status row-fluid detail"> - <h3>{% trans "Service Details" %}</h3> - <hr class="header_rule"> - <dl> - <dt>{% trans "Name" %}</dt> - <dd>{{ service_name }}</dd> - <dt>{% trans "Type" %}</dt> - <dd>{{ service_type }}</dd> - <dt>{% trans "Domain" %}</dt> - <dd>{{ service_domain }}</dd> - <dt>{% trans "Status" %}</dt> - <dd>{{ service_status }}</dd> - </dl> -</div> \ No newline at end of file diff --git a/dashboard/glazierdashboard/templates/_services_tabs.html b/dashboard/glazierdashboard/templates/_services_tabs.html deleted file mode 100644 index 3d0f583d..00000000 --- a/dashboard/glazierdashboard/templates/_services_tabs.html +++ /dev/null @@ -1,57 +0,0 @@ -{% extends "horizon/common/_modal_form.html" %} -{% load i18n horizon humanize %} - -{% block form_action %}{% url horizon:project:tabula:create %}?{{ request.POST.urlencode }}{% endblock %} - -{% block modal_id %}create_service{% endblock %} -{% block modal-header %}{% trans "Create Service" %}{% endblock %} - -{% block modal-body %} -<div class="left"> -<br> -{% if wizard.steps.next %} -<br><br><br> -{% endif %} -<table> -{{ wizard.management_form }} -{% if wizard.form.forms %} - {{ wizard.form.management_form }} - {% for form in wizard.form.forms %} - {{ form }} - {% endfor %} -{% else %} - {{ wizard.form }} -{% endif %} -{{ wizard.form.forms }} -</table> -</div> -<div class="right"> - {% if wizard.steps.prev %} - <H3>{{ service_type }} Service</H3> - {% if service_type == 'Active Directory' %} - <p>{% trans "Now you can set the parameters for Active Directory Service." %}</p> - <p>{% trans "You can create few Active Directory instances, in this case will be created one Main Active Directory server and few Secondary Active Directory servers." %}</p> - <p>{% trans "The DNS service will be automatically created on each Active Directory servers." %}</p> - {% else %} - <p>{% trans "Now you can set parameters for IIS Service." %}</p> - <p>{% trans "The IIS Service - it is the server with complex Internet Information Services infrastructure, which included to the domain infrasructure." %}</p> - <p>{% trans "Please, set the complex password for local administrator account." %}</p> - <p>{% trans "Also, you can add this IIS server to the existing domain and configure credentials for domain user." %}</p> - {% endif %} - - {% else %} - <h3>{% trans "Description" %}:</h3> - <p>{% trans "Now you can select the type of the service." %}</p> - <p>{% trans "The Active Directory Service allows to configure Domain Controllers with Active Directory and DNS infrastructure. You can create one Main Domain Controller and few Secondary Domain Controllers." %}</p> - <p>{% trans "The Internet Information Services allows to configure IIS servers, which can be included to the existing domain infrastructure." %}</p> - {% endif %} -</div> -{% endblock %} - -{% block modal-footer %} -{% if wizard.steps.prev %} -<input type="submit" class="btn btn-primary pull-right" value="{% trans 'Create' %}"/> -{% else %} -<button name="wizard_goto_step" class="btn btn-small" type="submit" value="{{ wizard.steps.next }}">{% trans "Next >" %}</button> -{% endif %} -{% endblock %} diff --git a/dashboard/glazierdashboard/templates/create.html b/dashboard/glazierdashboard/templates/create.html deleted file mode 100644 index 2dddf278..00000000 --- a/dashboard/glazierdashboard/templates/create.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends 'base.html' %} -{% load i18n %} -{% block title %}{% trans "Create Service" %}{% endblock %} - -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Create Service") %} -{% endblock page_header %} - -{% block main %} - {% include 'horizon/common/_workflow.html' %} -{% endblock %} diff --git a/dashboard/glazierdashboard/templates/create_dc.html b/dashboard/glazierdashboard/templates/create_dc.html deleted file mode 100644 index 2fc5894e..00000000 --- a/dashboard/glazierdashboard/templates/create_dc.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends 'base.html' %} -{% load i18n %} -{% block title %}{% trans "Create Windows Data Center" %}{% endblock %} - -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Create Windows Data Center") %} -{% endblock page_header %} - -{% block main %} - {% include 'horizon/common/_workflow.html' %} -{% endblock %} diff --git a/dashboard/glazierdashboard/templates/index.html b/dashboard/glazierdashboard/templates/index.html deleted file mode 100644 index 7c7147e4..00000000 --- a/dashboard/glazierdashboard/templates/index.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends 'base.html' %} -{% load i18n %} -{% block title %}{% trans "Environments" %}{% endblock %} - -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Environments") %} -{% endblock page_header %} - -{% block main %} - {{ table.render }} -{% endblock %} diff --git a/dashboard/glazierdashboard/templates/service_details.html b/dashboard/glazierdashboard/templates/service_details.html deleted file mode 100644 index bde27525..00000000 --- a/dashboard/glazierdashboard/templates/service_details.html +++ /dev/null @@ -1,15 +0,0 @@ -{% extends 'base.html' %} -{% load i18n sizeformat %} -{% block title %}{% trans "Service Detail" %}{% endblock %} - -{% block page_header %} - {% include "horizon/common/_page_header.html" with title="Service Detail: "|add:service.name %} -{% endblock page_header %} - -{% block main %} -<div class="row-fluid"> - <div class="span12"> - {{ tab_group.render }} - </div> -</div> -{% endblock %} diff --git a/dashboard/glazierdashboard/templates/services.html b/dashboard/glazierdashboard/templates/services.html deleted file mode 100644 index b009f6fa..00000000 --- a/dashboard/glazierdashboard/templates/services.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends 'base.html' %} -{% load i18n %} -{% block title %}{% trans "Environment Services" %}{% endblock %} - -{% block page_header %} - {% include "horizon/common/_page_header.html" with title="Environment "|add:environment_name %} -{% endblock page_header %} - -{% block main %} - {{ table.render }} -{% endblock %} diff --git a/dashboard/glazierdashboard/templates/update.html b/dashboard/glazierdashboard/templates/update.html deleted file mode 100644 index aba3dc9a..00000000 --- a/dashboard/glazierdashboard/templates/update.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends 'base.html' %} -{% load i18n %} -{% block title %}{% trans "Update Instance" %}{% endblock %} - -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Update Instance") %} -{% endblock page_header %} - -{% block main %} - {% include 'project/instances/_update.html' %} -{% endblock %} diff --git a/dashboard/glazierdashboard/test/__init__.py b/dashboard/glazierdashboard/test/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/dashboard/glazierdashboard/test/settings.py b/dashboard/glazierdashboard/test/settings.py deleted file mode 100644 index 501bc7f1..00000000 --- a/dashboard/glazierdashboard/test/settings.py +++ /dev/null @@ -1,70 +0,0 @@ -import socket - -from dashboard.settings import * - -socket.setdefaulttimeout(1) - -DEBUG = False -TEMPLATE_DEBUG = DEBUG - -SECRET_KEY = 'HELLA_SECRET!' - -DATABASES = {'default': {'ENGINE': 'django.db.backends.sqlite3'}} - -TESTSERVER = 'http://testserver' - -INSTALLED_APPS += ('django_nose',) - -MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage' - -TEST_RUNNER = 'django_nose.NoseTestSuiteRunner' -NOSE_ARGS = ['--nocapture', - '--nologcapture', - '--cover-package=windc'] - -EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend' -SESSION_ENGINE = 'django.contrib.sessions.backends.cache' - -OPENSTACK_ADDRESS = "localhost" -OPENSTACK_ADMIN_TOKEN = "openstack" -OPENSTACK_KEYSTONE_URL = "http://%s:5000/v2.0" % OPENSTACK_ADDRESS -OPENSTACK_KEYSTONE_ADMIN_URL = "http://%s:35357/v2.0" % OPENSTACK_ADDRESS -OPENSTACK_KEYSTONE_DEFAULT_ROLE = "Member" - -# Silence logging output during tests. -LOGGING = { - 'version': 1, - 'disable_existing_loggers': False, - 'handlers': { - 'null': { - 'level': 'DEBUG', - 'class': 'django.utils.log.NullHandler', - }, - }, - 'loggers': { - 'django.db.backends': { - 'handlers': ['null'], - 'propagate': False, - }, - 'horizon': { - 'handlers': ['null'], - 'propagate': False, - }, - 'novaclient': { - 'handlers': ['null'], - 'propagate': False, - }, - 'keystoneclient': { - 'handlers': ['null'], - 'propagate': False, - }, - 'quantum': { - 'handlers': ['null'], - 'propagate': False, - }, - 'nose.plugins.manager': { - 'handlers': ['null'], - 'propagate': False, - } - } -} diff --git a/dashboard/glazierdashboard/version.py b/dashboard/glazierdashboard/version.py deleted file mode 100644 index ac241ae7..00000000 --- a/dashboard/glazierdashboard/version.py +++ /dev/null @@ -1,20 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2012 OpenStack Foundation -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - - -from tabula.openstack.common import version as common_version - -version_info = common_version.VersionInfo('tabula') diff --git a/dashboard/manage.py b/dashboard/manage.py deleted file mode 100755 index 00c53032..00000000 --- a/dashboard/manage.py +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env python - -import sys -import os - - -if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "glazierdashboard.settings") - from django.core.management import execute_from_command_line - execute_from_command_line(sys.argv) diff --git a/dashboard/openstack-common.conf b/dashboard/openstack-common.conf deleted file mode 100644 index 507dc200..00000000 --- a/dashboard/openstack-common.conf +++ /dev/null @@ -1,7 +0,0 @@ -[DEFAULT] - -# The list of modules to copy from openstack-common -modules=setup,importutils,version - -# The base module to hold the copy of openstack.common -base=tabula diff --git a/dashboard/packages/python-portasclient-2013.1.a345.ga70b44e.tar.gz b/dashboard/packages/python-portasclient-2013.1.a345.ga70b44e.tar.gz deleted file mode 100644 index 92ff304b769240fed0ec9486d048fa4941061139..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23774 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 Z)}Qrf{aJt3pa0P3{{cPSxO)Hy0sz@5zR3Up diff --git a/dashboard/run_tests.sh b/dashboard/run_tests.sh deleted file mode 100755 index 6dac0d35..00000000 --- a/dashboard/run_tests.sh +++ /dev/null @@ -1,442 +0,0 @@ -#!/bin/bash - -set -o errexit - -# ---------------UPDATE ME-------------------------------# -# Increment me any time the environment should be rebuilt. -# This includes dependncy changes, directory renames, etc. -# Simple integer secuence: 1, 2, 3... -environment_version=31 -#--------------------------------------------------------# - -function usage { - echo "Usage: $0 [OPTION]..." - echo "Run Horizon's test suite(s)" - echo "" - echo " -V, --virtual-env Always use virtualenv. Install automatically" - echo " if not present" - echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local" - echo " environment" - echo " -c, --coverage Generate reports using Coverage" - echo " -f, --force Force a clean re-build of the virtual" - echo " environment. Useful when dependencies have" - echo " been added." - echo " -m, --manage Run a Django management command." - echo " --makemessages Update all translation files." - echo " --compilemessages Compile all translation files." - echo " -p, --pep8 Just run pep8" - echo " -t, --tabs Check for tab characters in files." - echo " -y, --pylint Just run pylint" - echo " -q, --quiet Run non-interactively. (Relatively) quiet." - echo " Implies -V if -N is not set." - echo " --only-selenium Run only the Selenium unit tests" - echo " --with-selenium Run unit tests including Selenium tests" - echo " --runserver Run the Django development server for" - echo " openstack_dashboard in the virtual" - echo " environment." - echo " --docs Just build the documentation" - echo " --backup-environment Make a backup of the environment on exit" - echo " --restore-environment Restore the environment before running" - echo " --destroy-environment DEstroy the environment and exit" - echo " -h, --help Print this usage message" - echo "" - echo "Note: with no options specified, the script will try to run the tests in" - echo " a virtual environment, If no virtualenv is found, the script will ask" - echo " if you would like to create one. If you prefer to run tests NOT in a" - echo " virtual environment, simply pass the -N option." - exit -} - -# DEFAULTS FOR RUN_TESTS.SH -# -root=`pwd` -venv=$root/.venv -with_venv=tools/with_venv.sh -included_dirs="openstack_dashboard horizon" - -always_venv=0 -backup_env=0 -command_wrapper="" -destroy=0 -force=0 -just_pep8=0 -just_pylint=0 -just_docs=0 -just_tabs=0 -never_venv=0 -quiet=0 -restore_env=0 -runserver=0 -only_selenium=0 -with_selenium=0 -testopts="" -testargs="" -with_coverage=0 -makemessages=0 -compilemessages=0 -manage=0 - -# Jenkins sets a "JOB_NAME" variable, if it's not set, we'll make it "default" -[ "$JOB_NAME" ] || JOB_NAME="default" - -function process_option { - case "$1" in - -h|--help) usage;; - -V|--virtual-env) always_venv=1; never_venv=0;; - -N|--no-virtual-env) always_venv=0; never_venv=1;; - -p|--pep8) just_pep8=1;; - -y|--pylint) just_pylint=1;; - -f|--force) force=1;; - -t|--tabs) just_tabs=1;; - -q|--quiet) quiet=1;; - -c|--coverage) with_coverage=1;; - -m|--manage) manage=1;; - --makemessages) makemessages=1;; - --compilemessages) compilemessages=1;; - --only-selenium) only_selenium=1;; - --with-selenium) with_selenium=1;; - --docs) just_docs=1;; - --runserver) runserver=1;; - --backup-environment) backup_env=1;; - --restore-environment) restore_env=1;; - --destroy-environment) destroy=1;; - -*) testopts="$testopts $1";; - *) testargs="$testargs $1" - esac -} - -function run_management_command { - ${command_wrapper} python $root/manage.py $testopts $testargs -} - -function run_server { - echo "Starting Django development server..." - ${command_wrapper} python $root/manage.py runserver $testopts $testargs - echo "Server stopped." -} - -function run_pylint { - echo "Running pylint ..." - PYTHONPATH=$root ${command_wrapper} pylint --rcfile=.pylintrc -f parseable $included_dirs > pylint.txt || true - CODE=$? - grep Global -A2 pylint.txt - if [ $CODE -lt 32 ]; then - echo "Completed successfully." - exit 0 - else - echo "Completed with problems." - exit $CODE - fi -} - -function run_pep8 { - echo "Running pep8 ..." - ${command_wrapper} pep8 $included_dirs -} - -function run_sphinx { - echo "Building sphinx..." - export DJANGO_SETTINGS_MODULE=openstack_dashboard.settings - ${command_wrapper} sphinx-build -b html doc/source doc/build/html - echo "Build complete." -} - -function tab_check { - TAB_VIOLATIONS=`find $included_dirs -type f -regex ".*\.\(css\|js\|py\|html\)" -print0 | xargs -0 awk '/\t/' | wc -l` - if [ $TAB_VIOLATIONS -gt 0 ]; then - echo "TABS! $TAB_VIOLATIONS of them! Oh no!" - HORIZON_FILES=`find $included_dirs -type f -regex ".*\.\(css\|js\|py|\html\)"` - for TABBED_FILE in $HORIZON_FILES - do - TAB_COUNT=`awk '/\t/' $TABBED_FILE | wc -l` - if [ $TAB_COUNT -gt 0 ]; then - echo "$TABBED_FILE: $TAB_COUNT" - fi - done - fi - return $TAB_VIOLATIONS; -} - -function destroy_venv { - echo "Cleaning environment..." - echo "Removing virtualenv..." - rm -rf $venv - echo "Virtualenv removed." - rm -f .environment_version - echo "Environment cleaned." -} - -function environment_check { - echo "Checking environment." - if [ -f .environment_version ]; then - ENV_VERS=`cat .environment_version` - if [ $ENV_VERS -eq $environment_version ]; then - if [ -e ${venv} ]; then - # If the environment exists and is up-to-date then set our variables - command_wrapper="${root}/${with_venv}" - echo "Environment is up to date." - return 0 - fi - fi - fi - - if [ $always_venv -eq 1 ]; then - install_venv - else - if [ ! -e ${venv} ]; then - echo -e "Environment not found. Install? (Y/n) \c" - else - echo -e "Your environment appears to be out of date. Update? (Y/n) \c" - fi - read update_env - if [ "x$update_env" = "xY" -o "x$update_env" = "x" -o "x$update_env" = "xy" ]; then - install_venv - else - # Set our command wrapper anyway. - command_wrapper="${root}/${with_venv}" - fi - fi -} - -function sanity_check { - # Anything that should be determined prior to running the tests, server, etc. - # Don't sanity-check anything environment-related in -N flag is set - if [ $never_venv -eq 0 ]; then - if [ ! -e ${venv} ]; then - echo "Virtualenv not found at $venv. Did install_venv.py succeed?" - exit 1 - fi - fi - # Remove .pyc files. This is sanity checking because they can linger - # after old files are deleted. - find . -name "*.pyc" -exec rm -rf {} \; -} - -function backup_environment { - if [ $backup_env -eq 1 ]; then - echo "Backing up environment \"$JOB_NAME\"..." - if [ ! -e ${venv} ]; then - echo "Environment not installed. Cannot back up." - return 0 - fi - if [ -d /tmp/.horizon_environment/$JOB_NAME ]; then - mv /tmp/.horizon_environment/$JOB_NAME /tmp/.horizon_environment/$JOB_NAME.old - rm -rf /tmp/.horizon_environment/$JOB_NAME - fi - mkdir -p /tmp/.horizon_environment/$JOB_NAME - cp -r $venv /tmp/.horizon_environment/$JOB_NAME/ - cp .environment_version /tmp/.horizon_environment/$JOB_NAME/ - # Remove the backup now that we've completed successfully - rm -rf /tmp/.horizon_environment/$JOB_NAME.old - echo "Backup completed" - fi -} - -function restore_environment { - if [ $restore_env -eq 1 ]; then - echo "Restoring environment from backup..." - if [ ! -d /tmp/.horizon_environment/$JOB_NAME ]; then - echo "No backup to restore from." - return 0 - fi - - cp -r /tmp/.horizon_environment/$JOB_NAME/.venv ./ || true - cp -r /tmp/.horizon_environment/$JOB_NAME/.environment_version ./ || true - - echo "Environment restored successfully." - fi -} - -function install_venv { - # Install with install_venv.py - export PIP_DOWNLOAD_CACHE=${PIP_DOWNLOAD_CACHE-/tmp/.pip_download_cache} - export PIP_USE_MIRRORS=true - if [ $quiet -eq 1 ]; then - export PIP_NO_INPUT=true - fi - echo "Fetching new src packages..." - rm -rf $venv/src - python tools/install_venv.py - command_wrapper="$root/${with_venv}" - # Make sure it worked and record the environment version - sanity_check - chmod -R 754 $venv - echo $environment_version > .environment_version -} - -function run_tests { - sanity_check - - if [ $with_selenium -eq 1 ]; then - export WITH_SELENIUM=1 - elif [ $only_selenium -eq 1 ]; then - export WITH_SELENIUM=1 - export SKIP_UNITTESTS=1 - fi - - if [ -z "$testargs" ]; then - run_tests_all - else - run_tests_subset - fi -} - -function run_tests_subset { - project=`echo $testargs | awk -F. '{print $1}'` - ${command_wrapper} python $root/manage.py test --settings=$project.test.settings $testopts $testargs -} - -function run_tests_all { - echo "Running Horizon application tests" - export NOSE_XUNIT_FILE=horizon/nosetests.xml - if [ "$NOSE_WITH_HTML_OUTPUT" = '1' ]; then - export NOSE_HTML_OUT_FILE='horizon_nose_results.html' - fi - ${command_wrapper} coverage erase - ${command_wrapper} coverage run -p $root/manage.py test horizon --settings=horizon.test.settings $testopts - # get results of the Horizon tests - HORIZON_RESULT=$? - - echo "Running openstack_dashboard tests" - export NOSE_XUNIT_FILE=openstack_dashboard/nosetests.xml - if [ "$NOSE_WITH_HTML_OUTPUT" = '1' ]; then - export NOSE_HTML_OUT_FILE='dashboard_nose_results.html' - fi - ${command_wrapper} coverage run -p $root/manage.py test openstack_dashboard --settings=openstack_dashboard.test.settings $testopts - # get results of the openstack_dashboard tests - DASHBOARD_RESULT=$? - - if [ $with_coverage -eq 1 ]; then - echo "Generating coverage reports" - ${command_wrapper} coverage combine - ${command_wrapper} coverage xml -i --omit='/usr*,setup.py,*egg*,.venv/*' - ${command_wrapper} coverage html -i --omit='/usr*,setup.py,*egg*,.venv/*' -d reports - fi - # Remove the leftover coverage files from the -p flag earlier. - rm -f .coverage.* - - if [ $(($HORIZON_RESULT || $DASHBOARD_RESULT)) -eq 0 ]; then - echo "Tests completed successfully." - else - echo "Tests failed." - fi - exit $(($HORIZON_RESULT || $DASHBOARD_RESULT)) -} - -function run_makemessages { - cd horizon - ${command_wrapper} $root/manage.py makemessages --all --no-obsolete - HORIZON_PY_RESULT=$? - ${command_wrapper} $root/manage.py makemessages -d djangojs --all --no-obsolete - HORIZON_JS_RESULT=$? - cd ../openstack_dashboard - ${command_wrapper} $root/manage.py makemessages --all --no-obsolete - DASHBOARD_RESULT=$? - cd .. - exit $(($HORIZON_PY_RESULT || $HORIZON_JS_RESULT || $DASHBOARD_RESULT)) -} - -function run_compilemessages { - cd horizon - ${command_wrapper} $root/manage.py compilemessages - HORIZON_PY_RESULT=$? - cd ../openstack_dashboard - ${command_wrapper} $root/manage.py compilemessages - DASHBOARD_RESULT=$? - cd .. - exit $(($HORIZON_PY_RESULT || $DASHBOARD_RESULT)) -} - - -# ---------PREPARE THE ENVIRONMENT------------ # - -# PROCESS ARGUMENTS, OVERRIDE DEFAULTS -for arg in "$@"; do - process_option $arg -done - -if [ $quiet -eq 1 ] && [ $never_venv -eq 0 ] && [ $always_venv -eq 0 ] -then - always_venv=1 -fi - -# If destroy is set, just blow it away and exit. -if [ $destroy -eq 1 ]; then - destroy_venv - exit 0 -fi - -# Ignore all of this if the -N flag was set -if [ $never_venv -eq 0 ]; then - - # Restore previous environment if desired - if [ $restore_env -eq 1 ]; then - restore_environment - fi - - # Remove the virtual environment if --force used - if [ $force -eq 1 ]; then - destroy_venv - fi - - # Then check if it's up-to-date - environment_check - - # Create a backup of the up-to-date environment if desired - if [ $backup_env -eq 1 ]; then - backup_environment - fi -fi - -# ---------EXERCISE THE CODE------------ # - -# Run management commands -if [ $manage -eq 1 ]; then - run_management_command - exit $? -fi - -# Build the docs -if [ $just_docs -eq 1 ]; then - run_sphinx - exit $? -fi - -# Update translation files -if [ $makemessages -eq 1 ]; then - run_makemessages - exit $? -fi - -# Compile translation files -if [ $compilemessages -eq 1 ]; then - run_compilemessages - exit $? -fi - -# PEP8 -if [ $just_pep8 -eq 1 ]; then - run_pep8 - exit $? -fi - -# Pylint -if [ $just_pylint -eq 1 ]; then - run_pylint - exit $? -fi - -# Tab checker -if [ $just_tabs -eq 1 ]; then - tab_check - exit $? -fi - -# Django development server -if [ $runserver -eq 1 ]; then - run_server - exit $? -fi - -# Full test suite -run_tests || exit diff --git a/dashboard/setup.cfg b/dashboard/setup.cfg deleted file mode 100644 index 79034b3c..00000000 --- a/dashboard/setup.cfg +++ /dev/null @@ -1,9 +0,0 @@ -[build_sphinx] -all_files = 1 -build-dir = doc/build -source-dir = doc/source - -[nosetests] -verbosity=2 -detailed-errors=1 - diff --git a/dashboard/setup.py b/dashboard/setup.py deleted file mode 100755 index 4f73e961..00000000 --- a/dashboard/setup.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2012 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Copyright 2012 Nebula, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import setuptools - -from glazierdashboard.openstack.common import setup - -requires = setup.parse_requirements() -depend_links = setup.parse_dependency_links() -project = 'tabula' - -setuptools.setup( - name=project, - version=setup.get_version(project, '2013.1'), - description="The OpenStack Dashboard.", - license='Apache 2.0', - author='OpenStack', - author_email='horizon@lists.launchpad.net', - url='https://github.com/openstack/horizon/', - packages=setuptools.find_packages(exclude=['bin']), - cmdclass=setup.get_cmdclass(), - include_package_data=True, - install_requires=requires, - dependency_links=depend_links, - classifiers=['Development Status :: 5 - Production/Stable', - 'Framework :: Django', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Topic :: Internet :: WWW/HTTP', - 'Environment :: OpenStack'] -) diff --git a/dashboard/tools/install_venv.py b/dashboard/tools/install_venv.py deleted file mode 100644 index 2aa61068..00000000 --- a/dashboard/tools/install_venv.py +++ /dev/null @@ -1,69 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Copyright 2010 OpenStack LLC. -# Copyright 2013 IBM Corp. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import os -import subprocess -import sys - -import install_venv_common as install_venv - - -def print_help(): - help = """ - Tabula development environment setup is complete. - - Tabula development uses virtualenv to track and manage Python dependencies - while in development and testing. - - To activate the Tabula virtualenv for the extent of your current shell session - you can run: - - $ source .venv/bin/activate - - Or, if you prefer, you can run commands in the virtualenv on a case by case - basis by running: - - $ tools/with_venv.sh <your command> - - Also, make test will automatically use the virtualenv. - """ - print help - - -def main(argv): - root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - venv = os.path.join(root, '.venv') - pip_requires = os.path.join(root, 'tools', 'pip-requires') - test_requires = os.path.join(root, 'tools', 'test-requires') - py_version = "python%s.%s" % (sys.version_info[0], sys.version_info[1]) - project = 'tabula' - install = install_venv.InstallVenv(root, venv, pip_requires, test_requires, - py_version, project) - options = install.parse_args(argv) - install.check_python_version() - install.check_dependencies() - install.create_virtualenv(no_site_packages=options.no_site_packages) - install.install_dependencies() - install.post_process() - print_help() - -if __name__ == '__main__': - main(sys.argv) diff --git a/dashboard/tools/install_venv_common.py b/dashboard/tools/install_venv_common.py deleted file mode 100644 index 1ea3b2df..00000000 --- a/dashboard/tools/install_venv_common.py +++ /dev/null @@ -1,219 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 OpenStack, LLC -# Copyright 2013 IBM Corp. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Provides methods needed by installation script for OpenStack development -virtual environments. - -Synced in from openstack-common -""" - -import argparse -import os -import subprocess -import sys - - -class InstallVenv(object): - - def __init__(self, root, venv, pip_requires, test_requires, py_version, - project): - self.root = root - self.venv = venv - self.pip_requires = pip_requires - self.test_requires = test_requires - self.py_version = py_version - self.project = project - - def die(self, message, *args): - print >> sys.stderr, message % args - sys.exit(1) - - def check_python_version(self): - if sys.version_info < (2, 6): - self.die("Need Python Version >= 2.6") - - def run_command_with_code(self, cmd, redirect_output=True, - check_exit_code=True): - """Runs a command in an out-of-process shell. - - Returns the output of that command. Working directory is self.root. - """ - if redirect_output: - stdout = subprocess.PIPE - else: - stdout = None - - proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout) - output = proc.communicate()[0] - if check_exit_code and proc.returncode != 0: - self.die('Command "%s" failed.\n%s', ' '.join(cmd), output) - return (output, proc.returncode) - - def run_command(self, cmd, redirect_output=True, check_exit_code=True): - return self.run_command_with_code(cmd, redirect_output, - check_exit_code)[0] - - def get_distro(self): - if (os.path.exists('/etc/fedora-release') or - os.path.exists('/etc/redhat-release')): - return Fedora(self.root, self.venv, self.pip_requires, - self.test_requires, self.py_version, self.project) - else: - return Distro(self.root, self.venv, self.pip_requires, - self.test_requires, self.py_version, self.project) - - def check_dependencies(self): - self.get_distro().install_virtualenv() - - def create_virtualenv(self, no_site_packages=True): - """Creates the virtual environment and installs PIP. - - Creates the virtual environment and installs PIP only into the - virtual environment. - """ - if not os.path.isdir(self.venv): - print 'Creating venv...', - if no_site_packages: - self.run_command(['virtualenv', '-q', '--no-site-packages', - self.venv]) - else: - self.run_command(['virtualenv', '-q', self.venv]) - print 'done.' - print 'Installing pip in venv...', - if not self.run_command(['tools/with_venv.sh', 'easy_install', - 'pip>1.0']).strip(): - self.die("Failed to install pip.") - print 'done.' - else: - print "venv already exists..." - pass - - def pip_install(self, *args): - self.run_command(['tools/with_venv.sh', - 'pip', 'install', '--upgrade'] + list(args), - redirect_output=False) - - def install_dependencies(self): - print 'Installing dependencies with pip (this can take a while)...' - - # First things first, make sure our venv has the latest pip and - # distribute. - # NOTE: we keep pip at version 1.1 since the most recent version causes - # the .venv creation to fail. See: - # https://bugs.launchpad.net/nova/+bug/1047120 - self.pip_install('pip==1.1') - self.pip_install('distribute') - - # Install greenlet by hand - just listing it in the requires file does - # not - # get it installed in the right order - self.pip_install('greenlet') - - self.pip_install('-r', self.test_requires) - self.pip_install('-r', self.pip_requires) - - def post_process(self): - self.get_distro().post_process() - - def parse_args(self, argv): - """Parses command-line arguments.""" - parser = argparse.ArgumentParser() - parser.add_argument('-n', '--no-site-packages', - action='store_true', - help="Do not inherit packages from global Python " - "install") - return parser.parse_args(argv[1:]) - - -class Distro(InstallVenv): - - def check_cmd(self, cmd): - return bool(self.run_command(['which', cmd], - check_exit_code=False).strip()) - - def install_virtualenv(self): - if self.check_cmd('virtualenv'): - return - - if self.check_cmd('easy_install'): - print 'Installing virtualenv via easy_install...', - if self.run_command(['easy_install', 'virtualenv']): - print 'Succeeded' - return - else: - print 'Failed' - - self.die('ERROR: virtualenv not found.\n\n%s development' - ' requires virtualenv, please install it using your' - ' favorite package management tool' % self.project) - - def post_process(self): - """Any distribution-specific post-processing gets done here. - - In particular, this is useful for applying patches to code inside - the venv. - """ - pass - - -class Fedora(Distro): - """This covers all Fedora-based distributions. - - Includes: Fedora, RHEL, CentOS, Scientific Linux - """ - - def check_pkg(self, pkg): - return self.run_command_with_code(['rpm', '-q', pkg], - check_exit_code=False)[1] == 0 - - def yum_install(self, pkg, **kwargs): - print "Attempting to install '%s' via yum" % pkg - self.run_command(['sudo', 'yum', 'install', '-y', pkg], **kwargs) - - def apply_patch(self, originalfile, patchfile): - self.run_command(['patch', originalfile, patchfile]) - - def install_virtualenv(self): - if self.check_cmd('virtualenv'): - return - - if not self.check_pkg('python-virtualenv'): - self.yum_install('python-virtualenv', check_exit_code=False) - - super(Fedora, self).install_virtualenv() - - def post_process(self): - """Workaround for a bug in eventlet. - - This currently affects RHEL6.1, but the fix can safely be - applied to all RHEL and Fedora distributions. - - This can be removed when the fix is applied upstream. - - Nova: https://bugs.launchpad.net/nova/+bug/884915 - Upstream: https://bitbucket.org/which_linden/eventlet/issue/89 - """ - - # Install "patch" program if it's not there - if not self.check_pkg('patch'): - self.yum_install('patch') - - # Apply the eventlet patch - self.apply_patch(os.path.join(self.venv, 'lib', self.py_version, - 'site-packages', - 'eventlet/green/subprocess.py'), - 'contrib/redhat-eventlet.patch') diff --git a/dashboard/tools/pip-requires b/dashboard/tools/pip-requires deleted file mode 100644 index bba8dd72..00000000 --- a/dashboard/tools/pip-requires +++ /dev/null @@ -1,6 +0,0 @@ -# Core Requirements -Django>=1.4,<1.5 -anyjson -#Fix for bug https://bugs.launchpad.net/python-keystoneclient/+bug/1116740 -backports.ssl_match_hostname -requests==0.14.2 \ No newline at end of file diff --git a/dashboard/tools/rfc.sh b/dashboard/tools/rfc.sh deleted file mode 100755 index 15781e52..00000000 --- a/dashboard/tools/rfc.sh +++ /dev/null @@ -1,145 +0,0 @@ -#!/bin/sh -e -# Copyright (c) 2010-2011 Gluster, Inc. <http://www.gluster.com> -# This initial version of this file was taken from the source tree -# of GlusterFS. It was not directly attributed, but is assumed to be -# Copyright (c) 2010-2011 Gluster, Inc and release GPLv3 -# Subsequent modifications are Copyright (c) 2012 OpenStack, LLC. -# -# GlusterFS is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published -# by the Free Software Foundation; either version 3 of the License, -# or (at your option) any later version. -# -# GlusterFS is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see -# <http://www.gnu.org/licenses/>. - - -branch="master"; - -set_hooks_commit_msg() -{ - top_dir=`git rev-parse --show-toplevel` - f="${top_dir}/.git/hooks/commit-msg"; - u="https://review.openstack.org/tools/hooks/commit-msg"; - - if [ -x "$f" ]; then - return; - fi - - curl -o $f $u || wget -O $f $u; - - chmod +x $f; - - GIT_EDITOR=true git commit --amend -} - -add_remote() -{ - username=$1 - project=$2 - - echo "No remote set, testing ssh://$username@review.openstack.org:29418" - if project_list=`ssh -p29418 -o StrictHostKeyChecking=no $username@review.openstack.org gerrit ls-projects 2>/dev/null` - then - echo "$username@review.openstack.org:29418 worked." - if echo $project_list | grep $project >/dev/null - then - echo "Creating a git remote called gerrit that maps to:" - echo " ssh://$username@review.openstack.org:29418/$project" - git remote add gerrit ssh://$username@review.openstack.org:29418/$project - else - echo "The current project name, $project, is not a known project." - echo "Please either reclone from github/gerrit or create a" - echo "remote named gerrit that points to the intended project." - return 1 - fi - - return 0 - fi - return 1 -} - -check_remote() -{ - if ! git remote | grep gerrit >/dev/null 2>&1 - then - origin_project=`git remote show origin | grep 'Fetch URL' | perl -nle '@fields = split(m|[:/]|); $len = $#fields; print $fields[$len-1], "/", $fields[$len];'` - if add_remote $USERNAME $origin_project - then - return 0 - else - echo "Your local name doesn't work on Gerrit." - echo -n "Enter Gerrit username (same as launchpad): " - read gerrit_user - if add_remote $gerrit_user $origin_project - then - return 0 - else - echo "Can't infer where gerrit is - please set a remote named" - echo "gerrit manually and then try again." - echo - echo "For more information, please see:" - echo "\thttp://wiki.openstack.org/GerritWorkflow" - exit 1 - fi - fi - fi -} - -rebase_changes() -{ - git fetch; - - GIT_EDITOR=true git rebase -i origin/$branch || exit $?; -} - - -assert_diverge() -{ - if ! git diff origin/$branch..HEAD | grep -q . - then - echo "No changes between the current branch and origin/$branch." - exit 1 - fi -} - - -main() -{ - set_hooks_commit_msg; - - check_remote; - - rebase_changes; - - assert_diverge; - - bug=$(git show --format='%s %b' | perl -nle 'if (/\b([Bb]ug|[Ll][Pp])\s*[#:]?\s*(\d+)/) {print "$2"; exit}') - - bp=$(git show --format='%s %b' | perl -nle 'if (/\b([Bb]lue[Pp]rint|[Bb][Pp])\s*[#:]?\s*([0-9a-zA-Z-_]+)/) {print "$2"; exit}') - - if [ "$DRY_RUN" = 1 ]; then - drier='echo -e Please use the following command to send your commits to review:\n\n' - else - drier= - fi - - local_branch=`git branch | grep -Ei "\* (.*)" | cut -f2 -d' '` - if [ -z "$bug" ]; then - if [ -z "$bp" ]; then - $drier git push gerrit HEAD:refs/for/$branch/$local_branch; - else - $drier git push gerrit HEAD:refs/for/$branch/bp/$bp; - fi - else - $drier git push gerrit HEAD:refs/for/$branch/bug/$bug; - fi -} - -main "$@" diff --git a/dashboard/tools/test-requires b/dashboard/tools/test-requires deleted file mode 100644 index c1da7cae..00000000 --- a/dashboard/tools/test-requires +++ /dev/null @@ -1,18 +0,0 @@ -distribute>=0.6.24 - -# Testing Requirements -coverage -django-nose -mox -nose -nose-exclude -nosexcover -openstack.nose_plugin -nosehtmloutput -pep8>=1.3 -pylint -selenium - -# Docs Requirements -sphinx -docutils==0.9.1 # for bug 1091333, remove after sphinx >1.1.3 is released. diff --git a/dashboard/tools/with_venv.sh b/dashboard/tools/with_venv.sh deleted file mode 100755 index c8d2940f..00000000 --- a/dashboard/tools/with_venv.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -TOOLS=`dirname $0` -VENV=$TOOLS/../.venv -source $VENV/bin/activate && $@ diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100644 index c4c2ae4b..00000000 --- a/docs/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -#IntelJ Idea -.idea/ - -#Build results -target - -#Translation build -*.mo -*.pot diff --git a/docs/LICENSE b/docs/LICENSE deleted file mode 100644 index 68c771a0..00000000 --- a/docs/LICENSE +++ /dev/null @@ -1,176 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - diff --git a/docs/README.rst b/docs/README.rst deleted file mode 100644 index 3725a9fd..00000000 --- a/docs/README.rst +++ /dev/null @@ -1,47 +0,0 @@ -Glazier Manuals -+++++++++++++++++ - -This repository contains documentation for the -Glazier project. It includes: - - * API Specification - * Architecture - -For more details, see the `Glazier <http://glazier.mirantis.com>`_. - -Prerequisites -============= -`Apache Maven <http://maven.apache.org/>`_ must be installed to build the -documentation. - -To install Maven 3 for Ubuntu 12.04 and later,and Debian wheezy and later:: - - apt-get install maven - -On Fedora 15 and later:: - - yum install maven3 - -Building -======== -The different manuals are in subdirectories of the -``docs/src/`` directory. - -To build a specific guide, look for a ``pom.xml`` file within a subdirectory, -then run the ``mvn`` command in that directory. For example:: - - cd docs/src/glazier-manual - mvn clean generate-sources - -The generated PDF documentation file is:: - - docs/src/glazier-manual/src/target/docbkx/pdf/glazier-manual.pdf - -The root of the generated HTML documentation is:: - - docs/src/glazier-manual/src/target/docbkx/webhelp/glazier-manual/content/index.html - -Installing -========== -Refer to http://glazier.openstack.org to see where these documents are published -and to learn more about the Glazier project. diff --git a/docs/src/glazier-manual/pom.xml b/docs/src/glazier-manual/pom.xml deleted file mode 100644 index 5bc6ada6..00000000 --- a/docs/src/glazier-manual/pom.xml +++ /dev/null @@ -1,54 +0,0 @@ -<!-- - Copyright (c) 2013 Mirantis, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - <modelVersion>4.0.0</modelVersion> - <groupId>com.mirantis.glazier</groupId> - <artifactId>manuals</artifactId> - <version>1.0.0-SNAPSHOT</version> - <packaging>jar</packaging> - <name>Glazier Project Documentation</name> - <build> - <plugins> - <plugin> - <groupId>com.agilejava.docbkx</groupId> - <artifactId>docbkx-maven-plugin</artifactId> - <executions> - <execution> - <goals> - <goal>generate-pdf</goal> - <goal>generate-webhelp</goal> - </goals> - <phase>generate-sources</phase> - </execution> - </executions> - <configuration> - <xincludeSupported>true</xincludeSupported> - <chunkSectionDepth>100</chunkSectionDepth> - <postProcess> - <copy todir="target/docbkx/webhelp/glazier-manual/content/figures"> - <fileset dir="src/docbkx/figures"> - <include name="**/*.png"/> - </fileset> - </copy> - </postProcess> - </configuration> - </plugin> - </plugins> - </build> -</project> diff --git a/docs/src/glazier-manual/src/docbkx/figures/api_workflow.png b/docs/src/glazier-manual/src/docbkx/figures/api_workflow.png deleted file mode 100755 index e1e19f82369dafbdb9b98b5c73c4f5a5fadb221e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26028 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 V)=*E)G7a`t8mc<?D^;u_{tr(bE;axF diff --git a/docs/src/glazier-manual/src/docbkx/figures/architecture_diagram.png b/docs/src/glazier-manual/src/docbkx/figures/architecture_diagram.png deleted file mode 100755 index cf374dd0367950d1ff033e17d40201b3226acfbc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30105 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 EA6A{cd;kCd diff --git a/docs/src/glazier-manual/src/docbkx/glazier-manual.xml b/docs/src/glazier-manual/src/docbkx/glazier-manual.xml deleted file mode 100644 index 042a2572..00000000 --- a/docs/src/glazier-manual/src/docbkx/glazier-manual.xml +++ /dev/null @@ -1,1910 +0,0 @@ -<!-- - Copyright (c) 2013 Mirantis, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<book xmlns="http://docbook.org/ns/docbook" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:xi="http://www.w3.org/2001/XInclude" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns:m="http://www.w3.org/1998/Math/MathML" - xmlns:html="http://www.w3.org/1999/xhtml" version="5.0" status="DRAFT" - xsi:schemaLocation="http://docbook.org/ns/docbook http://www.docbook.org/xml/5.0/xsd/docbook.xsd - http://www.w3.org/1999/xlink http://www.w3.org/1999/xlink.xsd -http://docbook.org/ns/docbook " xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <title>Glazier Project Documentation</title> - <info> - <author> - <personname> - <firstname/> - <surname/> - </personname> - <affiliation> - <orgname>Mirantis, Inc.</orgname> - </affiliation> - </author> - <copyright> - <year>2013</year> - <holder>Mirantis, Inc.</holder> - </copyright> - <releaseinfo>v0.1</releaseinfo> - <productname>Glazier™</productname> - <pubdate>2013-04-04</pubdate> - <legalnotice role="apache2"> - <annotation> - <remark>Copyright details are filled in by the template. Change - the value of the role - attribute on the legalnotice element to change the license. - </remark> - </annotation> - </legalnotice> - <abstract> - <para>This document is intended for individuals who wish to - configure - and use our product or intend to contribute. - </para> - </abstract> - <cover> - <para>this is a placeholder for the front cover</para> - </cover> - <cover> - <para>this is a placeholder for the back cover</para> - </cover> - </info> - <chapter> - <title>Overview</title> - <para>Welcome to Glazier Project. - </para> - <section> - <title>Intended Audience</title> - <para>This guide is intended to individuals who want to contribute - to our - project. - </para> - </section> - <section> - <title>Document Change History</title> - <para>This version of the Glazier Manual replaces and obsoletes all - previous versions. The - most recent changes are described in the table below: - </para> - <informaltable rules="all"> - <thead> - <tr> - <td align="center" colspan="1">Revision Date</td> - <td align="center" colspan="4">Summary of Changes</td> - </tr> - </thead> - <tbody> - <tr> - <td colspan="1" align="center">April. 4, 2013</td> - <td colspan="4"> - <itemizedlist spacing="compact"> - <listitem> - <para>Initial document creation.</para> - </listitem> - </itemizedlist> - </td> - </tr> - </tbody> - </informaltable> - </section> - <section> - <title>Additional Resources</title> - <itemizedlist spacing="compact"> - <listitem> - <para> - <link xlink:href="http://www.mirantis.com"> - Mirantis - Cloud Software - </link> - </para> - </listitem> - </itemizedlist> - </section> - </chapter> - <chapter id="architecture"> - <title>Architecture</title> - <figure xml:id="glazierarchitecture"> - <title>Architecture</title> - <mediaobject> - <imageobject role="fo"> - <imagedata fileref="figures/architecture_diagram.png" - contentwidth="5in"/> - </imageobject> - <imageobject role="html"> - <imagedata fileref="figures/architecture_diagram.png"/> - </imageobject> - </mediaobject> - </figure> - <para> - The Glazier architecture has the following components: - <itemizedlist spacing="compact"> - <listitem> - <para>Glazier Dashboard - UI for the Glazier, extends - Horizon - </para> - </listitem> - <listitem> - <para>REST API - exposes Glazier API via REST. Maintains - environment configurations in Database - </para> - </listitem> - <listitem> - <para>Glazier Python Client - Python client for Glazier - REST API - </para> - </listitem> - <listitem> - <para>Orchestration Engine - builds the environment as - configured by the user. - Turns environment configurations into a Heat templates - for VM provision and Glazier Agent commands for service - deployment - </para> - </listitem> - <listitem> - <para>Glazier Agent - built into Windows Server VM images. - Executes commands sent by Orchestration Engine - </para> - </listitem> - </itemizedlist> - </para> - </chapter> - <chapter> - <title>API Specification</title> - <informaltable rules="all"> - <thead> - <tr> - <td align="center" colspan="1">Revision Date</td> - <td align="center" colspan="4">Summary of Changes</td> - </tr> - </thead> - <tbody> - <tr> - <td colspan="1" align="center">February 4, 2013</td> - <td colspan="4"> - <itemizedlist spacing="compact"> - <listitem> - <para>Initial document creation</para> - </listitem> - </itemizedlist> - </td> - </tr> - <tr> - <td colspan="1" align="center">February 22, 2013</td> - <td colspan="4"> - <itemizedlist spacing="compact"> - <listitem> - <para>Enhance API with latest architecture - changes - </para> - </listitem> - </itemizedlist> - </td> - </tr> - <tr> - <td colspan="1" align="center">March 06, 2013</td> - <td colspan="4"> - <itemizedlist spacing="compact"> - <listitem> - <para>Fix specification according to remarks - from Dmitry Teselkin - </para> - </listitem> - </itemizedlist> - </td> - </tr> - </tbody> - </informaltable> - <section> - <title>Introduction</title> - <para>Glazier Service API is a programmatic interface used for interaction - with Glazier. Other interaction - mechanisms like Glazier Dashboard or Glazier CLI should use API as underlying - protocol for interaction. - </para> - - <glossary> - <para>For detailed information about entities and terms used in this document, please refer first to - <xref - linkend="architecture"/>. - </para> - <glossentry> - <glossterm>Environment</glossterm> - <glossdef> - <para>Environment is a set of logically related Services managed by a single tenant. Environment - defines Windows environment boundaries. - </para> - <para>Services within single Environment may comprise some complex configuration while Services - in different Environments are always independent from one another. Each Environment is - associated with single OpenStack project (tenant). - </para> - </glossdef> - </glossentry> - <glossentry> - <glossterm>Service</glossterm> - <glossdef> - <para>Service is building block of Windows environment. Service is a set of one or more Virtual - Machines sharing a common purpose and configured together. Each service belongs to a single - Environment and single Service Type. - </para> - <para>Services are comprised from one or more Service Units.</para> - </glossdef> - </glossentry> - <glossentry> - <glossterm>Service Type</glossterm> - <glossdef> - <para>Service type is definition for describing set of features exposed by service.</para> - </glossdef> - </glossentry> - <glossentry> - <glossterm>Service Unit</glossterm> - <glossdef> - <para>Service Units are the actual Windows Server VMs instantiated by OpenStack and then - configured according to its Service Type (this may also correspond to one of predefined - Windows Server roles). - </para> - </glossdef> - </glossentry> - <glossentry> - <glossterm>Service Metadata</glossterm> - <glossdef> - <para>Service Metadata is a JSON-encoded definition of Environment, its Services and their - Service Units along with all their attributes. Service Metadata may describe both current - and the intended state of the Environment. - </para> - </glossdef> - </glossentry> - <glossentry> - <glossterm>Session</glossterm> - <glossdef> - <para>All changes to environment done in scope of Session. After all changes to Environment - state are accumulated, changes actually are applied only after session is deployed. - </para> - </glossdef> - </glossentry> - </glossary> - </section> - <section> - <title>Return codes and errors</title> - <para>All REST API calls return the natural HTTP response codes for the operations, e.g. a successful GET - returns a HTTP 200, a successful PUT returns a HTTP 201, a GET for a non-existent entity returns HTTP - 404, unauthorized operations return HTTP 401 or HTTP 403, internal errors return HTTP 500. - </para> - </section> - <section> - <title>Response of POSTs and PUTs</title> - <para>All POST and PUT requests by convention should return the created object (in the case of POST, with a - generated ID) as if it was requested by GET. - </para> - </section> - <section> - <title>Authentication</title> - <para>All requests include a Keystone authentication token header (X-Auth-Token). Clients must authenticate - with Keystone before interacting with the Glazier service. - </para> - </section> - <section> - <title>Workflow</title> - <figure xml:id="api_workflow"> - <title>Sample Workflow</title> - <mediaobject> - <imageobject role="fo"> - <imagedata fileref="figures/api_workflow.png" - contentwidth="5in"/> - </imageobject> - <imageobject role="html"> - <imagedata fileref="figures/api_workflow.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Let’s review a sample workflow (series of API calls) for creating new Environment with Active - Directory Service deployment: - <orderedlist spacing="compact"> - <listitem> - <para>POST /environments/ - Creating new Environment</para> - </listitem> - <listitem> - <para>POST /environments/id/configure – Creating new configuration session for Environment - </para> - </listitem> - <listitem> - <para>POST /environments/id/activeDirectory – Creating new ActiveDirectory service</para> - </listitem> - <listitem> - <para>POST /environments/id/sessions/session_id/deploy – Saving and deploying changes</para> - </listitem> - </orderedlist> - </para> - </section> - <section> - <title>API</title> - <section> - <title>Environment API</title> - <para>This section describes API calls for Environment management.</para> - <table frame='all'> - <title>Environment Object</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="1*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Attribute</entry> - <entry>Type</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>id</entry> - <entry>guid</entry> - <entry>Unique ID</entry> - </row> - <row> - <entry>name</entry> - <entry>string</entry> - <entry>User-friendly name</entry> - </row> - <row> - <entry>created</entry> - <entry>datetime</entry> - <entry>Creation date and time in ISO format</entry> - </row> - <row> - <entry>updated</entry> - <entry>datetime</entry> - <entry>Modification date and time in ISO format</entry> - </row> - <row> - <entry>tenant_id</entry> - <entry>guid</entry> - <entry>Open Stack tenant id</entry> - </row> - <row> - <entry>status</entry> - <entry>string</entry> - <entry>Deployment status: draft, pending, inprogress, finished</entry> - </row> - </tbody> - </tgroup> - </table> - <section> - <title>Get a List of existing Environments</title> - <section> - <title>Call</title> - <table frame='all'> - <title>GET /environments Call</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="4*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Method</entry> - <entry>URI</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>GET</entry> - <entry>/environments</entry> - <entry>Get a list of existing Environments</entry> - </row> - </tbody> - </tgroup> - </table> - </section> - <section> - <title>Payload</title> - <para>None</para> - </section> - <section> - <title>Returns</title> - <para>This call returns list of environments. Only the basic properties are returned. For details see "Get Environment Detailed Information": - <programlisting> - <![CDATA[ - { - "environments": [ - { - "id": "0ce373a477f211e187a55404a662f968", - "name": "dc1", - "created": "2010-11-30T03:23:42Z", - "updated": "2010-11-30T03:23:44Z", - "tenant_id": "0849006f7ce94961b3aab4e46d6f229a" - }, - { - "id": "c697bd2429304820a928d145aa42af59", - "name": "dc2", - "created": "2010-11-30T03:23:42Z", - "updated": "2010-11-30T03:23:44Z", - "tenant_id": "0849006f7ce94961b3aab4e46d6f229a" - } - ] -} - ]]> - </programlisting> - </para> - </section> - </section> - <section> - <title>Create Environment instance</title> - <section> - <title>Call</title> - <table frame='all'> - <title>POST /environments Call</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="4*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Method</entry> - <entry>URI</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>POST</entry> - <entry>/environments</entry> - <entry>Create new Environment</entry> - </row> - </tbody> - </tgroup> - </table> - </section> - <section> - <title>Payload</title> - <programlisting> - <![CDATA[ -{ - "name": "env1" -} - ]]> - </programlisting> - </section> - <section> - <title>Returns</title> - <para>This call returns created environment: - <programlisting> - <![CDATA[ -{ - "id": "ce373a477f211e187a55404a662f968", - "name": "env1", - "created": "2010-11-30T03:23:42Z", - "updated": "2010-11-30T03:23:44Z", - "tenant_id": "0849006f7ce94961b3aab4e46d6f229a" -} - ]]> - </programlisting> - </para> - </section> - </section> - <section> - <title>Update Environment Instance</title> - <section> - <title>Call</title> - <table frame='all'> - <title>PUT /environments/&lt;id&gt; Call</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="4*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Method</entry> - <entry>URI</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>PUT</entry> - <entry>/environments/&lt;id&gt;</entry> - <entry>Update properties of Environment instance</entry> - </row> - </tbody> - </tgroup> - </table> - </section> - <section> - <title>Payload</title> - <programlisting> - <![CDATA[ -{ - "name": "env1-changed" -} - ]]> - </programlisting> - </section> - <section> - <title>Returns</title> - <para>This call returns modified environment object: - <programlisting> - <![CDATA[ -{ - "id": "ce373a477f211e187a55404a662f968", - "name": "env1-changed", - "created": "2010-11-30T03:23:42Z", - "updated": "2010-11-30T03:23:44Z", - "tenant_id": "0849006f7ce94961b3aab4e46d6f229a" -} - ]]> - </programlisting> - </para> - </section> - </section> - <section> - <title>Get Environment Instance Detailed Information</title> - <section> - <title>Call</title> - <table frame='all'> - <title>GET /environments/&lt;id&gt; Call</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="4*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Method</entry> - <entry>URI</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>GET</entry> - <entry>/environments/&lt;id&gt;</entry> - <entry>Returns detailed information about Environment including child entities</entry> - </row> - </tbody> - </tgroup> - </table> - </section> - <section> - <title>Payload</title> - <para>None</para> - </section> - <section> - <title>Returns</title> - <para>This call returns modified environment object: - <programlisting> - <![CDATA[ -{ - "id": "ce373a477f211e187a55404a662f968", - "name": "env1", - "created": "2010-11-30T03:23:42Z", - "updated": "2010-11-30T03:23:44Z", - "tenant_id": "0849006f7ce94961b3aab4e46d6f229a", - "status": "pending" -} - ]]> - </programlisting> - </para> - </section> - </section> - <section> - <title>Remove Environment</title> - <section> - <title>Call</title> - <table frame='all'> - <title>DELETE /environments/&lt;id&gt; Call</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="4*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Method</entry> - <entry>URI</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>DELETE</entry> - <entry>/environments/&lt;id&gt;</entry> - <entry>Remove specified Environment.</entry> - </row> - </tbody> - </tgroup> - </table> - </section> - <section> - <title>Payload</title> - <para>None</para> - </section> - <section> - <title>Returns</title> - <para>None</para> - </section> - </section> - </section> - <section> - <title>Environment Configuration API</title> - <para>Only one Environment can be configured at a time by only one user. This behavior archived by opening "configuration session" for some Environment by user, and locking this Environment for changes by other users. Only one open session per Environment is available.</para> - <table frame='all'> - <title>Configuration Session Object</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="1*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Attribute</entry> - <entry>Type</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>id</entry> - <entry>guid</entry> - <entry>Session unique ID</entry> - </row> - <row> - <entry>environment_id</entry> - <entry>guid</entry> - <entry>Environment that going to be modified during this session</entry> - </row> - <row> - <entry>created</entry> - <entry>datetime</entry> - <entry>Creation date and time in ISO format</entry> - </row> - <row> - <entry>updated</entry> - <entry>datetime</entry> - <entry>Modification date and time in ISO format</entry> - </row> - <row> - <entry>user_id</entry> - <entry>guid</entry> - <entry>Session owner guid</entry> - </row> - <row> - <entry>state</entry> - <entry>string</entry> - <entry>Session state. Could be: open, deploying, deployed</entry> - </row> - </tbody> - </tgroup> - </table> - <section> - <title>Get a List of Sessions</title> - <para>Only one open session can be for one Environment.</para> - <section> - <title>Call</title> - <table frame='all'> - <title>GET /environments/&lt;id&gt;/sessions Call</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="4*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Method</entry> - <entry>URI</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>GET</entry> - <entry>/environments/&lt;id&gt;/sessions</entry> - <entry>Get a list of open sessions</entry> - </row> - </tbody> - </tgroup> - </table> - </section> - <section> - <title>Payload</title> - <para>None</para> - </section> - <section> - <title>Returns</title> - <para>This call returns list of open sessions: - <programlisting> - <![CDATA[ - { - "sessions": [ - { - "id": "108bbd04084d4dc8a2e8986fa8fa5bf2", - "environment_id": "4dc8a2e8986fa8fa5bf24dc8a2e8986fa8", - "created": "2010-11-30T03:23:42Z", - "updated": "2010-11-30T03:23:54Z", - "user_id": "d7b501094caf4daab08469663a9e1a2b", - "state": "deployed" - }, - { - "id": "4aecdc2178b9430cbbb8db44fb7ac384", - "environment_id": "4dc8a2e8986fa8fa5bf24dc8a2e8986fa8", - "created": "2010-11-30T03:23:42Z", - "updated": "2010-11-30T03:23:54Z", - "user_id": "d7b501094caf4daab08469663a9e1a2b", - "state": "open" - } - ] -} - ]]> - </programlisting> - </para> - </section> - </section> - <section> - <title>Open session</title> - <para>During this call new working session is created, and session ID should be sent in header (X-Configuration-Session) to all next API calls.</para> - <section> - <title>Call</title> - <table frame='all'> - <title>POST /environments/&lt;id&gt;/configure Call</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="4*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Method</entry> - <entry>URI</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>POST</entry> - <entry>/environments/&lt;id&gt;/configure</entry> - <entry>Creating new configuration change session</entry> - </row> - </tbody> - </tgroup> - </table> - </section> - <section> - <title>Payload</title> - <para>None</para> - </section> - <section> - <title>Returns</title> - <para>This call returns created session: - <programlisting> - <![CDATA[ -{ - "id": "4aecdc2178b9430cbbb8db44fb7ac384", - "environment_id": "4dc8a2e8986fa8fa5bf24dc8a2e8986fa8", - "created": "2010-11-30T03:23:42Z", - "updated": "2010-11-30T03:23:54Z", - "user_id": "d7b501094caf4daab08469663a9e1a2b", - "state": "open" -} - ]]> - </programlisting> - </para> - </section> - </section> - <section> - <title>Deploy changes from Session</title> - <section> - <title>Call</title> - <table frame='all'> - <title>POST /environments/&lt;id&gt;/sessions/&lt;sessionId&gt;/deploy Call</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="4*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Method</entry> - <entry>URI</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>POST</entry> - <entry>/environments/&lt;id&gt;/sessions/&lt;sessionId&gt;/deploy</entry> - <entry>Deploying changes made in session with specified &lt;sessionId&gt;</entry> - </row> - </tbody> - </tgroup> - </table> - </section> - <section> - <title>Payload</title> - <para>None</para> - </section> - <section> - <title>Returns</title> - <para>This call returns deployed session: - <programlisting> - <![CDATA[ -{ - "id": "4aecdc2178b9430cbbb8db44fb7ac384", - "environment_id": "4dc8a2e8986fa8fa5bf24dc8a2e8986fa8", - "created": "2010-11-30T03:23:42Z", - "updated": "2010-11-30T03:23:54Z", - "user_id": "d7b501094caf4daab08469663a9e1a2b", - "state": "deploying" -} - ]]> - </programlisting> - </para> - </section> - </section> - <section> - <title>Get session information</title> - <section> - <title>Call</title> - <table frame='all'> - <title>GET /environments/&lt;id&gt;/sessions/&lt;sessionId&gt; Call</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="4*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Method</entry> - <entry>URI</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>GET</entry> - <entry>/environments/&lt;id&gt;/sessions/&lt;sessionId&gt;</entry> - <entry>Getting details about session with specified &lt;sessionId&gt;</entry> - </row> - </tbody> - </tgroup> - </table> - </section> - <section> - <title>Payload</title> - <para>None</para> - </section> - <section> - <title>Returns</title> - <para>This call returns session information: - <programlisting> - <![CDATA[ -{ - "id": "4aecdc2178b9430cbbb8db44fb7ac384", - "environment_id": "4dc8a2e8986fa8fa5bf24dc8a2e8986fa8", - "created": "2010-11-30T03:23:42Z", - "updated": "2010-11-30T03:23:54Z", - "user_id": "d7b501094caf4daab08469663a9e1a2b", - "state": "deploying" -} - ]]> - </programlisting> - </para> - </section> - </section> - <section> - <title>Delete Session </title> - <section> - <title>Call</title> - <table frame='all'> - <title>DELETE /environments/&lt;id&gt;/sessions/&lt;sessionId&gt; Call</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="4*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Method</entry> - <entry>URI</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>DELETE</entry> - <entry>/environments/&lt;id&gt;/sessions/&lt;sessionId&gt;</entry> - <entry>Delete session with specified &lt;sessionId&gt;</entry> - </row> - </tbody> - </tgroup> - </table> - </section> - <section> - <title>Payload</title> - <para>None</para> - </section> - <section> - <title>Returns</title> - <para>None</para> - </section> - </section> - </section> - <section> - <title>Active Directory API</title> - <para>This section describes API calls for Active Directory service management.</para> - <section> - <title>Get a List of existing Active Directory instances</title> - <table frame='all'> - <title>Active Directory Object</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="1*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Attribute</entry> - <entry>Type</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>id</entry> - <entry>guid</entry> - <entry>Unique ID</entry> - </row> - <row> - <entry>name</entry> - <entry>string</entry> - <entry>Domain name</entry> - </row> - <row> - <entry>created</entry> - <entry>datetime</entry> - <entry>Creation date and time in ISO format</entry> - </row> - <row> - <entry>updated</entry> - <entry>datetime</entry> - <entry>Modification date and time in ISO format</entry> - </row> - <row> - <entry>configuration</entry> - <entry>string</entry> - <entry>AD configuration: site, standalone, inTree</entry> - </row> - <row> - <entry>domain</entry> - <entry>string</entry> - <entry>Domain name (same as name)</entry> - </row> - <row> - <entry>units</entry> - <entry>object</entry> - <entry>Active Directory Unit object</entry> - </row> - </tbody> - </tgroup> - </table> - <table frame='all'> - <title>Active Directory Unit Object</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="1*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Attribute</entry> - <entry>Type</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>id</entry> - <entry>guid</entry> - <entry>Unique ID</entry> - </row> - <row> - <entry>isMaster</entry> - <entry>boolean</entry> - <entry>Is unit is master domain?</entry> - </row> - <row> - <entry>location</entry> - <entry>string</entry> - <entry>AvailabilityZone or specific physical datacenter.</entry> - </row> - </tbody> - </tgroup> - </table> - <section> - <title>Call</title> - <table frame='all'> - <title>GET /environments/&lt;id&gt;/activeDirectories Call</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="4*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Method</entry> - <entry>URI</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>GET</entry> - <entry>/environments/&lt;id&gt;/activeDirectories</entry> - <entry>Get a list of Active Directory instances</entry> - </row> - </tbody> - </tgroup> - </table> - </section> - <section> - <title>Payload</title> - <para>None</para> - </section> - <section> - <title>Returns</title> - <para>This call returns list of Active Directory instances: - <programlisting> - <![CDATA[ -{ - "activeDirectories": [{ - "id": "96365940588b479294fe8e6dc073db04", - "name": "acme.dc", - "created": "2010-11-30T03:23:42Z", - "updated": "2010-11-30T03:23:44Z", - "configuration": "standalone", - "units": [{ - "id": "d08887df15b94178b244904b506fe85b", - "isMaster": true, - "location": "west-dc" - }, { - "id": "dcf0de317e7046bea555539f19b8ea84", - "isMaster": false, - "location": "west-dc" - }] - }] -} - ]]> - </programlisting> - </para> - </section> - </section> - <section> - <title>Create Active Directory instance</title> - <table frame='all'> - <title>Active Directory Object</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="1*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Attribute</entry> - <entry>Type</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>name</entry> - <entry>string</entry> - <entry>Domain name</entry> - </row> - <row> - <entry>configuration</entry> - <entry>string</entry> - <entry>AD configuration: site, standalone, inTree</entry> - </row> - <row> - <entry>adminPassword</entry> - <entry>string</entry> - <entry>Password from domain administrator account</entry> - </row> - <row> - <entry>domain</entry> - <entry>string</entry> - <entry>Domain name (same as name)</entry> - </row> - <row> - <entry>units</entry> - <entry>object</entry> - <entry>Active Directory Unit object</entry> - </row> - </tbody> - </tgroup> - </table> - <table frame='all'> - <title>Active Directory Unit Object</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="1*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Attribute</entry> - <entry>Type</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>isMaster</entry> - <entry>boolean</entry> - <entry>Is unit is master domain?</entry> - </row> - <row> - <entry>recoveryPassword</entry> - <entry>string</entry> - <entry>Recovery password</entry> - </row> - <row> - <entry>location</entry> - <entry>string</entry> - <entry>AvailabilityZone or specific physical datacenter.</entry> - </row> - </tbody> - </tgroup> - </table> - <section> - <title>Call</title> - <table frame='all'> - <title>POST /environments/&lt;id&gt;/activeDirectories Call</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="4*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Method</entry> - <entry>URI</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>POST</entry> - <entry>/environments/&lt;id&gt;/activeDirectories</entry> - <entry>Create new Active Directory</entry> - </row> - </tbody> - </tgroup> - </table> - </section> - <section> - <title>Payload</title> - <programlisting> - <![CDATA[ -{ - "name": "acme.dc", - "configuration": "standalone", - "adminPassword": "password", - "domain": "acme.dc", - "units": [ - { - "isMaster": true, - "recoveryPassword": "password", - "location": "west-dc" - }, - { - "isMaster": false, - "recoveryPassword": "password", - "location": "west-dc" - } - ] -} - ]]> - </programlisting> - </section> - <section> - <title>Returns</title> - <para>This call returns created active directory domain: - <programlisting> - <![CDATA[ -{ - "id": "96365940588b479294fe8e6dc073db04", - "name": "acme.dc", - "created": "2010-11-30T03:23:42Z", - "updated": "2010-11-30T03:23:44Z", - "domain": "acme.dc", - "configuration": "standalone", - "units": [{ - "id": "d08887df15b94178b244904b506fe85b", - "isMaster": true, - "location": "west-dc" - }, { - "id": "dcf0de317e7046bea555539f19b8ea84", - "isMaster": false, - "location": "west-dc" - }] -} - ]]> - </programlisting> - </para> - </section> - </section> - <section> - <title>Update Active Directory instance</title> - <table frame='all'> - <title>Active Directory Object</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="1*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Attribute</entry> - <entry>Type</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>id</entry> - <entry>guid</entry> - <entry>Unique ID</entry> - </row> - <row> - <entry>name</entry> - <entry>string</entry> - <entry>Domain name</entry> - </row> - <row> - <entry>configuration</entry> - <entry>string</entry> - <entry>AD configuration: site, standalone, inTree</entry> - </row> - <row> - <entry>adminPassword</entry> - <entry>string</entry> - <entry>Password from domain administrator account</entry> - </row> - <row> - <entry>domain</entry> - <entry>string</entry> - <entry>Domain name (same as name)</entry> - </row> - <row> - <entry>units</entry> - <entry>object</entry> - <entry>Active Directory Unit object</entry> - </row> - </tbody> - </tgroup> - </table> - <table frame='all'> - <title>Active Directory Unit Object</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="1*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Attribute</entry> - <entry>Type</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>id</entry> - <entry>guid</entry> - <entry>Unique ID</entry> - </row> - <row> - <entry>isMaster</entry> - <entry>boolean</entry> - <entry>Is unit is master domain?</entry> - </row> - <row> - <entry>recoveryPassword</entry> - <entry>string</entry> - <entry>Recovery password</entry> - </row> - <row> - <entry>location</entry> - <entry>string</entry> - <entry>AvailabilityZone or specific physical datacenter.</entry> - </row> - </tbody> - </tgroup> - </table> - <section> - <title>Call</title> - <table frame='all'> - <title>PUT /environments/&lt;id&gt;/activeDirectories/&lt;serviceId&gt; Call</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="4*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Method</entry> - <entry>URI</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>PUT</entry> - <entry>/environments/&lt;id&gt;/activeDirectories/&lt;serviceId&gt;</entry> - <entry>Update existing Active Directory</entry> - </row> - </tbody> - </tgroup> - </table> - </section> - <section> - <title>Payload</title> - <programlisting> - <![CDATA[ -{ - "name": "acme.dc", - "configuration": "standalone", - "adminPassword": "password", - "domain": "acme.dc", - "units": [ - { - "isMaster": true, - "recoveryPassword": "new-rpassword", - "location": "west-dc" - }, - { - "isMaster": false, - "recoveryPassword": "new-rpassword", - "location": "west-dc" - } - ] -} - ]]> - </programlisting> - </section> - <section> - <title>Returns</title> - <para>This call returns updated active directory domain: - <programlisting> - <![CDATA[ -{ - "id": "96365940588b479294fe8e6dc073db04", - "name": "acme.dc", - "created": "2010-11-30T03:23:42Z", - "updated": "2010-11-30T03:23:44Z", - "configuration": "standalone", - "domain": "acme.dc", - "units": [{ - "id": "d08887df15b94178b244904b506fe85b", - "isMaster": true, - "location": "west-dc" - }, { - "id": "dcf0de317e7046bea555539f19b8ea84", - "isMaster": false, - "location": "west-dc" - }] -} - ]]> - </programlisting> - </para> - </section> - </section> - <section> - <title>Get Information about Active Directory instance</title> - <table frame='all'> - <title>Active Directory Object</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="1*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Attribute</entry> - <entry>Type</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>id</entry> - <entry>guid</entry> - <entry>Unique ID</entry> - </row> - <row> - <entry>name</entry> - <entry>string</entry> - <entry>Domain name</entry> - </row> - <row> - <entry>created</entry> - <entry>datetime</entry> - <entry>Creation date and time in ISO format</entry> - </row> - <row> - <entry>updated</entry> - <entry>datetime</entry> - <entry>Modification date and time in ISO format</entry> - </row> - <row> - <entry>configuration</entry> - <entry>string</entry> - <entry>AD configuration: site, standalone, inTree</entry> - </row> - <row> - <entry>domain</entry> - <entry>string</entry> - <entry>Domain name (same as name)</entry> - </row> - <row> - <entry>units</entry> - <entry>object</entry> - <entry>Active Directory Unit object</entry> - </row> - </tbody> - </tgroup> - </table> - <table frame='all'> - <title>Active Directory Unit Object</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="1*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Attribute</entry> - <entry>Type</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>id</entry> - <entry>guid</entry> - <entry>Unique ID</entry> - </row> - <row> - <entry>isMaster</entry> - <entry>boolean</entry> - <entry>Is unit is master domain?</entry> - </row> - <row> - <entry>location</entry> - <entry>string</entry> - <entry>AvailabilityZone or specific physical datacenter.</entry> - </row> - </tbody> - </tgroup> - </table> - <section> - <title>Call</title> - <table frame='all'> - <title>GET /environments/&lt;id&gt;/activeDirectories/&lt;serviceId&gt; Call</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="4*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Method</entry> - <entry>URI</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>GET</entry> - <entry>/environments/&lt;id&gt;/activeDirectories/&lt;serviceId&gt;</entry> - <entry>Return specified Active Directory instance</entry> - </row> - </tbody> - </tgroup> - </table> - </section> - <section> - <title>Payload</title> - <para>None</para> - </section> - <section> - <title>Returns</title> - <para>This call returns modified environment object: - <programlisting> - <![CDATA[ -{ - "id": "96365940588b479294fe8e6dc073db04", - "name": "acme.dc", - "created": "2010-11-30T03:23:42Z", - "updated": "2010-11-30T03:23:44Z", - "configuration": "standalone", - "domain": "acme.dc", - "units": [{ - "id": "d08887df15b94178b244904b506fe85b", - "isMaster": true, - "location": "west-dc" - }, { - "id": "dcf0de317e7046bea555539f19b8ea84", - "isMaster": false, - "location": "west-dc" - }] -} - ]]> - </programlisting> - </para> - </section> - </section> - </section> - <section> - <title>Web Server API</title> - <para>This section describes API calls for managing Windows web-server software – IIS.</para> - <table frame='all'> - <title>Web Server Object</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="1*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Attribute</entry> - <entry>Type</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>id</entry> - <entry>guid</entry> - <entry>Unique ID</entry> - </row> - <row> - <entry>name</entry> - <entry>string</entry> - <entry>User-friendly name</entry> - </row> - <row> - <entry>created</entry> - <entry>datetime</entry> - <entry>Creation date and time in ISO format</entry> - </row> - <row> - <entry>updated</entry> - <entry>datetime</entry> - <entry>Modification date and time in ISO format</entry> - </row> - <row> - <entry>domain</entry> - <entry>string</entry> - <entry>Domain name (same as name)</entry> - </row> - <row> - <entry>units</entry> - <entry>object</entry> - <entry>Web Server Unit object</entry> - </row> - </tbody> - </tgroup> - </table> - <table frame='all'> - <title>Web Server Unit Object</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="1*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Attribute</entry> - <entry>Type</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>id</entry> - <entry>guid</entry> - <entry>Unique ID</entry> - </row> - <row> - <entry>endpoint</entry> - <entry>object</entry> - <entry>Unit Endpoint</entry> - </row> - <row> - <entry>location</entry> - <entry>string</entry> - <entry>AvailabilityZone or specific physical datacenter.</entry> - </row> - </tbody> - </tgroup> - </table> - <table frame='all'> - <title>Web Server Unit Endpoint Object</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="1*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Attribute</entry> - <entry>Type</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>host</entry> - <entry>string</entry> - <entry>Host address for IIS Management Console connection</entry> - </row> - </tbody> - </tgroup> - </table> - <section> - <title>Get a List of existing Web Servers</title> - <section> - <title>Call</title> - <table frame='all'> - <title>GET /environments/&lt;id&gt;/webServers Call</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="4*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Method</entry> - <entry>URI</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>GET</entry> - <entry>/environments/&lt;id&gt;/webServers</entry> - <entry>Get a list of existing Web Servers</entry> - </row> - </tbody> - </tgroup> - </table> - </section> - <section> - <title>Payload</title> - <para>None</para> - </section> - <section> - <title>Returns</title> - <para>This call returns list of web servers: - <programlisting> - <![CDATA[ -{ - "webServers": - [ - { - "id": "0ce373a477f211e187a55404a662f968", - "name": "frontend", - "created": "2010-11-30T03:23:42Z", - "updated": "2010-11-30T03:23:44Z", - "domain": "ACME", - "units": [{ - "id": "1bf3491c409b4541b6f18ea5988a6437", - "endpoint": { - "host": "10.0.0.2" - }, - "location": "west-dc" - }] - }, - { - "id": "c697bd2429304820a928d145aa42af59", - "name": "backend", - "created": "2010-11-30T03:23:42Z", - "updated": "2010-11-30T03:23:44Z", - "domain": "ACME" , - "units": [{ - "id": "eb32f97866d24001baa430cb34e4049f", - "endpoint": { - "host": "10.0.0.3" - }, - "location": "west-dc" - }] - } - ] -} ]]> - </programlisting> - </para> - </section> - </section> - <section> - <title>Create Web Server instance</title> - <section> - <title>Call</title> - <table frame='all'> - <title>POST /environments/&lt;id&gt;/webServers Call</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="4*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Method</entry> - <entry>URI</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>POST</entry> - <entry>/environments/&lt;id&gt;/webServers</entry> - <entry>Create new Web Server</entry> - </row> - </tbody> - </tgroup> - </table> - </section> - <section> - <title>Payload</title> - <programlisting> - <![CDATA[ -{ - "name": "frontend", - "domain": { - "name": "ACME", - "credentials": { - "username": "admin", - "password": "password" - } - }, - "credentials": { - "username": "admin", - "password": "password" - } - "location": "west-dc" -} - ]]> - </programlisting> - </section> - <section> - <title>Returns</title> - <para>This call returns created web server: - <programlisting> - <![CDATA[ -{ - "id": "ce373a477f211e187a55404a662f968", - "name": "frontend", - "created": "2010-11-30T03:23:42Z", - "updated": "2010-11-30T03:23:44Z", - "domain": "ACME", - "units": [{ - "id": "1bf3491c409b4541b6f18ea5988a6437", - "location": "west-dc" - }] -} - ]]> - </programlisting> - </para> - </section> - </section> - <section> - <title>Update Web Server Instance</title> - <section> - <title>Call</title> - <table frame='all'> - <title>PUT /environments/&lt;id&gt;/webServers/&lt;serviceId&gt; Call</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="4*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Method</entry> - <entry>URI</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>PUT</entry> - <entry>/environments/&lt;id&gt;/webServers/&lt;serviceId&gt;</entry> - <entry>Update properties of WebServer instance</entry> - </row> - </tbody> - </tgroup> - </table> - </section> - <section> - <title>Payload</title> - <programlisting> - <![CDATA[ -{ - "name": "frontend-changed", - "domain": { - "name": "ACME", - "username": "admin", - "password": "password" - }, - "credentials": { - "username": "admin", - "password": "password" - } -} - ]]> - </programlisting> - </section> - <section> - <title>Returns</title> - <para>This call returns modified web server object: - <programlisting> - <![CDATA[ -{ - "id": "ce373a477f211e187a55404a662f968", - "name": "frontend", - "created": "2010-11-30T03:23:42Z", - "updated": "2010-11-30T03:23:44Z", - "domain": "ACME", - "units": [{ - "id": "1bf3491c409b4541b6f18ea5988a6437", - "location": "west-dc" - }] -} - ]]> - </programlisting> - </para> - </section> - </section> - <section> - <title>Get Web Server Instance Detailed Information</title> - <section> - <title>Call</title> - <table frame='all'> - <title>GET /environments/&lt;id&gt;/webServers/&lt;serviceId&gt; Call</title> - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colnum="1" colname="col1" colwidth="1*"/> - <colspec colnum="2" colname="col2" colwidth="4*"/> - <colspec colnum="3" colname="col3" colwidth="4*"/> - <thead> - <row> - <entry>Method</entry> - <entry>URI</entry> - <entry>Description</entry> - </row> - </thead> - <tbody> - <row> - <entry>GET</entry> - <entry>/environments/&lt;id&gt;/webServers/&lt;serviceId&gt;</entry> - <entry>Returns detailed information about Web Server</entry> - </row> - </tbody> - </tgroup> - </table> - </section> - <section> - <title>Payload</title> - <para>None</para> - </section> - <section> - <title>Returns</title> - <para>This call returns modified environment object: - <programlisting> - <![CDATA[ -{ - "id": "c697bd2429304820a928d145aa42af59", - "name": "backend", - "created": "2010-11-30T03:23:42Z", - "updated": "2010-11-30T03:23:44Z", - "domain": "ACME" , - "units": [{ - "id": "eb32f97866d24001baa430cb34e4049f", - "endpoint": { - "host": "10.0.0.3" - }, - "location": "west-dc" - }] -} - ]]> - </programlisting> - </para> - </section> - </section> - </section> - </section> - </chapter> -</book> diff --git a/WindowsAgent/packages/repositories.config b/packages/repositories.config similarity index 100% rename from WindowsAgent/packages/repositories.config rename to packages/repositories.config diff --git a/python-glazierclient/.gitignore b/python-glazierclient/.gitignore deleted file mode 100644 index f5f39680..00000000 --- a/python-glazierclient/.gitignore +++ /dev/null @@ -1,23 +0,0 @@ -#IntelJ Idea -.idea/ - -#virtualenv -.venv/ - -#Build results -build/ -dist/ -*.egg-info/ - -#Python -*.pyc - -#Translation build -*.mo -*.pot - -#SQLite Database files -*.sqlite - -#Autogenerated Documentation -doc/source/api \ No newline at end of file diff --git a/python-glazierclient/README.rst b/python-glazierclient/README.rst deleted file mode 100644 index b7a3f782..00000000 --- a/python-glazierclient/README.rst +++ /dev/null @@ -1,9 +0,0 @@ -Python bindings to the Glazier API -===================== -This is a client library for Glazier built on the Glazier API. It -provides a Python API (the ``glazierclient`` module) and a command-line tool -(``glazier``). - -SEE ALSO --------- -* `Glazier <http://glazier.mirantis.com>`__ diff --git a/python-glazierclient/doc/source/_static/.placeholder b/python-glazierclient/doc/source/_static/.placeholder deleted file mode 100755 index e69de29b..00000000 diff --git a/python-glazierclient/doc/source/_templates/.placeholder b/python-glazierclient/doc/source/_templates/.placeholder deleted file mode 100755 index e69de29b..00000000 diff --git a/python-glazierclient/doc/source/_theme/theme.conf b/python-glazierclient/doc/source/_theme/theme.conf deleted file mode 100755 index 9a8facf4..00000000 --- a/python-glazierclient/doc/source/_theme/theme.conf +++ /dev/null @@ -1,2 +0,0 @@ -[theme] -inherit = default \ No newline at end of file diff --git a/python-glazierclient/doc/source/conf.py b/python-glazierclient/doc/source/conf.py deleted file mode 100644 index 38d8e651..00000000 --- a/python-glazierclient/doc/source/conf.py +++ /dev/null @@ -1,73 +0,0 @@ -# -*- coding: utf-8 -*- -# -import os - -project = 'python-glazierclient' - -# -- General configuration ---------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', - 'sphinx.ext.intersphinx', - 'sphinx.ext.coverage', - 'sphinx.ext.pngmath', - 'sphinx.ext.ifconfig', - 'sphinx.ext.graphviz'] - -# autodoc generation is a bit aggressive and a nuisance when doing heavy -# text edit cycles. -# execute "export SPHINX_DEBUG=1" in your terminal to disable - -# Add any paths that contain templates here, relative to this directory. -templates_path = [] -if os.getenv('HUDSON_PUBLISH_DOCS'): - templates_path = ['_ga', '_templates'] -else: - templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -copyright = u'Mirantis, Inc' -exclude_trees = ['api'] - -# If true, '()' will be appended to :func: etc. cross-reference text. -add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -add_module_names = True - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# -- Options for HTML output -------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. Major themes that come with -# Sphinx are currently 'default' and 'sphinxdoc'. -html_theme = 'default' - -# Output file base name for HTML help builder. -htmlhelp_basename = '%sdoc' % project - - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass -# [howto/manual]). -latex_documents = [ - ( - 'index', - '%s.tex' % project, - u'%s Documentation' % project, - u'Mirantis, Inc', - 'manual' - ), -] - -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'http://docs.python.org/': None} diff --git a/python-glazierclient/doc/source/index.rst b/python-glazierclient/doc/source/index.rst deleted file mode 100644 index 395524e6..00000000 --- a/python-glazierclient/doc/source/index.rst +++ /dev/null @@ -1,48 +0,0 @@ -.. - Copyright (c) 2013 Mirantis, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); you may - not use this file except in compliance with the License. You may obtain - a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - License for the specific language governing permissions and limitations - under the License. - -================== -Glazier API Client -================== -In order to use the python api directly, you must first obtain an auth token and identify which endpoint you wish to speak to. Once you have done so, you can use the API like so:: - - >>> from glazierclient import Client - >>> glazier = Client('1', endpoint=GLAZIER_URL, token=OS_AUTH_TOKEN) -... - - -Command-line Tool -================= -In order to use the CLI, you must provide your OpenStack username, password, tenant, and auth endpoint. Use the corresponding configuration options (``--os-username``, ``--os-password``, ``--os-tenant-id``, and ``--os-auth-url``) or set them in environment variables:: - - export OS_USERNAME=user - export OS_PASSWORD=pass - export OS_TENANT_ID=b363706f891f48019483f8bd6503c54b - export OS_AUTH_URL=http://auth.example.com:5000/v2.0 - -The command line tool will attempt to reauthenticate using your provided credentials for every request. You can override this behavior by manually supplying an auth token using ``--os-image-url`` and ``--os-auth-token``. You can alternatively set these environment variables:: - - export GLAZIER_URL=http://glazier.example.org:8082/ - export OS_AUTH_TOKEN=3bcc3d3a03f44e3d8377f9247b0ad155 - -Once you've configured your authentication parameters, you can run ``glazier help`` to see a complete listing of available commands. - - -Release Notes -============= - -0.1.0 ------ -* Initial release diff --git a/python-glazierclient/glazierclient/__init__.py b/python-glazierclient/glazierclient/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/python-glazierclient/glazierclient/client.py b/python-glazierclient/glazierclient/client.py deleted file mode 100644 index 84779d10..00000000 --- a/python-glazierclient/glazierclient/client.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -from glazierclient.common import utils - - -def Client(version, *args, **kwargs): - module = utils.import_versioned_module(version, 'client') - client_class = getattr(module, 'Client') - return client_class(*args, **kwargs) diff --git a/python-glazierclient/glazierclient/common/__init__.py b/python-glazierclient/glazierclient/common/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/python-glazierclient/glazierclient/common/base.py b/python-glazierclient/glazierclient/common/base.py deleted file mode 100644 index e92def98..00000000 --- a/python-glazierclient/glazierclient/common/base.py +++ /dev/null @@ -1,166 +0,0 @@ -# Copyright 2012 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Base utilities to build API operation managers and objects on top of. -""" - -import copy - - -# Python 2.4 compat -try: - all -except NameError: - def all(iterable): - return True not in (not x for x in iterable) - - -def getid(obj): - """ - Abstracts the common pattern of allowing both an object or an object's ID - (UUID) as a parameter when dealing with relationships. - """ - try: - return obj.id - except AttributeError: - return obj - - -class Manager(object): - """ - Managers interact with a particular type of API (servers, flavors, images, - etc.) and provide CRUD operations for them. - """ - resource_class = None - - def __init__(self, api): - self.api = api - - def _list(self, url, response_key=None, obj_class=None, - body=None, headers={}): - - resp, body = self.api.json_request('GET', url, headers=headers) - - if obj_class is None: - obj_class = self.resource_class - - if response_key: - if not response_key in body: - body[response_key] = [] - data = body[response_key] - else: - data = body - return [obj_class(self, res, loaded=True) for res in data if res] - - def _delete(self, url, headers={}): - self.api.raw_request('DELETE', url, headers=headers) - - def _update(self, url, body, response_key=None): - resp, body = self.api.json_request('PUT', url, body=body) - # PUT requests may not return a body - if body: - if response_key: - return self.resource_class(self, body[response_key]) - return self.resource_class(self, body) - - def _create(self, url, body=None, response_key=None, - return_raw=False, headers={}): - - if body: - resp, body = self.api.json_request('POST', url, - body=body, headers=headers) - else: - resp, body = self.api.json_request('POST', url, headers=headers) - if return_raw: - if response_key: - return body[response_key] - return body - if response_key: - return self.resource_class(self, body[response_key]) - return self.resource_class(self, body) - - def _get(self, url, response_key=None, return_raw=False): - resp, body = self.api.json_request('GET', url) - if return_raw: - if response_key: - return body[response_key] - return body - if response_key: - return self.resource_class(self, body[response_key]) - return self.resource_class(self, body) - - -class Resource(object): - """ - A resource represents a particular instance of an object (tenant, user, - etc). This is pretty much just a bag for attributes. - - :param manager: Manager object - :param info: dictionary representing resource attributes - :param loaded: prevent lazy-loading if set to True - """ - def __init__(self, manager, info, loaded=False): - self.manager = manager - self._info = info - self._add_details(info) - self._loaded = loaded - - def _add_details(self, info): - for (k, v) in info.iteritems(): - setattr(self, k, v) - - def __getattr__(self, k): - if k not in self.__dict__: - # NOTE(bcwaldon): disallow lazy-loading if already loaded once - if not self.is_loaded(): - self.get() - return self.__getattr__(k) - - raise AttributeError(k) - else: - return self.__dict__[k] - - def __repr__(self): - reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_' and - k != 'manager') - info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys) - return "<%s %s>" % (self.__class__.__name__, info) - - def get(self): - # set_loaded() first ... so if we have to bail, we know we tried. - self.set_loaded(True) - if not hasattr(self.manager, 'get'): - return - - new = self.manager.get(self.id) - if new: - self._add_details(new._info) - - def __eq__(self, other): - if not isinstance(other, self.__class__): - return False - if hasattr(self, 'id') and hasattr(other, 'id'): - return self.id == other.id - return self._info == other._info - - def is_loaded(self): - return self._loaded - - def set_loaded(self, val): - self._loaded = val - - def to_dict(self): - return copy.deepcopy(self._info) diff --git a/python-glazierclient/glazierclient/common/exceptions.py b/python-glazierclient/glazierclient/common/exceptions.py deleted file mode 100644 index d3d3cab6..00000000 --- a/python-glazierclient/glazierclient/common/exceptions.py +++ /dev/null @@ -1,163 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import sys - - -class BaseException(Exception): - """An error occurred.""" - def __init__(self, message=None): - self.message = message - - def __str__(self): - return self.message or self.__class__.__doc__ - - -class CommandError(BaseException): - """Invalid usage of CLI""" - - -class InvalidEndpoint(BaseException): - """The provided endpoint is invalid.""" - - -class CommunicationError(BaseException): - """Unable to communicate with server.""" - - -class ClientException(Exception): - """DEPRECATED""" - - -class HTTPException(ClientException): - """Base exception for all HTTP-derived exceptions""" - code = 'N/A' - - def __init__(self, details=None): - self.details = details - - def __str__(self): - return "%s (HTTP %s)" % (self.__class__.__name__, self.code) - - -class HTTPMultipleChoices(HTTPException): - code = 300 - - def __str__(self): - self.details = ("Requested version of OpenStack Images API is not" - "available.") - return "%s (HTTP %s) %s" % (self.__class__.__name__, self.code, - self.details) - - -class BadRequest(HTTPException): - """DEPRECATED""" - code = 400 - - -class HTTPBadRequest(BadRequest): - pass - - -class Unauthorized(HTTPException): - """DEPRECATED""" - code = 401 - - -class HTTPUnauthorized(Unauthorized): - pass - - -class Forbidden(HTTPException): - """DEPRECATED""" - code = 403 - - -class HTTPForbidden(Forbidden): - pass - - -class NotFound(HTTPException): - """DEPRECATED""" - code = 404 - - -class HTTPNotFound(NotFound): - pass - - -class HTTPMethodNotAllowed(HTTPException): - code = 405 - - -class Conflict(HTTPException): - """DEPRECATED""" - code = 409 - - -class HTTPConflict(Conflict): - pass - - -class OverLimit(HTTPException): - """DEPRECATED""" - code = 413 - - -class HTTPOverLimit(OverLimit): - pass - - -class HTTPInternalServerError(HTTPException): - code = 500 - - -class HTTPNotImplemented(HTTPException): - code = 501 - - -class HTTPBadGateway(HTTPException): - code = 502 - - -class ServiceUnavailable(HTTPException): - """DEPRECATED""" - code = 503 - - -class HTTPServiceUnavailable(ServiceUnavailable): - pass - - -#NOTE(bcwaldon): Build a mapping of HTTP codes to corresponding exception -# classes -_code_map = {} -for obj_name in dir(sys.modules[__name__]): - if obj_name.startswith('HTTP'): - obj = getattr(sys.modules[__name__], obj_name) - _code_map[obj.code] = obj - - -def from_response(response): - """Return an instance of an HTTPException based on httplib response.""" - cls = _code_map.get(response.status, HTTPException) - return cls() - - -class NoTokenLookupException(Exception): - """DEPRECATED""" - pass - - -class EndpointNotFound(Exception): - """DEPRECATED""" - pass diff --git a/python-glazierclient/glazierclient/common/http.py b/python-glazierclient/glazierclient/common/http.py deleted file mode 100644 index 3f1e26f6..00000000 --- a/python-glazierclient/glazierclient/common/http.py +++ /dev/null @@ -1,275 +0,0 @@ -# Copyright 2012 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import copy -import httplib -import logging -import socket -import StringIO -import urlparse - -import os -from glazierclient.common import exceptions - - -try: - import ssl -except ImportError: - #TODO(bcwaldon): Handle this failure more gracefully - pass - -try: - import json -except ImportError: - import simplejson as json - -# Python 2.5 compat fix -if not hasattr(urlparse, 'parse_qsl'): - import cgi - urlparse.parse_qsl = cgi.parse_qsl - -LOG = logging.getLogger(__name__) -USER_AGENT = 'python-glazierclient' -CHUNKSIZE = 1024 * 64 # 64kB - - -class HTTPClient(object): - - def __init__(self, endpoint, **kwargs): - self.endpoint = endpoint - self.auth_token = kwargs.get('token') - self.connection_params = self.get_connection_params(endpoint, **kwargs) - - @staticmethod - def get_connection_params(endpoint, **kwargs): - parts = urlparse.urlparse(endpoint) - - _args = (parts.hostname, parts.port, parts.path) - _kwargs = {'timeout': float(kwargs.get('timeout', 600))} - - if parts.scheme == 'https': - _class = VerifiedHTTPSConnection - _kwargs['ca_file'] = kwargs.get('ca_file', None) - _kwargs['cert_file'] = kwargs.get('cert_file', None) - _kwargs['key_file'] = kwargs.get('key_file', None) - _kwargs['insecure'] = kwargs.get('insecure', False) - elif parts.scheme == 'http': - _class = httplib.HTTPConnection - else: - msg = 'Unsupported scheme: %s' % parts.scheme - raise exceptions.InvalidEndpoint(msg) - - return (_class, _args, _kwargs) - - def get_connection(self): - _class = self.connection_params[0] - try: - return _class(*self.connection_params[1], - **self.connection_params[2]) - except httplib.InvalidURL: - raise exceptions.InvalidEndpoint() - - def log_curl_request(self, method, url, kwargs): - curl = ['curl -i -X %s' % method] - - for (key, value) in kwargs['headers'].items(): - header = '-H \'%s: %s\'' % (key, value) - curl.append(header) - - conn_params_fmt = [ - ('key_file', '--key %s'), - ('cert_file', '--cert %s'), - ('ca_file', '--cacert %s'), - ] - for (key, fmt) in conn_params_fmt: - value = self.connection_params[2].get(key) - if value: - curl.append(fmt % value) - - if self.connection_params[2].get('insecure'): - curl.append('-k') - - if 'body' in kwargs: - curl.append('-d \'%s\'' % kwargs['body']) - - curl.append('%s%s' % (self.endpoint, url)) - LOG.debug(' '.join(curl)) - - @staticmethod - def log_http_response(resp, body=None): - status = (resp.version / 10.0, resp.status, resp.reason) - dump = ['\nHTTP/%.1f %s %s' % status] - dump.extend(['%s: %s' % (k, v) for k, v in resp.getheaders()]) - dump.append('') - if body: - dump.extend([body, '']) - LOG.debug('\n'.join(dump)) - - def _http_request(self, url, method, **kwargs): - """ Send an http request with the specified characteristics. - - Wrapper around httplib.HTTP(S)Connection.request to handle tasks such - as setting headers and error handling. - """ - # Copy the kwargs so we can reuse the original in case of redirects - kwargs['headers'] = copy.deepcopy(kwargs.get('headers', {})) - kwargs['headers'].setdefault('User-Agent', USER_AGENT) - if self.auth_token: - kwargs['headers'].setdefault('X-Auth-Token', self.auth_token) - - self.log_curl_request(method, url, kwargs) - conn = self.get_connection() - - try: - conn_params = self.connection_params[1][2] - conn_url = os.path.normpath('%s/%s' % (conn_params, url)) - conn.request(method, conn_url, **kwargs) - resp = conn.getresponse() - except socket.gaierror as e: - message = "Error finding address for %(url)s: %(e)s" % locals() - raise exceptions.InvalidEndpoint(message=message) - except (socket.error, socket.timeout) as e: - endpoint = self.endpoint - message = "Error communicating with %(endpoint)s %(e)s" % locals() - raise exceptions.CommunicationError(message=message) - - body_iter = ResponseBodyIterator(resp) - - # Read body into string if it isn't obviously image data - if resp.getheader('content-type', None) != 'application/octet-stream': - body_str = ''.join([chunk for chunk in body_iter]) - self.log_http_response(resp, body_str) - body_iter = StringIO.StringIO(body_str) - else: - self.log_http_response(resp) - - if 400 <= resp.status < 600: - LOG.warn("Request returned failure status.") - raise exceptions.from_response(resp) - elif resp.status in (301, 302, 305): - # Redirected. Reissue the request to the new location. - return self._http_request(resp['location'], method, **kwargs) - elif resp.status == 300: - raise exceptions.from_response(resp) - - return resp, body_iter - - def json_request(self, method, url, **kwargs): - kwargs.setdefault('headers', {}) - kwargs['headers'].setdefault('Content-Type', 'application/json') - kwargs['headers'].setdefault('Accept', 'application/json') - - if 'body' in kwargs: - kwargs['body'] = json.dumps(kwargs['body']) - - resp, body_iter = self._http_request(url, method, **kwargs) - - if 'application/json' in resp.getheader('content-type', None): - body = ''.join([chunk for chunk in body_iter]) - try: - body = json.loads(body) - except ValueError: - LOG.error('Could not decode response body as JSON') - else: - body = None - - return resp, body - - def raw_request(self, method, url, **kwargs): - kwargs.setdefault('headers', {}) - kwargs['headers'].setdefault('Content-Type', - 'application/octet-stream') - return self._http_request(url, method, **kwargs) - - -class VerifiedHTTPSConnection(httplib.HTTPSConnection): - """httplib-compatibile connection using client-side SSL authentication - - :see http://code.activestate.com/recipes/ - 577548-https-httplib-client-connection-with-certificate-v/ - """ - - def __init__(self, host, port, key_file=None, cert_file=None, - ca_file=None, timeout=None, insecure=False): - httplib.HTTPSConnection.__init__(self, host, port, key_file=key_file, - cert_file=cert_file) - self.key_file = key_file - self.cert_file = cert_file - if ca_file is not None: - self.ca_file = ca_file - else: - self.ca_file = self.get_system_ca_file() - self.timeout = timeout - self.insecure = insecure - - def connect(self): - """ - Connect to a host on a given (SSL) port. - If ca_file is pointing somewhere, use it to check Server Certificate. - - Redefined/copied and extended from httplib.py:1105 (Python 2.6.x). - This is needed to pass cert_reqs=ssl.CERT_REQUIRED as parameter to - ssl.wrap_socket(), which forces SSL to check server certificate against - our client certificate. - """ - sock = socket.create_connection((self.host, self.port), self.timeout) - - if self._tunnel_host: - self.sock = sock - self._tunnel() - - if self.insecure is True: - kwargs = {'cert_reqs': ssl.CERT_NONE} - else: - kwargs = {'cert_reqs': ssl.CERT_REQUIRED, 'ca_certs': self.ca_file} - - if self.cert_file: - kwargs['certfile'] = self.cert_file - if self.key_file: - kwargs['keyfile'] = self.key_file - - self.sock = ssl.wrap_socket(sock, **kwargs) - - @staticmethod - def get_system_ca_file(): - """"Return path to system default CA file""" - # Standard CA file locations for Debian/Ubuntu, RedHat/Fedora, - # Suse, FreeBSD/OpenBSD - ca_path = ['/etc/ssl/certs/ca-certificates.crt', - '/etc/pki/tls/certs/ca-bundle.crt', - '/etc/ssl/ca-bundle.pem', - '/etc/ssl/cert.pem'] - for ca in ca_path: - if os.path.exists(ca): - return ca - return None - - -class ResponseBodyIterator(object): - """A class that acts as an iterator over an HTTP response.""" - - def __init__(self, resp): - self.resp = resp - - def __iter__(self): - while True: - yield self.next() - - def next(self): - chunk = self.resp.read(CHUNKSIZE) - if chunk: - return chunk - else: - raise StopIteration() diff --git a/python-glazierclient/glazierclient/common/utils.py b/python-glazierclient/glazierclient/common/utils.py deleted file mode 100644 index 7063e65f..00000000 --- a/python-glazierclient/glazierclient/common/utils.py +++ /dev/null @@ -1,119 +0,0 @@ -# Copyright 2012 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import sys -import uuid - -import os -from glazierclient.common import exceptions -import prettytable -from glazierclient.openstack.common import importutils - - -# Decorator for cli-args -def arg(*args, **kwargs): - def _decorator(func): - # Because of the sematics of decorator composition if we just append - # to the options list positional options will appear to be backwards. - func.__dict__.setdefault('arguments', []).insert(0, (args, kwargs)) - return func - return _decorator - - -def pretty_choice_list(l): - return ', '.join("'%s'" % i for i in l) - - -def print_list(objs, fields, field_labels, formatters={}, sortby=0): - pt = prettytable.PrettyTable([f for f in field_labels], caching=False) - pt.align = 'l' - - for o in objs: - row = [] - for field in fields: - if field in formatters: - row.append(formatters[field](o)) - else: - data = getattr(o, field, None) or '' - row.append(data) - pt.add_row(row) - print pt.get_string(sortby=field_labels[sortby]) - - -def print_dict(d, formatters={}): - pt = prettytable.PrettyTable(['Property', 'Value'], caching=False) - pt.align = 'l' - - for field in d.keys(): - if field in formatters: - pt.add_row([field, formatters[field](d[field])]) - else: - pt.add_row([field, d[field]]) - print pt.get_string(sortby='Property') - - -def find_resource(manager, name_or_id): - """Helper for the _find_* methods.""" - # first try to get entity as integer id - try: - if isinstance(name_or_id, int) or name_or_id.isdigit(): - return manager.get(int(name_or_id)) - except exceptions.NotFound: - pass - - # now try to get entity as uuid - try: - uuid.UUID(str(name_or_id)) - return manager.get(name_or_id) - except (ValueError, exceptions.NotFound): - pass - - # finally try to find entity by name - try: - return manager.find(name=name_or_id) - except exceptions.NotFound: - msg = "No %s with a name or ID of '%s' exists." % \ - (manager.resource_class.__name__.lower(), name_or_id) - raise exceptions.CommandError(msg) - - -def string_to_bool(arg): - return arg.strip().lower() in ('t', 'true', 'yes', '1') - - -def env(*vars, **kwargs): - """Search for the first defined of possibly many env vars - - Returns the first environment variable defined in vars, or - returns the default defined in kwargs. - """ - for v in vars: - value = os.environ.get(v, None) - if value: - return value - return kwargs.get('default', '') - - -def import_versioned_module(version, submodule=None): - module = 'glazierclient.v%s' % version - if submodule: - module = '.'.join((module, submodule)) - return importutils.import_module(module) - - -def exit(msg=''): - if msg: - print >> sys.stderr, msg - sys.exit(1) diff --git a/python-glazierclient/glazierclient/openstack/__init__.py b/python-glazierclient/glazierclient/openstack/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/python-glazierclient/glazierclient/openstack/common/__init__.py b/python-glazierclient/glazierclient/openstack/common/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/python-glazierclient/glazierclient/openstack/common/importutils.py b/python-glazierclient/glazierclient/openstack/common/importutils.py deleted file mode 100644 index 9dec764f..00000000 --- a/python-glazierclient/glazierclient/openstack/common/importutils.py +++ /dev/null @@ -1,67 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Import related utilities and helper functions. -""" - -import sys -import traceback - - -def import_class(import_str): - """Returns a class from a string including module and class""" - mod_str, _sep, class_str = import_str.rpartition('.') - try: - __import__(mod_str) - return getattr(sys.modules[mod_str], class_str) - except (ValueError, AttributeError): - raise ImportError('Class %s cannot be found (%s)' % - (class_str, - traceback.format_exception(*sys.exc_info()))) - - -def import_object(import_str, *args, **kwargs): - """Import a class and return an instance of it.""" - return import_class(import_str)(*args, **kwargs) - - -def import_object_ns(name_space, import_str, *args, **kwargs): - """ - Import a class and return an instance of it, first by trying - to find the class in a default namespace, then failing back to - a full path if not found in the default namespace. - """ - import_value = "%s.%s" % (name_space, import_str) - try: - return import_class(import_value)(*args, **kwargs) - except ImportError: - return import_class(import_str)(*args, **kwargs) - - -def import_module(import_str): - """Import a module.""" - __import__(import_str) - return sys.modules[import_str] - - -def try_import(import_str, default=None): - """Try to import a module and if it fails return default.""" - try: - return import_module(import_str) - except ImportError: - return default diff --git a/python-glazierclient/glazierclient/openstack/common/setup.py b/python-glazierclient/glazierclient/openstack/common/setup.py deleted file mode 100644 index 80a0ecee..00000000 --- a/python-glazierclient/glazierclient/openstack/common/setup.py +++ /dev/null @@ -1,359 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. -# Copyright 2012-2013 Hewlett-Packard Development Company, L.P. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Utilities with minimum-depends for use in setup.py -""" - -import email -import os -import re -import subprocess -import sys - -from setuptools.command import sdist - - -def parse_mailmap(mailmap='.mailmap'): - mapping = {} - if os.path.exists(mailmap): - with open(mailmap, 'r') as fp: - for l in fp: - try: - canonical_email, alias = re.match( - r'[^#]*?(<.+>).*(<.+>).*', l).groups() - except AttributeError: - continue - mapping[alias] = canonical_email - return mapping - - -def _parse_git_mailmap(git_dir, mailmap='.mailmap'): - mailmap = os.path.join(os.path.dirname(git_dir), mailmap) - return parse_mailmap(mailmap) - - -def canonicalize_emails(changelog, mapping): - """Takes in a string and an email alias mapping and replaces all - instances of the aliases in the string with their real email. - """ - for alias, email_address in mapping.iteritems(): - changelog = changelog.replace(alias, email_address) - return changelog - - -# Get requirements from the first file that exists -def get_reqs_from_files(requirements_files): - for requirements_file in requirements_files: - if os.path.exists(requirements_file): - with open(requirements_file, 'r') as fil: - return fil.read().split('\n') - return [] - - -def parse_requirements(requirements_files=['requirements.txt', - 'tools/pip-requires']): - requirements = [] - for line in get_reqs_from_files(requirements_files): - # For the requirements list, we need to inject only the portion - # after egg= so that distutils knows the package it's looking for - # such as: - # -e git://github.com/openstack/nova/master#egg=nova - if re.match(r'\s*-e\s+', line): - requirements.append(re.sub(r'\s*-e\s+.*#egg=(.*)$', r'\1', - line)) - # such as: - # http://github.com/openstack/nova/zipball/master#egg=nova - elif re.match(r'\s*https?:', line): - requirements.append(re.sub(r'\s*https?:.*#egg=(.*)$', r'\1', - line)) - # -f lines are for index locations, and don't get used here - elif re.match(r'\s*-f\s+', line): - pass - # argparse is part of the standard library starting with 2.7 - # adding it to the requirements list screws distro installs - elif line == 'argparse' and sys.version_info >= (2, 7): - pass - else: - requirements.append(line) - - return requirements - - -def parse_dependency_links(requirements_files=['requirements.txt', - 'tools/pip-requires']): - dependency_links = [] - # dependency_links inject alternate locations to find packages listed - # in requirements - for line in get_reqs_from_files(requirements_files): - # skip comments and blank lines - if re.match(r'(\s*#)|(\s*$)', line): - continue - # lines with -e or -f need the whole line, minus the flag - if re.match(r'\s*-[ef]\s+', line): - dependency_links.append(re.sub(r'\s*-[ef]\s+', '', line)) - # lines that are only urls can go in unmolested - elif re.match(r'\s*https?:', line): - dependency_links.append(line) - return dependency_links - - -def _run_shell_command(cmd, throw_on_error=False): - if os.name == 'nt': - output = subprocess.Popen(["cmd.exe", "/C", cmd], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - else: - output = subprocess.Popen(["/bin/sh", "-c", cmd], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - out = output.communicate() - if output.returncode and throw_on_error: - raise Exception("%s returned %d" % cmd, output.returncode) - if len(out) == 0: - return None - if len(out[0].strip()) == 0: - return None - return out[0].strip() - - -def _get_git_directory(): - parent_dir = os.path.dirname(__file__) - while True: - git_dir = os.path.join(parent_dir, '.git') - if os.path.exists(git_dir): - return git_dir - parent_dir, child = os.path.split(parent_dir) - if not child: # reached to root dir - return None - - -def write_git_changelog(): - """Write a changelog based on the git changelog.""" - new_changelog = 'ChangeLog' - git_dir = _get_git_directory() - if not os.getenv('SKIP_WRITE_GIT_CHANGELOG'): - if git_dir: - git_log_cmd = 'git --git-dir=%s log --stat' % git_dir - changelog = _run_shell_command(git_log_cmd) - mailmap = _parse_git_mailmap(git_dir) - with open(new_changelog, "w") as changelog_file: - changelog_file.write(canonicalize_emails(changelog, mailmap)) - else: - open(new_changelog, 'w').close() - - -def generate_authors(): - """Create AUTHORS file using git commits.""" - jenkins_email = 'jenkins@review.(openstack|stackforge).org' - old_authors = 'AUTHORS.in' - new_authors = 'AUTHORS' - git_dir = _get_git_directory() - if not os.getenv('SKIP_GENERATE_AUTHORS'): - if git_dir: - # don't include jenkins email address in AUTHORS file - git_log_cmd = ("git --git-dir=" + git_dir + - " log --format='%aN <%aE>' | sort -u | " - "egrep -v '" + jenkins_email + "'") - changelog = _run_shell_command(git_log_cmd) - mailmap = _parse_git_mailmap(git_dir) - with open(new_authors, 'w') as new_authors_fh: - new_authors_fh.write(canonicalize_emails(changelog, mailmap)) - if os.path.exists(old_authors): - with open(old_authors, "r") as old_authors_fh: - new_authors_fh.write('\n' + old_authors_fh.read()) - else: - open(new_authors, 'w').close() - - -_rst_template = """%(heading)s -%(underline)s - -.. automodule:: %(module)s - :members: - :undoc-members: - :show-inheritance: -""" - - -def get_cmdclass(): - """Return dict of commands to run from setup.py.""" - - cmdclass = dict() - - def _find_modules(arg, dirname, files): - for filename in files: - if filename.endswith('.py') and filename != '__init__.py': - arg["%s.%s" % (dirname.replace('/', '.'), - filename[:-3])] = True - - class LocalSDist(sdist.sdist): - """Builds the ChangeLog and Authors files from VC first.""" - - def run(self): - write_git_changelog() - generate_authors() - # sdist.sdist is an old style class, can't use super() - sdist.sdist.run(self) - - cmdclass['sdist'] = LocalSDist - - # If Sphinx is installed on the box running setup.py, - # enable setup.py to build the documentation, otherwise, - # just ignore it - try: - from sphinx.setup_command import BuildDoc - - class LocalBuildDoc(BuildDoc): - - builders = ['html', 'man'] - - def generate_autoindex(self): - print "**Autodocumenting from %s" % os.path.abspath(os.curdir) - modules = {} - option_dict = self.distribution.get_option_dict('build_sphinx') - source_dir = os.path.join(option_dict['source_dir'][1], 'api') - if not os.path.exists(source_dir): - os.makedirs(source_dir) - for pkg in self.distribution.packages: - if '.' not in pkg: - os.path.walk(pkg, _find_modules, modules) - module_list = modules.keys() - module_list.sort() - autoindex_filename = os.path.join(source_dir, 'autoindex.rst') - with open(autoindex_filename, 'w') as autoindex: - autoindex.write(""".. toctree:: - :maxdepth: 1 - -""") - for module in module_list: - output_filename = os.path.join(source_dir, - "%s.rst" % module) - heading = "The :mod:`%s` Module" % module - underline = "=" * len(heading) - values = dict(module=module, heading=heading, - underline=underline) - - print "Generating %s" % output_filename - with open(output_filename, 'w') as output_file: - output_file.write(_rst_template % values) - autoindex.write(" %s.rst\n" % module) - - def run(self): - if not os.getenv('SPHINX_DEBUG'): - self.generate_autoindex() - - for builder in self.builders: - self.builder = builder - self.finalize_options() - self.project = self.distribution.get_name() - self.version = self.distribution.get_version() - self.release = self.distribution.get_version() - BuildDoc.run(self) - - class LocalBuildLatex(LocalBuildDoc): - builders = ['latex'] - - cmdclass['build_sphinx'] = LocalBuildDoc - cmdclass['build_sphinx_latex'] = LocalBuildLatex - except ImportError: - pass - - return cmdclass - - -def _get_revno(git_dir): - """Return the number of commits since the most recent tag. - - We use git-describe to find this out, but if there are no - tags then we fall back to counting commits since the beginning - of time. - """ - describe = _run_shell_command( - "git --git-dir=%s describe --always" % git_dir) - if "-" in describe: - return describe.rsplit("-", 2)[-2] - - # no tags found - revlist = _run_shell_command( - "git --git-dir=%s rev-list --abbrev-commit HEAD" % git_dir) - return len(revlist.splitlines()) - - -def _get_version_from_git(pre_version): - """Return a version which is equal to the tag that's on the current - revision if there is one, or tag plus number of additional revisions - if the current revision has no tag.""" - - git_dir = _get_git_directory() - if git_dir: - if pre_version: - try: - return _run_shell_command( - "git --git-dir=" + git_dir + " describe --exact-match", - throw_on_error=True).replace('-', '.') - except Exception: - sha = _run_shell_command( - "git --git-dir=" + git_dir + " log -n1 --pretty=format:%h") - return "%s.a%s.g%s" % (pre_version, _get_revno(git_dir), sha) - else: - return _run_shell_command( - "git --git-dir=" + git_dir + " describe --always").replace( - '-', '.') - return None - - -def _get_version_from_pkg_info(package_name): - """Get the version from PKG-INFO file if we can.""" - try: - pkg_info_file = open('PKG-INFO', 'r') - except (IOError, OSError): - return None - try: - pkg_info = email.message_from_file(pkg_info_file) - except email.MessageError: - return None - # Check to make sure we're in our own dir - if pkg_info.get('Name', None) != package_name: - return None - return pkg_info.get('Version', None) - - -def get_version(package_name, pre_version=None): - """Get the version of the project. First, try getting it from PKG-INFO, if - it exists. If it does, that means we're in a distribution tarball or that - install has happened. Otherwise, if there is no PKG-INFO file, pull the - version from git. - - We do not support setup.py version sanity in git archive tarballs, nor do - we support packagers directly sucking our git repo into theirs. We expect - that a source tarball be made from our git repo - or that if someone wants - to make a source tarball from a fork of our repo with additional tags in it - that they understand and desire the results of doing that. - """ - version = os.environ.get("OSLO_PACKAGE_VERSION", None) - if version: - return version - version = _get_version_from_pkg_info(package_name) - if version: - return version - version = _get_version_from_git(pre_version) - if version: - return version - raise Exception("Versioning for this project requires either an sdist" - " tarball, or access to an upstream git repository.") diff --git a/python-glazierclient/glazierclient/openstack/common/version.py b/python-glazierclient/glazierclient/openstack/common/version.py deleted file mode 100644 index d0cad115..00000000 --- a/python-glazierclient/glazierclient/openstack/common/version.py +++ /dev/null @@ -1,94 +0,0 @@ - -# Copyright 2012 OpenStack LLC -# Copyright 2012-2013 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Utilities for consuming the version from pkg_resources. -""" - -import pkg_resources - - -class VersionInfo(object): - - def __init__(self, package): - """Object that understands versioning for a package - :param package: name of the python package, such as glance, or - python-glanceclient - """ - self.package = package - self.release = None - self.version = None - self._cached_version = None - - def __str__(self): - """Make the VersionInfo object behave like a string.""" - return self.version_string() - - def __repr__(self): - """Include the name.""" - return "VersionInfo(%s:%s)" % (self.package, self.version_string()) - - def _get_version_from_pkg_resources(self): - """Get the version of the package from the pkg_resources record - associated with the package.""" - try: - requirement = pkg_resources.Requirement.parse(self.package) - provider = pkg_resources.get_provider(requirement) - return provider.version - except pkg_resources.DistributionNotFound: - # The most likely cause for this is running tests in a tree - # produced from a tarball where the package itself has not been - # installed into anything. Revert to setup-time logic. - from glazierclient.openstack.common import setup - return setup.get_version(self.package) - - def release_string(self): - """Return the full version of the package including suffixes indicating - VCS status. - """ - if self.release is None: - self.release = self._get_version_from_pkg_resources() - - return self.release - - def version_string(self): - """Return the short version minus any alpha/beta tags.""" - if self.version is None: - parts = [] - for part in self.release_string().split('.'): - if part[0].isdigit(): - parts.append(part) - else: - break - self.version = ".".join(parts) - - return self.version - - # Compatibility functions - canonical_version_string = version_string - version_string_with_vcs = release_string - - def cached_version_string(self, prefix=""): - """Generate an object which will expand in a string context to - the results of version_string(). We do this so that don't - call into pkg_resources every time we start up a program when - passing version information into the CONF constructor, but - rather only do the calculation when and if a version is requested - """ - if not self._cached_version: - self._cached_version = "%s%s" % (prefix, - self.version_string()) - return self._cached_version diff --git a/python-glazierclient/glazierclient/shell.py b/python-glazierclient/glazierclient/shell.py deleted file mode 100644 index bbadc89d..00000000 --- a/python-glazierclient/glazierclient/shell.py +++ /dev/null @@ -1,314 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Command-line interface to the Glazier Project. -""" - -import argparse -import logging -import sys - -import httplib2 -from keystoneclient.v2_0 import client as ksclient -from glazierclient import client as glazierclient -from glazierclient.common import utils, exceptions - - -logger = logging.getLogger(__name__) - - -class GlazierShell(object): - def get_base_parser(self): - parser = argparse.ArgumentParser( - prog='glazier', - description=__doc__.strip(), - epilog='See glazier help COMMAND" ' - 'for help on a specific command.', - add_help=False, - formatter_class=HelpFormatter, - ) - - # Global arguments - parser.add_argument('-h', '--help', - action='store_true', - help=argparse.SUPPRESS,) - - parser.add_argument('-d', '--debug', - default=bool(utils.env('GLAZIERCLIENT_DEBUG')), - action='store_true', - help='Defaults to env[GLAZIERCLIENT_DEBUG]') - - parser.add_argument('-v', '--verbose', - default=False, action="store_true", - help="Print more verbose output") - - parser.add_argument('-k', '--insecure', - default=False, - action='store_true', - help="Explicitly allow glazierclient to perform " - "\"insecure\" SSL (https) requests. " - "The server's certificate will " - "not be verified against any certificate " - "authorities. This option should be used " - "with caution.") - - parser.add_argument('--cert-file', - help='Path of certificate file to use in SSL ' - 'connection. This file can optionally be ' - 'prepended with the private key.') - - parser.add_argument('--key-file', - help='Path of client key to use in SSL connection.' - ' This option is not necessary if your ' - 'key is prepended to your cert file.') - - parser.add_argument('--ca-file', - help='Path of CA SSL certificate(s) used to verify' - ' the remote server certificate. Without ' - 'this option glance looks for the default ' - 'system CA certificates.') - - parser.add_argument('--timeout', - default=600, - help='Number of seconds to wait for a response') - - parser.add_argument('--os-username', - default=utils.env('OS_USERNAME'), - help='Defaults to env[OS_USERNAME]') - - parser.add_argument('--os-password', - default=utils.env('OS_PASSWORD'), - help='Defaults to env[OS_PASSWORD]') - - parser.add_argument('--os-tenant-id', - default=utils.env('OS_TENANT_ID'), - help='Defaults to env[OS_TENANT_ID]') - - parser.add_argument('--os-tenant-name', - default=utils.env('OS_TENANT_NAME'), - help='Defaults to env[OS_TENANT_NAME]') - - parser.add_argument('--os-auth-url', - default=utils.env('OS_AUTH_URL'), - help='Defaults to env[OS_AUTH_URL]') - - parser.add_argument('--os-region-name', - default=utils.env('OS_REGION_NAME'), - help='Defaults to env[OS_REGION_NAME]') - - parser.add_argument('--os-auth-token', - default=utils.env('OS_AUTH_TOKEN'), - help='Defaults to env[OS_AUTH_TOKEN]') - - parser.add_argument('--glazier-url', - default=utils.env('GLAZIER_URL'), - help='Defaults to env[GLAZIER_URL]') - - parser.add_argument('--glazier-api-version', - default=utils.env( - 'GLAZIER_API_VERSION', default='1'), - help='Defaults to env[GLAZIER_API_VERSION] ' - 'or 1') - - parser.add_argument('--os-service-type', - default=utils.env('OS_SERVICE_TYPE'), - help='Defaults to env[OS_SERVICE_TYPE]') - - parser.add_argument('--os-endpoint-type', - default=utils.env('OS_ENDPOINT_TYPE'), - help='Defaults to env[OS_ENDPOINT_TYPE]') - - return parser - - def get_subcommand_parser(self, version): - parser = self.get_base_parser() - - self.subcommands = {} - subparsers = parser.add_subparsers(metavar='<subcommand>') - submodule = utils.import_versioned_module(version, 'shell') - self._find_actions(subparsers, submodule) - self._find_actions(subparsers, self) - - return parser - - def _find_actions(self, subparsers, actions_module): - for attr in (a for a in dir(actions_module) if a.startswith('do_')): - # I prefer to be hypen-separated instead of underscores. - command = attr[3:].replace('_', '-') - callback = getattr(actions_module, attr) - desc = callback.__doc__ or '' - help = desc.strip().split('\n')[0] - arguments = getattr(callback, 'arguments', []) - - subparser = subparsers.add_parser(command, help=help, - description=desc, - add_help=False, - formatter_class=HelpFormatter) - subparser.add_argument('-h', '--help', action='help', - help=argparse.SUPPRESS) - self.subcommands[command] = subparser - for (args, kwargs) in arguments: - subparser.add_argument(*args, **kwargs) - subparser.set_defaults(func=callback) - - def _get_ksclient(self, **kwargs): - """Get an endpoint and auth token from Keystone. - - :param username: name of user - :param password: user's password - :param tenant_id: unique identifier of tenant - :param tenant_name: name of tenant - :param auth_url: endpoint to authenticate against - """ - return ksclient.Client(username=kwargs.get('username'), - password=kwargs.get('password'), - tenant_id=kwargs.get('tenant_id'), - tenant_name=kwargs.get('tenant_name'), - auth_url=kwargs.get('auth_url'), - insecure=kwargs.get('insecure')) - - def _get_endpoint(self, client, **kwargs): - """Get an endpoint using the provided keystone client.""" - return client.service_catalog.url_for( - service_type=kwargs.get('service_type') or 'metering', - endpoint_type=kwargs.get('endpoint_type') or 'publicURL') - - def _setup_debugging(self, debug): - if debug: - logging.basicConfig( - format="%(levelname)s (%(module)s:%(lineno)d) %(message)s", - level=logging.DEBUG) - - httplib2.debuglevel = 1 - else: - logging.basicConfig( - format="%(levelname)s %(message)s", - level=logging.INFO) - - def main(self, argv): - # Parse args once to find version - parser = self.get_base_parser() - (options, args) = parser.parse_known_args(argv) - self._setup_debugging(options.debug) - - # build available subcommands based on version - api_version = options.glazier_api_version - subcommand_parser = self.get_subcommand_parser(api_version) - self.parser = subcommand_parser - - # Handle top-level --help/-h before attempting to parse - # a command off the command line - if options.help or not argv: - self.do_help(options) - return 0 - - # Parse args again and call whatever callback was selected - args = subcommand_parser.parse_args(argv) - - # Short-circuit and deal with help command right away. - if args.func == self.do_help: - self.do_help(args) - return 0 - - if args.os_auth_token and args.glazier_url: - token = args.os_auth_token - endpoint = args.glazier_url - else: - if not args.os_username: - raise exceptions.CommandError("You must provide a username " - "via either --os-username " - "or via env[OS_USERNAME]") - - if not args.os_password: - raise exceptions.CommandError("You must provide a password " - "via either --os-password " - "or via env[OS_PASSWORD]") - - if not (args.os_tenant_id or args.os_tenant_name): - raise exceptions.CommandError("You must provide a tenant_id " - "via either --os-tenant-id " - "or via env[OS_TENANT_ID]") - - if not args.os_auth_url: - raise exceptions.CommandError("You must provide an auth url " - "via either --os-auth-url or " - "via env[OS_AUTH_URL]") - kwargs = { - 'username': args.os_username, - 'password': args.os_password, - 'tenant_id': args.os_tenant_id, - 'tenant_name': args.os_tenant_name, - 'auth_url': args.os_auth_url, - 'service_type': args.os_service_type, - 'endpoint_type': args.os_endpoint_type, - 'insecure': args.insecure - } - _ksclient = self._get_ksclient(**kwargs) - token = args.os_auth_token or _ksclient.auth_token - - url = args.glazier_url - endpoint = url or self._get_endpoint(_ksclient, **kwargs) - - kwargs = { - 'token': token, - 'insecure': args.insecure, - 'timeout': args.timeout, - 'ca_file': args.ca_file, - 'cert_file': args.cert_file, - 'key_file': args.key_file, - } - - client = glazierclient.Client(api_version, endpoint, **kwargs) - - try: - args.func(client, args) - except exceptions.Unauthorized: - msg = "Invalid OpenStack Identity credentials." - raise exceptions.CommandError(msg) - - @utils.arg('command', metavar='<subcommand>', nargs='?', - help='Display help for <subcommand>') - def do_help(self, args): - """ - Display help about this program or one of its subcommands. - """ - if getattr(args, 'command', None): - if args.command in self.subcommands: - self.subcommands[args.command].print_help() - else: - msg = "'%s' is not a valid subcommand" - raise exceptions.CommandError(msg % args.command) - else: - self.parser.print_help() - - -class HelpFormatter(argparse.HelpFormatter): - def start_section(self, heading): - # Title-case the headings - heading = '%s%s' % (heading[0].upper(), heading[1:]) - super(HelpFormatter, self).start_section(heading) - - -def main(): - try: - GlazierShell().main(sys.argv[1:]) - - except Exception, e: - print >> sys.stderr, e - sys.exit(1) - - -if __name__ == "__main__": - main() diff --git a/python-glazierclient/glazierclient/v1/__init__.py b/python-glazierclient/glazierclient/v1/__init__.py deleted file mode 100644 index dbe3f042..00000000 --- a/python-glazierclient/glazierclient/v1/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from glazierclient.v1.client import Client diff --git a/python-glazierclient/glazierclient/v1/client.py b/python-glazierclient/glazierclient/v1/client.py deleted file mode 100644 index 960691ce..00000000 --- a/python-glazierclient/glazierclient/v1/client.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from glazierclient.common import http -from glazierclient.v1 import environments, sessions, services - - -class Client(http.HTTPClient): - """Client for the Glazier v1 API. - - :param string endpoint: A user-supplied endpoint URL for the service. - :param string token: Token for authentication. - :param integer timeout: Allows customization of the timeout for client - http requests. (optional) - """ - - def __init__(self, *args, **kwargs): - """ Initialize a new client for the Glazier v1 API. """ - super(Client, self).__init__(*args, **kwargs) - self.environments = environments.EnvironmentManager(self) - self.sessions = sessions.SessionManager(self) - self.activeDirectories = services.ActiveDirectoryManager(self) - self.webServers = services.WebServerManager(self) diff --git a/python-glazierclient/glazierclient/v1/environments.py b/python-glazierclient/glazierclient/v1/environments.py deleted file mode 100644 index 43705c01..00000000 --- a/python-glazierclient/glazierclient/v1/environments.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from glazierclient.common import base - - -class Environment(base.Resource): - def __repr__(self): - return "<Environment %s>" % self._info - - def data(self, **kwargs): - return self.manager.data(self, **kwargs) - - -class EnvironmentManager(base.Manager): - resource_class = Environment - - def list(self): - return self._list('environments', 'environments') - - def create(self, name): - return self._create('environments', {'name': name}) - - def update(self, environment_id, name): - return self._update('environments/{id}'.format(id=environment_id), - {'name': name}) - - def delete(self, environment_id): - return self._delete('environments/{id}'.format(id=environment_id)) - - def get(self, environment_id): - return self._get("environments/{id}".format(id=environment_id)) diff --git a/python-glazierclient/glazierclient/v1/services.py b/python-glazierclient/glazierclient/v1/services.py deleted file mode 100644 index 39874a68..00000000 --- a/python-glazierclient/glazierclient/v1/services.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from glazierclient.common import base - - -class ActiveDirectory(base.Resource): - def __repr__(self): - return '<ActiveDirectory %s>' % self._info - - def data(self, **kwargs): - return self.manager.data(self, **kwargs) - - -class ActiveDirectoryManager(base.Manager): - resource_class = ActiveDirectory - - def list(self, environment_id, session_id=None): - if session_id: - headers = {'X-Configuration-Session': session_id} - else: - headers = {} - - return self._list('environments/{id}/activeDirectories'. - format(id=environment_id), - 'activeDirectories', - headers=headers) - - def create(self, environment_id, session_id, active_directory): - headers = {'X-Configuration-Session': session_id} - - return self._create('environments/{id}/activeDirectories'. - format(id=environment_id), - active_directory, - headers=headers) - - def delete(self, environment_id, session_id, service_id): - headers = {'X-Configuration-Session': session_id} - path = 'environments/{id}/activeDirectories/{active_directory_id}' - path = path.format(id=environment_id, active_directory_id=service_id) - - return self._delete(path, headers=headers) - - -class WebServer(base.Resource): - def __repr__(self): - return '<WebServer %s>' % self._info - - def data(self, **kwargs): - return self.manager.data(self, **kwargs) - - -class WebServerManager(base.Manager): - resource_class = WebServer - - def list(self, environment_id, session_id=None): - if session_id: - headers = {'X-Configuration-Session': session_id} - else: - headers = {} - - return self._list('environments/{id}/webServers'. - format(id=environment_id), - 'webServers', - headers=headers) - - def create(self, environment_id, session_id, web_server): - headers = {'X-Configuration-Session': session_id} - - return self._create('environments/{id}/webServers'. - format(id=environment_id), - web_server, - headers=headers) - - def delete(self, environment_id, session_id, service_id): - headers = {'X-Configuration-Session': session_id} - - return self._delete('environments/{id}/webServers/{web_server_id}' - .format(id=environment_id, - web_server_id=service_id), - headers=headers) diff --git a/python-glazierclient/glazierclient/v1/sessions.py b/python-glazierclient/glazierclient/v1/sessions.py deleted file mode 100644 index 2efcf7f3..00000000 --- a/python-glazierclient/glazierclient/v1/sessions.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from glazierclient.common import base - - -class Session(base.Resource): - def __repr__(self): - return '<Session %s>' % self._info - - def data(self, **kwargs): - return self.manager.data(self, **kwargs) - - -class Status(base.Resource): - def __repr__(self): - return '<Status %s>' % self._info - - def data(self, **kwargs): - return self.manager.data(self, **kwargs) - - -class SessionManager(base.Manager): - resource_class = Session - - def list(self, environment_id): - return self._list('environments/{id}/sessions'. - format(id=environment_id), 'sessions') - - def get(self, environment_id, session_id): - return self._get('environments/{id}/sessions/{session_id}'. - format(id=environment_id, session_id=session_id)) - - def configure(self, environment_id): - return self._create('environments/{id}/configure'. - format(id=environment_id), None) - - def deploy(self, environment_id, session_id): - path = 'environments/{id}/sessions/{session_id}/deploy' - self.api.json_request('POST', - path.format(id=environment_id, - session_id=session_id)) - - def reports(self, environment_id, session_id, service_id=None): - path = 'environments/{id}/sessions/{session_id}/reports' - path = path.format(id=environment_id, session_id=session_id) - if service_id: - path += '?service_id={0}'.format(service_id) - - resp, body = self.api.json_request('GET', path) - - data = body.get('reports', []) - return [Status(self, res, loaded=True) for res in data if res] - - def delete(self, environment_id, session_id): - return self._delete("environments/{id}/sessions/{session_id}". - format(id=environment_id, session_id=session_id)) diff --git a/python-glazierclient/glazierclient/v1/shell.py b/python-glazierclient/glazierclient/v1/shell.py deleted file mode 100644 index 8f4a619c..00000000 --- a/python-glazierclient/glazierclient/v1/shell.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from glazierclient.common import utils - - -def do_environment_list(cc, args={}): - """List the environments""" - environments = cc.environments.list() - field_labels = ['ID', 'Name', 'Created', 'Updated'] - fields = ['id', 'name', 'created', 'updated'] - utils.print_list(environments, fields, field_labels, sortby=0) diff --git a/python-glazierclient/glazierclient/version.py b/python-glazierclient/glazierclient/version.py deleted file mode 100644 index 8c38a387..00000000 --- a/python-glazierclient/glazierclient/version.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - - -from glazierclient.openstack.common import version as common_version - -version_info = common_version.VersionInfo('python-glazierclient') diff --git a/python-glazierclient/openstack-common.conf b/python-glazierclient/openstack-common.conf deleted file mode 100644 index eefdecce..00000000 --- a/python-glazierclient/openstack-common.conf +++ /dev/null @@ -1,7 +0,0 @@ -[DEFAULT] - -# The list of modules to copy from openstack-common -modules=setup,importutils,version - -# The base module to hold the copy of openstack.common -base=glazierclient diff --git a/python-glazierclient/run_tests.sh b/python-glazierclient/run_tests.sh deleted file mode 100755 index 1e790721..00000000 --- a/python-glazierclient/run_tests.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash - -function usage { - echo "Usage: $0 [OPTION]..." - echo "Run python-glazierclient's test suite(s)" - echo "" - echo " -p, --pep8 Just run pep8" - echo " -h, --help Print this usage message" - echo "" - echo "This script is deprecated and currently retained for compatibility." - echo 'You can run the full test suite for multiple environments by running "tox".' - echo 'You can run tests for only python 2.7 by running "tox -e py27", or run only' - echo 'the pep8 tests with "tox -e pep8".' - exit -} - -command -v tox > /dev/null 2>&1 -if [ $? -ne 0 ]; then - echo 'This script requires "tox" to run.' - echo 'You can install it with "pip install tox".' - exit 1; -fi - -just_pep8=0 - -function process_option { - case "$1" in - -h|--help) usage;; - -p|--pep8) let just_pep8=1;; - esac -} - -for arg in "$@"; do - process_option $arg -done - -if [ $just_pep8 -eq 1 ]; then - tox -e pep8 - exit -fi - -tox -e py27 $toxargs 2>&1 | tee run_tests.err.log || exit -if [ ${PIPESTATUS[0]} -ne 0 ]; then - exit ${PIPESTATUS[0]} -fi - -if [ -z "$toxargs" ]; then - tox -e pep8 -fi diff --git a/python-glazierclient/setup.cfg b/python-glazierclient/setup.cfg deleted file mode 100644 index 78493eca..00000000 --- a/python-glazierclient/setup.cfg +++ /dev/null @@ -1,15 +0,0 @@ -[nosetests] -cover-package = glazierclient -cover-html = true -cover-erase = true -cover-inclusive = true -verbosity=2 -detailed-errors=1 - -[build_sphinx] -source-dir = doc/source -build-dir = doc/build -all_files = 1 - -[upload_sphinx] -upload-dir = doc/build/html diff --git a/python-glazierclient/setup.py b/python-glazierclient/setup.py deleted file mode 100644 index 56cbead2..00000000 --- a/python-glazierclient/setup.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import setuptools - -from glazierclient.openstack.common import setup - -project = 'python-glazierclient' - - -setuptools.setup( - name=project, - version=setup.get_version(project, '2013.1'), - author='Mirantis, Inc.', - author_email='smelikyan@mirantis.com', - description="Client library for Glazier Project", - license='Apache', - url='http://glazier.mirantis.com/', - packages=setuptools.find_packages(exclude=['tests', 'tests.*']), - include_package_data=True, - install_requires=setup.parse_requirements(), - test_suite="nose.collector", - cmdclass=setup.get_cmdclass(), - classifiers=[ - 'Development Status :: 4 - Beta', - 'Environment :: Console', - 'Intended Audience :: Developers', - 'Intended Audience :: Information Technology', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - ], - entry_points={ - 'console_scripts': ['glazier = glazierclient.shell:main'] - }, - dependency_links=setup.parse_dependency_links(), - tests_require=setup.parse_requirements(['tools/test-requires']), - setup_requires=['setuptools-git>=0.4'], -) diff --git a/python-glazierclient/tests/__init__.py b/python-glazierclient/tests/__init__.py deleted file mode 100644 index 207fa154..00000000 --- a/python-glazierclient/tests/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. \ No newline at end of file diff --git a/python-glazierclient/tests/glazierclient/__init__.py b/python-glazierclient/tests/glazierclient/__init__.py deleted file mode 100644 index 207fa154..00000000 --- a/python-glazierclient/tests/glazierclient/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. \ No newline at end of file diff --git a/python-glazierclient/tests/glazierclient/test_client_with_fake_http.py b/python-glazierclient/tests/glazierclient/test_client_with_fake_http.py deleted file mode 100644 index 7c7eaad1..00000000 --- a/python-glazierclient/tests/glazierclient/test_client_with_fake_http.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import unittest -import logging -from httpretty import HTTPretty, httprettified -from glazierclient.client import Client - - -LOG = logging.getLogger('Unit tests') - - -class UnitTestsForClassesAndFunctions(unittest.TestCase): - - @httprettified - def test_client_env_list_with_empty_list(self): - HTTPretty.register_uri(HTTPretty.GET, - "http://no-resolved-host:8001/environments", - body='{"environments": []}', - adding_headers={ - 'Content-Type': 'application/json',}) - endpoint = 'http://no-resolved-host:8001' - test_client = Client('1', endpoint=endpoint, token='1', timeout=10) - - result = test_client.environments.list() - assert result == [] - - @httprettified - def test_client_env_list_with_elements(self): - body = ('{"environments":[' - '{"id": "0ce373a477f211e187a55404a662f968",' - '"name": "dc1",' - '"created": "2010-11-30T03:23:42Z",' - '"updated": "2010-11-30T03:23:44Z",' - '"tenant-id": "0849006f7ce94961b3aab4e46d6f229a"},' - '{"id": "0ce373a477f211e187a55404a662f961",' - '"name": "dc2",' - '"created": "2010-11-30T03:23:42Z",' - '"updated": "2010-11-30T03:23:44Z",' - '"tenant-id": "0849006f7ce94961b3aab4e4626f229a"}' - ']}') - HTTPretty.register_uri(HTTPretty.GET, - "http://no-resolved-host:8001/environments", - body=body, - adding_headers={ - 'Content-Type': 'application/json',}) - endpoint = 'http://no-resolved-host:8001' - test_client = Client('1', endpoint=endpoint, token='1', timeout=10) - - result = test_client.environments.list() - assert result[0].name == 'dc1' - assert result[-1].name == 'dc2' - - @httprettified - def test_client_env_create(self): - body = ('{"id": "0ce373a477f211e187a55404a662f968",' - '"name": "test",' - '"created": "2010-11-30T03:23:42Z",' - '"updated": "2010-11-30T03:23:44Z",' - '"tenant-id": "0849006f7ce94961b3aab4e46d6f229a"}' - ) - HTTPretty.register_uri(HTTPretty.POST, - "http://no-resolved-host:8001/environments", - body=body, - adding_headers={ - 'Content-Type': 'application/json',}) - endpoint = 'http://no-resolved-host:8001' - test_client = Client('1', endpoint=endpoint, token='1', timeout=10) - - result = test_client.environments.create('test') - assert result.name == 'test' - - @httprettified - def test_client_ad_list(self): - body = ('{"activeDirectories": [{' - '"id": "1",' - '"name": "dc1",' - '"created": "2010-11-30T03:23:42Z",' - '"updated": "2010-11-30T03:23:44Z",' - '"configuration": "standalone",' - '"units": [{' - '"id": "0ce373a477f211e187a55404a662f961",' - '"type": "master",' - '"location": "test"}]}]}') - url = ("http://no-resolved-host:8001/environments" - "/1/activeDirectories") - HTTPretty.register_uri(HTTPretty.GET, url, - body=body, - adding_headers={ - 'Content-Type': 'application/json',}) - endpoint = 'http://no-resolved-host:8001' - test_client = Client('1', endpoint=endpoint, token='1', timeout=10) - - result = test_client.activeDirectories.list('1', 'test') - assert result[0].name == 'dc1' - - @httprettified - def test_client_ad_create(self): - body = ('{' - '"id": "1",' - '"name": "ad1",' - '"created": "2010-11-30T03:23:42Z",' - '"updated": "2010-11-30T03:23:44Z",' - '"configuration": "standalone",' - '"units": [{' - '"id": "0ce373a477f211e187a55404a662f961",' - '"type": "master",' - '"location": "test"}]}') - url = ("http://no-resolved-host:8001/environments" - "/1/activeDirectories") - HTTPretty.register_uri(HTTPretty.POST, url, - body=body, - adding_headers={ - 'Content-Type': 'application/json',}) - endpoint = 'http://no-resolved-host:8001' - test_client = Client('1', endpoint=endpoint, token='1', timeout=10) - - result = test_client.activeDirectories.create('1', 'test', 'ad1') - assert result.name == 'ad1' - - @httprettified - def test_client_ad_list_without_elements(self): - body = ('{"activeDirectories": []}') - url = ("http://no-resolved-host:8001/environments" - "/1/activeDirectories") - HTTPretty.register_uri(HTTPretty.GET, url, - body=body, - adding_headers={ - 'Content-Type': 'application/json',}) - endpoint = 'http://no-resolved-host:8001' - test_client = Client('1', endpoint=endpoint, token='1', timeout=10) - - result = test_client.activeDirectories.list('1', 'test') - assert result == [] - - @httprettified - def test_client_iis_list(self): - body = ('{"webServers": [{' - '"id": "1",' - '"name": "iis11",' - '"created": "2010-11-30T03:23:42Z",' - '"updated": "2010-11-30T03:23:44Z",' - '"domain": "acme",' - '"units": [{' - '"id": "0ce373a477f211e187a55404a662f961",' - '"endpoint": {"host": "1.1.1.1"},' - '"location": "test"}]}]}') - url = ("http://no-resolved-host:8001/environments" - "/1/webServers") - HTTPretty.register_uri(HTTPretty.GET, url, - body=body, - adding_headers={ - 'Content-Type': 'application/json',}) - endpoint = 'http://no-resolved-host:8001' - test_client = Client('1', endpoint=endpoint, token='1', timeout=10) - - result = test_client.webServers.list('1', 'test') - assert result[0].name == 'iis11' - - @httprettified - def test_client_iis_create(self): - body = ('{' - '"id": "1",' - '"name": "iis12",' - '"created": "2010-11-30T03:23:42Z",' - '"updated": "2010-11-30T03:23:44Z",' - '"domain": "acme",' - '"units": [{' - '"id": "0ce373a477f211e187a55404a662f961",' - '"endpoint": {"host": "1.1.1.1"},' - '"location": "test"}]}') - url = ("http://no-resolved-host:8001/environments" - "/1/webServers") - HTTPretty.register_uri(HTTPretty.POST, url, - body=body, - adding_headers={ - 'Content-Type': 'application/json',}) - endpoint = 'http://no-resolved-host:8001' - test_client = Client('1', endpoint=endpoint, token='1', timeout=10) - - result = test_client.webServers.create('1', 'test', 'iis12') - assert result.name == 'iis12' - - @httprettified - def test_client_iis_list_without_elements(self): - body = ('{"webServers": []}') - url = ("http://no-resolved-host:8001/environments" - "/1/webServers") - HTTPretty.register_uri(HTTPretty.GET, url, - body=body, - adding_headers={ - 'Content-Type': 'application/json',}) - endpoint = 'http://no-resolved-host:8001' - test_client = Client('1', endpoint=endpoint, token='1', timeout=10) - - result = test_client.webServers.list('1', 'test') - assert result == [] diff --git a/python-glazierclient/tests/glazierclient/test_methods.py b/python-glazierclient/tests/glazierclient/test_methods.py deleted file mode 100644 index 31b9751b..00000000 --- a/python-glazierclient/tests/glazierclient/test_methods.py +++ /dev/null @@ -1,481 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import unittest -import logging -from mock import MagicMock - -from glazierclient.client import Client -import glazierclient.v1.environments as environments -import glazierclient.v1.services as services -import glazierclient.v1.sessions as sessions - -def my_mock(*a, **b): - return [a, b] - -LOG = logging.getLogger('Unit tests') -api = MagicMock(json_request=my_mock) - - -class UnitTestsForClassesAndFunctions(unittest.TestCase): - - def test_create_client_instance(self): - - endpoint = 'http://no-resolved-host:8001' - test_client = Client('1', endpoint=endpoint, token='1', timeout=10) - - assert test_client.environments is not None - assert test_client.sessions is not None - assert test_client.activeDirectories is not None - assert test_client.webServers is not None - - def test_env_manager_list(self): - manager = environments.EnvironmentManager(api) - result = manager.list() - assert result == [] - - def test_env_manager_create(self): - manager = environments.EnvironmentManager(api) - result = manager.create('test') - assert result.body == {'name': 'test'} - - def test_env_manager_create_with_named_parameters(self): - manager = environments.EnvironmentManager(api) - result = manager.create(name='test') - assert result.body == {'name': 'test'} - - def test_env_manager_create_negative_without_parameters(self): - result = 'Exception' - manager = environments.EnvironmentManager(api) - try: - result = manager.create() - except TypeError: - pass - assert result is 'Exception' - - def test_env_manager_delete(self): - manager = environments.EnvironmentManager(api) - result = manager.delete('test') - assert result is None - - def test_env_manager_delete_with_named_parameters(self): - manager = environments.EnvironmentManager(api) - result = manager.delete(environment_id='1') - assert result is None - - def test_env_manager_delete_negative_without_parameters(self): - result = 'Exception' - manager = environments.EnvironmentManager(api) - try: - result = manager.delete() - except TypeError: - pass - assert result is 'Exception' - - def test_env_manager_update(self): - manager = environments.EnvironmentManager(api) - result = manager.update('1', 'test') - assert result.body == {'name': 'test'} - - def test_env_manager_update_with_named_parameters(self): - manager = environments.EnvironmentManager(api) - result = manager.update(environment_id='1', - name='test') - assert result.body == {'name': 'test'} - - def test_env_manager_update_negative_with_one_parameter(self): - result = 'Exception' - manager = environments.EnvironmentManager(api) - try: - result = manager.update('test') - except TypeError: - pass - assert result is 'Exception' - - def test_env_manager_update_negative_without_parameters(self): - result = 'Exception' - manager = environments.EnvironmentManager(api) - try: - result = manager.update() - except TypeError: - pass - assert result is 'Exception' - - def test_env_manager_get(self): - manager = environments.EnvironmentManager(api) - result = manager.get('test') - ## WTF? - assert result.manager is not None - - def test_env(self): - environment = environments.Environment(api, api) - assert environment.data() is not None - - def test_ad_manager_list_with_one_parameter(self): - manager = services.ActiveDirectoryManager(api) - result = manager.list('datacenter1') - assert result == [] - - def test_ad_manager_list_with_all_parameters(self): - manager = services.ActiveDirectoryManager(api) - result = manager.list('test', '1') - assert result == [] - - def test_ad_manager_list_with_named_parameters(self): - manager = services.ActiveDirectoryManager(api) - result = manager.list(environment_id='test', session_id='1') - assert result == [] - - def test_ad_manager_list_with_named_parameter(self): - manager = services.ActiveDirectoryManager(api) - result = manager.list(environment_id='test') - assert result == [] - - def test_ad_manager_list_negative_without_parameters(self): - result = 'Exception' - manager = services.ActiveDirectoryManager(api) - try: - result = manager.list() - except TypeError: - pass - assert result is 'Exception' - - def test_ad_manager_create(self): - manager = services.ActiveDirectoryManager(api) - result = manager.create('datacenter1', 'session1', 'test') - assert result.headers == {'X-Configuration-Session': 'session1'} - assert result.body == 'test' - - def test_ad_manager_create_with_named_parameters(self): - manager = services.ActiveDirectoryManager(api) - result = manager.create(environment_id='datacenter1', - session_id='session2', - active_directory='test2') - assert result.headers == {'X-Configuration-Session': 'session2'} - assert result.body == 'test2' - - def test_ad_manager_create_negative_with_two_parameters(self): - result = 'Exception' - manager = services.ActiveDirectoryManager(api) - try: - result = manager.create('datacenter1', 'session1') - except TypeError: - pass - assert result is 'Exception' - - def test_ad_manager_create_negative_with_one_parameter(self): - result = 'Exception' - manager = services.ActiveDirectoryManager(api) - try: - result = manager.create('datacenter1') - except TypeError: - pass - assert result is 'Exception' - - def test_ad_manager_create_negative_without_parameters(self): - result = 'Exception' - manager = services.ActiveDirectoryManager(api) - try: - result = manager.create() - except TypeError: - pass - assert result is 'Exception' - - def test_ad_manager_delete(self): - manager = services.ActiveDirectoryManager(api) - result = manager.delete('datacenter1', 'session1', 'test') - assert result is None - - def test_ad_manager_delete_with_named_parameters(self): - manager = services.ActiveDirectoryManager(api) - result = manager.delete(environment_id='datacenter1', - session_id='session1', - service_id='test') - assert result is None - - def test_ad_manager_delete_negative_with_two_parameters(self): - result = 'Exception' - manager = services.ActiveDirectoryManager(api) - try: - result = manager.delete('datacenter1', 'session1') - except TypeError: - pass - assert result == 'Exception' - - def test_ad_manager_delete_negative_with_one_parameter(self): - result = 'Exception' - manager = services.ActiveDirectoryManager(api) - try: - result = manager.delete('datacenter1') - except TypeError: - pass - assert result == 'Exception' - - def test_ad_manager_delete_negative_without_parameters(self): - result = 'Exception' - manager = services.ActiveDirectoryManager(api) - try: - result = manager.delete() - except TypeError: - pass - assert result == 'Exception' - - def test_iis_manager_list_with_one_parameter(self): - manager = services.WebServerManager(api) - result = manager.list('datacenter1') - assert result == [] - - def test_iis_manager_list_with_named_parameter(self): - manager = services.WebServerManager(api) - result = manager.list(environment_id='datacenter1') - assert result == [] - - def test_iis_manager_list_with_all_parameters(self): - manager = services.WebServerManager(api) - result = manager.list('test', '1') - assert result == [] - - def test_iis_manager_list_with_named_parameters(self): - manager = services.WebServerManager(api) - result = manager.list(environment_id='test', - session_id='1') - assert result == [] - - def test_iis_manager_list_negative_without_parameters(self): - result = 'Exception' - manager = services.WebServerManager(api) - try: - result = manager.list() - except TypeError: - pass - assert result == 'Exception' - - def test_iis_manager_create(self): - manager = services.WebServerManager(api) - result = manager.create('datacenter1', 'session1', 'test') - assert result.headers == {'X-Configuration-Session': 'session1'} - assert result.body == 'test' - - def test_iis_manager_create_with_named_parameters(self): - manager = services.WebServerManager(api) - result = manager.create(environment_id='datacenter', - session_id='session', - web_server='test2') - assert result.headers == {'X-Configuration-Session': 'session'} - assert result.body == 'test2' - - def test_iis_manager_create_negative_with_two_parameters(self): - result = 'Exception' - manager = services.WebServerManager(api) - try: - result = manager.create('datacenter1', 'session1') - except TypeError: - pass - assert result == 'Exception' - - def test_iis_manager_create_negative_with_one_parameter(self): - result = 'Exception' - manager = services.WebServerManager(api) - try: - result = manager.create('datacenter1') - except TypeError: - pass - assert result == 'Exception' - - def test_iis_manager_create_negative_without_parameters(self): - result = 'Exception' - manager = services.WebServerManager(api) - try: - result = manager.create() - except TypeError: - pass - assert result == 'Exception' - - def test_iis_manager_delete(self): - manager = services.WebServerManager(api) - result = manager.delete('datacenter1', 'session1', 'test') - assert result is None - - def test_iis_manager_delete_with_named_parameters(self): - manager = services.WebServerManager(api) - result = manager.delete(environment_id='datacenter', - session_id='session', - service_id='test') - assert result is None - - def test_iis_manager_delete_negative_with_two_parameters(self): - result = 'Exception' - manager = services.WebServerManager(api) - try: - result = manager.delete('datacenter1', 'session1') - except TypeError: - pass - assert result == 'Exception' - - def test_iis_manager_delete_negative_with_one_parameter(self): - result = 'Exception' - manager = services.WebServerManager(api) - try: - result = manager.delete('datacenter1') - except TypeError: - pass - assert result == 'Exception' - - def test_iis_manager_delete_negative_without_parameters(self): - result = 'Exception' - manager = services.WebServerManager(api) - try: - result = manager.delete() - except TypeError: - pass - assert result == 'Exception' - - def test_service_ad(self): - service_ad = services.ActiveDirectory(api, api) - assert service_ad.data() is not None - - def test_service_iis(self): - service_iis = services.ActiveDirectory(api, api) - assert service_iis.data() is not None - - def test_session_manager_list(self): - manager = sessions.SessionManager(api) - result = manager.list('datacenter1') - assert result == [] - - def test_session_manager_list_with_named_parameters(self): - manager = sessions.SessionManager(api) - result = manager.list(environment_id='datacenter1') - assert result == [] - - def test_session_manager_list_negative_without_parameters(self): - result = 'Exception' - manager = sessions.SessionManager(api) - try: - result = manager.list() - except TypeError: - pass - assert result == 'Exception' - - def test_session_manager_delete(self): - manager = sessions.SessionManager(api) - result = manager.delete('datacenter1', 'session1') - assert result is None - - def test_session_manager_delete_with_named_parameters(self): - manager = sessions.SessionManager(api) - result = manager.delete(environment_id='datacenter1', - session_id='session1') - assert result is None - - def test_session_manager_delete_negative_with_one_parameter(self): - result = 'Exception' - manager = sessions.SessionManager(api) - try: - result = manager.delete('datacenter1') - except TypeError: - pass - assert result == 'Exception' - - def test_session_manager_delete_negative_without_parameters(self): - result = 'Exception' - manager = sessions.SessionManager(api) - try: - result = manager.delete() - except TypeError: - pass - assert result == 'Exception' - - def test_session_manager_get(self): - manager = sessions.SessionManager(api) - result = manager.get('datacenter1', 'session1') - # WTF? - assert result.manager is not None - - def test_session_manager_configure(self): - manager = sessions.SessionManager(api) - result = manager.configure('datacenter1') - assert result is not None - - def test_session_manager_configure_with_named_parameter(self): - manager = sessions.SessionManager(api) - result = manager.configure(environment_id='datacenter1') - assert result is not None - - def test_session_manager_configure_negative_without_parameters(self): - result = 'Exception' - manager = sessions.SessionManager(api) - try: - result = manager.configure() - except TypeError: - pass - assert result == 'Exception' - - def test_session_manager_deploy(self): - manager = sessions.SessionManager(api) - result = manager.deploy('datacenter1', '1') - assert result is None - - def test_session_manager_deploy_with_named_parameters(self): - manager = sessions.SessionManager(api) - result = manager.deploy(environment_id='datacenter1', - session_id='1') - assert result is None - - def test_session_manager_deploy_negative_with_one_parameter(self): - result = 'Exception' - manager = sessions.SessionManager(api) - try: - result = manager.deploy('datacenter1') - except TypeError: - pass - assert result == 'Exception' - - def test_session_manager_deploy_negative_without_parameters(self): - result = 'Exception' - manager = sessions.SessionManager(api) - try: - result = manager.deploy() - except TypeError: - pass - assert result == 'Exception' - - def test_session_manager_reports(self): - manager = sessions.SessionManager(api) - result = manager.reports('datacenter1', '1') - assert result == [] - - def test_session_manager_reports_with_named_parameters(self): - manager = sessions.SessionManager(api) - result = manager.reports(environment_id='datacenter1', - session_id='1') - assert result == [] - - def test_session_manager_reports_negative_with_one_parameter(self): - result = 'Exception' - manager = sessions.SessionManager(api) - try: - result = manager.reports('datacenter1') - except TypeError: - pass - assert result == 'Exception' - - def test_session_manager_reports_negative_without_parameters(self): - result = 'Exception' - manager = sessions.SessionManager(api) - try: - result = manager.reports() - except TypeError: - pass - assert result == 'Exception' diff --git a/python-glazierclient/tools/install_venv.py b/python-glazierclient/tools/install_venv.py deleted file mode 100644 index 91903d6c..00000000 --- a/python-glazierclient/tools/install_venv.py +++ /dev/null @@ -1,75 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Copyright 2010 OpenStack LLC. -# Copyright 2013 IBM Corp. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Installation script for python-glazierclient's development virtualenv -""" - -import os -import subprocess -import sys - -import install_venv_common as install_venv - - -def print_help(): - help = """ - Glazier development environment setup is complete. - - Glazier development uses virtualenv to track and manage Python dependencies - while in development and testing. - - To activate the Glazier virtualenv for the extent of your current shell session - you can run: - - $ source .venv/bin/activate - - Or, if you prefer, you can run commands in the virtualenv on a case by case - basis by running: - - $ tools/with_venv.sh <your command> - - Also, make test will automatically use the virtualenv. - """ - print help - - -def main(argv): - root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - venv = os.path.join(root, '.venv') - pip_requires = os.path.join(root, 'tools', 'pip-requires') - test_requires = os.path.join(root, 'tools', 'test-requires') - py_version = "python%s.%s" % (sys.version_info[0], sys.version_info[1]) - project = 'python-glazierclient' - install = install_venv.InstallVenv(root, venv, pip_requires, test_requires, - py_version, project) - options = install.parse_args(argv) - install.check_python_version() - install.check_dependencies() - install.create_virtualenv(no_site_packages=options.no_site_packages) - install.install_dependencies() - install.run_command([os.path.join(venv, 'bin/python'), - 'setup.py', 'develop']) - install.post_process() - print_help() - -if __name__ == '__main__': - main(sys.argv) diff --git a/python-glazierclient/tools/install_venv_common.py b/python-glazierclient/tools/install_venv_common.py deleted file mode 100644 index fd9076f0..00000000 --- a/python-glazierclient/tools/install_venv_common.py +++ /dev/null @@ -1,219 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 OpenStack, LLC -# Copyright 2013 IBM Corp. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Provides methods needed by installation script for OpenStack development -virtual environments. - -Synced in from openstack-common -""" - -import argparse -import os -import subprocess -import sys - - -class InstallVenv(object): - - def __init__(self, root, venv, pip_requires, test_requires, py_version, - project): - self.root = root - self.venv = venv - self.pip_requires = pip_requires - self.test_requires = test_requires - self.py_version = py_version - self.project = project - - def die(self, message, *args): - print >> sys.stderr, message % args - sys.exit(1) - - def check_python_version(self): - if sys.version_info < (2, 6): - self.die("Need Python Version >= 2.6") - - def run_command_with_code(self, cmd, redirect_output=True, - check_exit_code=True): - """Runs a command in an out-of-process shell. - - Returns the output of that command. Working directory is self.root. - """ - if redirect_output: - stdout = subprocess.PIPE - else: - stdout = None - - proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout) - output = proc.communicate()[0] - if check_exit_code and proc.returncode != 0: - self.die('Command "%s" failed.\n%s', ' '.join(cmd), output) - return (output, proc.returncode) - - def run_command(self, cmd, redirect_output=True, check_exit_code=True): - return self.run_command_with_code(cmd, redirect_output, - check_exit_code)[0] - - def get_distro(self): - if (os.path.exists('/etc/fedora-release') or - os.path.exists('/etc/redhat-release')): - return Fedora(self.root, self.venv, self.pip_requires, - self.test_requires, self.py_version, self.project) - else: - return Distro(self.root, self.venv, self.pip_requires, - self.test_requires, self.py_version, self.project) - - def check_dependencies(self): - self.get_distro().install_virtualenv() - - def create_virtualenv(self, no_site_packages=True): - """Creates the virtual environment and installs PIP. - - Creates the virtual environment and installs PIP only into the - virtual environment. - """ - if not os.path.isdir(self.venv): - print 'Creating venv...', - if no_site_packages: - self.run_command(['virtualenv', '-q', '--no-site-packages', - self.venv]) - else: - self.run_command(['virtualenv', '-q', self.venv]) - print 'done.' - print 'Installing pip in venv...', - if not self.run_command(['tools/with_venv.sh', 'easy_install', - 'pip>1.0']).strip(): - self.die("Failed to install pip.") - print 'done.' - else: - print "venv already exists..." - pass - - def pip_install(self, *args): - self.run_command(['tools/with_venv.sh', - 'pip', 'install', '--upgrade'] + list(args), - redirect_output=False) - - def install_dependencies(self): - print 'Installing dependencies with pip (this can take a while)...' - - # First things first, make sure our venv has the latest pip and - # distribute. - # NOTE: we keep pip at version 1.1 since the most recent version causes - # the .venv creation to fail. See: - # https://bugs.launchpad.net/nova/+bug/1047120 - self.pip_install('pip==1.1') - self.pip_install('distribute') - - # Install greenlet by hand - just listing it in the requires file does - # not - # get it installed in the right order - self.pip_install('greenlet') - - self.pip_install('-r', self.pip_requires) - self.pip_install('-r', self.test_requires) - - def post_process(self): - self.get_distro().post_process() - - def parse_args(self, argv): - """Parses command-line arguments.""" - parser = argparse.ArgumentParser() - parser.add_argument('-n', '--no-site-packages', - action='store_true', - help="Do not inherit packages from global Python " - "install") - return parser.parse_args(argv[1:]) - - -class Distro(InstallVenv): - - def check_cmd(self, cmd): - return bool(self.run_command(['which', cmd], - check_exit_code=False).strip()) - - def install_virtualenv(self): - if self.check_cmd('virtualenv'): - return - - if self.check_cmd('easy_install'): - print 'Installing virtualenv via easy_install...', - if self.run_command(['easy_install', 'virtualenv']): - print 'Succeeded' - return - else: - print 'Failed' - - self.die('ERROR: virtualenv not found.\n\n%s development' - ' requires virtualenv, please install it using your' - ' favorite package management tool' % self.project) - - def post_process(self): - """Any distribution-specific post-processing gets done here. - - In particular, this is useful for applying patches to code inside - the venv. - """ - pass - - -class Fedora(Distro): - """This covers all Fedora-based distributions. - - Includes: Fedora, RHEL, CentOS, Scientific Linux - """ - - def check_pkg(self, pkg): - return self.run_command_with_code(['rpm', '-q', pkg], - check_exit_code=False)[1] == 0 - - def yum_install(self, pkg, **kwargs): - print "Attempting to install '%s' via yum" % pkg - self.run_command(['sudo', 'yum', 'install', '-y', pkg], **kwargs) - - def apply_patch(self, originalfile, patchfile): - self.run_command(['patch', originalfile, patchfile]) - - def install_virtualenv(self): - if self.check_cmd('virtualenv'): - return - - if not self.check_pkg('python-virtualenv'): - self.yum_install('python-virtualenv', check_exit_code=False) - - super(Fedora, self).install_virtualenv() - - def post_process(self): - """Workaround for a bug in eventlet. - - This currently affects RHEL6.1, but the fix can safely be - applied to all RHEL and Fedora distributions. - - This can be removed when the fix is applied upstream. - - Nova: https://bugs.launchpad.net/nova/+bug/884915 - Upstream: https://bitbucket.org/which_linden/eventlet/issue/89 - """ - - # Install "patch" program if it's not there - if not self.check_pkg('patch'): - self.yum_install('patch') - - # Apply the eventlet patch - self.apply_patch(os.path.join(self.venv, 'lib', self.py_version, - 'site-packages', - 'eventlet/green/subprocess.py'), - 'contrib/redhat-eventlet.patch') diff --git a/python-glazierclient/tools/pip-requires b/python-glazierclient/tools/pip-requires deleted file mode 100644 index 632d17be..00000000 --- a/python-glazierclient/tools/pip-requires +++ /dev/null @@ -1,5 +0,0 @@ -argparse -prettytable>=0.6,<0.7 -python-keystoneclient>=0.1.2 -httplib2 -iso8601>=0.1.4 diff --git a/python-glazierclient/tools/test-requires b/python-glazierclient/tools/test-requires deleted file mode 100644 index 98870ef5..00000000 --- a/python-glazierclient/tools/test-requires +++ /dev/null @@ -1,15 +0,0 @@ -distribute>=0.6.24 - -mock -anyjson -mox -nose -nose-exclude -nosexcover -openstack.nose_plugin -nosehtmloutput -pep8==1.3.3 -setuptools-git>=0.4 -sphinx>=1.1.2 -unittest2 -httpretty diff --git a/python-glazierclient/tools/with_venv.sh b/python-glazierclient/tools/with_venv.sh deleted file mode 100755 index c8d2940f..00000000 --- a/python-glazierclient/tools/with_venv.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -TOOLS=`dirname $0` -VENV=$TOOLS/../.venv -source $VENV/bin/activate && $@ diff --git a/python-glazierclient/tox.ini b/python-glazierclient/tox.ini deleted file mode 100644 index 244d2b6a..00000000 --- a/python-glazierclient/tox.ini +++ /dev/null @@ -1,46 +0,0 @@ -[tox] -envlist = py27,pep8 - -[testenv] -setenv = VIRTUAL_ENV={envdir} - NOSE_WITH_OPENSTACK=1 - NOSE_OPENSTACK_COLOR=1 - NOSE_OPENSTACK_RED=0.05 - NOSE_OPENSTACK_YELLOW=0.025 - NOSE_OPENSTACK_SHOW_ELAPSED=1 -deps = -r{toxinidir}/tools/pip-requires - -r{toxinidir}/tools/test-requires -commands = nosetests - -[testenv:pep8] -deps = pep8==1.3.3 -commands = pep8 --repeat --show-source glazierclient setup.py - -[testenv:venv] -commands = {posargs} - -[testenv:cover] -commands = nosetests --cover-erase --cover-package=glazierclient --with-xcoverage - -[tox:jenkins] -downloadcache = ~/cache/pip - -[testenv:jenkins26] -basepython = python2.6 -setenv = NOSE_WITH_XUNIT=1 -deps = file://{toxinidir}/.cache.bundle - -[testenv:jenkins27] -basepython = python2.7 -setenv = NOSE_WITH_XUNIT=1 -deps = file://{toxinidir}/.cache.bundle - -[testenv:jenkinscover] -deps = file://{toxinidir}/.cache.bundle -setenv = NOSE_WITH_XUNIT=1 -commands = nosetests --cover-erase --cover-package=glazierclient --with-xcoverage - -[testenv:jenkinsvenv] -deps = file://{toxinidir}/.cache.bundle -setenv = NOSE_WITH_XUNIT=1 -commands = {posargs} diff --git a/tests/REST_service/test.py b/tests/REST_service/test.py deleted file mode 100644 index 0957378c..00000000 --- a/tests/REST_service/test.py +++ /dev/null @@ -1,172 +0,0 @@ -# -*- coding: utf-8 -*- - - -import unittest2 -import requests -import json -import logging -import thread -import time - -logging.basicConfig() -LOG = logging.getLogger(' REST service tests') - -class TestSuite(unittest2.TestCase): - - def setUp(self): - self.max_count = 200 - self.url = 'http://localhost:8082/environments' - self.headers = {'X-Auth-Token': '3685674500ff83eb62b5c5d453e0cacd'} - self.lock_list = [] - self.responses = [] - self.state = 'stop' - - def tearDown(self): - self.headers = {} - - def run_in_parrallel(self, func, parameter): - lock = thread.allocate_lock() - lock.acquire() - self.lock_list.append(lock) - thread.start_new_thread(func, (parameter, lock)) - - def wait_threads(self): - self.state = 'run' - while(any([l.locked() for l in self.lock_list])): - pass - - LOG.critical(self.lock_list) - - def create_environment(self, name, lock): - while (self.state == 'stop'): - pass - - body = '{"name": "%s"}' % name - response = requests.request('POST', - self.url, - headers=self.headers, - data=body) - - self.responses.append(response) - - return lock.release() - - - def delete_environment(self, id, lock): - while (self.state == 'stop'): - pass - - id = '/' + id - - response = requests.request('DELETE', self.url+id, headers=self.headers) - - self.responses.append(response) - - return lock.release() - - def test_01_delete_environments(self): - - response = requests.request('GET', self.url, headers=self.headers) - - result = json.loads(response._content) - environments = result['environments'] - - for env in environments: - self.run_in_parrallel(self.delete_environment, env['id']) - - self.wait_threads() - - for response in self.responses: - assert response.status_code is 200 - - def test_02_list_environments(self): - - response = requests.request('GET', self.url, headers=self.headers) - - result = json.loads(response._content) - environments = result['environments'] - - assert len(environments) is 0 - - def test_03_create_environment(self): - - self.headers.update({'Content-Type': 'application/json'}) - body = '{"name": "test"}' - - response = requests.request('POST', self.url, headers=self.headers, - data=body) - - environment = json.loads(response._content) - - assert environment['name'] == 'test' - - def test_04_delete_environment(self): - - id = None - - response = requests.request('GET', self.url, headers=self.headers) - - result = json.loads(response._content) - environments = result['environments'] - - for env in environments: - if env['name'] == 'test': - id = '/' + env['id'] - - assert id is not None - - response = requests.request('DELETE', self.url+id, headers=self.headers) - - assert response.status_code is 200 - - def test_05_create_environments(self): - - self.headers.update({'Content-Type': 'application/json'}) - - for i in range(self.max_count): - name = "environment%s" % i - self.run_in_parrallel(self.create_environment, name) - - self.wait_threads() - - for response in self.responses: - assert response.status_code is 200 - - def test_06_list_environments(self): - - response = requests.request('GET', self.url, headers=self.headers) - - result = json.loads(response._content) - environments = result['environments'] - - assert len(environments) == self.max_count - - - def test_07_list_environments(self): - - response = requests.request('GET', self.url, headers=self.headers) - - result = json.loads(response._content) - environments = result['environments'] - - assert len(environments) > 0 - - def test_08_list_environments(self): - - response = requests.request('GET', self.url, headers=self.headers) - - result = json.loads(response._content) - environments = result['environments'] - - for env in environments: - assert env.get('id', None) is not None - assert env.get('name', None) is not None - assert env.get('status', None) is not None - assert env.get('updated', None) is not None - assert env.get('created', None) is not None - assert env.get('tenant_id', None) is not None - - - -if __name__ == '__main__': - unittest2.main() \ No newline at end of file diff --git a/tests/deploy.sh b/tests/deploy.sh deleted file mode 100755 index cad10a20..00000000 --- a/tests/deploy.sh +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/expect -d -# The following directories should be created for this script: -# /opt/stack/devstack -# /opt/stack/keero -# the ssh key should be in directory /opt/stack/.ssh/ -# the iso file with windows should be in directory /opt/stack/ - -set timeout 1200 - -send_user "\n\nStart to login to the test bed...\n\n" - -spawn /usr/bin/ssh [lindex $argv 0]@[lindex $argv 1] -expect "password" -send -- "EVYiMCVZX9\n" -expect "*#*" - -send -- "su - stack\n" -expect "*$*" - -send -- "sudo killall python\n" -expect "*$*" -send -- "cd ~/devstack\n" -expect "*$*" -send -- "./unstack.sh\n" -expect "*$*" -send -- "./stack.sh\n" -expect "*/usr/bin/service: 123: exec: status: not found*" -send -- "y\n" -expect "*stack.sh completed*" - -send -- "sudo rabbitmq-plugins enable rabbitmq_management\n" -expect "*$*" -send -- "sudo service rabbitmq-server restart\n" -expect "*$*" -send -- "sudo rabbitmqctl add_user keero keero\n" -expect "*$*" -send -- "sudo rabbitmqctl set_user_tags keero administrator\n" -expect "*$*" - - -send -- "source openrc admin admin\n" -expect "*$*" - -send -- "cd ~\n" -expect "*$*" - -send -- "nova keypair-add keero-linux-keys > heat_key.priv\n" -expect "*$*" - -send -- "glance image-create --name 'ws-2012-full' --is-public true --container-format ovf --disk-format qcow2 < ws-2012-full.qcow2\n" -expect "*$*" - -send -- "cd ~/keero\n" -expect "*$*" -send -- "git pull\n" -expect "/.ssh/id_rsa" -send -- "swordfish\n" -expect "*$*" -send -- "cp -Rf ~/keero/dashboard/windc /opt/stack/horizon/openstack_dashboard/dashboards/project\n" -expect "*$*" -send -- "cp -f ~/keero/dashboard/api/windc.py /opt/stack/horizon/openstack_dashboard/api/\n" -expect "*$*" -send -- "cd ~/keero/python-portasclient\n" -expect "*$*" -send -- "sudo python setup.py install\n" -expect "*$*" -send -- "cd ~/keero/portas\n" -expect "*$*" -send -- "./tools/with_venv.sh ./bin/portas-api --config-file=./etc/portas-api.conf & > ~/APIservice.log\n" -sleep 10 -send -- "\n" -expect "*$*" -send -- "cd ~/keero/conductor\n" -expect "*$*" -send -- "./tools/with_venv.sh ./bin/app.py & > ~/conductor.log\n" -sleep 10 -send -- "\n" -expect "*$*" -send -- "logout\n" -expect "*#*" - diff --git a/tests/selenium/conf.ini b/tests/selenium/conf.ini deleted file mode 100644 index 12af6f58..00000000 --- a/tests/selenium/conf.ini +++ /dev/null @@ -1,4 +0,0 @@ -[server] -address=http://172.18.124.101:8080 -user=admin -password=swordfish \ No newline at end of file diff --git a/tests/selenium/environments_page.py b/tests/selenium/environments_page.py deleted file mode 100644 index 0475463a..00000000 --- a/tests/selenium/environments_page.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import re -from services_page import ServicesPage -import page - - -class EnvironmentsPage(page.Page): - name = 'Environments' - - def create_environment(self, name): - self.Refresh() - self.Button('Create Environment').Click() - self.EditBox('id_name').Set(name) - self.Button('Create').Click() - - def delete_environment(self, name): - self.Refresh() - - link = self.Link(name).Address() - environment_id = re.search('tabula/(\S+)', link).group(0)[7:-1] - - self.Button('More', environment_id).Click() - self.Button('Delete', environment_id).Click() - # confirm: - self.Button('Delete Environment').Click() - - def select_environment(self, name): - self.Link(name).Click() - page = ServicesPage(self.driver) - return page - - def deploy_environment(self, name): - self.Refresh() - - link = self.Link(name).Address() - environment_id = re.search('tabula/(\S+)', link).group(0)[7:-1] - - self.Button('More', environment_id).Click() - self.Button('Deploy', environment_id).Click() - - def get_environment_status(self, name): - self.Refresh() - - link = self.Link(name).Address() - environment_id = re.search('tabula/(\S+)', link).group(0)[7:-1] - - return self.TableCell('Status', environment_id).Text() diff --git a/tests/selenium/login_page.py b/tests/selenium/login_page.py deleted file mode 100644 index 9d5b2b17..00000000 --- a/tests/selenium/login_page.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import ConfigParser -import page - - -class LoginPage(page.Page): - - def login(self): - config = ConfigParser.RawConfigParser() - config.read('conf.ini') - url = config.get('server', 'address') - user = config.get('server', 'user') - password = config.get('server', 'password') - - self.Open(url) - - self.EditBox('username').Set(user) - self.EditBox('password').Set(password) - xpath = "//button[@type='submit']" - self.Button(xpath).Click() - - return self diff --git a/tests/selenium/objects.xml b/tests/selenium/objects.xml deleted file mode 100644 index 8af03755..00000000 --- a/tests/selenium/objects.xml +++ /dev/null @@ -1,73 +0,0 @@ -<objects> - <object> - <type>Button</type> - <name>Create</name> - <parameter>//input[@value='Create']</parameter> - </object> - <object> - <type>Button</type> - <pagename>Services</pagename> - <name>More</name> - <parameter>.//*[@id='services__row__%s']/td[5]/div/a[2]</parameter> - </object> - <object> - <type>Button</type> - <pagename>Services</pagename> - <name>Delete</name> - <parameter>services__row_%s__action_delete</parameter> - </object> - <object> - <type>Button</type> - <pagename>Services</pagename> - <name>CreateService</name> - <parameter>services__action_CreateService</parameter> - </object> - <object> - <type>Button</type> - <pagename>DataCenters</pagename> - <name>More</name> - <parameter>.//*[@id='windc__row__%s']/td[4]/div/a[2]</parameter> - </object> - <object> - <type>Button</type> - <pagename>DataCenters</pagename> - <name>Delete</name> - <parameter>windc__row_%s__action_delete</parameter> - </object> - <object> - <type>Button</type> - <pagename>DataCenters</pagename> - <name>Deploy</name> - <parameter>windc__row_%s__action_deploy</parameter> - </object> - <object> - <type>TableCell</type> - <pagename>DataCenters</pagename> - <name>Status</name> - <parameter>.//*[@id='windc__row__%s']/td[3]</parameter> - </object> - <object> - <type>TableCell</type> - <pagename>ServicesDetails</pagename> - <name>Name</name> - <parameter>.//*[@id='services_details___service']/div/dl/dd[1]</parameter> - </object> - <object> - <type>TableCell</type> - <pagename>ServicesDetails</pagename> - <name>Domain</name> - <parameter>.//*[@id='services_details___service']/div/dl/dd[3]</parameter> - </object> - <object> - <type>Link</type> - <pagename>ServicesDetails</pagename> - <name>Service</name> - <parameter>.//*[@id='services_details']/li[1]/a</parameter> - </object> - <object> - <type>TableCell</type> - <pagename>Services</pagename> - <name>Status</name> - <parameter>.//*[@id='services__row__%s']/td[4]</parameter> - </object> -</objects> \ No newline at end of file diff --git a/tests/selenium/page.py b/tests/selenium/page.py deleted file mode 100644 index f4050633..00000000 --- a/tests/selenium/page.py +++ /dev/null @@ -1,276 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging -from selenium.webdriver.support.ui import Select -import xml.etree.ElementTree as ET - - -logging.basicConfig() -LOG = logging.getLogger(' Page object: ') -""" - Disable selenium logging: -""" -logger = logging.getLogger('selenium.webdriver.remote.remote_connection') -logger.setLevel('ERROR') - - -class ObjectsLibrary: - file = None - objects = [] - - def __init__(self, file_name='objects.xml'): - """ - Initialization of the Objects Library. - Read objects descriptions from XML file. - """ - self.file = file_name - tree = ET.parse(self.file) - objects = tree.getroot() - self.objects = [] - for element in objects: - object = {} - for parameter in element: - object.update({parameter.tag: parameter.text}) - self.objects.append(object) - - def get_object(self, name, pagename): - """ - Search objects in Objects Library. - """ - for object in self.objects: - obj_pagename = object.get('pagename', None) - if object['name'] == name and (obj_pagename == pagename - or obj_pagename is None): - return object['parameter'] - return None - - -class TableCellClass: - table = None - - def __init__(self, obj): - if not obj: - LOG.error('TableCell does not found') - self.table = obj - - def Text(self): - if self.table: - LOG.critical(self.table.text) - return self.table.text - else: - return '' - - -class ButtonClass: - button = None - - def __init__(self, obj): - if not obj: - LOG.error('Button does not found') - self.button = obj - - def Click(self): - if self.button: - self.button.click() - - def isPresented(self): - if self.button: - return True - return False - - -class LinkClass: - link = None - - def __init__(self, obj): - if not obj: - LOG.error('Link does not found') - self.link = obj - - def Click(self): - if self.link: - self.link.click() - - def isPresented(self): - if self.link: - return True - return False - - def Address(self): - if self.link: - return self.link.get_attribute('href') - else: - return '' - - -class EditBoxClass: - - def __init__(self, obj): - if not obj: - LOG.error('EditBox does not found') - self.edit = obj - - def isPresented(self): - if self.edit: - return True - return False - - def Set(self, value): - if self.edit: - try: - self.edit.clear() - self.edit.send_keys(value) - except: - LOG.error('Can not set value for text box.') - - def Text(self): - if self.edit: - return self.edit.get_text() - else: - return '' - - -class DropDownListClass: - select = None - - def __init__(self, obj): - if not obj: - LOG.error('DropDownList does not found') - self.select = obj - - def isPresented(self): - if self.select is not None: - return True - return False - - def Set(self, value): - if self.select: - try: - Select(self.select).select_by_visible_text(value) - except: - LOG.error('Can not select element %s from drop down list.' - .format(value)) - - def Text(self): - if self.select: - return self.select.get_text() - else: - return '' - - -error_msg = """ - Object with parameter: %s - does not found on page. - """ - - -class Page: - - driver = None - timeout = 10 - lib = None - name = None - - def __init__(self, driver): - driver.set_page_load_timeout(self.timeout) - driver.implicitly_wait(0.01) - self.driver = driver - - def _find_element(self, name, parameter=None): - """ - This method allows to find element, - based on descriptions in Object Library, - xpath, id, name or pertial link text. - If parameter != None will be used name % parameter - """ - lib = ObjectsLibrary() - if lib.get_object(name, self.name): - name = lib.get_object(name, self.name) - - if parameter: - name = name % parameter - - obj = None - k = 0 - - while (obj is None and k < self.timeout): - k += 1 - - try: - obj = self.driver.find_element_by_name(name) - return obj - except: - pass - try: - obj = self.driver.find_element_by_id(name) - return obj - except: - pass - try: - obj = self.driver.find_element_by_xpath(name) - return obj - except: - pass - try: - obj = self.driver.find_element_by_partial_link_text(name) - return obj - except: - pass - - LOG.error(error_msg % name) - return None - - def Open(self, url): - self.driver.get(url) - - def Refresh(self): - self.driver.refresh() - - def TableCell(self, name, parameter=None): - obj = self._find_element(name, parameter) - table = TableCellClass(obj) - return table - - def Button(self, name, parameter=None): - obj = self._find_element(name, parameter) - button = ButtonClass(obj) - return button - - def Link(self, name, parameter=None): - obj = self._find_element(name, parameter) - link = LinkClass(obj) - return link - - def EditBox(self, name, parameter=None): - obj = self._find_element(name, parameter) - edit = EditBoxClass(obj) - return edit - - def DropDownList(self, name, parameter=None): - obj = self._find_element(name, parameter) - select = DropDownListClass(obj) - return select - - def Navigate(self, path): - """ - This method allows to navigate by - webUI menu button and links to - the specific page - """ - steps = path.split(':') - - for step in steps: - self.Button(step).Click() diff --git a/tests/selenium/services_details_page.py b/tests/selenium/services_details_page.py deleted file mode 100644 index eefafd4e..00000000 --- a/tests/selenium/services_details_page.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import page - - -class ServicesDetailsPage(page.Page): - - name = 'ServicesDetails' - - def get_service_name(self): - self.Refresh() - self.Link('Service').Click() - return self.TableCell('Name').Text() - - def get_service_domain(self): - self.Refresh() - self.Link('Service').Click() - return self.TableCell('Domain').Text() \ No newline at end of file diff --git a/tests/selenium/services_page.py b/tests/selenium/services_page.py deleted file mode 100644 index c511bfa0..00000000 --- a/tests/selenium/services_page.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import page -import re -from services_details_page import ServicesDetailsPage - - -class ServicesPage(page.Page): - - name = 'Services' - - def create_service(self, parameters): - service_type = parameters[0] - parameters = parameters[1] - self.Refresh() - - self.Button('CreateService').Click() - self.DropDownList('0-service').Set(service_type) - self.Button('wizard_goto_step').Click() - - for key in parameters: - try: - self.EditBox(key).Set(parameters[key]) - except: - pass - try: - self.DropDownList(key).Set(parameters[key]) - except: - pass - - self.Button('Create').Click() - - def delete_service(self, name): - self.Refresh() - - link = self.Link(name).Address() - - service_id = re.search('tabula/(\S+)', link).group(0)[7:-1] - - self.Button('More', service_id).Click() - self.Button('Delete', service_id).Click() - # confirm: - self.Button('Delete Service').Click() - - def select_service(self, name): - self.Link(name).Click() - page = ServicesDetailsPage(self.driver) - return page - - def get_service_status(self, name): - self.Refresh() - - link = self.Link(name).Address() - service_id = re.search('tabula/(\S+)', link).group(0)[7:-8] - - return self.TableCell('Status', service_id).Text() diff --git a/tests/selenium/test.py b/tests/selenium/test.py deleted file mode 100644 index 87acf6bf..00000000 --- a/tests/selenium/test.py +++ /dev/null @@ -1,182 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import unittest2 -import logging -from login_page import LoginPage -from environments_page import EnvironmentsPage -from selenium import webdriver - - -logging.basicConfig() -LOG = logging.getLogger(' Tests: ') - - -def generate_ad(name="test", count=1): - """ - This function generates parameters for - Active Directory service - """ - ad_parameters = {'1-dc_name': name, - '1-dc_count': count, - '1-adm_password': "P@ssw0rd", - '1-recovery_password': "P@ssw0rd2"} - return ['Active Directory', ad_parameters] - -def generate_iis(name="test", domain="test"): - """ - This function generates parameters for - Internet Information Services service - """ - iis_parameters = {'1-iis_name': name, - '1-adm_password': "P@ssw0rd", - '1-iis_domain': domain} - return ['Internet Information Services', iis_parameters] - - -environment_for_deploy = 'environment_for_deploy' - - -class SanityTests(unittest2.TestCase): - - screenshots = 0 # Make screenshots for the end of each test - - @classmethod - def setUpClass(self): - """ - Open browser, navigate to the login page, - login and navigate to the Windows Data Centers page - """ - driver = webdriver.Firefox() - self.page = LoginPage(driver) - self.page.login() - - @classmethod - def tearDownClass(self): - """ - Close browser - """ - self.page.driver.close() - - def setUp(self): - """ - Navigate to the start page - """ - self.page.Navigate('Project:Environments') - self.page = EnvironmentsPage(driver) - - def tearDown(self): - """ - Make screenshot - """ - self.screenshots += 1 - self.page.driver.save_screenshot("screen_%s.png" % self.screenshots) - - def test_001_create_environment(self): - self.page.create_environment('dc1') - assert self.page.Link('dc1').isPresented() - - def test_002_delete_environment(self): - self.page.delete_environment('dc1') - assert not self.page.Link('dc1').isPresented() - - def test_003_configure_environment_before_deploy(self): - ad_name = "AD.net" - iis_name = "iis_server" - self.page.create_environment(environment_for_deploy) - self.page = self.page.select_environment(environment_for_deploy) - - self.page.create_service(generate_ad(ad_name, 2)) - assert self.page.Link(ad_name).isPresented() - - self.page.create_service(generate_iis(iis_name, ad_name)) - assert self.page.Link(iis_name).isPresented() - - def test_004_deploy_environment(self): - self.page.deploy_environment(environment_for_deploy) - status = self.page.get_environment_status(environment_for_deploy) - assert 'Deploy in progress' in status - - def test_005_create_environments(self): - for i in range(1, 10): - name = "environment" + str(i) - self.page.create_environment(name) - assert self.page.Link(name).isPresented() - - def test_006_delete_environments(self): - self.page.delete_environment('environment1') - self.page.delete_environment('environment9') - assert not self.page.Link('environment1').isPresented() - assert not self.page.Link('environment9').isPresented() - - for i in range(2, 9): - name = 'environment' + str(i) - assert self.page.Link(name).isPresented() - - def test_007_create_service_ad(self): - name = "dc001.local" - self.page.create_environment('test05') - self.page = self.page.select_environment('test05') - self.page.create_service(generate_ad(name, 1)) - assert self.page.Link(name).isPresented() - - def test_008_create_service_ad_two_instances(self): - name = "dc002.local" - self.page.create_environment('test06') - self.page = self.page.select_environment('test06') - self.page.create_service(generate_ad(name, 2)) - assert self.page.Link(name).isPresented() - - def test_009_create_service_ad_with_iis(self): - ad_name = "dc003.local" - self.page.create_environment('test07') - self.page = self.page.select_environment('test07') - self.page.create_service(generate_ad(ad_name, 3)) - assert self.page.Link(ad_name).isPresented() - - for i in range(5): - iis_name = 'iis_server' + str(i) - self.page.create_service(generate_iis(iis_name, ad_name)) - assert self.page.Link(iis_name).isPresented() - - def test_010_delete_environment_with_services(self): - name = "test07" - self.page.delete_environment(name) - assert not self.page.Link(name).isPresented() - - def test_011_service_deploy_in_progress_status(self): - ad_name = "AD.net" - iis_name = "iis_server" - self.page = self.page.select_environment(environment_for_deploy) - ad_status = self.page.get_service_status(ad_name) - iis_status = self.page.get_service_status(iis_name) - - assert 'Deploy in progress' in ad_status - assert 'Deploy in progress' in iis_status - - def test_012_show_service_details_for_deploy(self): - ad_name = "AD.net" - iis_name = "iis_server" - self.page = self.page.select_environment(environment_for_deploy) - self.page = self.page.select_service(iis_name) - name = self.page.get_service_name() - domain = self.page.get_service_domain() - - assert name == iis_name - assert name == ad_name - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/selenium/tools/test_requires b/tests/selenium/tools/test_requires deleted file mode 100644 index 3dc2329e..00000000 --- a/tests/selenium/tools/test_requires +++ /dev/null @@ -1,8 +0,0 @@ -xvfb -x11-xkb-utils -xfonts-100dpi -xfonts-75dpi -xfonts-scalable -xfonts-cyrillic -xserver-xorg-core -selenium \ No newline at end of file diff --git a/tests/selenium/tox.ini b/tests/selenium/tox.ini deleted file mode 100644 index 06904ba0..00000000 --- a/tests/selenium/tox.ini +++ /dev/null @@ -1,28 +0,0 @@ -[tox] -envlist = py27,pep8 - -[testenv] -setenv = VIRTUAL_ENV={envdir} - NOSE_WITH_OPENSTACK=1 - NOSE_OPENSTACK_COLOR=1 - NOSE_OPENSTACK_RED=0.05 - NOSE_OPENSTACK_YELLOW=0.025 - NOSE_OPENSTACK_SHOW_ELAPSED=1 -deps = -r{toxinidir}/tools/test-requires -commands = Xvfb -fp /usr/share/fonts/X11/misc/ :22 -screen 0 1024x768x16 2>&1 & export DISPLAY=:22 & nosetests - -[testenv:venv] -commands = {posargs} - -[tox:jenkins] -downloadcache = ~/cache/pip - -[testenv:jenkins27] -basepython = python2.7 -setenv = NOSE_WITH_XUNIT=1 -deps = file://{toxinidir}/.cache.bundle - -[testenv:jenkinsvenv] -deps = file://{toxinidir}/.cache.bundle -setenv = NOSE_WITH_XUNIT=1 -commands = {posargs}