Browse Source

StarlingX open source release updates

Signed-off-by: Scott Little <scott.little@windriver.com>
Scott Little 8 months ago
commit
e82c7b4336
100 changed files with 9017 additions and 0 deletions
  1. 12
    0
      .coveragerc
  2. 11
    0
      .coveragerc_xml
  3. 57
    0
      .gitignore
  4. 3
    0
      .mailmap
  5. 14
    0
      .testr.conf
  6. 14
    0
      CONTRIBUTING.rst
  7. 20
    0
      CONTRIBUTORS.wrs
  8. 4
    0
      HACKING.rst
  9. 176
    0
      LICENSE
  10. 9
    0
      MANIFEST.in
  11. 6
    0
      README.rst
  12. 58
    0
      README_DC
  13. 2
    0
      babel.cfg
  14. 25
    0
      dcmanager/__init__.py
  15. 31
    0
      dcmanager/api/README.rst
  16. 0
    0
      dcmanager/api/__init__.py
  17. 111
    0
      dcmanager/api/api_config.py
  18. 97
    0
      dcmanager/api/app.py
  19. 14
    0
      dcmanager/api/controllers/README.rst
  20. 0
    0
      dcmanager/api/controllers/__init__.py
  21. 48
    0
      dcmanager/api/controllers/restcomm.py
  22. 64
    0
      dcmanager/api/controllers/root.py
  23. 0
    0
      dcmanager/api/controllers/v1/__init__.py
  24. 87
    0
      dcmanager/api/controllers/v1/alarm_manager.py
  25. 63
    0
      dcmanager/api/controllers/v1/root.py
  26. 688
    0
      dcmanager/api/controllers/v1/subclouds.py
  27. 244
    0
      dcmanager/api/controllers/v1/sw_update_options.py
  28. 196
    0
      dcmanager/api/controllers/v1/sw_update_strategy.py
  29. 78
    0
      dcmanager/api/enforcer.py
  30. 18
    0
      dcmanager/cmd/README.rst
  31. 0
    0
      dcmanager/cmd/__init__.py
  32. 78
    0
      dcmanager/cmd/api.py
  33. 85
    0
      dcmanager/cmd/manage.py
  34. 64
    0
      dcmanager/cmd/manager.py
  35. 0
    0
      dcmanager/common/__init__.py
  36. 159
    0
      dcmanager/common/config.py
  37. 90
    0
      dcmanager/common/consts.py
  38. 154
    0
      dcmanager/common/context.py
  39. 148
    0
      dcmanager/common/exceptions.py
  40. 27
    0
      dcmanager/common/i18n.py
  41. 124
    0
      dcmanager/common/manager.py
  42. 118
    0
      dcmanager/common/messaging.py
  43. 56
    0
      dcmanager/common/policy.py
  44. 89
    0
      dcmanager/common/serializer.py
  45. 91
    0
      dcmanager/common/utils.py
  46. 48
    0
      dcmanager/common/version.py
  47. 15
    0
      dcmanager/config-generator.conf
  48. 0
    0
      dcmanager/db/__init__.py
  49. 394
    0
      dcmanager/db/api.py
  50. 0
    0
      dcmanager/db/sqlalchemy/__init__.py
  51. 602
    0
      dcmanager/db/sqlalchemy/api.py
  52. 4
    0
      dcmanager/db/sqlalchemy/migrate_repo/README
  53. 0
    0
      dcmanager/db/sqlalchemy/migrate_repo/__init__.py
  54. 5
    0
      dcmanager/db/sqlalchemy/migrate_repo/manage.py
  55. 25
    0
      dcmanager/db/sqlalchemy/migrate_repo/migrate.cfg
  56. 197
    0
      dcmanager/db/sqlalchemy/migrate_repo/versions/001_first_version.py
  57. 0
    0
      dcmanager/db/sqlalchemy/migrate_repo/versions/__init__.py
  58. 47
    0
      dcmanager/db/sqlalchemy/migration.py
  59. 169
    0
      dcmanager/db/sqlalchemy/models.py
  60. 55
    0
      dcmanager/db/utils.py
  61. 5
    0
      dcmanager/drivers/README.rst
  62. 0
    0
      dcmanager/drivers/__init__.py
  63. 31
    0
      dcmanager/drivers/base.py
  64. 0
    0
      dcmanager/drivers/openstack/__init__.py
  65. 196
    0
      dcmanager/drivers/openstack/patching_v1.py
  66. 124
    0
      dcmanager/drivers/openstack/sysinv_v1.py
  67. 157
    0
      dcmanager/drivers/openstack/vim.py
  68. 23
    0
      dcmanager/manager/README.rst
  69. 0
    0
      dcmanager/manager/__init__.py
  70. 248
    0
      dcmanager/manager/patch_audit_manager.py
  71. 91
    0
      dcmanager/manager/scheduler.py
  72. 236
    0
      dcmanager/manager/service.py
  73. 259
    0
      dcmanager/manager/subcloud_audit_manager.py
  74. 590
    0
      dcmanager/manager/subcloud_manager.py
  75. 1483
    0
      dcmanager/manager/sw_update_manager.py
  76. 0
    0
      dcmanager/objects/__init__.py
  77. 76
    0
      dcmanager/objects/base.py
  78. 0
    0
      dcmanager/rpc/__init__.py
  79. 102
    0
      dcmanager/rpc/client.py
  80. 0
    0
      dcmanager/tests/__init__.py
  81. 27
    0
      dcmanager/tests/base.py
  82. 3
    0
      dcmanager/tests/data/ipv6_R5_install/dcmanager/migrate_version.json
  83. 6
    0
      dcmanager/tests/data/ipv6_R5_install/dcmanager/strategy_steps.json
  84. 17
    0
      dcmanager/tests/data/ipv6_R5_install/dcmanager/subcloud_status.json
  85. 5
    0
      dcmanager/tests/data/ipv6_R5_install/dcmanager/subclouds.json
  86. 3
    0
      dcmanager/tests/data/ipv6_R5_install/dcmanager/sw_update_opts_default.json
  87. 3
    0
      dcmanager/tests/data/ipv6_R5_install/dcmanager/sw_update_strategy.json
  88. 30
    0
      dcmanager/tests/data/ipv6_R5_install/keystone/assignment.json
  89. 185
    0
      dcmanager/tests/data/ipv6_R5_install/keystone/endpoint.json
  90. 24
    0
      dcmanager/tests/data/ipv6_R5_install/keystone/local_user.json
  91. 6
    0
      dcmanager/tests/data/ipv6_R5_install/keystone/migrate_version.json
  92. 24
    0
      dcmanager/tests/data/ipv6_R5_install/keystone/password.json
  93. 9
    0
      dcmanager/tests/data/ipv6_R5_install/keystone/project.json
  94. 7
    0
      dcmanager/tests/data/ipv6_R5_install/keystone/region.json
  95. 3
    0
      dcmanager/tests/data/ipv6_R5_install/keystone/revocation_event.json
  96. 7
    0
      dcmanager/tests/data/ipv6_R5_install/keystone/role.json
  97. 20
    0
      dcmanager/tests/data/ipv6_R5_install/keystone/service.json
  98. 6
    0
      dcmanager/tests/data/ipv6_R5_install/keystone/strategy_steps.json
  99. 17
    0
      dcmanager/tests/data/ipv6_R5_install/keystone/subcloud_status.json
  100. 0
    0
      dcmanager/tests/data/ipv6_R5_install/keystone/subclouds.json

+ 12
- 0
.coveragerc View File

@@ -0,0 +1,12 @@
1
+[run]
2
+branch = True
3
+parallel = True
4
+source =
5
+    dcmanager
6
+    dcorch
7
+
8
+[report]
9
+ignore_errors = True
10
+omit = 
11
+    */tests/*
12
+

+ 11
- 0
.coveragerc_xml View File

@@ -0,0 +1,11 @@
1
+[run]
2
+branch = True
3
+parallel = True
4
+source =
5
+    distributedcloud
6
+
7
+[report]
8
+ignore_errors = True
9
+omit = 
10
+    */tests/*
11
+

+ 57
- 0
.gitignore View File

@@ -0,0 +1,57 @@
1
+*.py[cod]
2
+
3
+# C extensions
4
+*.so
5
+
6
+# Packages
7
+*.egg
8
+*.egg-info
9
+dist
10
+build
11
+eggs
12
+parts
13
+var
14
+sdist
15
+develop-eggs
16
+.installed.cfg
17
+lib
18
+lib64
19
+
20
+# Installer logs
21
+pip-log.txt
22
+
23
+# Unit test / coverage reports
24
+.coverage
25
+cover
26
+coverage.xml
27
+.coverage\.*
28
+.current.cfg
29
+.tox
30
+nosetests.xml
31
+.testrepository
32
+.venv
33
+
34
+# Translations
35
+*.mo
36
+
37
+# Mr Developer
38
+.mr.developer.cfg
39
+.project
40
+.pydevproject
41
+
42
+# Complexity
43
+output/*.html
44
+output/*/index.html
45
+
46
+# Sphinx
47
+doc/build
48
+
49
+# pbr generates these
50
+AUTHORS
51
+ChangeLog
52
+
53
+# Editors
54
+*~
55
+.*.swp
56
+.*sw?
57
+

+ 3
- 0
.mailmap View File

@@ -0,0 +1,3 @@
1
+# Format is:
2
+# <preferred e-mail> <other e-mail 1>
3
+Dimitri Mazmanov <dimitri.mazmanov@ericsson.com>

+ 14
- 0
.testr.conf View File

@@ -0,0 +1,14 @@
1
+# The sed command is required for coverage to work.
2
+# otherwise testr will pass --source distributedcloud when invoking coverage
3
+# which breaks the source definitions in the .coveragerc file
4
+[DEFAULT]
5
+test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} 
6
+             OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} 
7
+             OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} 
8
+             PYTHON=$(echo ${PYTHON:-python} | sed 's/--source distributedcloud//g')
9
+             ${PYTHON} -m subunit.run discover -s dcmanager  $LISTOPT $IDOPTION
10
+             ${PYTHON} -m subunit.run discover -s dcorch $LISTOPT $IDOPTION
11
+test_id_option=--load-list $IDFILE
12
+test_list_option=--list
13
+test_run_concurrency=echo 5
14
+

+ 14
- 0
CONTRIBUTING.rst View File

@@ -0,0 +1,14 @@
1
+If you would like to contribute to the development of OpenStack, you must
2
+follow the steps in this page:
3
+
4
+   http://docs.openstack.org/infra/manual/developers.html
5
+
6
+If you already have a good understanding of how the system works and your
7
+OpenStack accounts are set up, you can skip to the development workflow
8
+section of this documentation to learn how changes to OpenStack should be
9
+submitted for review via the Gerrit tool:
10
+
11
+   http://docs.openstack.org/infra/manual/developers.html#development-workflow
12
+
13
+Pull requests submitted through GitHub will be ignored.
14
+

+ 20
- 0
CONTRIBUTORS.wrs View File

@@ -0,0 +1,20 @@
1
+The following contributors from Wind River have developed the seed code in this
2
+repository.  We look forward to community collaboration and contributions for
3
+additional features, enhancements and refactoring.
4
+Contributors:
5
+=============
6
+Al Bailey
7
+Alex Kozyrev
8
+Andy Ning
9
+Angie Wang
10
+Bart Wensley
11
+Chris Friesen
12
+John Kung
13
+Kam Nasim
14
+Kevin Smith
15
+Lachlan Plant
16
+Saju Oommen
17
+Stefan Dinescu
18
+Tao Liu
19
+Tyler Smith
20
+

+ 4
- 0
HACKING.rst View File

@@ -0,0 +1,4 @@
1
+DistributedCloud Style Commandments
2
+===============================================
3
+
4
+Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/

+ 176
- 0
LICENSE View File

@@ -0,0 +1,176 @@
1
+
2
+                                 Apache License
3
+                           Version 2.0, January 2004
4
+                        http://www.apache.org/licenses/
5
+
6
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+   1. Definitions.
9
+
10
+      "License" shall mean the terms and conditions for use, reproduction,
11
+      and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+      "Licensor" shall mean the copyright owner or entity authorized by
14
+      the copyright owner that is granting the License.
15
+
16
+      "Legal Entity" shall mean the union of the acting entity and all
17
+      other entities that control, are controlled by, or are under common
18
+      control with that entity. For the purposes of this definition,
19
+      "control" means (i) the power, direct or indirect, to cause the
20
+      direction or management of such entity, whether by contract or
21
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+      outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+      "You" (or "Your") shall mean an individual or Legal Entity
25
+      exercising permissions granted by this License.
26
+
27
+      "Source" form shall mean the preferred form for making modifications,
28
+      including but not limited to software source code, documentation
29
+      source, and configuration files.
30
+
31
+      "Object" form shall mean any form resulting from mechanical
32
+      transformation or translation of a Source form, including but
33
+      not limited to compiled object code, generated documentation,
34
+      and conversions to other media types.
35
+
36
+      "Work" shall mean the work of authorship, whether in Source or
37
+      Object form, made available under the License, as indicated by a
38
+      copyright notice that is included in or attached to the work
39
+      (an example is provided in the Appendix below).
40
+
41
+      "Derivative Works" shall mean any work, whether in Source or Object
42
+      form, that is based on (or derived from) the Work and for which the
43
+      editorial revisions, annotations, elaborations, or other modifications
44
+      represent, as a whole, an original work of authorship. For the purposes
45
+      of this License, Derivative Works shall not include works that remain
46
+      separable from, or merely link (or bind by name) to the interfaces of,
47
+      the Work and Derivative Works thereof.
48
+
49
+      "Contribution" shall mean any work of authorship, including
50
+      the original version of the Work and any modifications or additions
51
+      to that Work or Derivative Works thereof, that is intentionally
52
+      submitted to Licensor for inclusion in the Work by the copyright owner
53
+      or by an individual or Legal Entity authorized to submit on behalf of
54
+      the copyright owner. For the purposes of this definition, "submitted"
55
+      means any form of electronic, verbal, or written communication sent
56
+      to the Licensor or its representatives, including but not limited to
57
+      communication on electronic mailing lists, source code control systems,
58
+      and issue tracking systems that are managed by, or on behalf of, the
59
+      Licensor for the purpose of discussing and improving the Work, but
60
+      excluding communication that is conspicuously marked or otherwise
61
+      designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+      "Contributor" shall mean Licensor and any individual or Legal Entity
64
+      on behalf of whom a Contribution has been received by Licensor and
65
+      subsequently incorporated within the Work.
66
+
67
+   2. Grant of Copyright License. Subject to the terms and conditions of
68
+      this License, each Contributor hereby grants to You a perpetual,
69
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+      copyright license to reproduce, prepare Derivative Works of,
71
+      publicly display, publicly perform, sublicense, and distribute the
72
+      Work and such Derivative Works in Source or Object form.
73
+
74
+   3. Grant of Patent License. Subject to the terms and conditions of
75
+      this License, each Contributor hereby grants to You a perpetual,
76
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+      (except as stated in this section) patent license to make, have made,
78
+      use, offer to sell, sell, import, and otherwise transfer the Work,
79
+      where such license applies only to those patent claims licensable
80
+      by such Contributor that are necessarily infringed by their
81
+      Contribution(s) alone or by combination of their Contribution(s)
82
+      with the Work to which such Contribution(s) was submitted. If You
83
+      institute patent litigation against any entity (including a
84
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+      or a Contribution incorporated within the Work constitutes direct
86
+      or contributory patent infringement, then any patent licenses
87
+      granted to You under this License for that Work shall terminate
88
+      as of the date such litigation is filed.
89
+
90
+   4. Redistribution. You may reproduce and distribute copies of the
91
+      Work or Derivative Works thereof in any medium, with or without
92
+      modifications, and in Source or Object form, provided that You
93
+      meet the following conditions:
94
+
95
+      (a) You must give any other recipients of the Work or
96
+          Derivative Works a copy of this License; and
97
+
98
+      (b) You must cause any modified files to carry prominent notices
99
+          stating that You changed the files; and
100
+
101
+      (c) You must retain, in the Source form of any Derivative Works
102
+          that You distribute, all copyright, patent, trademark, and
103
+          attribution notices from the Source form of the Work,
104
+          excluding those notices that do not pertain to any part of
105
+          the Derivative Works; and
106
+
107
+      (d) If the Work includes a "NOTICE" text file as part of its
108
+          distribution, then any Derivative Works that You distribute must
109
+          include a readable copy of the attribution notices contained
110
+          within such NOTICE file, excluding those notices that do not
111
+          pertain to any part of the Derivative Works, in at least one
112
+          of the following places: within a NOTICE text file distributed
113
+          as part of the Derivative Works; within the Source form or
114
+          documentation, if provided along with the Derivative Works; or,
115
+          within a display generated by the Derivative Works, if and
116
+          wherever such third-party notices normally appear. The contents
117
+          of the NOTICE file are for informational purposes only and
118
+          do not modify the License. You may add Your own attribution
119
+          notices within Derivative Works that You distribute, alongside
120
+          or as an addendum to the NOTICE text from the Work, provided
121
+          that such additional attribution notices cannot be construed
122
+          as modifying the License.
123
+
124
+      You may add Your own copyright statement to Your modifications and
125
+      may provide additional or different license terms and conditions
126
+      for use, reproduction, or distribution of Your modifications, or
127
+      for any such Derivative Works as a whole, provided Your use,
128
+      reproduction, and distribution of the Work otherwise complies with
129
+      the conditions stated in this License.
130
+
131
+   5. Submission of Contributions. Unless You explicitly state otherwise,
132
+      any Contribution intentionally submitted for inclusion in the Work
133
+      by You to the Licensor shall be under the terms and conditions of
134
+      this License, without any additional terms or conditions.
135
+      Notwithstanding the above, nothing herein shall supersede or modify
136
+      the terms of any separate license agreement you may have executed
137
+      with Licensor regarding such Contributions.
138
+
139
+   6. Trademarks. This License does not grant permission to use the trade
140
+      names, trademarks, service marks, or product names of the Licensor,
141
+      except as required for reasonable and customary use in describing the
142
+      origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+   7. Disclaimer of Warranty. Unless required by applicable law or
145
+      agreed to in writing, Licensor provides the Work (and each
146
+      Contributor provides its Contributions) on an "AS IS" BASIS,
147
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+      implied, including, without limitation, any warranties or conditions
149
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+      PARTICULAR PURPOSE. You are solely responsible for determining the
151
+      appropriateness of using or redistributing the Work and assume any
152
+      risks associated with Your exercise of permissions under this License.
153
+
154
+   8. Limitation of Liability. In no event and under no legal theory,
155
+      whether in tort (including negligence), contract, or otherwise,
156
+      unless required by applicable law (such as deliberate and grossly
157
+      negligent acts) or agreed to in writing, shall any Contributor be
158
+      liable to You for damages, including any direct, indirect, special,
159
+      incidental, or consequential damages of any character arising as a
160
+      result of this License or out of the use or inability to use the
161
+      Work (including but not limited to damages for loss of goodwill,
162
+      work stoppage, computer failure or malfunction, or any and all
163
+      other commercial damages or losses), even if such Contributor
164
+      has been advised of the possibility of such damages.
165
+
166
+   9. Accepting Warranty or Additional Liability. While redistributing
167
+      the Work or Derivative Works thereof, You may choose to offer,
168
+      and charge a fee for, acceptance of support, warranty, indemnity,
169
+      or other liability obligations and/or rights consistent with this
170
+      License. However, in accepting such obligations, You may act only
171
+      on Your own behalf and on Your sole responsibility, not on behalf
172
+      of any other Contributor, and only if You agree to indemnify,
173
+      defend, and hold each Contributor harmless for any liability
174
+      incurred by, or claims asserted against, such Contributor by reason
175
+      of your accepting any such warranty or additional liability.
176
+

+ 9
- 0
MANIFEST.in View File

@@ -0,0 +1,9 @@
1
+include AUTHORS
2
+include ChangeLog
3
+include dcmanager/db/sqlalchemy/migrate_repo/migrate.cfg
4
+include dcorch/db/sqlalchemy/migrate_repo/migrate.cfg
5
+exclude .gitignore
6
+exclude .gitreview
7
+exclude .current.cfg
8
+
9
+global-exclude *.pyc

+ 6
- 0
README.rst View File

@@ -0,0 +1,6 @@
1
+DistributedCloud
2
+===============================
3
+
4
+Wind River's Distributed Cloud system supports an edge computing solution by providing
5
+central management and orchestration for a geographically distributed network of Titanium
6
+Cloud systems.

+ 58
- 0
README_DC View File

@@ -0,0 +1,58 @@
1
+# These are instructions for building and installing Distributed Cloud
2
+
3
+# Packages and configure script are now included in the load
4
+
5
+# Run configure script (on target - requires root privileges)
6
+
7
+configure_dc.sh
8
+
9
+# To use DC Manager CLI (these are just examples)
10
+
11
+source /etc/nova/openrc
12
+# Add subclouds (description and location are optional)
13
+dcmanager subcloud add --name=subcloud1 \
14
+    --description="subcloud1 description" \
15
+    --location="subcloud 1 location" \
16
+    --management-subnet=192.168.101.0/24 \
17
+    --management-start-ip=192.168.101.2 \
18
+    --management-end-ip=192.168.101.50 \
19
+    --management-gateway-ip=192.168.101.1 \
20
+    --systemcontroller-gateway-ip=192.168.204.101
21
+dcmanager subcloud add --name=subcloud2 \
22
+    --management-subnet=192.168.102.0/24 \
23
+    --management-start-ip=192.168.102.2 \
24
+    --management-end-ip=192.168.102.50 \
25
+    --management-gateway-ip=192.168.102.1 \
26
+    --systemcontroller-gateway-ip=192.168.204.101
27
+# List all subclouds
28
+dcmanager subcloud list
29
+# Show a single subcloud
30
+dcmanager subcloud show 1
31
+dcmanager subcloud show subcloud2
32
+# Update subcloud description or location
33
+dcmanager subcloud update 1 \
34
+    --description="new subcloud1 description" \
35
+    --location="new subcloud1 location"
36
+# Generate config for a subcloud (additional items are optional)
37
+dcmanager subcloud generate-config 1 \
38
+    --management-interface-port=enp0s8 \
39
+    --management-interface-mtu=1500 \
40
+    --oam-subnet=10.10.10.0/24 \
41
+    --oam-gateway-ip=10.10.10.1 \
42
+    --oam-floating-ip=10.10.10.12 \
43
+    --oam-unit-0-ip=10.10.10.13 \
44
+    --oam-unit-1-ip=10.10.10.14 \
45
+    --oam-interface-port=enp0s3 \
46
+    --oam-interface-mtu=1500
47
+dcmanager subcloud generate-config 2
48
+# Unlock a subcloud
49
+dcmanager subcloud unlock 1
50
+# Lock a subcloud
51
+dcmanager subcloud lock 1
52
+# Delete a subcloud (must be locked)
53
+dcmanager subcloud delete 1
54
+
55
+# To use DC Orchestrator API directly
56
+
57
+run "openstack token issue", then copy the token.  Then to add a subcloud it's something like this:
58
+curl -H "Content-Type: application/json" -H "X-Auth-Token: gAAAAABZ3pT6ZLUaMJfTjAius1zFjcYq25JyiI-eHJe_m5B4NheiN_T94wbG-NrFAAbYNKkOb90MdQ5fnTMjGi1QqZyJ9Rkyg2ZvnaI3Sj8Cw6cSl7goyG0rzseP9b1qADmvX66aqZx79pQQUE0EcC2YDPh-mwgYRoerjuNQ_DGYeWOfZxa06kk "  -X POST -d '{"subcloud":"subcloud2"}' http://127.0.0.1:8118/v1.0/d9f1bcfd50b447de993ec90614e9bdc8/subclouds

+ 2
- 0
babel.cfg View File

@@ -0,0 +1,2 @@
1
+[python: **.py]
2
+

+ 25
- 0
dcmanager/__init__.py View File

@@ -0,0 +1,25 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
4
+# not use this file except in compliance with the License. You may obtain
5
+# a copy of the License at
6
+#
7
+#      http://www.apache.org/licenses/LICENSE-2.0
8
+#
9
+# Unless required by applicable law or agreed to in writing, software
10
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
+# License for the specific language governing permissions and limitations
13
+# under the License.
14
+#
15
+# Copyright (c) 2017 Wind River Systems, Inc.
16
+#
17
+# The right to copy, distribute, modify, or otherwise make use
18
+# of this software may be licensed only pursuant to the terms
19
+# of an applicable Wind River license agreement.
20
+#
21
+
22
+import pbr.version
23
+
24
+
25
+__version__ = pbr.version.VersionInfo('distributedcloud').version_string()

+ 31
- 0
dcmanager/api/README.rst View File

@@ -0,0 +1,31 @@
1
+===============================
2
+api
3
+===============================
4
+
5
+DC Manager API is Web Server Gateway Interface (WSGI) application to receive
6
+and process API calls, including keystonemiddleware to do the authentication,
7
+parameter check and validation, convert API calls to job rpc message, and
8
+then send the job to DC Manager Manager through the queue. If the job will
9
+be processed by DC Manager Manager in synchronous way, the DC Manager API will
10
+wait for the response from the DC Manager Manager. Otherwise, the DC Manager
11
+API will send response to the API caller first, and then send the job to
12
+DC Manager Manager in asynchronous way.
13
+
14
+Multiple DC Manager API could run in parallel, and also can work in
15
+multi-worker mode.
16
+
17
+Multiple DC Manager API will be designed and run in stateless mode, persistent
18
+data will be accessed (read and write) from the DC Manager Database through
19
+the DAL module.
20
+
21
+Setup and encapsulate the API WSGI app
22
+
23
+app.py:
24
+    Setup and encapsulate the API WSGI app, including integrate the
25
+    keystonemiddleware app
26
+
27
+api_config.py:
28
+    API configuration loading and init
29
+
30
+enforcer.py
31
+    Enforces policies on the version2 APIs

+ 0
- 0
dcmanager/api/__init__.py View File


+ 111
- 0
dcmanager/api/api_config.py View File

@@ -0,0 +1,111 @@
1
+# Copyright 2015 Huawei Technologies Co., Ltd.
2
+# All Rights Reserved.
3
+#
4
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+#    not use this file except in compliance with the License. You may obtain
6
+#    a copy of the License at
7
+#
8
+#         http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+#    Unless required by applicable law or agreed to in writing, software
11
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+#    License for the specific language governing permissions and limitations
14
+#    under the License.
15
+#
16
+# Copyright (c) 2017 Wind River Systems, Inc.
17
+#
18
+# The right to copy, distribute, modify, or otherwise make use
19
+# of this software may be licensed only pursuant to the terms
20
+# of an applicable Wind River license agreement.
21
+#
22
+
23
+"""
24
+Routines for configuring DC Manager, largely copy from Neutron
25
+"""
26
+
27
+import os
28
+import sys
29
+
30
+
31
+from oslo_config import cfg
32
+from oslo_log import log as logging
33
+
34
+from dcmanager.common.i18n import _
35
+
36
+
37
+# from dcmanager import policy
38
+from dcmanager.common import version
39
+
40
+LOG = logging.getLogger(__name__)
41
+
42
+common_opts = [
43
+    cfg.StrOpt('bind_host', default='0.0.0.0',
44
+               help=_("The host IP to bind to")),
45
+    cfg.IntOpt('bind_port', default=8119,
46
+               help=_("The port to bind to")),
47
+    cfg.IntOpt('api_workers', default=2,
48
+               help=_("number of api workers")),
49
+    cfg.StrOpt('state_path',
50
+               default=os.path.join(os.path.dirname(__file__), '../'),
51
+               help='Top-level directory for maintaining dcmanager state'),
52
+    cfg.StrOpt('api_extensions_path', default="",
53
+               help=_("The path for API extensions")),
54
+    cfg.StrOpt('auth_strategy', default='keystone',
55
+               help=_("The type of authentication to use")),
56
+    cfg.BoolOpt('allow_bulk', default=True,
57
+                help=_("Allow the usage of the bulk API")),
58
+    cfg.BoolOpt('allow_pagination', default=False,
59
+                help=_("Allow the usage of the pagination")),
60
+    cfg.BoolOpt('allow_sorting', default=False,
61
+                help=_("Allow the usage of the sorting")),
62
+    cfg.StrOpt('pagination_max_limit', default="-1",
63
+               help=_("The maximum number of items returned in a single "
64
+                      "response, value was 'infinite' or negative integer "
65
+                      "means no limit")),
66
+]
67
+
68
+
69
+def init(args, **kwargs):
70
+    # Register the configuration options
71
+    cfg.CONF.register_opts(common_opts)
72
+
73
+    # ks_session.Session.register_conf_options(cfg.CONF)
74
+    # auth.register_conf_options(cfg.CONF)
75
+    logging.register_options(cfg.CONF)
76
+
77
+    cfg.CONF(args=args, project='dcmanager',
78
+             version='%%(prog)s %s' % version.version_info.release_string(),
79
+             **kwargs)
80
+
81
+
82
+def setup_logging():
83
+    """Sets up the logging options for a log with supplied name."""
84
+    product_name = "dcmanager"
85
+    logging.setup(cfg.CONF, product_name)
86
+    LOG.info("Logging enabled!")
87
+    LOG.info("%(prog)s version %(version)s",
88
+             {'prog': sys.argv[0],
89
+              'version': version.version_info.release_string()})
90
+    LOG.debug("command line: %s", " ".join(sys.argv))
91
+
92
+
93
+def reset_service():
94
+    # Reset worker in case SIGHUP is called.
95
+    # Note that this is called only in case a service is running in
96
+    # daemon mode.
97
+    setup_logging()
98
+
99
+    # TODO(joehuang) enforce policy later
100
+    # policy.refresh()
101
+
102
+
103
+def test_init():
104
+    # Register the configuration options
105
+    cfg.CONF.register_opts(common_opts)
106
+    logging.register_options(cfg.CONF)
107
+    setup_logging()
108
+
109
+
110
+def list_opts():
111
+    yield None, common_opts

+ 97
- 0
dcmanager/api/app.py View File

@@ -0,0 +1,97 @@
1
+# Copyright (c) 2015 Huawei, Tech. Co,. Ltd.
2
+# All Rights Reserved.
3
+#
4
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+#    not use this file except in compliance with the License. You may obtain
6
+#    a copy of the License at
7
+#
8
+#         http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+#    Unless required by applicable law or agreed to in writing, software
11
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+#    License for the specific language governing permissions and limitations
14
+#    under the License.
15
+#
16
+# Copyright (c) 2017 Wind River Systems, Inc.
17
+#
18
+# The right to copy, distribute, modify, or otherwise make use
19
+# of this software may be licensed only pursuant to the terms
20
+# of an applicable Wind River license agreement.
21
+#
22
+
23
+import pecan
24
+
25
+from keystonemiddleware import auth_token
26
+from oslo_config import cfg
27
+from oslo_middleware import request_id
28
+from oslo_service import service
29
+
30
+from dcmanager.common import context as ctx
31
+from dcmanager.common.i18n import _
32
+
33
+
34
+def setup_app(*args, **kwargs):
35
+
36
+    opts = cfg.CONF.pecan
37
+    config = {
38
+        'server': {
39
+            'port': cfg.CONF.bind_port,
40
+            'host': cfg.CONF.bind_host
41
+        },
42
+        'app': {
43
+            'root': 'dcmanager.api.controllers.root.RootController',
44
+            'modules': ['dcmanager.api'],
45
+            "debug": opts.debug,
46
+            "auth_enable": opts.auth_enable,
47
+            'errors': {
48
+                400: '/error',
49
+                '__force_dict__': True
50
+                }
51
+            }
52
+        }
53
+
54
+    pecan_config = pecan.configuration.conf_from_dict(config)
55
+
56
+    # app_hooks = [], hook collection will be put here later
57
+
58
+    app = pecan.make_app(
59
+        pecan_config.app.root,
60
+        debug=False,
61
+        wrap_app=_wrap_app,
62
+        force_canonical=False,
63
+        hooks=lambda: [ctx.AuthHook()],
64
+        guess_content_type_from_ext=True
65
+    )
66
+
67
+    return app
68
+
69
+
70
+def _wrap_app(app):
71
+    app = request_id.RequestId(app)
72
+    if cfg.CONF.pecan.auth_enable and cfg.CONF.auth_strategy == 'keystone':
73
+        conf = dict(cfg.CONF.keystone_authtoken)
74
+        # Change auth decisions of requests to the app itself.
75
+        conf.update({'delay_auth_decision': True})
76
+
77
+        # NOTE: Policy enforcement works only if Keystone
78
+        # authentication is enabled. No support for other authentication
79
+        # types at this point.
80
+        return auth_token.AuthProtocol(app, conf)
81
+    else:
82
+        return app
83
+
84
+
85
+_launcher = None
86
+
87
+
88
+def serve(api_service, conf, workers=1):
89
+    global _launcher
90
+    if _launcher:
91
+        raise RuntimeError(_('serve() can only be called once'))
92
+
93
+    _launcher = service.launch(conf, api_service, workers=workers)
94
+
95
+
96
+def wait():
97
+    _launcher.wait()

+ 14
- 0
dcmanager/api/controllers/README.rst View File

@@ -0,0 +1,14 @@
1
+===============================
2
+controllers
3
+===============================
4
+
5
+API request processing
6
+
7
+root.py:
8
+    API root request
9
+
10
+subclouds.py
11
+    Controller for all the subcloud related requests
12
+
13
+restcomm.py:
14
+    common functionality used in API

+ 0
- 0
dcmanager/api/controllers/__init__.py View File


+ 48
- 0
dcmanager/api/controllers/restcomm.py View File

@@ -0,0 +1,48 @@
1
+# Copyright (c) 2015 Huawei Tech. Co., Ltd.
2
+# All Rights Reserved.
3
+#
4
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+#    not use this file except in compliance with the License. You may obtain
6
+#    a copy of the License at
7
+#
8
+#         http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+#    Unless required by applicable law or agreed to in writing, software
11
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+#    License for the specific language governing permissions and limitations
14
+#    under the License.
15
+#
16
+# Copyright (c) 2017 Wind River Systems, Inc.
17
+#
18
+# The right to copy, distribute, modify, or otherwise make use
19
+# of this software may be licensed only pursuant to the terms
20
+# of an applicable Wind River license agreement.
21
+#
22
+
23
+
24
+from pecan import request
25
+
26
+import dcmanager.common.context as k_context
27
+
28
+
29
+def extract_context_from_environ():
30
+    context_paras = {'auth_token': 'HTTP_X_AUTH_TOKEN',
31
+                     'user': 'HTTP_X_USER_ID',
32
+                     'project': 'HTTP_X_TENANT_ID',
33
+                     'user_name': 'HTTP_X_USER_NAME',
34
+                     'tenant_name': 'HTTP_X_PROJECT_NAME',
35
+                     'domain': 'HTTP_X_DOMAIN_ID',
36
+                     'roles': 'HTTP_X_ROLE',
37
+                     'user_domain': 'HTTP_X_USER_DOMAIN_ID',
38
+                     'project_domain': 'HTTP_X_PROJECT_DOMAIN_ID',
39
+                     'request_id': 'openstack.request_id'}
40
+
41
+    environ = request.environ
42
+
43
+    for key in context_paras:
44
+        context_paras[key] = environ.get(context_paras[key])
45
+    role = environ.get('HTTP_X_ROLE')
46
+
47
+    context_paras['is_admin'] = 'admin' in role.split(',')
48
+    return k_context.RequestContext(**context_paras)

+ 64
- 0
dcmanager/api/controllers/root.py View File

@@ -0,0 +1,64 @@
1
+# Copyright (c) 2015 Huawei Tech. Co., Ltd.
2
+# All Rights Reserved.
3
+#
4
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+#    not use this file except in compliance with the License. You may obtain
6
+#    a copy of the License at
7
+#
8
+#         http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+#    Unless required by applicable law or agreed to in writing, software
11
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+#    License for the specific language governing permissions and limitations
14
+#    under the License.
15
+#
16
+# Copyright (c) 2017 Wind River Systems, Inc.
17
+#
18
+# The right to copy, distribute, modify, or otherwise make use
19
+# of this software may be licensed only pursuant to the terms
20
+# of an applicable Wind River license agreement.
21
+#
22
+
23
+
24
+import pecan
25
+
26
+from dcmanager.api.controllers.v1 import root as v1_root
27
+
28
+
29
+class RootController(object):
30
+
31
+    @pecan.expose('json')
32
+    def _lookup(self, version, *remainder):
33
+        version = str(version)
34
+        minor_version = version[-1]
35
+        major_version = version[1]
36
+        remainder = remainder + (minor_version,)
37
+        if major_version == '1':
38
+            return v1_root.Controller(), remainder
39
+
40
+    @pecan.expose(generic=True, template='json')
41
+    def index(self):
42
+        return {
43
+            "versions": [
44
+                {
45
+                    "status": "CURRENT",
46
+                    "links": [
47
+                        {
48
+                            "rel": "self",
49
+                            "href": pecan.request.application_url + "/v1.0/"
50
+                            }
51
+                        ],
52
+                    "id": "v1.0",
53
+                    "updated": "2017-10-2"
54
+                    }
55
+                ]
56
+            }
57
+
58
+    @index.when(method='POST')
59
+    @index.when(method='PUT')
60
+    @index.when(method='DELETE')
61
+    @index.when(method='HEAD')
62
+    @index.when(method='PATCH')
63
+    def not_supported(self):
64
+        pecan.abort(405)

+ 0
- 0
dcmanager/api/controllers/v1/__init__.py View File


+ 87
- 0
dcmanager/api/controllers/v1/alarm_manager.py View File

@@ -0,0 +1,87 @@
1
+# Copyright (c) 2017 Ericsson AB.
2
+# All Rights Reserved.
3
+#
4
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+#    not use this file except in compliance with the License. You may obtain
6
+#    a copy of the License at
7
+#
8
+#         http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+#    Unless required by applicable law or agreed to in writing, software
11
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+#    License for the specific language governing permissions and limitations
14
+#    under the License.
15
+#
16
+# Copyright (c) 2017 Wind River Systems, Inc.
17
+#
18
+# The right to copy, distribute, modify, or otherwise make use
19
+# of this software may be licensed only pursuant to the terms
20
+# of an applicable Wind River license agreement.
21
+#
22
+
23
+from dcmanager.api.controllers import restcomm
24
+from dcorch.rpc import client as dcorch_rpc_client
25
+from oslo_log import log as logging
26
+from pecan import expose
27
+
28
+LOG = logging.getLogger(__name__)
29
+
30
+
31
+class SubcloudAlarmController(object):
32
+    VERSION_ALIASES = {
33
+        'Newton': '1.0',
34
+    }
35
+
36
+    def __init__(self, *args, **kwargs):
37
+        super(SubcloudAlarmController, self).__init__(*args, **kwargs)
38
+        self.dcorch_rpc_client = dcorch_rpc_client.EngineClient()
39
+
40
+    # to do the version compatibility for future purpose
41
+    def _determine_version_cap(self, target):
42
+        version_cap = 1.0
43
+        return version_cap
44
+
45
+    @expose(generic=True, template='json')
46
+    def index(self):
47
+        # Route the request to specific methods with parameters
48
+        pass
49
+
50
+    def _get_alarm_aggregates(self):
51
+        summary = []
52
+        context = restcomm.extract_context_from_environ()
53
+        alarms = self.dcorch_rpc_client.get_alarm_summary(context)
54
+        for alarm in alarms:
55
+            alarm_dict = {'region_name': alarm['region_name'],
56
+                          'uuid': alarm['uuid'],
57
+                          'critical_alarms': alarm['critical_alarms'],
58
+                          'major_alarms': alarm['major_alarms'],
59
+                          'minor_alarms': alarm['minor_alarms'],
60
+                          'warnings': alarm['warnings'],
61
+                          'cloud_status': alarm['cloud_status']}
62
+            summary.append(alarm_dict)
63
+        return {'alarm_summary': summary}
64
+
65
+    @index.when(method='GET', template='json')
66
+    def get(self):
67
+        """Get List of alarm summarys
68
+
69
+        """
70
+        return self._get_alarm_aggregates()
71
+
72
+    def _get_alarm_summary(self):
73
+        alarms = self._get_alarm_aggregates()
74
+        summary = {'critical': 0,
75
+                   'degraded': 0,
76
+                   'ok': 0,
77
+                   'unreachable': 0}
78
+        for alarm in alarms:
79
+            summary[alarm['cloud_status']] += 1
80
+        return summary
81
+
82
+    @index.when(method='summary', template='json')
83
+    def summary(self):
84
+        """Get an agregate of all subcloud status
85
+
86
+        """
87
+        return self._get_alarm_summary()

+ 63
- 0
dcmanager/api/controllers/v1/root.py View File

@@ -0,0 +1,63 @@
1
+# Copyright (c) 2017 Ericsson AB.
2
+# All Rights Reserved.
3
+#
4
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+#    not use this file except in compliance with the License. You may obtain
6
+#    a copy of the License at
7
+#
8
+#         http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+#    Unless required by applicable law or agreed to in writing, software
11
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+#    License for the specific language governing permissions and limitations
14
+#    under the License.
15
+#
16
+# Copyright (c) 2017 Wind River Systems, Inc.
17
+#
18
+# The right to copy, distribute, modify, or otherwise make use
19
+# of this software may be licensed only pursuant to the terms
20
+# of an applicable Wind River license agreement.
21
+#
22
+
23
+
24
+from dcmanager.api.controllers.v1 import alarm_manager
25
+from dcmanager.api.controllers.v1 import subclouds
26
+from dcmanager.api.controllers.v1 import sw_update_options
27
+from dcmanager.api.controllers.v1 import sw_update_strategy
28
+
29
+import pecan
30
+
31
+
32
+class Controller(object):
33
+
34
+    def _get_resource_controller(self, remainder):
35
+
36
+        if not remainder:
37
+            pecan.abort(404)
38
+            return
39
+        minor_version = remainder[-1]
40
+        remainder = remainder[:-1]
41
+        sub_controllers = dict()
42
+        if minor_version == '0':
43
+            sub_controllers["subclouds"] = subclouds.SubcloudsController
44
+            sub_controllers["alarms"] = alarm_manager.SubcloudAlarmController
45
+            sub_controllers["sw-update-strategy"] = \
46
+                sw_update_strategy.SwUpdateStrategyController
47
+            sub_controllers["sw-update-options"] = \
48
+                sw_update_options.SwUpdateOptionsController
49
+
50
+        for name, ctrl in sub_controllers.items():
51
+            setattr(self, name, ctrl)
52
+
53
+        resource = remainder[0]
54
+        if resource not in sub_controllers:
55
+            pecan.abort(404)
56
+            return
57
+
58
+        remainder = remainder[1:]
59
+        return sub_controllers[resource](), remainder
60
+
61
+    @pecan.expose()
62
+    def _lookup(self, *remainder):
63
+        return self._get_resource_controller(remainder)

+ 688
- 0
dcmanager/api/controllers/v1/subclouds.py View File

@@ -0,0 +1,688 @@
1
+# Copyright (c) 2017 Ericsson AB.
2
+# All Rights Reserved.
3
+#
4
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+#    not use this file except in compliance with the License. You may obtain
6
+#    a copy of the License at
7
+#
8
+#         http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+#    Unless required by applicable law or agreed to in writing, software
11
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+#    License for the specific language governing permissions and limitations
14
+#    under the License.
15
+#
16
+# Copyright (c) 2017 Wind River Systems, Inc.
17
+#
18
+# The right to copy, distribute, modify, or otherwise make use
19
+# of this software may be licensed only pursuant to the terms
20
+# of an applicable Wind River license agreement.
21
+#
22
+
23
+import keyring
24
+from netaddr import IPAddress
25
+from netaddr import IPNetwork
26
+from netaddr import IPRange
27
+from oslo_config import cfg
28
+from oslo_log import log as logging
29
+from oslo_messaging import RemoteError
30
+
31
+import pecan
32
+from pecan import expose
33
+from pecan import request
34
+
35
+from configutilities.common import crypt
36
+from configutilities.common.exceptions import ValidateFail
37
+from configutilities.common.utils import validate_address_str
38
+from configutilities.common.utils import validate_network_str
39
+
40
+from dcorch.drivers.openstack.keystone_v3 import KeystoneClient
41
+
42
+from dcmanager.api.controllers import restcomm
43
+from dcmanager.common import consts
44
+from dcmanager.common import exceptions
45
+from dcmanager.common.i18n import _
46
+from dcmanager.db import api as db_api
47
+from dcmanager.drivers.openstack.sysinv_v1 import SysinvClient
48
+from dcmanager.rpc import client as rpc_client
49
+
50
+from Crypto.Hash import MD5
51
+import json
52
+
53
+CONF = cfg.CONF
54
+LOG = logging.getLogger(__name__)
55
+# System mode
56
+SYSTEM_MODE_DUPLEX = "duplex"
57
+SYSTEM_MODE_SIMPLEX = "simplex"
58
+SYSTEM_MODE_DUPLEX_DIRECT = "duplex-direct"
59
+
60
+
61
+class SubcloudsController(object):
62
+    VERSION_ALIASES = {
63
+        'Newton': '1.0',
64
+    }
65
+
66
+    def __init__(self):
67
+        super(SubcloudsController, self).__init__()
68
+        self.rpc_client = rpc_client.ManagerClient()
69
+
70
+    # to do the version compatibility for future purpose
71
+    def _determine_version_cap(self, target):
72
+        version_cap = 1.0
73
+        return version_cap
74
+
75
+    @expose(generic=True, template='json')
76
+    def index(self):
77
+        # Route the request to specific methods with parameters
78
+        pass
79
+
80
+    def _validate_subcloud_config(self,
81
+                                  context,
82
+                                  name,
83
+                                  management_subnet_str,
84
+                                  management_start_ip_str,
85
+                                  management_end_ip_str,
86
+                                  management_gateway_ip_str,
87
+                                  systemcontroller_gateway_ip_str):
88
+        """Check whether subcloud config is valid."""
89
+
90
+        # Validate the name
91
+        if name.isdigit():
92
+            pecan.abort(400, _("name must contain alphabetic characters"))
93
+
94
+        if name in [consts.DEFAULT_REGION_NAME,
95
+                    consts.SYSTEM_CONTROLLER_NAME]:
96
+            pecan.abort(400, _("name cannot be %(bad_name1)s or %(bad_name2)s")
97
+                        % {'bad_name1': consts.DEFAULT_REGION_NAME,
98
+                           'bad_name2': consts.SYSTEM_CONTROLLER_NAME})
99
+
100
+        # Parse/validate the management subnet
101
+        subcloud_subnets = []
102
+        subclouds = db_api.subcloud_get_all(context)
103
+        for subcloud in subclouds:
104
+            subcloud_subnets.append(IPNetwork(subcloud.management_subnet))
105
+
106
+        MIN_MANAGEMENT_SUBNET_SIZE = 8
107
+        # subtract 3 for network, gateway and broadcast addresses.
108
+        MIN_MANAGEMENT_ADDRESSES = MIN_MANAGEMENT_SUBNET_SIZE - 3
109
+
110
+        management_subnet = None
111
+        try:
112
+            management_subnet = validate_network_str(
113
+                management_subnet_str,
114
+                minimum_size=MIN_MANAGEMENT_SUBNET_SIZE,
115
+                existing_networks=subcloud_subnets)
116
+        except ValidateFail as e:
117
+            LOG.exception(e)
118
+            pecan.abort(400, _("management-subnet invalid: %s") % e)
119
+
120
+        # Parse/validate the start/end addresses
121
+        management_start_ip = None
122
+        try:
123
+            management_start_ip = validate_address_str(
124
+                management_start_ip_str, management_subnet)
125
+        except ValidateFail as e:
126
+            LOG.exception(e)
127
+            pecan.abort(400, _("management-start-ip invalid: %s") % e)
128
+
129
+        management_end_ip = None
130
+        try:
131
+            management_end_ip = validate_address_str(
132
+                management_end_ip_str, management_subnet)
133
+        except ValidateFail as e:
134
+            LOG.exception(e)
135
+            pecan.abort(400, _("management-end-ip invalid: %s") % e)
136
+
137
+        if not management_start_ip < management_end_ip:
138
+            pecan.abort(
139
+                400,
140
+                _("management-start-ip  not less than management-end-ip"))
141
+
142
+        if not len(IPRange(management_start_ip, management_end_ip)) >= \
143
+                MIN_MANAGEMENT_ADDRESSES:
144
+            pecan.abort(
145
+                400,
146
+                _("management address range must contain at least %d "
147
+                  "addresses") % MIN_MANAGEMENT_ADDRESSES)
148
+
149
+        # Parse/validate the gateway
150
+        try:
151
+            validate_address_str(
152
+                management_gateway_ip_str, management_subnet)
153
+        except ValidateFail as e:
154
+            LOG.exception(e)
155
+            pecan.abort(400, _("management-gateway-ip invalid: %s") % e)
156
+
157
+        # Ensure subcloud management gateway is not within the actual subcloud
158
+        # management subnet address pool for consistency with the
159
+        # systemcontroller gateway restriction below. Address collision
160
+        # is not a concern as the address is added to sysinv.
161
+        subcloud_mgmt_address_start = IPAddress(management_start_ip_str)
162
+        subcloud_mgmt_address_end = IPAddress(management_end_ip_str)
163
+        subcloud_mgmt_gw_ip = IPAddress(management_gateway_ip_str)
164
+        if ((subcloud_mgmt_gw_ip >= subcloud_mgmt_address_start) and
165
+                (subcloud_mgmt_gw_ip <= subcloud_mgmt_address_end)):
166
+            pecan.abort(400, _("management-gateway-ip invalid, "
167
+                               "is within management pool: %(start)s - "
168
+                               "%(end)s") %
169
+                        {'start': subcloud_mgmt_address_start,
170
+                         'end': subcloud_mgmt_address_end})
171
+
172
+        # Ensure systemcontroller gateway is in the management subnet
173
+        # for the systemcontroller region.
174
+        management_address_pool = self._get_management_address_pool(context)
175
+        systemcontroller_subnet_str = "%s/%d" % (
176
+            management_address_pool.network,
177
+            management_address_pool.prefix)
178
+        systemcontroller_subnet = IPNetwork(systemcontroller_subnet_str)
179
+        try:
180
+            validate_address_str(
181
+                systemcontroller_gateway_ip_str, systemcontroller_subnet)
182
+        except ValidateFail as e:
183
+            LOG.exception(e)
184
+            pecan.abort(400, _("systemcontroller-gateway-ip invalid: %s") % e)
185
+        # Ensure systemcontroller gateway is not within the actual
186
+        # management subnet address pool to prevent address collision.
187
+        mgmt_address_start = IPAddress(management_address_pool.ranges[0][0])
188
+        mgmt_address_end = IPAddress(management_address_pool.ranges[0][1])
189
+        systemcontroller_gw_ip = IPAddress(systemcontroller_gateway_ip_str)
190
+        if ((systemcontroller_gw_ip >= mgmt_address_start) and
191
+                (systemcontroller_gw_ip <= mgmt_address_end)):
192
+            pecan.abort(400, _("systemcontroller-gateway-ip invalid, "
193
+                               "is within management pool: %(start)s - "
194
+                               "%(end)s") %
195
+                        {'start': mgmt_address_start, 'end': mgmt_address_end})
196
+
197
+    def _create_subcloud_config_file(self, context, subcloud, payload):
198
+        """Creates the subcloud config file for a subcloud."""
199
+        DEFAULT_STR = '<EDIT>'
200
+
201
+        pxe_cidr = payload.get(
202
+            'pxe-subnet', DEFAULT_STR)
203
+        management_vlan = payload.get(
204
+            'management-vlan', DEFAULT_STR)
205
+        management_interface_mtu = payload.get(
206
+            'management-interface-mtu', DEFAULT_STR)
207
+        management_interface_ports = payload.get(
208
+            'management-interface-port', DEFAULT_STR)
209
+        oam_cidr = payload.get(
210
+            'oam-subnet', DEFAULT_STR)
211
+        oam_gateway = payload.get(
212
+            'oam-gateway-ip', DEFAULT_STR)
213
+        oam_ip_floating_address = payload.get(
214
+            'oam-floating-ip', DEFAULT_STR)
215
+        oam_ip_unit_0_address = payload.get(
216
+            'oam-unit-0-ip', DEFAULT_STR)
217
+        oam_ip_unit_1_address = payload.get(
218
+            'oam-unit-1-ip', DEFAULT_STR)
219
+        oam_interface_mtu = payload.get(
220
+            'oam-interface-mtu', DEFAULT_STR)
221
+        oam_interface_ports = payload.get(
222
+            'oam-interface-port', DEFAULT_STR)
223
+        system_mode = payload.get(
224
+            'system-mode', DEFAULT_STR)
225
+
226
+        management_address_pool = self._get_management_address_pool(context)
227
+        systemcontroller_subnet = "%s/%d" % (
228
+            management_address_pool.network,
229
+            management_address_pool.prefix)
230
+        sc_mgmt_floating_ip = management_address_pool.floating_address
231
+
232
+        subcloud_config = ""
233
+        if system_mode in [SYSTEM_MODE_SIMPLEX, SYSTEM_MODE_DUPLEX,
234
+                           SYSTEM_MODE_DUPLEX_DIRECT]:
235
+            subcloud_config += (
236
+                "[SYSTEM]\n"
237
+                "SYSTEM_MODE={}\n".format(system_mode))
238
+
239
+        if system_mode == SYSTEM_MODE_SIMPLEX:
240
+            subcloud_oamip_config = (
241
+                "IP_ADDRESS = {oam_ip_floating_address}\n"
242
+            ).format(
243
+                oam_ip_floating_address=oam_ip_floating_address,
244
+            )
245
+        else:
246
+            subcloud_oamip_config = (
247
+                "IP_FLOATING_ADDRESS = {oam_ip_floating_address}\n"
248
+                "IP_UNIT_0_ADDRESS = {oam_ip_unit_0_address}\n"
249
+                "IP_UNIT_1_ADDRESS = {oam_ip_unit_1_address}\n"
250
+            ).format(
251
+                oam_ip_floating_address=oam_ip_floating_address,
252
+                oam_ip_unit_0_address=oam_ip_unit_0_address,
253
+                oam_ip_unit_1_address=oam_ip_unit_1_address,
254
+            )
255
+
256
+        MIN_MANAGEMENT_SUBNET_SIZE = 8
257
+        tmp_management_subnet = validate_network_str(
258
+            subcloud.management_subnet,
259
+            minimum_size=MIN_MANAGEMENT_SUBNET_SIZE)
260
+
261
+        is_ipv6_mgmt = (tmp_management_subnet.version == 6)
262
+
263
+        # If ipv6 then we need pxe subnet and management_vlan.
264
+        # If user specified pxe boot subnet, then management vlan is required
265
+        # and vice versa
266
+        if is_ipv6_mgmt or (pxe_cidr != DEFAULT_STR) or \
267
+                (management_vlan != DEFAULT_STR):
268
+            subcloud_config += (
269
+                "[REGION2_PXEBOOT_NETWORK]\n"
270
+                "PXEBOOT_CIDR = {pxe_cidr}\n"
271
+                "[MGMT_NETWORK]\n"
272
+                "VLAN = {management_vlan}\n"
273
+            ).format(
274
+                pxe_cidr=pxe_cidr,
275
+                management_vlan=management_vlan,
276
+            )
277
+        else:
278
+            subcloud_config += "[MGMT_NETWORK]\n"
279
+
280
+        subcloud_config += (
281
+            "CIDR = {management_cidr}\n"
282
+            "GATEWAY = {management_gateway}\n"
283
+            "IP_START_ADDRESS = {management_ip_start_address}\n"
284
+            "IP_END_ADDRESS = {management_ip_end_address}\n"
285
+            "DYNAMIC_ALLOCATION = Y\n"
286
+            "LOGICAL_INTERFACE = LOGICAL_INTERFACE_1\n"
287
+            "[LOGICAL_INTERFACE_1]\n"
288
+            "LAG_INTERFACE = N\n"
289
+            "INTERFACE_MTU = {management_interface_mtu}\n"
290
+            "INTERFACE_PORTS = {management_interface_ports}\n"
291
+            "[OAM_NETWORK]\n"
292
+            "CIDR = {oam_cidr}\n"
293
+            "GATEWAY = {oam_gateway}\n" +
294
+            subcloud_oamip_config +
295
+            "LOGICAL_INTERFACE = LOGICAL_INTERFACE_2\n"
296
+            "[LOGICAL_INTERFACE_2]\n"
297
+            "LAG_INTERFACE = N\n"
298
+            "INTERFACE_MTU = {oam_interface_mtu}\n"
299
+            "INTERFACE_PORTS = {oam_interface_ports}\n"
300
+            "[SHARED_SERVICES]\n"
301
+            "SYSTEM_CONTROLLER_SUBNET = {systemcontroller_subnet}\n"
302
+            "SYSTEM_CONTROLLER_FLOATING_ADDRESS = {sc_mgmt_floating_ip}\n"
303
+            "REGION_NAME = SystemController\n"
304
+            "ADMIN_PROJECT_NAME = admin\n"
305
+            "ADMIN_USER_NAME = admin\n"
306
+            "ADMIN_PASSWORD = {admin_password}\n"
307
+            "KEYSTONE_ADMINURL = {keystone_adminurl}\n"
308
+            "KEYSTONE_SERVICE_NAME = keystone\n"
309
+            "KEYSTONE_SERVICE_TYPE = identity\n"
310
+            "GLANCE_SERVICE_NAME = glance\n"
311
+            "GLANCE_SERVICE_TYPE = image\n"
312
+            "GLANCE_CACHED = True\n"
313
+            "[REGION_2_SERVICES]\n"
314
+            "REGION_NAME = {region_2_name}\n"
315
+            "[VERSION]\n"
316
+            "RELEASE = {release}\n"
317
+        ).format(
318
+            management_cidr=subcloud.management_subnet,
319
+            management_gateway=subcloud.management_gateway_ip,
320
+            management_ip_start_address=subcloud.management_start_ip,
321
+            management_ip_end_address=subcloud.management_end_ip,
322
+            management_interface_mtu=management_interface_mtu,
323
+            management_interface_ports=management_interface_ports,
324
+            oam_cidr=oam_cidr,
325
+            oam_gateway=oam_gateway,
326
+            oam_interface_mtu=oam_interface_mtu,
327
+            oam_interface_ports=oam_interface_ports,
328
+            systemcontroller_subnet=systemcontroller_subnet,
329
+            sc_mgmt_floating_ip=sc_mgmt_floating_ip,
330
+            admin_password=cfg.CONF.cache.admin_password,
331
+            keystone_adminurl=cfg.CONF.cache.auth_uri,
332
+            region_2_name=subcloud.name,
333
+            release=subcloud.software_version,
334
+        )
335
+        return subcloud_config
336
+
337
+    def _get_subcloud_users(self):
338
+        """Get the subcloud users and passwords from keyring"""
339
+        DEFAULT_SERVICE_PROJECT_NAME = 'services'
340
+        # First entry is openstack user name, second entry is the user stored
341
+        # in keyring. Not sure why heat_admin uses a different keystone name.
342
+        SUBCLOUD_USERS = [
343
+            ('nova', 'nova'),
344
+            ('placement', 'placement'),
345
+            ('sysinv', 'sysinv'),
346
+            ('patching', 'patching'),
347
+            ('heat', 'heat'),
348
+            ('ceilometer', 'ceilometer'),
349
+            ('vim', 'vim'),
350
+            ('aodh', 'aodh'),
351
+            ('panko', 'panko'),
352
+            ('mtce', 'mtce'),
353
+            ('cinder', 'cinder'),
354
+            ('glance', 'glance'),
355
+            ('neutron', 'neutron'),
356
+            ('heat_admin', 'heat-domain'),
357
+            ('gnocchi', 'gnocchi')
358
+        ]
359
+
360
+        user_list = list()
361
+        for user in SUBCLOUD_USERS:
362
+            password = keyring.get_password(user[1],
363
+                                            DEFAULT_SERVICE_PROJECT_NAME)
364
+            if password:
365
+                user_dict = dict()
366
+                user_dict['name'] = user[0]
367
+                user_dict['password'] = password
368
+                user_list.append(user_dict)
369
+            else:
370
+                LOG.error("User %s not found in keyring as %s" % (user[0],
371
+                                                                  user[1]))
372
+                pecan.abort(500, _('System configuration error'))
373
+
374
+        return user_list
375
+
376
+    def _get_management_address_pool(self, context):
377
+        """Get the system controller's management address pool"""
378
+        session = KeystoneClient().endpoint_cache.get_session_from_token(
379
+            context.auth_token, context.project)
380
+        sysinv_client = SysinvClient(consts.DEFAULT_REGION_NAME, session)
381
+        return sysinv_client.get_management_address_pool()
382
+
383
+    @index.when(method='GET', template='json')
384
+    def get(self, subcloud_ref=None, qualifier=None):
385
+        """Get details about subcloud.
386
+
387
+        :param subcloud_ref: ID or name of subcloud
388
+        """
389
+        context = restcomm.extract_context_from_environ()
390
+
391
+        if subcloud_ref is None:
392
+            # List of subclouds requested
393
+            subclouds = db_api.subcloud_get_all_with_status(context)
394
+            result = dict()
395
+            result['subclouds'] = []
396
+            first_time = True
397
+            subcloud_list = []
398
+            subcloud_status_list = []
399
+
400
+            # We get back a subcloud, subcloud_status pair for every
401
+            # subcloud_status entry corresponding to a subcloud.  (Subcloud
402
+            # info repeats)
403
+            # Aggregate all the sync status for each of the
404
+            # endpoints per subcloud into an overall sync status
405
+            for subcloud, subcloud_status in subclouds:
406
+                subcloud_dict = db_api.subcloud_db_model_to_dict(subcloud)
407
+                subcloud_status_dict = db_api.subcloud_status_db_model_to_dict(
408
+                    subcloud_status)
409
+                subcloud_dict.update(subcloud_status_dict)
410
+
411
+                if not first_time:
412
+                    if subcloud_list[-1]['id'] == subcloud_dict['id']:
413
+                        # We have a match for this subcloud id already,
414
+                        # check if we have a same sync_status
415
+                        if subcloud_list[-1][consts.SYNC_STATUS] != \
416
+                                subcloud_dict[consts.SYNC_STATUS]:
417
+                            subcloud_list[-1][consts.SYNC_STATUS] = \
418
+                                consts.SYNC_STATUS_OUT_OF_SYNC
419
+
420
+                        if subcloud_status:
421
+                            subcloud_status_list.append(
422
+                                db_api.subcloud_endpoint_status_db_model_to_dict(  # noqa
423
+                                    subcloud_status))
424
+                        subcloud_list[-1][
425
+                            consts.ENDPOINT_SYNC_STATUS] = subcloud_status_list
426
+
427
+                    else:
428
+                        subcloud_status_list = []
429
+                        if subcloud_status:
430
+                            subcloud_status_list.append(
431
+                                db_api.subcloud_endpoint_status_db_model_to_dict(  # noqa
432
+                                    subcloud_status))
433
+
434
+                        subcloud_list.append(subcloud_dict)
435
+                else:
436
+                    if subcloud_status:
437
+                        subcloud_status_list.append(
438
+                            db_api.subcloud_endpoint_status_db_model_to_dict(
439
+                                subcloud_status))
440
+                    subcloud_list.append(subcloud_dict)
441
+
442
+                first_time = False
443
+
444
+            for s in subcloud_list:
445
+                result['subclouds'].append(s)
446
+
447
+            return result
448
+        else:
449
+            # Single subcloud requested
450
+            subcloud = None
451
+            subcloud_dict = dict()
452
+            subcloud_status_list = []
453
+            endpoint_sync_dict = dict()
454
+
455
+            if subcloud_ref.isdigit():
456
+                # Look up subcloud as an ID
457
+                try:
458
+                    subcloud = db_api.subcloud_get(context, subcloud_ref)
459
+                except exceptions.SubcloudNotFound:
460
+                    pecan.abort(404, _('Subcloud not found'))
461
+            else:
462
+                # Look up subcloud by name
463
+                try:
464
+                    subcloud = db_api.subcloud_get_by_name(context,
465
+                                                           subcloud_ref)
466
+                except exceptions.SubcloudNameNotFound:
467
+                    pecan.abort(404, _('Subcloud not found'))
468
+
469
+            subcloud_id = subcloud.id
470
+
471
+            if qualifier:
472
+                # Configuration for this subcloud requested.
473
+                # Encrypt before sending.
474
+                if qualifier == 'config':
475
+                    result = dict()
476
+                    user_list = self._get_subcloud_users()
477
+
478
+                    # Use a hash of the subcloud name + management subnet
479
+                    # as the encryption key
480
+                    hashstring = subcloud.name + subcloud.management_subnet
481
+                    h = MD5.new()
482
+                    h.update(hashstring)
483
+                    encryption_key = h.hexdigest()
484
+                    user_list_string = json.dumps(user_list)
485
+                    user_list_encrypted = crypt.urlsafe_encrypt(
486
+                        encryption_key,
487
+                        user_list_string)
488
+                    result['users'] = user_list_encrypted
489
+                    return result
490
+                else:
491
+                    pecan.abort(400, _('Invalid request'))
492
+            else:
493
+                # Data for this subcloud requested
494
+                # Build up and append a dictionary of the endpoints
495
+                # sync status to the result.
496
+                for subcloud, subcloud_status in db_api. \
497
+                        subcloud_get_with_status(context, subcloud_id):
498
+                    subcloud_dict = db_api.subcloud_db_model_to_dict(
499
+                        subcloud)
500
+                    # may be empty subcloud_status entry, account for this
501
+                    if subcloud_status:
502
+                        subcloud_status_list.append(
503
+                            db_api.subcloud_endpoint_status_db_model_to_dict(
504
+                                subcloud_status))
505
+                endpoint_sync_dict = {consts.ENDPOINT_SYNC_STATUS:
506
+                                      subcloud_status_list}
507
+                subcloud_dict.update(endpoint_sync_dict)
508
+
509
+                return subcloud_dict
510
+
511
+    @index.when(method='POST', template='json')
512
+    def post(self, subcloud_ref=None, qualifier=None):
513
+        """Create a new subcloud.
514
+
515
+        :param subcloud_ref: ID of or name subcloud (only used when generating
516
+                             config)
517
+        :param qualifier: if 'config', returns the config INI file for the
518
+                          subcloud
519
+        """
520
+
521
+        context = restcomm.extract_context_from_environ()
522
+
523
+        if subcloud_ref is None:
524
+            payload = eval(request.body)
525
+            if not payload:
526
+                pecan.abort(400, _('Body required'))
527
+            name = payload.get('name')
528
+            if not name:
529
+                pecan.abort(400, _('name required'))
530
+            management_subnet = payload.get('management-subnet')
531
+            if not management_subnet:
532
+                pecan.abort(400, _('management-subnet required'))
533
+            management_start_ip = payload.get('management-start-ip')
534
+            if not management_start_ip:
535
+                pecan.abort(400, _('management-start-ip required'))
536
+            management_end_ip = payload.get('management-end-ip')
537
+            if not management_end_ip:
538
+                pecan.abort(400, _('management-end-ip required'))
539
+            management_gateway_ip = payload.get('management-gateway-ip')
540
+            if not management_gateway_ip:
541
+                pecan.abort(400, _('management-gateway-ip required'))
542
+            systemcontroller_gateway_ip = \
543
+                payload.get('systemcontroller-gateway-ip')
544
+            if not systemcontroller_gateway_ip:
545
+                pecan.abort(400, _('systemcontroller-gateway-ip required'))
546
+
547
+            self._validate_subcloud_config(context,
548
+                                           name,
549
+                                           management_subnet,
550
+                                           management_start_ip,
551
+                                           management_end_ip,
552
+                                           management_gateway_ip,
553
+                                           systemcontroller_gateway_ip)
554
+
555
+            try:
556
+                # Ask dcmanager-manager to add the subcloud.
557
+                # It will do all the real work...
558
+                return self.rpc_client.add_subcloud(context, payload)
559
+            except RemoteError as e:
560
+                pecan.abort(422, e.value)
561
+            except Exception as e:
562
+                LOG.exception(e)
563
+                pecan.abort(500, _('Unable to create subcloud'))
564
+        elif qualifier:
565
+            if qualifier == 'config':
566
+                subcloud = None
567
+
568
+                if subcloud_ref.isdigit():
569
+                    # Look up subcloud as an ID
570
+                    try:
571
+                        subcloud = db_api.subcloud_get(context, subcloud_ref)
572
+                    except exceptions.SubcloudNotFound:
573
+                        pecan.abort(404, _('Subcloud not found'))
574
+                else:
575
+                    # Look up subcloud by name
576
+                    try:
577
+                        subcloud = db_api.subcloud_get_by_name(context,
578
+                                                               subcloud_ref)
579
+                    except exceptions.SubcloudNameNotFound:
580
+                        pecan.abort(404, _('Subcloud not found'))
581
+
582
+                payload = dict()
583
+                if request.body:
584
+                    payload = eval(request.body)
585
+                config_file = self._create_subcloud_config_file(
586
+                    context, subcloud, payload)
587
+                result = dict()
588
+                result['config'] = config_file
589
+                return result
590
+            else:
591
+                pecan.abort(400, _('Invalid request'))
592
+        else:
593
+            pecan.abort(400, _('Invalid request'))
594
+
595
+    @index.when(method='PATCH', template='json')
596
+    def patch(self, subcloud_ref=None):
597
+        """Update a subcloud.
598
+
599
+        :param subcloud_ref: ID or name of subcloud to update
600
+        """
601
+
602
+        context = restcomm.extract_context_from_environ()
603
+        subcloud = None
604
+
605
+        if subcloud_ref is None:
606
+            pecan.abort(400, _('Subcloud ID required'))
607
+
608
+        payload = eval(request.body)
609
+        if not payload:
610
+            pecan.abort(400, _('Body required'))
611
+
612
+        if subcloud_ref.isdigit():
613
+            # Look up subcloud as an ID
614
+            try:
615
+                subcloud = db_api.subcloud_get(context, subcloud_ref)
616
+            except exceptions.SubcloudNotFound:
617
+                pecan.abort(404, _('Subcloud not found'))
618
+        else:
619
+            # Look up subcloud by name
620
+            try:
621
+                subcloud = db_api.subcloud_get_by_name(context,
622
+                                                       subcloud_ref)
623
+            except exceptions.SubcloudNameNotFound:
624
+                pecan.abort(404, _('Subcloud not found'))
625
+
626
+        subcloud_id = subcloud.id
627
+
628
+        management_state = payload.get('management-state')
629
+        description = payload.get('description')
630
+        location = payload.get('location')
631
+
632
+        if not (management_state or description or location):
633
+            pecan.abort(400, _('nothing to update'))
634
+
635
+        # Syntax checking
636
+        if management_state and \
637
+                management_state not in [consts.MANAGEMENT_UNMANAGED,
638
+                                         consts.MANAGEMENT_MANAGED]:
639
+            pecan.abort(400, _('Invalid management-state'))
640
+
641
+        try:
642
+            # Inform dcmanager-manager that subcloud has been updated.
643
+            # It will do all the real work...
644
+            subcloud = self.rpc_client.update_subcloud(
645
+                context, subcloud_id, management_state=management_state,
646
+                description=description, location=location)
647
+            return subcloud
648
+        except RemoteError as e:
649
+            pecan.abort(422, e.value)
650
+        except Exception as e:
651
+            # additional exceptions.
652
+            LOG.exception(e)
653
+            pecan.abort(500, _('Unable to update subcloud'))
654
+
655
+    @index.when(method='delete', template='json')
656
+    def delete(self, subcloud_ref):
657
+        """Delete a subcloud.
658
+
659
+        :param subcloud_ref: ID or name of subcloud to delete.
660
+        """
661
+        context = restcomm.extract_context_from_environ()
662
+        subcloud = None
663
+
664
+        if subcloud_ref.isdigit():
665
+            # Look up subcloud as an ID
666
+            try:
667
+                subcloud = db_api.subcloud_get(context, subcloud_ref)
668
+            except exceptions.SubcloudNotFound:
669
+                pecan.abort(404, _('Subcloud not found'))
670
+        else:
671
+            # Look up subcloud by name
672
+            try:
673
+                subcloud = db_api.subcloud_get_by_name(context,
674
+                                                       subcloud_ref)
675
+            except exceptions.SubcloudNameNotFound:
676
+                pecan.abort(404, _('Subcloud not found'))
677
+
678
+        subcloud_id = subcloud.id
679
+
680
+        try:
681
+            # Ask dcmanager-manager to delete the subcloud.
682
+            # It will do all the real work...
683
+            return self.rpc_client.delete_subcloud(context, subcloud_id)
684
+        except RemoteError as e:
685
+            pecan.abort(422, e.value)
686
+        except Exception as e:
687
+            LOG.exception(e)
688
+            pecan.abort(500, _('Unable to delete subcloud'))

+ 244
- 0
dcmanager/api/controllers/v1/sw_update_options.py View File

@@ -0,0 +1,244 @@
1
+# Copyright (c) 2017 Ericsson AB.
2
+# All Rights Reserved.
3
+#
4
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+#    not use this file except in compliance with the License. You may obtain
6
+#    a copy of the License at
7
+#
8
+#         http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+#    Unless required by applicable law or agreed to in writing, software
11
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+#    License for the specific language governing permissions and limitations
14
+#    under the License.
15
+#
16
+# Copyright (c) 2017 Wind River Systems, Inc.
17
+#
18
+# The right to copy, distribute, modify, or otherwise make use
19
+# of this software may be licensed only pursuant to the terms
20
+# of an applicable Wind River license agreement.
21
+#
22
+
23
+from oslo_config import cfg
24
+from oslo_log import log as logging
25
+
26
+import pecan
27
+from pecan import expose
28
+from pecan import request
29
+
30
+from dcmanager.api.controllers import restcomm
31
+from dcmanager.common import consts
32
+from dcmanager.common import exceptions
33
+from dcmanager.common.i18n import _
34
+from dcmanager.common import utils
35
+from dcmanager.db import api as db_api
36
+from dcmanager.rpc import client as rpc_client
37
+
38
+CONF = cfg.CONF
39
+LOG = logging.getLogger(__name__)
40
+
41
+
42
+class SwUpdateOptionsController(object):
43
+
44
+    def __init__(self):
45
+        super(SwUpdateOptionsController, self).__init__()
46
+        self.rpc_client = rpc_client.ManagerClient()
47
+
48
+    @expose(generic=True, template='json')
49
+    def index(self):
50
+        # Route the request to specific methods with parameters
51
+        pass
52
+
53
+    @index.when(method='GET', template='json')
54
+    def get(self, subcloud_ref=None):
55
+        """Get details about software update options.
56
+
57
+        :param subcloud: name or id of subcloud (optional)
58
+        """
59
+        context = restcomm.extract_context_from_environ()
60
+
61
+        if subcloud_ref is None:
62
+            # List of all subcloud options requested.
63
+            # Prepend the all clouds default options to the result.
64
+
65
+            result = dict()
66
+            result['sw-update-options'] = list()
67
+
68
+            default_sw_update_opts_dict = utils.get_sw_update_opts(
69
+                context)
70
+
71
+            result['sw-update-options'].append(default_sw_update_opts_dict)
72
+
73
+            subclouds = db_api.sw_update_opts_get_all_plus_subcloud_info(
74
+                context)
75
+
76
+            for subcloud, sw_update_opts in subclouds:
77
+                if sw_update_opts:
78
+                    result['sw-update-options'].append(
79
+                        db_api.sw_update_opts_w_name_db_model_to_dict(
80
+                            sw_update_opts, subcloud.name))
81
+
82
+            return result
83
+
84
+        elif subcloud_ref == consts.DEFAULT_REGION_NAME:
85
+            # Default options requested, guaranteed to succeed
86
+
87
+            return utils.get_sw_update_opts(context)
88
+
89
+        else:
90
+            # Specific subcloud options requested
91
+
92
+            if subcloud_ref.isdigit():
93
+                # Look up subcloud as an ID
94
+                try:
95
+                    subcloud = db_api.subcloud_get(context, subcloud_ref)
96
+                except exceptions.SubcloudNotFound:
97
+                    pecan.abort(404, _('Subcloud not found'))
98
+            else:
99
+                # Look up subcloud by name
100
+                try:
101
+                    subcloud = db_api.subcloud_get_by_name(context,
102
+                                                           subcloud_ref)
103
+                except exceptions.SubcloudNameNotFound:
104
+                    pecan.abort(404, _('Subcloud not found'))
105
+
106
+            try:
107
+                return utils.get_sw_update_opts(
108
+                    context, subcloud_id=subcloud.id)
109
+            except Exception as e:
110
+                pecan.abort(404, _('%s') % e)
111
+
112
+    @index.when(method='POST', template='json')
113
+    def post(self, subcloud_ref=None):
114
+        """Update or create sw update options.
115
+
116
+        :param subcloud: name or id of subcloud (optional)
117
+        """
118
+
119
+        # Note creating or updating subcloud specific options require
120
+        # setting all options.
121
+
122
+        context = restcomm.extract_context_from_environ()
123
+
124
+        payload = eval(request.body)
125
+        if not payload:
126
+            pecan.abort(400, _('Body required'))
127
+
128
+        if subcloud_ref == consts.DEFAULT_REGION_NAME:
129
+
130
+            # update default options
131
+            subcloud_name = consts.SW_UPDATE_DEFAULT_TITLE
132
+
133
+            if db_api.sw_update_opts_default_get(context):
134
+                # entry already in db, update it.
135
+                try:
136
+                    sw_update_opts_ref = db_api.sw_update_opts_default_update(
137
+                        context,
138
+                        payload['storage-apply-type'],
139
+                        payload['compute-apply-type'],
140
+                        payload['max-parallel-computes'],
141
+                        payload['alarm-restriction-type'],
142
+                        payload['default-instance-action'])
143
+                except Exception as e:
144
+                    LOG.exception(e)
145
+                    raise e
146
+            else:
147
+                # no entry in db, create one.
148
+                try:
149
+                    sw_update_opts_ref = db_api.sw_update_opts_default_create(
150
+                        context,
151
+                        payload['storage-apply-type'],
152
+                        payload['compute-apply-type'],
153
+                        payload['max-parallel-computes'],
154
+                        payload['alarm-restriction-type'],
155
+                        payload['default-instance-action'])
156
+                except Exception as e:
157
+                    LOG.exception(e)
158
+                    raise e
159
+        else:
160
+            # update subcloud options
161
+
162
+            if subcloud_ref.isdigit():
163
+                # Look up subcloud as an ID
164
+                try:
165
+                    subcloud = db_api.subcloud_get(context, subcloud_ref)
166
+                except exceptions.SubcloudNotFound:
167
+                    pecan.abort(404, _('Subcloud not found'))
168
+
169
+                subcloud_name = subcloud.name
170
+
171
+            else:
172
+                # Look up subcloud by name
173
+                try:
174
+                    subcloud = db_api.subcloud_get_by_name(context,
175
+                                                           subcloud_ref)
176
+                except exceptions.SubcloudNameNotFound:
177
+                    pecan.abort(404, _('Subcloud not found'))
178
+
179
+                subcloud_name = subcloud_ref
180
+
181
+            sw_update_opts = db_api.sw_update_opts_get(context,
182
+                                                       subcloud.id)
183
+
184
+            if sw_update_opts is None:
185
+                sw_update_opts_ref = db_api.sw_update_opts_create(
186
+                    context,
187
+                    subcloud.id,
188
+                    payload['storage-apply-type'],
189
+                    payload['compute-apply-type'],
190
+                    payload['max-parallel-computes'],
191
+                    payload['alarm-restriction-type'],
192
+                    payload['default-instance-action'])
193
+
194
+            else:
195
+                # a row is present in table, update
196
+                sw_update_opts_ref = db_api.sw_update_opts_update(
197
+                    context,
198
+                    subcloud.id,
199
+                    payload['storage-apply-type'],
200
+                    payload['compute-apply-type'],
201
+                    payload['max-parallel-computes'],
202
+                    payload['alarm-restriction-type'],
203
+                    payload['default-instance-action'])
204
+
205
+        return db_api.sw_update_opts_w_name_db_model_to_dict(
206
+            sw_update_opts_ref, subcloud_name)
207
+
208
+    @index.when(method='delete', template='json')
209
+    def delete(self, subcloud_ref):
210
+        """Delete the software update options."""
211
+
212
+        context = restcomm.extract_context_from_environ()
213
+
214
+        if subcloud_ref == consts.DEFAULT_REGION_NAME:
215
+            # Delete defaults.
216
+            # Note by deleting these, the next get will repopulate with
217
+            # the global constants.
218
+
219
+            try:
220
+                db_api.sw_update_opts_default_destroy(context)
221
+            except Exception:
222
+                return
223
+        else:
224
+
225
+            if subcloud_ref.isdigit():
226
+                # Look up subcloud as an ID
227
+                try:
228
+                    subcloud = db_api.subcloud_get(context, subcloud_ref)
229
+                except exceptions.SubcloudNotFound:
230
+                    pecan.abort(404, _('Subcloud not found'))
231
+
232
+            else:
233
+                # Look up subcloud by name
234
+                try:
235
+                    subcloud = db_api.subcloud_get_by_name(context,
236
+                                                           subcloud_ref)
237
+                except exceptions.SubcloudNameNotFound:
238
+                    pecan.abort(404, _('Subcloud not found'))
239
+
240
+            # Delete the subcloud specific options
241
+            if db_api.sw_update_opts_get(context, subcloud.id):
242
+                db_api.sw_update_opts_destroy(context, subcloud.id)
243
+            else:
244
+                pecan.abort(404, _('Subcloud patch options not found'))

+ 196
- 0
dcmanager/api/controllers/v1/sw_update_strategy.py View File

@@ -0,0 +1,196 @@
1
+# Copyright (c) 2017 Ericsson AB.
2
+# All Rights Reserved.
3
+#
4
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+#    not use this file except in compliance with the License. You may obtain
6
+#    a copy of the License at
7
+#
8
+#         http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+#    Unless required by applicable law or agreed to in writing, software
11
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+#    License for the specific language governing permissions and limitations
14
+#    under the License.
15
+#
16
+# Copyright (c) 2017 Wind River Systems, Inc.
17
+#
18
+# The right to copy, distribute, modify, or otherwise make use
19
+# of this software may be licensed only pursuant to the terms
20
+# of an applicable Wind River license agreement.
21
+#
22
+
23
+from oslo_config import cfg
24
+from oslo_log import log as logging
25
+from oslo_messaging import RemoteError
26
+
27
+import pecan
28
+from pecan import expose
29
+from pecan import request
30
+
31
+from dcmanager.api.controllers import restcomm
32
+from dcmanager.common import consts
33
+from dcmanager.common import exceptions
34
+from dcmanager.common.i18n import _
35
+from dcmanager.db import api as db_api
36
+from dcmanager.rpc import client as rpc_client
37
+
38
+CONF = cfg.CONF
39
+LOG = logging.getLogger(__name__)
40
+
41
+
42
+class SwUpdateStrategyController(object):
43
+
44
+    def __init__(self):
45
+        super(SwUpdateStrategyController, self).__init__()
46
+        self.rpc_client = rpc_client.ManagerClient()
47
+
48
+    @expose(generic=True, template='json')
49
+    def index(self):
50
+        # Route the request to specific methods with parameters
51
+        pass
52
+
53
+    @index.when(method='GET', template='json')
54
+    def get(self, steps=None, cloud_name=None):
55
+        """Get details about software update strategy.
56
+
57
+        :param steps: get the steps for this strategy (optional)
58
+        :param cloud_name: name of cloud (optional)
59
+        """
60
+        context = restcomm.extract_context_from_environ()
61
+
62
+        if steps is None:
63
+            # Strategy requested
64
+            strategy = None
65
+            try:
66
+                strategy = db_api.sw_update_strategy_get(context)
67
+            except exceptions.NotFound:
68
+                pecan.abort(404, _('Strategy not found'))
69
+
70
+            strategy_dict = db_api.sw_update_strategy_db_model_to_dict(
71
+                strategy)
72
+            return strategy_dict
73
+
74
+        elif steps == "steps":
75
+            # Steps for the strategy requested
76
+            if cloud_name is None:
77
+                # List of steps requested
78
+                result = dict()
79
+                result['strategy-steps'] = list()
80
+                strategy_steps = db_api.strategy_step_get_all(context)
81
+                for strategy_step in strategy_steps:
82
+                    result['strategy-steps'].append(
83
+                        db_api.strategy_step_db_model_to_dict(strategy_step))
84
+
85
+                return result
86
+            else:
87
+                # Single step requested
88
+                strategy_step = None
89
+                if cloud_name == consts.SYSTEM_CONTROLLER_NAME:
90
+                    # The system controller step does not map to a subcloud,
91
+                    # so has no name.
92
+                    try:
93
+                        strategy_step = db_api.strategy_step_get(context, None)
94
+                    except exceptions.StrategyStepNotFound:
95
+                        pecan.abort(404, _('Strategy step not found'))
96
+                else:
97
+                    try:
98
+                        strategy_step = db_api.strategy_step_get_by_name(
99
+                            context, cloud_name)
100
+                    except exceptions.StrategyStepNameNotFound:
101
+                        pecan.abort(404, _('Strategy step not found'))
102
+
103
+                strategy_step_dict = db_api.strategy_step_db_model_to_dict(
104
+                    strategy_step)
105
+                return strategy_step_dict
106
+
107
+    @index.when(method='POST', template='json')
108
+    def post(self, actions=None):
109
+        """Create a new software update strategy."""
110
+        context = restcomm.extract_context_from_environ()
111
+
112
+        payload = eval(request.body)
113
+        if not payload:
114
+            pecan.abort(400, _('Body required'))
115
+
116
+        if actions is None:
117
+            # Validate any options that were supplied
118
+            strategy_type = payload.get('type')
119
+            if not strategy_type:
120
+                pecan.abort(400, _('type required'))
121
+            if strategy_type not in consts.SW_UPDATE_TYPE_PATCH:
122
+                pecan.abort(400, _('type invalid'))
123
+
124
+            subcloud_apply_type = payload.get('subcloud-apply-type')
125
+            if subcloud_apply_type is not None:
126
+                if subcloud_apply_type not in [
127
+                        consts.SUBCLOUD_APPLY_TYPE_PARALLEL,
128
+                        consts.SUBCLOUD_APPLY_TYPE_SERIAL]:
129
+                    pecan.abort(400, _('subcloud-apply-type invalid'))
130
+
131
+            max_parallel_subclouds_str = payload.get('max-parallel-subclouds')
132
+            if max_parallel_subclouds_str is not None:
133
+                max_parallel_subclouds = None
134
+                try:
135
+                    max_parallel_subclouds = int(max_parallel_subclouds_str)
136
+                except ValueError:
137
+                    pecan.abort(400, _('max-parallel-subclouds invalid'))
138
+                # TODO(Bart): Decide on a maximum
139
+                if max_parallel_subclouds < 1 or max_parallel_subclouds > 100:
140
+                    pecan.abort(400, _('max-parallel-subclouds invalid'))
141
+
142
+            stop_on_failure = payload.get('stop-on-failure')
143
+            if stop_on_failure is not None:
144
+                if stop_on_failure not in ["true", "false"]:
145
+                    pecan.abort(400, _('stop-on-failure invalid'))
146
+
147
+            try:
148
+                # Ask dcmanager-manager to create the strategy.
149
+                # It will do all the real work...
150
+                return self.rpc_client.create_sw_update_strategy(context,
151
+                                                                 payload)
152
+            except RemoteError as e:
153
+                pecan.abort(422, e.value)
154
+            except Exception as e:
155
+                LOG.exception(e)
156
+                pecan.abort(500, _('Unable to create strategy'))
157
+        elif actions == 'actions':
158
+            # Apply or abort a strategy
159
+            action = payload.get('action')
160
+            if not action:
161
+                pecan.abort(400, _('action required'))
162
+            if action == consts.SW_UPDATE_ACTION_APPLY:
163
+                try:
164
+                    # Ask dcmanager-manager to apply the strategy.
165
+                    # It will do all the real work...
166
+                    return self.rpc_client.apply_sw_update_strategy(context)
167
+                except RemoteError as e:
168
+                    pecan.abort(422, e.value)
169
+                except Exception as e:
170
+                    LOG.exception(e)
171
+                    pecan.abort(500, _('Unable to apply strategy'))
172
+            elif action == consts.SW_UPDATE_ACTION_ABORT:
173
+                try:
174
+                    # Ask dcmanager-manager to abort the strategy.
175
+                    # It will do all the real work...
176
+                    return self.rpc_client.abort_sw_update_strategy(context)
177
+                except RemoteError as e:
178
+                    pecan.abort(422, e.value)
179
+                except Exception as e:
180
+                    LOG.exception(e)
181
+                    pecan.abort(500, _('Unable to abort strategy'))
182
+
183
+    @index.when(method='delete', template='json')
184
+    def delete(self):
185
+        """Delete the software update strategy."""
186
+        context = restcomm.extract_context_from_environ()
187
+
188
+        try:
189
+            # Ask dcmanager-manager to delete the strategy.
190
+            # It will do all the real work...
191
+            return self.rpc_client.delete_sw_update_strategy(context)
192
+        except RemoteError as e:
193
+            pecan.abort(422, e.value)
194
+        except Exception as e:
195
+            LOG.exception(e)
196
+            pecan.abort(500, _('Unable to delete strategy'))

+ 78
- 0
dcmanager/api/enforcer.py View File

@@ -0,0 +1,78 @@
1
+# Copyright 2017 Ericsson AB.
2
+#
3
+#    Licensed under the Apache License, Version 2.0 (the "License");
4
+#    you may not use this file except in compliance with the License.
5
+#    You may obtain a copy of the License at
6
+#
7
+#        http://www.apache.org/licenses/LICENSE-2.0
8
+#
9
+#    Unless required by applicable law or agreed to in writing, software
10
+#    distributed under the License is distributed on an "AS IS" BASIS,
11
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+#    See the License for the specific language governing permissions and
13
+#    limitations under the License.
14
+#
15
+# Copyright (c) 2017 Wind River Systems, Inc.
16
+#
17
+# The right to copy, distribute, modify, or otherwise make use
18
+# of this software may be licensed only pursuant to the terms
19
+# of an applicable Wind River license agreement.
20
+#
21
+
22
+"""Policy enforcer for DC Manager."""
23
+
24
+from oslo_config import cfg
25
+from oslo_policy import policy
26
+
27
+from dcmanager.common import exceptions as exc
28
+
29
+
30
+_ENFORCER = None
31
+
32
+
33
+def enforce(action, context, target=None, do_raise=True,
34
+            exc=exc.NotAuthorized):
35
+    """Verify that the action is valid on the target in this context.
36
+
37
+    :param action: String, representing the action to be checked.
38
+                   This should be colon separated for clarity.
39
+                   i.e. ``sync:list``
40
+    :param context: DC Manager context.
41
+    :param target: Dictionary, representing the object of the action.
42
+                   For object creation, this should be a dictionary
43
+                   representing the location of the object.
44
+                   e.g. ``{'project_id': context.project}``
45
+    :param do_raise: if True (the default), raises specified exception.
46
+    :param exc: Exception to be raised if not authorized. Default is
47
+                dcmanager.common.exceptions.NotAuthorized.
48
+
49
+    :return: returns True if authorized and False if not authorized and
50
+             do_raise is False.
51
+    """
52
+    if cfg.CONF.auth_strategy != 'keystone':
53
+        # Policy enforcement is supported now only with Keystone
54
+        # authentication.
55
+        return
56
+
57
+    target_obj = {
58
+        'project_id': context.project,
59
+        'user_id': context.user,
60
+    }
61
+
62
+    target_obj.update(target or {})
63
+    _ensure_enforcer_initialization()
64
+
65
+    try:
66
+        _ENFORCER.enforce(action, target_obj, context.to_dict(),
67
+                          do_raise=do_raise, exc=exc)
68
+        return True
69
+
70
+    except Exception:
71
+        return False
72
+
73
+
74
+def _ensure_enforcer_initialization():
75
+    global _ENFORCER
76
+    if not _ENFORCER:
77
+        _ENFORCER = policy.Enforcer(cfg.CONF)
78
+        _ENFORCER.load_rules()

+ 18
- 0
dcmanager/cmd/README.rst View File

@@ -0,0 +1,18 @@
1
+===============================
2
+cmd
3
+===============================
4
+
5
+Scripts to start the DC Manager API and Manager services
6
+
7
+api.py:
8
+    start API service
9
+    python api.py --config-file=/etc/dcmanager.conf
10
+
11
+manager.py:
12
+    start Manager service
13
+    python manager.py --config-file=/etc/dcmanager.conf
14
+
15
+manage.py:
16
+    CLI interface for dcmanager database management
17
+    dcmanager-manage --config-file /etc/dcmanager.conf db_sync
18
+    dcmanager-manage --config-file /etc/dcmanager.conf db_version

+ 0
- 0
dcmanager/cmd/__init__.py View File


+ 78
- 0
dcmanager/cmd/api.py View File

@@ -0,0 +1,78 @@
1
+# Copyright 2015 Huawei Technologies Co., Ltd.
2
+# All Rights Reserved.
3
+#
4
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+#    not use this file except in compliance with the License. You may obtain
6
+#    a copy of the License at
7
+#
8
+#         http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+#    Unless required by applicable law or agreed to in writing, software
11
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+#    License for the specific language governing permissions and limitations
14
+#    under the License.
15
+#
16
+# Copyright (c) 2017 Wind River Systems, Inc.
17
+#
18
+# The right to copy, distribute, modify, or otherwise make use
19
+# of this software may be licensed only pursuant to the terms
20
+# of an applicable Wind River license agreement.
21
+#
22
+
23
+# Much of this module is based on the work of the Ironic team
24
+# see http://git.openstack.org/cgit/openstack/ironic/tree/ironic/cmd/api.py
25
+
26
+
27
+import sys
28
+
29
+import eventlet
30
+from oslo_config import cfg
31
+from oslo_log import log as logging
32
+from oslo_service import systemd
33
+from oslo_service import wsgi
34
+
35
+import logging as std_logging
36
+
37
+from dcmanager.api import api_config
38
+from dcmanager.api import app
39
+
40
+from dcmanager.common import config
41
+from dcmanager.common import messaging
42
+from dcorch.common import messaging as dcorch_messaging
43
+CONF = cfg.CONF
44
+config.register_options()
45
+LOG = logging.getLogger('dcmanager.api')
46
+eventlet.monkey_patch(os=False)
47
+
48
+
49
+def main():
50
+    api_config.init(sys.argv[1:])
51
+    api_config.setup_logging()
52
+    application = app.setup_app()
53
+
54
+    host = CONF.bind_host
55
+    port = CONF.bind_port
56
+    workers = CONF.api_workers
57
+
58
+    if workers < 1:
59
+        LOG.warning("Wrong worker number, worker = %(workers)s", workers)
60
+        workers = 1
61
+
62
+    LOG.info("Server on http://%(host)s:%(port)s with %(workers)s",
63
+             {'host': host, 'port': port, 'workers': workers})
64
+    messaging.setup()
65
+    dcorch_messaging.setup()
66
+    systemd.notify_once()
67
+    service = wsgi.Server(CONF, "DCManager", application, host, port)
68
+
69
+    app.serve(service, CONF, workers)
70
+
71
+    LOG.info("Configuration:")
72
+    CONF.log_opt_values(LOG, std_logging.INFO)
73
+
74
+    app.wait()
75
+
76
+
77
+if __name__ == '__main__':
78
+    main()

+ 85
- 0
dcmanager/cmd/manage.py View File

@@ -0,0 +1,85 @@
1
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
2
+# not use this file except in compliance with the License. You may obtain
3
+# a copy of the License at
4
+#
5
+#         http://www.apache.org/licenses/LICENSE-2.0
6
+#
7
+# Unless required by applicable law or agreed to in writing, software
8
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10
+# License for the specific language governing permissions and limitations
11
+# under the License.
12
+#
13
+# Copyright (c) 2017 Wind River Systems, Inc.
14
+#
15
+# The right to copy, distribute, modify, or otherwise make use
16
+# of this software may be licensed only pursuant to the terms
17
+# of an applicable Wind River license agreement.
18
+#
19
+
20
+"""
21
+CLI interface for DC Manager management.
22
+"""
23
+
24
+import sys
25
+
26
+from oslo_config import cfg
27
+from oslo_log import log as logging
28
+
29
+from dcmanager.common import config
30
+from dcmanager.db import api
31
+from dcmanager import version
32
+
33
+config.register_options()
34
+CONF = cfg.CONF
35
+
36
+
37
+def do_db_version():
38
+    '''Print database's current migration level.'''
39
+    print(api.db_version(api.get_engine()))
40
+
41
+
42
+def do_db_sync():
43
+    '''Place a database under migration control and upgrade.
44
+
45
+    DB is created first if necessary.
46
+    '''
47
+    api.db_sync(api.get_engine(), CONF.command.version)
48
+
49
+
50
+def add_command_parsers(subparsers):
51
+    parser = subparsers.add_parser('db_version')
52
+    parser.set_defaults(func=do_db_version)
53
+
54
+    parser = subparsers.add_parser('db_sync')
55
+    parser.set_defaults(func=do_db_sync)
56
+    parser.add_argument('version', nargs='?')
57
+    parser.add_argument('current_version', nargs='?')
58
+
59
+command_opt = cfg.SubCommandOpt('command',
60
+                                title='Commands',
61
+                                help='Show available commands.',
62
+                                handler=add_command_parsers)
63
+
64
+
65
+def main():
66
+    logging.register_options(CONF)
67
+    logging.setup(CONF, 'dcmanager-manage')
68
+    CONF.register_cli_opt(command_opt)
69
+
70
+    try:
71
+        default_config_files = cfg.find_config_files('dcmanager',
72
+                                                     'dcmanager-engine')
73
+        CONF(sys.argv[1:], project='dcmanager', prog='dcmanager-manage',
74
+             version=version.version_info.version_string(),
75
+             default_config_files=default_config_files)
76
+    except RuntimeError as e:
77
+        sys.exit("ERROR: %s" % e)
78
+
79
+    try:
80
+        CONF.command.func()
81
+    except Exception as e:
82
+        sys.exit("ERROR: %s" % e)
83
+
84
+if __name__ == '__main__':
85
+    main()

+ 64
- 0
dcmanager/cmd/manager.py View File

@@ -0,0 +1,64 @@
1
+#!/usr/bin/env python
2
+#
3
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
4
+#    not use this file except in compliance with the License. You may obtain
5
+#    a copy of the License at
6
+#
7
+#         http://www.apache.org/licenses/LICENSE-2.0
8
+#
9
+#    Unless required by applicable law or agreed to in writing, software
10
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
+#    License for the specific language governing permissions and limitations
13
+#    under the License.
14
+#
15
+# Copyright (c) 2017 Wind River Systems, Inc.
16
+#
17
+# The right to copy, distribute, modify, or otherwise make use
18
+# of this software may be licensed only pursuant to the terms
19
+# of an applicable Wind River license agreement.
20
+#
21
+
22
+"""
23
+DC Manager Engine Server.
24
+"""
25
+
26
+import eventlet
27
+eventlet.monkey_patch()
28
+
29
+from oslo_config import cfg
30
+from oslo_i18n import _lazy
31
+from oslo_log import log as logging
32
+from oslo_service import service
33
+
34
+from dcmanager.common import config
35
+from dcmanager.common import consts
36
+from dcmanager.common import messaging
37
+from dcorch.common import messaging as dcorch_messaging
38
+
39
+_lazy.enable_lazy()
40
+config.register_options()
41
+config.register_keystone_options()
42
+LOG = logging.getLogger('dcmanager.engine')
43
+
44
+
45
+def main():
46
+    logging.register_options(cfg.CONF)
47
+    cfg.CONF(project='dcmanager', prog='dcmanager-engine')
48
+    logging.setup(cfg.CONF, 'dcmanager-engine')
49
+    logging.set_defaults()
50
+    messaging.setup()
51
+    dcorch_messaging.setup()
52
+
53
+    from dcmanager.manager import service as manager
54
+
55
+    srv = manager.DCManagerService(cfg.CONF.host,
56
+                                   consts.TOPIC_DC_MANAGER)
57
+    launcher = service.launch(cfg.CONF,
58
+                              srv, workers=cfg.CONF.workers)
59
+    # the following periodic tasks are intended serve as HA checking
60
+    # srv.create_periodic_tasks()
61
+    launcher.wait()
62
+
63
+if __name__ == '__main__':
64
+    main()

+ 0
- 0
dcmanager/common/__init__.py View File


+ 159
- 0
dcmanager/common/config.py View File

@@ -0,0 +1,159 @@
1
+#    Copyright 2016 Ericsson AB
2
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
3
+#    not use this file except in compliance with the License. You may obtain
4
+#    a copy of the License at
5
+#
6
+#         http://www.apache.org/licenses/LICENSE-2.0
7
+#
8
+#    Unless required by applicable law or agreed to in writing, software
9
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11
+#    License for the specific language governing permissions and limitations
12
+#    under the License.
13
+#
14
+# Copyright (c) 2017 Wind River Systems, Inc.
15
+#
16
+# The right to copy, distribute, modify, or otherwise make use
17
+# of this software may be licensed only pursuant to the terms
18
+# of an applicable Wind River license agreement.
19
+#
20
+
21
+"""
22
+File to store all the configurations
23
+"""
24
+from oslo_config import cfg
25
+from oslo_utils import importutils
26
+
27
+# Ensure keystonemiddleware options are imported
28
+importutils.import_module('keystonemiddleware.auth_token')
29
+
30
+global_opts = [