summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api-ref/source/certificates.inc26
-rw-r--r--api-ref/source/conf.py2
-rw-r--r--api-ref/source/index.rst2
-rw-r--r--api-ref/source/parameters.yaml18
-rw-r--r--api-ref/source/quotas.inc124
-rw-r--r--api-ref/source/samples/quota-create-req.json5
-rw-r--r--api-ref/source/samples/quota-create-resp.json8
-rw-r--r--api-ref/source/samples/quota-get-all-resp.json12
-rw-r--r--api-ref/source/samples/quota-get-one-resp.json8
-rw-r--r--api-ref/source/samples/quota-udpate-resp.json8
-rw-r--r--api-ref/source/samples/quota-update-req.json3
-rw-r--r--api-ref/source/samples/stats-get-resp.json4
-rw-r--r--api-ref/source/samples/versions-get-resp.json2
-rw-r--r--api-ref/source/stats.inc82
-rw-r--r--contrib/drivers/k8s_opensuse_v1/templates/fragments/configure-flanneld-master.sh2
-rw-r--r--contrib/drivers/k8s_opensuse_v1/templates/fragments/write-heat-params-master.yaml4
-rw-r--r--contrib/drivers/k8s_opensuse_v1/templates/kubecluster.yaml40
-rw-r--r--contrib/drivers/k8s_opensuse_v1/templates/kubemaster.yaml28
-rw-r--r--devstack/lib/magnum1
-rw-r--r--doc/source/dev/quickstart.rst15
-rw-r--r--doc/source/userguide.rst19
-rw-r--r--etc/magnum/api-paste.ini7
-rw-r--r--etc/magnum/policy.json10
-rw-r--r--install-guide/source/conf.py2
-rw-r--r--magnum/api/controllers/v1/__init__.py22
-rw-r--r--magnum/api/controllers/v1/certificate.py11
-rw-r--r--magnum/api/controllers/v1/cluster.py29
-rw-r--r--magnum/api/controllers/v1/collection.py4
-rw-r--r--magnum/api/controllers/v1/quota.py213
-rw-r--r--magnum/api/controllers/v1/stats.py72
-rw-r--r--magnum/api/controllers/versions.py5
-rw-r--r--magnum/api/rest_api_version_history.rst31
-rw-r--r--magnum/api/validation.py35
-rw-r--r--magnum/cmd/__init__.py20
-rw-r--r--magnum/cmd/api.py4
-rw-r--r--magnum/common/cert_manager/barbican_cert_manager.py8
-rw-r--r--magnum/common/clients.py5
-rw-r--r--magnum/common/config.py14
-rw-r--r--magnum/common/context.py14
-rw-r--r--magnum/common/exception.py10
-rw-r--r--magnum/common/profiler.py86
-rw-r--r--magnum/common/rpc.py44
-rw-r--r--magnum/common/rpc_service.py30
-rw-r--r--magnum/conductor/api.py6
-rw-r--r--magnum/conductor/handlers/ca_conductor.py8
-rw-r--r--magnum/conductor/handlers/cluster_conductor.py2
-rw-r--r--magnum/conductor/handlers/conductor_listener.py3
-rw-r--r--magnum/conductor/handlers/indirection_api.py2
-rw-r--r--magnum/conductor/k8s_api.py18
-rw-r--r--magnum/conductor/monitors.py19
-rw-r--r--magnum/conductor/scale_manager.py51
-rw-r--r--magnum/conf/__init__.py4
-rw-r--r--magnum/conf/profiler.py27
-rw-r--r--magnum/conf/quota.py38
-rw-r--r--magnum/db/api.py93
-rw-r--r--magnum/db/sqlalchemy/api.py115
-rw-r--r--magnum/drivers/common/driver.py14
-rw-r--r--magnum/drivers/common/k8s_monitor.py (renamed from magnum/conductor/k8s_monitor.py)4
-rw-r--r--magnum/drivers/common/k8s_scale_manager.py33
-rw-r--r--magnum/drivers/common/templates/environments/no_private_network.yaml8
-rw-r--r--magnum/drivers/common/templates/environments/with_private_network.yaml8
-rw-r--r--magnum/drivers/common/templates/fragments/configure_docker_storage_driver_atomic.sh39
-rw-r--r--magnum/drivers/common/templates/fragments/network_switcher_existing.yaml27
-rw-r--r--magnum/drivers/common/templates/fragments/network_switcher_private.yaml27
-rw-r--r--magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-master.sh20
-rw-r--r--magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-minion.sh4
-rw-r--r--magnum/drivers/common/templates/kubernetes/fragments/enable-kube-controller-manager-scheduler.sh75
-rw-r--r--magnum/drivers/common/templates/kubernetes/fragments/make-cert-client.sh2
-rw-r--r--magnum/drivers/common/templates/kubernetes/fragments/make-cert.sh2
-rw-r--r--magnum/drivers/common/templates/kubernetes/fragments/network-config-service.sh4
-rw-r--r--magnum/drivers/common/templates/kubernetes/fragments/write-heat-params-master.yaml3
-rw-r--r--magnum/drivers/common/templates/kubernetes/fragments/write-network-config.sh2
-rw-r--r--magnum/drivers/common/templates/lb.yaml4
-rw-r--r--magnum/drivers/common/templates/network.yaml47
-rw-r--r--magnum/drivers/common/templates/swarm/fragments/cfn-signal.sh3
-rw-r--r--magnum/drivers/common/templates/swarm/fragments/configure-etcd.sh27
-rw-r--r--magnum/drivers/common/templates/swarm/fragments/enable-services.sh4
-rw-r--r--magnum/drivers/common/templates/swarm/fragments/make-cert.py6
-rw-r--r--magnum/drivers/common/templates/swarm/fragments/network-config-service.sh24
-rw-r--r--magnum/drivers/common/templates/swarm/fragments/network-service.sh28
-rw-r--r--magnum/drivers/common/templates/swarm/fragments/write-cluster-failure-service.yaml3
-rw-r--r--magnum/drivers/common/templates/swarm/fragments/write-swarm-agent-service.sh39
-rw-r--r--magnum/drivers/common/templates/swarm/fragments/write-swarm-master-service.sh20
-rw-r--r--magnum/drivers/heat/k8s_fedora_template_def.py21
-rw-r--r--magnum/drivers/heat/k8s_template_def.py10
-rw-r--r--magnum/drivers/heat/swarm_fedora_template_def.py18
-rw-r--r--magnum/drivers/heat/template_def.py28
-rw-r--r--magnum/drivers/k8s_coreos_v1/driver.py8
-rw-r--r--magnum/drivers/k8s_coreos_v1/template_def.py16
-rw-r--r--magnum/drivers/k8s_coreos_v1/templates/fragments/add-proxy.yaml5
-rw-r--r--magnum/drivers/k8s_coreos_v1/templates/fragments/configure-etcd.yaml35
-rw-r--r--magnum/drivers/k8s_coreos_v1/templates/fragments/create-kube-namespace.yaml5
-rw-r--r--magnum/drivers/k8s_coreos_v1/templates/fragments/enable-kube-apiserver.yaml14
-rw-r--r--magnum/drivers/k8s_coreos_v1/templates/fragments/enable-kube-controller-manager.yaml11
-rw-r--r--magnum/drivers/k8s_coreos_v1/templates/fragments/enable-kube-proxy-master.yaml9
-rw-r--r--magnum/drivers/k8s_coreos_v1/templates/fragments/enable-kube-proxy-minion.yaml20
-rw-r--r--magnum/drivers/k8s_coreos_v1/templates/fragments/enable-kube-scheduler.yaml9
-rw-r--r--magnum/drivers/k8s_coreos_v1/templates/fragments/enable-kubelet-master.yaml12
-rw-r--r--magnum/drivers/k8s_coreos_v1/templates/fragments/enable-kubelet-minion.yaml13
-rw-r--r--magnum/drivers/k8s_coreos_v1/templates/fragments/enable-network-service-client.yaml72
-rw-r--r--magnum/drivers/k8s_coreos_v1/templates/fragments/enable-network-service.yaml27
-rw-r--r--magnum/drivers/k8s_coreos_v1/templates/fragments/make-cert-client.yaml25
-rw-r--r--magnum/drivers/k8s_coreos_v1/templates/fragments/make-cert.yaml26
-rw-r--r--magnum/drivers/k8s_coreos_v1/templates/fragments/wc-notify.yaml2
-rw-r--r--magnum/drivers/k8s_coreos_v1/templates/fragments/write-heat-params-master.yaml6
-rw-r--r--magnum/drivers/k8s_coreos_v1/templates/fragments/write-heat-params.yaml3
-rw-r--r--magnum/drivers/k8s_coreos_v1/templates/fragments/write-network-config.yaml2
-rw-r--r--magnum/drivers/k8s_coreos_v1/templates/kubecluster.yaml49
-rw-r--r--magnum/drivers/k8s_coreos_v1/templates/kubemaster.yaml25
-rw-r--r--magnum/drivers/k8s_coreos_v1/templates/kubeminion.yaml10
-rw-r--r--magnum/drivers/k8s_fedora_atomic_v1/driver.py8
-rw-r--r--magnum/drivers/k8s_fedora_atomic_v1/templates/kubecluster.yaml41
-rw-r--r--magnum/drivers/k8s_fedora_atomic_v1/templates/kubemaster.yaml22
-rw-r--r--magnum/drivers/k8s_fedora_atomic_v1/version.py2
-rw-r--r--magnum/drivers/k8s_fedora_ironic_v1/driver.py8
-rw-r--r--magnum/drivers/k8s_fedora_ironic_v1/templates/kubecluster.yaml137
-rw-r--r--magnum/drivers/k8s_fedora_ironic_v1/templates/kubemaster.yaml44
-rw-r--r--magnum/drivers/k8s_fedora_ironic_v1/templates/kubeminion.yaml335
-rw-r--r--magnum/drivers/k8s_fedora_ironic_v1/templates/kubeminion_software_configs.yaml325
-rw-r--r--magnum/drivers/k8s_fedora_ironic_v1/version.py2
-rw-r--r--magnum/drivers/mesos_ubuntu_v1/driver.py8
-rw-r--r--magnum/drivers/mesos_ubuntu_v1/monitor.py (renamed from magnum/conductor/mesos_monitor.py)0
-rw-r--r--magnum/drivers/mesos_ubuntu_v1/scale_manager.py43
-rw-r--r--magnum/drivers/mesos_ubuntu_v1/template_def.py14
-rw-r--r--magnum/drivers/mesos_ubuntu_v1/templates/mesos_slave_software_configs.yaml187
-rw-r--r--magnum/drivers/mesos_ubuntu_v1/templates/mesoscluster.yaml75
-rw-r--r--magnum/drivers/mesos_ubuntu_v1/templates/mesosslave.yaml191
-rw-r--r--magnum/drivers/swarm_fedora_atomic_v1/driver.py4
-rw-r--r--magnum/drivers/swarm_fedora_atomic_v1/monitor.py (renamed from magnum/conductor/swarm_monitor.py)0
-rw-r--r--magnum/drivers/swarm_fedora_atomic_v1/templates/cluster.yaml14
-rw-r--r--magnum/drivers/swarm_fedora_atomic_v1/templates/swarmmaster.yaml2
-rw-r--r--magnum/drivers/swarm_fedora_atomic_v1/templates/swarmnode.yaml2
-rw-r--r--magnum/drivers/swarm_fedora_atomic_v1/version.py2
-rw-r--r--magnum/objects/__init__.py8
-rw-r--r--magnum/objects/cluster.py17
-rw-r--r--magnum/objects/fields.py12
-rw-r--r--magnum/objects/magnum_service.py4
-rw-r--r--magnum/objects/quota.py142
-rw-r--r--magnum/objects/stats.py44
-rw-r--r--magnum/service/periodic.py2
-rwxr-xr-xmagnum/tests/contrib/copy_instance_logs.sh10
-rwxr-xr-xmagnum/tests/contrib/gate_hook.sh10
-rwxr-xr-xmagnum/tests/contrib/post_test_hook.sh19
-rw-r--r--magnum/tests/fake_policy.py3
-rw-r--r--magnum/tests/functional/api/v1/clients/cert_client.py2
-rw-r--r--magnum/tests/functional/api/v1/test_cluster.py14
-rw-r--r--magnum/tests/functional/k8s/test_k8s_python_client.py8
-rw-r--r--magnum/tests/functional/k8s_coreos/test_k8s_python_client.py6
-rw-r--r--magnum/tests/functional/k8s_ironic/test_k8s_python_client.py5
-rw-r--r--magnum/tests/functional/python_client_base.py36
-rw-r--r--magnum/tests/unit/api/controllers/auth-paste.ini2
-rw-r--r--magnum/tests/unit/api/controllers/auth-root-access.ini2
-rw-r--r--magnum/tests/unit/api/controllers/auth-v1-access.ini2
-rw-r--r--magnum/tests/unit/api/controllers/noauth-paste.ini2
-rw-r--r--magnum/tests/unit/api/controllers/test_root.py10
-rw-r--r--magnum/tests/unit/api/controllers/v1/test_certificate.py91
-rw-r--r--magnum/tests/unit/api/controllers/v1/test_cluster.py24
-rw-r--r--magnum/tests/unit/api/controllers/v1/test_quota.py233
-rw-r--r--magnum/tests/unit/api/controllers/v1/test_stats.py130
-rw-r--r--magnum/tests/unit/api/test_validation.py156
-rw-r--r--magnum/tests/unit/api/utils.py4
-rw-r--r--magnum/tests/unit/common/cert_manager/test_barbican.py6
-rw-r--r--magnum/tests/unit/common/test_clients.py14
-rw-r--r--magnum/tests/unit/common/test_context.py23
-rw-r--r--magnum/tests/unit/common/test_profiler.py75
-rw-r--r--magnum/tests/unit/common/test_rpc.py252
-rw-r--r--magnum/tests/unit/conductor/handlers/test_k8s_cluster_conductor.py72
-rw-r--r--magnum/tests/unit/conductor/handlers/test_mesos_cluster_conductor.py23
-rw-r--r--magnum/tests/unit/conductor/handlers/test_swarm_cluster_conductor.py27
-rw-r--r--magnum/tests/unit/conductor/test_monitors.py48
-rw-r--r--magnum/tests/unit/conductor/test_scale_manager.py29
-rw-r--r--magnum/tests/unit/db/test_cluster.py20
-rw-r--r--magnum/tests/unit/db/test_magnum_service.py14
-rw-r--r--magnum/tests/unit/db/test_quota.py120
-rw-r--r--magnum/tests/unit/db/utils.py28
-rw-r--r--magnum/tests/unit/drivers/test_template_definition.py18
-rw-r--r--magnum/tests/unit/objects/test_magnum_service.py9
-rw-r--r--magnum/tests/unit/objects/test_objects.py4
-rw-r--r--magnum/tests/unit/objects/utils.py30
-rw-r--r--releasenotes/notes/bp-secure-etcd-cluster-coe-5abd22546f05a85b.yaml6
-rw-r--r--releasenotes/notes/integrate-osprofiler-79bdf2d0cd8a39fb.yaml5
-rw-r--r--releasenotes/notes/quota-api-182cd1bc9e706b17.yaml5
-rw-r--r--releasenotes/notes/rotate-cluster-cert-9f84deb0adf9afb1.yaml5
-rw-r--r--releasenotes/notes/stats-api-68bc66147ac027e6.yaml6
-rw-r--r--releasenotes/source/index.rst2
-rw-r--r--releasenotes/source/liberty.rst6
-rw-r--r--releasenotes/source/mitaka.rst6
-rw-r--r--requirements.txt14
-rw-r--r--setup.cfg6
-rw-r--r--test-requirements.txt3
-rw-r--r--tox.ini8
191 files changed, 4492 insertions, 1289 deletions
diff --git a/api-ref/source/certificates.inc b/api-ref/source/certificates.inc
index 18ff87c..59276e2 100644
--- a/api-ref/source/certificates.inc
+++ b/api-ref/source/certificates.inc
@@ -118,3 +118,29 @@ Response Example
118 118
119.. literalinclude:: samples/certificates-ca-sign-resp.json 119.. literalinclude:: samples/certificates-ca-sign-resp.json
120 :language: javascript 120 :language: javascript
121
122Rotate the CA certificate for a bay/cluster
123===========================================
124
125.. rest_method:: PATCH /v1/certificates/{bay_uuid/cluster_uuid}
126
127Rotate the CA certificate for a bay/cluster and invalidate all user
128certificates.
129
130Response Codes
131--------------
132
133.. rest_status_code:: success status.yaml
134
135 - 202
136
137.. rest_status_code:: error status.yaml
138
139 - 400
140
141Request
142-------
143
144.. rest_parameters:: parameters.yaml
145
146 - cluster: cluster_id
diff --git a/api-ref/source/conf.py b/api-ref/source/conf.py
index b64b5db..15c960a 100644
--- a/api-ref/source/conf.py
+++ b/api-ref/source/conf.py
@@ -108,7 +108,7 @@ pygments_style = 'sphinx'
108# Config logABug feature 108# Config logABug feature
109# source tree 109# source tree
110giturl = ( 110giturl = (
111 u'http://git.openstack.org/cgit/openstack/magnum/tree/api-ref/source') 111 u'https://git.openstack.org/cgit/openstack/magnum/tree/api-ref/source')
112# html_context allows us to pass arbitrary values into the html template 112# html_context allows us to pass arbitrary values into the html template
113html_context = {'bug_tag': 'api-ref', 113html_context = {'bug_tag': 'api-ref',
114 'giturl': giturl, 114 'giturl': giturl,
diff --git a/api-ref/source/index.rst b/api-ref/source/index.rst
index d30ebd4..9c829ec 100644
--- a/api-ref/source/index.rst
+++ b/api-ref/source/index.rst
@@ -14,3 +14,5 @@
14.. include:: clustertemplates.inc 14.. include:: clustertemplates.inc
15.. include:: certificates.inc 15.. include:: certificates.inc
16.. include:: mservices.inc 16.. include:: mservices.inc
17.. include:: stats.inc
18.. include:: quotas.inc
diff --git a/api-ref/source/parameters.yaml b/api-ref/source/parameters.yaml
index 35ebdb7..fe6f8b7 100644
--- a/api-ref/source/parameters.yaml
+++ b/api-ref/source/parameters.yaml
@@ -32,6 +32,12 @@ clustertemplate_ident:
32 required: true 32 required: true
33 description: | 33 description: |
34 The UUID or name of cluster templates in Magnum. 34 The UUID or name of cluster templates in Magnum.
35project_id:
36 type: string
37 in: path
38 required: true
39 description: |
40 Project ID.
35 41
36# Body params 42# Body params
37api_address: 43api_address:
@@ -106,6 +112,12 @@ cluster_list:
106 required: true 112 required: true
107 description: | 113 description: |
108 The list of all clusters in Magnum. 114 The list of all clusters in Magnum.
115clusters:
116 type: integer
117 in: body
118 required: true
119 description: |
120 The number of clusters.
109clustertemplate_id: 121clustertemplate_id:
110 type: UUID 122 type: UUID
111 in: body 123 in: body
@@ -417,6 +429,12 @@ node_count:
417 in: body 429 in: body
418 required: true 430 required: true
419 type: integer 431 type: integer
432nodes:
433 description: |
434 The total number of nodes including master nodes.
435 in: body
436 required: true
437 type: integer
420op: 438op:
421 description: | 439 description: |
422 The operation used to modify resource's attributes. Supported operations 440 The operation used to modify resource's attributes. Supported operations
diff --git a/api-ref/source/quotas.inc b/api-ref/source/quotas.inc
new file mode 100644
index 0000000..a80f01e
--- /dev/null
+++ b/api-ref/source/quotas.inc
@@ -0,0 +1,124 @@
1.. -*- rst -*-
2
3=================
4 Magnum Quota API
5=================
6
7Lists, creates, shows details, and updates Quotas.
8
9Set new quota
10==================
11
12.. rest_method:: POST /v1/quotas
13
14Create new quota for a project.
15
16Response Codes
17--------------
18
19.. rest_status_code:: success status.yaml
20
21 - 201
22
23.. rest_status_code:: error status.yaml
24
25 - 400
26 - 401
27 - 403
28 - 404
29
30Request Example
31----------------
32
33.. literalinclude:: samples/quota-create-req.json
34 :language: javascript
35
36Response Example
37----------------
38
39.. literalinclude:: samples/quota-create-resp.json
40 :language: javascript
41
42List all quotas
43================
44
45.. rest_method:: GET /v1/quotas
46
47List all quotas in Magnum.
48
49Response Codes
50--------------
51
52.. rest_status_code:: success status.yaml
53
54 - 200
55
56.. rest_status_code:: error status.yaml
57
58 - 401
59 - 403
60
61Response Example
62----------------
63
64.. literalinclude:: samples/quota-get-all-resp.json
65 :language: javascript
66
67Show details of a quota
68=========================
69
70.. rest_method:: GET /v1/quotas/{project_id}/{resource}
71
72Get quota information for the given project_id and resource.
73
74Response Codes
75--------------
76
77.. rest_status_code:: success status.yaml
78
79 - 200
80
81.. rest_status_code:: error status.yaml
82
83 - 401
84 - 403
85 - 404
86
87Response Example
88----------------
89
90.. literalinclude:: samples/quota-get-one-resp.json
91 :language: javascript
92
93Update a resource quota
94=============================
95
96.. rest_method:: PATCH /v1/quotas/{project_id}/{resource}
97
98Update resource quota for the given projec id.
99
100Response Codes
101--------------
102
103.. rest_status_code:: success status.yaml
104
105 - 202
106
107.. rest_status_code:: error status.yaml
108
109 - 400
110 - 401
111 - 403
112 - 404
113
114Request Example
115----------------
116
117.. literalinclude:: samples/quota-update-req.json
118 :language: javascript
119
120Response Example
121----------------
122
123.. literalinclude:: samples/quota-update-resp.json
124 :language: javascript
diff --git a/api-ref/source/samples/quota-create-req.json b/api-ref/source/samples/quota-create-req.json
new file mode 100644
index 0000000..c5f941c
--- /dev/null
+++ b/api-ref/source/samples/quota-create-req.json
@@ -0,0 +1,5 @@
1{
2 "project_id": "aa5436ab58144c768ca4e9d2e9f5c3b2",
3 "resource": "Cluster",
4 "hard_limit": 10
5} \ No newline at end of file
diff --git a/api-ref/source/samples/quota-create-resp.json b/api-ref/source/samples/quota-create-resp.json
new file mode 100644
index 0000000..05499ee
--- /dev/null
+++ b/api-ref/source/samples/quota-create-resp.json
@@ -0,0 +1,8 @@
1{
2 "resource": "Cluster",
3 "created_at": "2017-01-17T17:35:48+00:00",
4 "updated_at": null,
5 "hard_limit": 1,
6 "project_id": "aa5436ab58144c768ca4e9d2e9f5c3b2",
7 "id": 26
8} \ No newline at end of file
diff --git a/api-ref/source/samples/quota-get-all-resp.json b/api-ref/source/samples/quota-get-all-resp.json
new file mode 100644
index 0000000..c9321fb
--- /dev/null
+++ b/api-ref/source/samples/quota-get-all-resp.json
@@ -0,0 +1,12 @@
1{
2 "quotas": [
3 {
4 "resource": "Cluster",
5 "created_at": "2017-01-17T17:35:49+00:00",
6 "updated_at": "2017-01-17T17:38:21+00:00",
7 "hard_limit": 10,
8 "project_id": "aa5436ab58144c768ca4e9d2e9f5c3b2",
9 "id": 26
10 }
11 ]
12} \ No newline at end of file
diff --git a/api-ref/source/samples/quota-get-one-resp.json b/api-ref/source/samples/quota-get-one-resp.json
new file mode 100644
index 0000000..0cb18cd
--- /dev/null
+++ b/api-ref/source/samples/quota-get-one-resp.json
@@ -0,0 +1,8 @@
1{
2 "resource": "Cluster",
3 "created_at": "2017-01-17T17:35:49+00:00",
4 "updated_at": "2017-01-17T17:38:20+00:00",
5 "hard_limit": 10,
6 "project_id": "aa5436ab58144c768ca4e9d2e9f5c3b2",
7 "id": 26
8} \ No newline at end of file
diff --git a/api-ref/source/samples/quota-udpate-resp.json b/api-ref/source/samples/quota-udpate-resp.json
new file mode 100644
index 0000000..0cb18cd
--- /dev/null
+++ b/api-ref/source/samples/quota-udpate-resp.json
@@ -0,0 +1,8 @@
1{
2 "resource": "Cluster",
3 "created_at": "2017-01-17T17:35:49+00:00",
4 "updated_at": "2017-01-17T17:38:20+00:00",
5 "hard_limit": 10,
6 "project_id": "aa5436ab58144c768ca4e9d2e9f5c3b2",
7 "id": 26
8} \ No newline at end of file
diff --git a/api-ref/source/samples/quota-update-req.json b/api-ref/source/samples/quota-update-req.json
new file mode 100644
index 0000000..8d83881
--- /dev/null
+++ b/api-ref/source/samples/quota-update-req.json
@@ -0,0 +1,3 @@
1{
2 "hard_limit": 10
3} \ No newline at end of file
diff --git a/api-ref/source/samples/stats-get-resp.json b/api-ref/source/samples/stats-get-resp.json
new file mode 100644
index 0000000..3fe26dd
--- /dev/null
+++ b/api-ref/source/samples/stats-get-resp.json
@@ -0,0 +1,4 @@
1{
2 "clusters": 1,
3 "nodes": 2
4}
diff --git a/api-ref/source/samples/versions-get-resp.json b/api-ref/source/samples/versions-get-resp.json
index c6a50c7..319d9bd 100644
--- a/api-ref/source/samples/versions-get-resp.json
+++ b/api-ref/source/samples/versions-get-resp.json
@@ -3,7 +3,7 @@
3 { 3 {
4 "status":"CURRENT", 4 "status":"CURRENT",
5 "min_version":"1.1", 5 "min_version":"1.1",
6 "max_version":"1.3", 6 "max_version":"1.4",
7 "id":"v1", 7 "id":"v1",
8 "links":[ 8 "links":[
9 { 9 {
diff --git a/api-ref/source/stats.inc b/api-ref/source/stats.inc
new file mode 100644
index 0000000..d600060
--- /dev/null
+++ b/api-ref/source/stats.inc
@@ -0,0 +1,82 @@
1.. -*- rst -*-
2
3=================
4 Magnum Stats API
5=================
6
7An admin user can get stats for the given tenant and also overall system stats.
8A non-admin user can get self stats.
9
10Show stats for a tenant
11=======================
12
13.. rest_method:: GET /v1/stats?project_id=<project_id>
14
15Get stats based on project id.
16
17Response Codes
18--------------
19
20.. rest_status_code:: success status.yaml
21
22 - 200
23
24.. rest_status_code:: error status.yaml
25
26 - 401
27 - 403
28
29Request
30-------
31
32.. rest_parameters:: parameters.yaml
33
34 - project_id: project_id
35
36Response
37--------
38
39.. rest_parameters:: parameters.yaml
40
41 - clusters: clusters
42 - nodes: nodes
43
44Response Example
45----------------
46
47.. literalinclude:: samples/stats-get-resp.json
48 :language: javascript
49
50Show overall stats
51==================
52
53.. rest_method:: GET /v1/stats
54
55Show overall Magnum system stats.
56If the requester is non-admin user show self stats.
57
58Response Codes
59--------------
60
61.. rest_status_code:: success status.yaml
62
63 - 200
64
65.. rest_status_code:: error status.yaml
66
67 - 401
68 - 403
69
70Response
71--------
72
73.. rest_parameters:: parameters.yaml
74
75 - clusters: clusters
76 - nodes: nodes
77
78Response Example
79----------------
80
81.. literalinclude:: samples/stats-get-resp.json
82 :language: javascript
diff --git a/contrib/drivers/k8s_opensuse_v1/templates/fragments/configure-flanneld-master.sh b/contrib/drivers/k8s_opensuse_v1/templates/fragments/configure-flanneld-master.sh
index 844f57b..0e2c9c4 100644
--- a/contrib/drivers/k8s_opensuse_v1/templates/fragments/configure-flanneld-master.sh
+++ b/contrib/drivers/k8s_opensuse_v1/templates/fragments/configure-flanneld-master.sh
@@ -30,6 +30,8 @@ cat > $FLANNEL_JSON <<EOF
30{ 30{
31 "Network": "$FLANNEL_NETWORK_CIDR", 31 "Network": "$FLANNEL_NETWORK_CIDR",
32 "Subnetlen": $FLANNEL_NETWORK_SUBNETLEN, 32 "Subnetlen": $FLANNEL_NETWORK_SUBNETLEN,
33 "SubnetMin": "$FLANNEL_NETWORK_SUBNET_MIN",
34 "SubnetMax": "$FLANNEL_NETWORK_SUBNET_MAX",
33 "Backend": { 35 "Backend": {
34 "Type": "$FLANNEL_BACKEND" 36 "Type": "$FLANNEL_BACKEND"
35 } 37 }
diff --git a/contrib/drivers/k8s_opensuse_v1/templates/fragments/write-heat-params-master.yaml b/contrib/drivers/k8s_opensuse_v1/templates/fragments/write-heat-params-master.yaml
index 2748c06..c65c9cd 100644
--- a/contrib/drivers/k8s_opensuse_v1/templates/fragments/write-heat-params-master.yaml
+++ b/contrib/drivers/k8s_opensuse_v1/templates/fragments/write-heat-params-master.yaml
@@ -11,6 +11,8 @@ write_files:
11 NETWORK_DRIVER="$NETWORK_DRIVER" 11 NETWORK_DRIVER="$NETWORK_DRIVER"
12 FLANNEL_NETWORK_CIDR="$FLANNEL_NETWORK_CIDR" 12 FLANNEL_NETWORK_CIDR="$FLANNEL_NETWORK_CIDR"
13 FLANNEL_NETWORK_SUBNETLEN="$FLANNEL_NETWORK_SUBNETLEN" 13 FLANNEL_NETWORK_SUBNETLEN="$FLANNEL_NETWORK_SUBNETLEN"
14 FLANNEL_NETWORK_SUBNET_MIN="$FLANNEL_NETWORK_SUBNET_MIN"
15 FLANNEL_NETWORK_SUBNET_MAX="$FLANNEL_NETWORK_SUBNET_MAX"
14 FLANNEL_BACKEND="$FLANNEL_BACKEND" 16 FLANNEL_BACKEND="$FLANNEL_BACKEND"
15 PORTAL_NETWORK_CIDR="$PORTAL_NETWORK_CIDR" 17 PORTAL_NETWORK_CIDR="$PORTAL_NETWORK_CIDR"
16 ETCD_DISCOVERY_URL="$ETCD_DISCOVERY_URL" 18 ETCD_DISCOVERY_URL="$ETCD_DISCOVERY_URL"
@@ -23,3 +25,5 @@ write_files:
23 KUBE_VERSION="$KUBE_VERSION" 25 KUBE_VERSION="$KUBE_VERSION"
24 CLUSTER_UUID="$CLUSTER_UUID" 26 CLUSTER_UUID="$CLUSTER_UUID"
25 MAGNUM_URL="$MAGNUM_URL" 27 MAGNUM_URL="$MAGNUM_URL"
28 SYSTEM_PODS_INITIAL_DELAY="$SYSTEM_PODS_INITIAL_DELAY"
29 SYSTEM_PODS_TIMEOUT="$SYSTEM_PODS_TIMEOUT"
diff --git a/contrib/drivers/k8s_opensuse_v1/templates/kubecluster.yaml b/contrib/drivers/k8s_opensuse_v1/templates/kubecluster.yaml
index a623c06..d517931 100644
--- a/contrib/drivers/k8s_opensuse_v1/templates/kubecluster.yaml
+++ b/contrib/drivers/k8s_opensuse_v1/templates/kubecluster.yaml
@@ -71,6 +71,16 @@ parameters:
71 description: size of subnet assigned to each minion 71 description: size of subnet assigned to each minion
72 default: 24 72 default: 24
73 73
74 flannel_network_subnet_min:
75 type: string
76 description: minimum subnet
77 default: 10.100.50.0
78
79 flannel_network_subnet_max:
80 type: string
81 description: maximum subnet
82 default: 10.100.199.0
83
74 flannel_backend: 84 flannel_backend:
75 type: string 85 type: string
76 description: > 86 description: >
@@ -79,6 +89,20 @@ parameters:
79 constraints: 89 constraints:
80 - allowed_values: ["udp", "vxlan", "host-gw"] 90 - allowed_values: ["udp", "vxlan", "host-gw"]
81 91
92 system_pods_initial_delay:
93 type: number
94 description: >
95 health check, time to wait for system pods (podmaster, scheduler) to boot
96 (in seconds)
97 default: 30
98
99 system_pods_timeout:
100 type: number
101 description: >
102 health check, timeout for system pods (podmaster, scheduler) to answer.
103 (in seconds)
104 default: 5
105
82 kube_allow_priv: 106 kube_allow_priv:
83 type: string 107 type: string
84 description: > 108 description: >
@@ -350,6 +374,18 @@ resources:
350 - protocol: tcp 374 - protocol: tcp
351 port_range_min: 6443 375 port_range_min: 6443
352 port_range_max: 6443 376 port_range_max: 6443
377 - protocol: tcp
378 port_range_min: 10250
379 port_range_max: 10250
380 - protocol: tcp
381 port_range_min: 30000
382 port_range_max: 32767
383 - protocol: udp
384 port_range_min: 8285
385 port_range_max: 8285
386 - protocol: udp
387 port_range_min: 8472
388 port_range_max: 8472
353 389
354 secgroup_kube_minion: 390 secgroup_kube_minion:
355 type: OS::Neutron::SecurityGroup 391 type: OS::Neutron::SecurityGroup
@@ -487,6 +523,10 @@ resources:
487 flannel_backend: {get_param: flannel_backend} 523 flannel_backend: {get_param: flannel_backend}
488 flannel_network_cidr: {get_param: flannel_network_cidr} 524 flannel_network_cidr: {get_param: flannel_network_cidr}
489 flannel_network_subnetlen: {get_param: flannel_network_subnetlen} 525 flannel_network_subnetlen: {get_param: flannel_network_subnetlen}
526 flannel_network_subnet_min: {get_param: flannel_network_subnet_min}
527 flannel_network_subnet_max: {get_param: flannel_network_subnet_max}
528 system_pods_initial_delay: {get_param: system_pods_initial_delay}
529 system_pods_timeout: {get_param: system_pods_timeout}
490 portal_network_cidr: {get_param: portal_network_cidr} 530 portal_network_cidr: {get_param: portal_network_cidr}
491 discovery_url: {get_param: discovery_url} 531 discovery_url: {get_param: discovery_url}
492 cluster_uuid: {get_param: cluster_uuid} 532 cluster_uuid: {get_param: cluster_uuid}
diff --git a/contrib/drivers/k8s_opensuse_v1/templates/kubemaster.yaml b/contrib/drivers/k8s_opensuse_v1/templates/kubemaster.yaml
index 34a7a95..5f59373 100644
--- a/contrib/drivers/k8s_opensuse_v1/templates/kubemaster.yaml
+++ b/contrib/drivers/k8s_opensuse_v1/templates/kubemaster.yaml
@@ -49,6 +49,16 @@ parameters:
49 description: size of subnet assigned to each master 49 description: size of subnet assigned to each master
50 default: 24 50 default: 24
51 51
52 flannel_network_subnet_min:
53 type: string
54 description: minimum subnet
55 default: 10.100.50.0
56
57 flannel_network_subnet_max:
58 type: string
59 description: maximum subnet
60 default: 10.100.199.0
61
52 flannel_backend: 62 flannel_backend:
53 type: string 63 type: string
54 description: > 64 description: >
@@ -56,6 +66,20 @@ parameters:
56 constraints: 66 constraints:
57 - allowed_values: ["udp", "vxlan", "host-gw"] 67 - allowed_values: ["udp", "vxlan", "host-gw"]
58 68
69 system_pods_initial_delay:
70 type: number
71 description: >
72 health check, time to wait for system pods (podmaster, scheduler) to boot
73 (in seconds)
74 default: 30
75
76 system_pods_timeout:
77 type: number
78 description: >
79 health check, timeout for system pods (podmaster, scheduler) to answer.
80 (in seconds)
81 default: 5
82
59 discovery_url: 83 discovery_url:
60 type: string 84 type: string
61 description: > 85 description: >
@@ -197,7 +221,11 @@ resources:
197 "$NETWORK_DRIVER": {get_param: network_driver} 221 "$NETWORK_DRIVER": {get_param: network_driver}
198 "$FLANNEL_NETWORK_CIDR": {get_param: flannel_network_cidr} 222 "$FLANNEL_NETWORK_CIDR": {get_param: flannel_network_cidr}
199 "$FLANNEL_NETWORK_SUBNETLEN": {get_param: flannel_network_subnetlen} 223 "$FLANNEL_NETWORK_SUBNETLEN": {get_param: flannel_network_subnetlen}
224 "$FLANNEL_NETWORK_SUBNET_MIN": {get_param: flannel_network_subnet_min}
225 "$FLANNEL_NETWORK_SUBNET_MAX": {get_param: flannel_network_subnet_max}
200 "$FLANNEL_BACKEND": {get_param: flannel_backend} 226 "$FLANNEL_BACKEND": {get_param: flannel_backend}
227 "$SYSTEM_PODS_INITIAL_DELAY": {get_param: system_pods_initial_delay}
228 "$SYSTEM_PODS_TIMEOUT": {get_param: system_pods_timeout}
201 "$PORTAL_NETWORK_CIDR": {get_param: portal_network_cidr} 229 "$PORTAL_NETWORK_CIDR": {get_param: portal_network_cidr}
202 "$ETCD_DISCOVERY_URL": {get_param: discovery_url} 230 "$ETCD_DISCOVERY_URL": {get_param: discovery_url}
203 "$AUTH_URL": {get_param: auth_url} 231 "$AUTH_URL": {get_param: auth_url}
diff --git a/devstack/lib/magnum b/devstack/lib/magnum
index 96bf4a0..f95b587 100644
--- a/devstack/lib/magnum
+++ b/devstack/lib/magnum
@@ -287,6 +287,7 @@ function install_magnumclient {
287 if use_library_from_git "python-magnumclient"; then 287 if use_library_from_git "python-magnumclient"; then
288 git_clone_by_name "python-magnumclient" 288 git_clone_by_name "python-magnumclient"
289 setup_dev_lib "python-magnumclient" 289 setup_dev_lib "python-magnumclient"
290 sudo install -D -m 0644 -o $STACK_USER {${GITDIR["python-magnumclient"]}/tools/,/etc/bash_completion.d/}magnum.bash_completion
290 fi 291 fi
291} 292}
292 293
diff --git a/doc/source/dev/quickstart.rst b/doc/source/dev/quickstart.rst
index 908e347..5bef05e 100644
--- a/doc/source/dev/quickstart.rst
+++ b/doc/source/dev/quickstart.rst
@@ -19,17 +19,17 @@ Install OS-specific prerequisites::
19 19
20 # Ubuntu/Debian: 20 # Ubuntu/Debian:
21 sudo apt-get update 21 sudo apt-get update
22 sudo apt-get install -y python-dev libssl-dev libxml2-dev \ 22 sudo apt-get install -y python-dev libssl-dev libxml2-dev curl \
23 libmysqlclient-dev libxslt-dev libpq-dev git \ 23 libmysqlclient-dev libxslt-dev libpq-dev git \
24 libffi-dev gettext build-essential python3.4-dev 24 libffi-dev gettext build-essential python3.4-dev
25 25
26 # Fedora/RHEL: 26 # Fedora/RHEL:
27 sudo yum install -y python-devel openssl-devel mysql-devel \ 27 sudo yum install -y python-devel openssl-devel mysql-devel curl \
28 libxml2-devel libxslt-devel postgresql-devel git \ 28 libxml2-devel libxslt-devel postgresql-devel git \
29 libffi-devel gettext gcc 29 libffi-devel gettext gcc
30 30
31 # openSUSE/SLE 12: 31 # openSUSE/SLE 12:
32 sudo zypper --non-interactive install git libffi-devel \ 32 sudo zypper --non-interactive install git libffi-devel curl \
33 libmysqlclient-devel libopenssl-devel libxml2-devel \ 33 libmysqlclient-devel libopenssl-devel libxml2-devel \
34 libxslt-devel postgresql-devel python-devel \ 34 libxslt-devel postgresql-devel python-devel \
35 gettext-runtime 35 gettext-runtime
@@ -373,11 +373,12 @@ Using a Kubernetes Cluster
373k8s cluster created previously. 373k8s cluster created previously.
374 374
375Kubernetes provides a number of examples you can use to check that things are 375Kubernetes provides a number of examples you can use to check that things are
376working. You may need to clone kubernetes using:: 376working. You may need to download kubectl binary for interacting with k8s
377cluster using::
377 378
378 wget https://github.com/kubernetes/kubernetes/releases/download/v1.0.1/kubernetes.tar.gz 379 curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.2.0/bin/linux/amd64/kubectl
379 tar -xvzf kubernetes.tar.gz 380 chmod +x ./kubectl
380 sudo cp -a kubernetes/platforms/linux/amd64/kubectl /usr/bin/kubectl 381 sudo mv ./kubectl /usr/local/bin/kubectl
381 382
382We first need to setup the certs to allow Kubernetes to authenticate our 383We first need to setup the certs to allow Kubernetes to authenticate our
383connection. Please refer to 384connection. Please refer to
diff --git a/doc/source/userguide.rst b/doc/source/userguide.rst
index ffe9eec..773a373 100644
--- a/doc/source/userguide.rst
+++ b/doc/source/userguide.rst
@@ -302,6 +302,8 @@ the table are linked to more details elsewhere in the user guide.
302| | - binpack | | 302| | - binpack | |
303| | - random | | 303| | - random | |
304+---------------------------------------+--------------------+---------------+ 304+---------------------------------------+--------------------+---------------+
305| `admission_control_list`_ | see below | see below |
306+---------------------------------------+--------------------+---------------+
305 307
306 308
307======= 309=======
@@ -1013,6 +1015,17 @@ Log into the servers
1013 You can log into the master servers using the login 'fedora' and the 1015 You can log into the master servers using the login 'fedora' and the
1014 keypair specified in the ClusterTemplate. 1016 keypair specified in the ClusterTemplate.
1015 1017
1018In addition to the common attributes in the ClusterTemplate, you can specify
1019the following attributes that are specific to Kubernetes by using the
1020labels attribute.
1021
1022_`admission_control_list`
1023 This label corresponds to Kubernetes parameter for the API server '--admission-control'.
1024 For more details, refer to the `Admission Controllers
1025 <https://kubernetes.io/docs/admin/admission-controllers//>`_.
1026 The default value corresponds to the one recommended in this doc
1027 for our current Kubernetes version.
1028
1016External load balancer for services 1029External load balancer for services
1017----------------------------------- 1030-----------------------------------
1018 1031
@@ -1710,9 +1723,9 @@ saved in local files as follows::
1710For Kubernetes, you need to get 'kubectl', a kubernetes CLI tool, to 1723For Kubernetes, you need to get 'kubectl', a kubernetes CLI tool, to
1711communicate with the cluster:: 1724communicate with the cluster::
1712 1725
1713 wget https://github.com/kubernetes/kubernetes/releases/download/v1.2.0/kubernetes.tar.gz 1726 curl -O https://storage.googleapis.com/kubernetes-release/release/v1.2.0/bin/linux/amd64/kubectl
1714 tar -xzvf kubernetes.tar.gz 1727 chmod +x kubectl
1715 sudo cp -a kubernetes/platforms/linux/amd64/kubectl /usr/bin/kubectl 1728 sudo mv kubectl /usr/local/bin/kubectl
1716 1729
1717Now let's run some 'kubectl' commands to check the secure communication. 1730Now let's run some 'kubectl' commands to check the secure communication.
1718If you used 'cluster-config', then you can simply run the 'kubectl' command 1731If you used 'cluster-config', then you can simply run the 'kubectl' command
diff --git a/etc/magnum/api-paste.ini b/etc/magnum/api-paste.ini
index 67aab9d..d1f56fb 100644
--- a/etc/magnum/api-paste.ini
+++ b/etc/magnum/api-paste.ini
@@ -1,5 +1,5 @@
1[pipeline:main] 1[pipeline:main]
2pipeline = cors healthcheck http_proxy_to_wsgi request_id authtoken api_v1 2pipeline = cors healthcheck http_proxy_to_wsgi request_id osprofiler authtoken api_v1
3 3
4[app:api_v1] 4[app:api_v1]
5paste.app_factory = magnum.api.app:app_factory 5paste.app_factory = magnum.api.app:app_factory
@@ -8,11 +8,14 @@ paste.app_factory = magnum.api.app:app_factory
8acl_public_routes = /, /v1 8acl_public_routes = /, /v1
9paste.filter_factory = magnum.api.middleware.auth_token:AuthTokenMiddleware.factory 9paste.filter_factory = magnum.api.middleware.auth_token:AuthTokenMiddleware.factory
10 10
11[filter:osprofiler]
12paste.filter_factory = magnum.common.profiler:WsgiMiddleware.factory
13
11[filter:request_id] 14[filter:request_id]
12paste.filter_factory = oslo_middleware:RequestId.factory 15paste.filter_factory = oslo_middleware:RequestId.factory
13 16
14[filter:cors] 17[filter:cors]
15paste.filter_factory = oslo_middleware.cors:filter_factory 18paste.filter_factory = oslo_middleware.cors:filter_factory
16oslo_config_project = magnum 19oslo_config_project = magnum
17 20
18[filter:healthcheck] 21[filter:healthcheck]
diff --git a/etc/magnum/policy.json b/etc/magnum/policy.json
index 6a84d2f..86cea75 100644
--- a/etc/magnum/policy.json
+++ b/etc/magnum/policy.json
@@ -35,8 +35,16 @@
35 "clustertemplate:update": "rule:default", 35 "clustertemplate:update": "rule:default",
36 "clustertemplate:publish": "rule:admin_or_owner", 36 "clustertemplate:publish": "rule:admin_or_owner",
37 37
38 "quotas:get": "rule:default",
39 "quotas:get_all": "rule:admin_api",
40 "quotas:create": "rule:admin_api",
41 "quotas:update": "rule:admin_api",
42 "quotas:delete": "rule:admin_api",
43
38 "certificate:create": "rule:admin_or_user", 44 "certificate:create": "rule:admin_or_user",
39 "certificate:get": "rule:admin_or_user", 45 "certificate:get": "rule:admin_or_user",
46 "certificate:rotate_ca": "rule:admin_or_owner",
40 47
41 "magnum-service:get_all": "rule:admin_api" 48 "magnum-service:get_all": "rule:admin_api",
49 "stats:get_all": "rule:admin_or_owner"
42} 50}
diff --git a/install-guide/source/conf.py b/install-guide/source/conf.py
index f8c7998..ba96a51 100644
--- a/install-guide/source/conf.py
+++ b/install-guide/source/conf.py
@@ -74,7 +74,7 @@ release = magnum_version.version_string_with_vcs()
74# from git log. 74# from git log.
75# bug_tag: Tag for categorizing the bug. Must be set manually. 75# bug_tag: Tag for categorizing the bug. Must be set manually.
76# These variables are passed to the logabug code via html_context. 76# These variables are passed to the logabug code via html_context.
77giturl = u'http://git.openstack.org/cgit/openstack/magnum/tree/' 77giturl = u'https://git.openstack.org/cgit/openstack/magnum/tree/'
78giturl += u'install-guide/source' 78giturl += u'install-guide/source'
79git_cmd = "/usr/bin/git log | head -n1 | cut -f2 -d' '" 79git_cmd = "/usr/bin/git log | head -n1 | cut -f2 -d' '"
80gitsha = os.popen(git_cmd).read().strip('\n') 80gitsha = os.popen(git_cmd).read().strip('\n')
diff --git a/magnum/api/controllers/v1/__init__.py b/magnum/api/controllers/v1/__init__.py
index 92ce996..3c99b4e 100644
--- a/magnum/api/controllers/v1/__init__.py
+++ b/magnum/api/controllers/v1/__init__.py
@@ -30,6 +30,8 @@ from magnum.api.controllers.v1 import certificate
30from magnum.api.controllers.v1 import cluster 30from magnum.api.controllers.v1 import cluster
31from magnum.api.controllers.v1 import cluster_template 31from magnum.api.controllers.v1 import cluster_template
32from magnum.api.controllers.v1 import magnum_services 32from magnum.api.controllers.v1 import magnum_services
33from magnum.api.controllers.v1 import quota
34from magnum.api.controllers.v1 import stats
33from magnum.api.controllers import versions as ver 35from magnum.api.controllers import versions as ver
34from magnum.api import expose 36from magnum.api import expose
35from magnum.api import http_error 37from magnum.api import http_error
@@ -85,12 +87,18 @@ class V1(controllers_base.APIBase):
85 clusters = [link.Link] 87 clusters = [link.Link]
86 """Links to the clusters resource""" 88 """Links to the clusters resource"""
87 89
90 quotas = [link.Link]
91 """Links to the quotas resource"""
92
88 certificates = [link.Link] 93 certificates = [link.Link]
89 """Links to the certificates resource""" 94 """Links to the certificates resource"""
90 95
91 mservices = [link.Link] 96 mservices = [link.Link]
92 """Links to the magnum-services resource""" 97 """Links to the magnum-services resource"""
93 98
99 stats = [link.Link]
100 """Links to the stats resource"""
101
94 @staticmethod 102 @staticmethod
95 def convert(): 103 def convert():
96 v1 = V1() 104 v1 = V1()
@@ -129,6 +137,12 @@ class V1(controllers_base.APIBase):
129 pecan.request.host_url, 137 pecan.request.host_url,
130 'clusters', '', 138 'clusters', '',
131 bookmark=True)] 139 bookmark=True)]
140 v1.quotas = [link.Link.make_link('self', pecan.request.host_url,
141 'quotas', ''),
142 link.Link.make_link('bookmark',
143 pecan.request.host_url,
144 'quotas', '',
145 bookmark=True)]
132 v1.certificates = [link.Link.make_link('self', pecan.request.host_url, 146 v1.certificates = [link.Link.make_link('self', pecan.request.host_url,
133 'certificates', ''), 147 'certificates', ''),
134 link.Link.make_link('bookmark', 148 link.Link.make_link('bookmark',
@@ -141,6 +155,12 @@ class V1(controllers_base.APIBase):
141 pecan.request.host_url, 155 pecan.request.host_url,
142 'mservices', '', 156 'mservices', '',
143 bookmark=True)] 157 bookmark=True)]
158 v1.stats = [link.Link.make_link('self', pecan.request.host_url,
159 'stats', ''),
160 link.Link.make_link('bookmark',
161 pecan.request.host_url,
162 'stats', '',
163 bookmark=True)]
144 return v1 164 return v1
145 165
146 166
@@ -151,8 +171,10 @@ class Controller(controllers_base.Controller):
151 baymodels = baymodel.BayModelsController() 171 baymodels = baymodel.BayModelsController()
152 clusters = cluster.ClustersController() 172 clusters = cluster.ClustersController()
153 clustertemplates = cluster_template.ClusterTemplatesController() 173 clustertemplates = cluster_template.ClusterTemplatesController()
174 quotas = quota.QuotaController()
154 certificates = certificate.CertificateController() 175 certificates = certificate.CertificateController()
155 mservices = magnum_services.MagnumServiceController() 176 mservices = magnum_services.MagnumServiceController()
177 stats = stats.StatsController()
156 178
157 @expose.expose(V1) 179 @expose.expose(V1)
158 def get(self): 180 def get(self):
diff --git a/magnum/api/controllers/v1/certificate.py b/magnum/api/controllers/v1/certificate.py
index 432ad18..6068d12 100644
--- a/magnum/api/controllers/v1/certificate.py
+++ b/magnum/api/controllers/v1/certificate.py
@@ -166,3 +166,14 @@ class CertificateController(base.Controller):
166 new_cert = pecan.request.rpcapi.sign_certificate(cluster, 166 new_cert = pecan.request.rpcapi.sign_certificate(cluster,
167 cert_obj) 167 cert_obj)
168 return Certificate.convert_with_links(new_cert) 168 return Certificate.convert_with_links(new_cert)
169
170 @expose.expose(None, types.uuid_or_name, status_code=202)
171 def patch(self, cluster_ident):
172 context = pecan.request.context
173 cluster = api_utils.get_resource('Cluster', cluster_ident)
174 policy.enforce(context, 'certificate:rotate_ca', cluster,
175 action='certificate:rotate_ca')
176 if cluster.cluster_template.tls_disabled:
177 raise exception.NotSupported("Rotating the CA certificate on a "
178 "non-TLS cluster is not supported")
179 pecan.request.rpcapi.rotate_ca_certificate(cluster)
diff --git a/magnum/api/controllers/v1/cluster.py b/magnum/api/controllers/v1/cluster.py
index a7d9c9f..e6ad47f 100644
--- a/magnum/api/controllers/v1/cluster.py
+++ b/magnum/api/controllers/v1/cluster.py
@@ -28,16 +28,19 @@ from magnum.api.controllers.v1 import collection
28from magnum.api.controllers.v1 import types 28from magnum.api.controllers.v1 import types
29from magnum.api import expose 29from magnum.api import expose
30from magnum.api import utils as api_utils 30from magnum.api import utils as api_utils
31from magnum.api.validation import validate_cluster_properties 31from magnum.api import validation
32from magnum.common import clients 32from magnum.common import clients
33from magnum.common import exception 33from magnum.common import exception
34from magnum.common import name_generator 34from magnum.common import name_generator
35from magnum.common import policy 35from magnum.common import policy
36import magnum.conf
37from magnum.i18n import _
36from magnum.i18n import _LW 38from magnum.i18n import _LW
37from magnum import objects 39from magnum import objects
38from magnum.objects import fields 40from magnum.objects import fields
39 41
40LOG = logging.getLogger(__name__) 42LOG = logging.getLogger(__name__)
43CONF = magnum.conf.CONF
41 44
42 45
43class ClusterID(wtypes.Base): 46class ClusterID(wtypes.Base):
@@ -353,7 +356,26 @@ class ClustersController(base.Controller):
353 356
354 return cluster 357 return cluster
355 358
359 def _check_cluster_quota_limit(self, context):
360 try:
361 # Check if there is any explicit quota limit set in Quotas table
362 quota = objects.Quota.get_quota_by_project_id_resource(
363 context,
364 context.project_id,
365 'Cluster')
366 cluster_limit = quota.hard_limit
367 except exception.QuotaNotFound:
368 # If explicit quota was not set for the project, use default limit
369 cluster_limit = CONF.quotas.max_clusters_per_project
370
371 if objects.Cluster.get_count_all(context) >= cluster_limit:
372 msg = _("You have reached the maximum clusters per project, "
373 "%d. You may delete a cluster to make room for a new "
374 "one.") % cluster_limit
375 raise exception.ResourceLimitExceeded(msg=msg)
376
356 @expose.expose(ClusterID, body=Cluster, status_code=202) 377 @expose.expose(ClusterID, body=Cluster, status_code=202)
378 @validation.enforce_cluster_type_supported()
357 def post(self, cluster): 379 def post(self, cluster):
358 """Create a new cluster. 380 """Create a new cluster.
359 381
@@ -362,6 +384,9 @@ class ClustersController(base.Controller):
362 context = pecan.request.context 384 context = pecan.request.context
363 policy.enforce(context, 'cluster:create', 385 policy.enforce(context, 'cluster:create',
364 action='cluster:create') 386 action='cluster:create')
387
388 self._check_cluster_quota_limit(context)
389
365 temp_id = cluster.cluster_template_id 390 temp_id = cluster.cluster_template_id
366 cluster_template = objects.ClusterTemplate.get_by_uuid(context, 391 cluster_template = objects.ClusterTemplate.get_by_uuid(context,
367 temp_id) 392 temp_id)
@@ -449,7 +474,7 @@ class ClustersController(base.Controller):
449 474
450 delta = cluster.obj_what_changed() 475 delta = cluster.obj_what_changed()
451 476
452 validate_cluster_properties(delta) 477 validation.validate_cluster_properties(delta)
453 return cluster 478 return cluster
454 479
455 @expose.expose(None, types.uuid_or_name, status_code=204) 480 @expose.expose(None, types.uuid_or_name, status_code=204)
diff --git a/magnum/api/controllers/v1/collection.py b/magnum/api/controllers/v1/collection.py
index 347568b..c17251e 100644
--- a/magnum/api/controllers/v1/collection.py
+++ b/magnum/api/controllers/v1/collection.py
@@ -33,7 +33,7 @@ class Collection(base.APIBase):
33 """Return whether collection has more items.""" 33 """Return whether collection has more items."""
34 return len(self.collection) and len(self.collection) == limit 34 return len(self.collection) and len(self.collection) == limit
35 35
36 def get_next(self, limit, url=None, **kwargs): 36 def get_next(self, limit, url=None, marker_attribute='uuid', **kwargs):
37 """Return a link to the next subset of the collection.""" 37 """Return a link to the next subset of the collection."""
38 if not self.has_next(limit): 38 if not self.has_next(limit):
39 return wtypes.Unset 39 return wtypes.Unset
@@ -42,7 +42,7 @@ class Collection(base.APIBase):
42 q_args = ''.join(['%s=%s&' % (key, kwargs[key]) for key in kwargs]) 42 q_args = ''.join(['%s=%s&' % (key, kwargs[key]) for key in kwargs])
43 next_args = '?%(args)slimit=%(limit)d&marker=%(marker)s' % { 43 next_args = '?%(args)slimit=%(limit)d&marker=%(marker)s' % {
44 'args': q_args, 'limit': limit, 44 'args': q_args, 'limit': limit,
45 'marker': self.collection[-1].uuid} 45 'marker': getattr(self.collection[-1], marker_attribute)}
46 46
47 return link.Link.make_link('next', pecan.request.host_url, 47 return link.Link.make_link('next', pecan.request.host_url,
48 resource_url, next_args).href 48 resource_url, next_args).href
diff --git a/magnum/api/controllers/v1/quota.py b/magnum/api/controllers/v1/quota.py
new file mode 100644
index 0000000..ad4ec35
--- /dev/null
+++ b/magnum/api/controllers/v1/quota.py
@@ -0,0 +1,213 @@
1# Copyright 2013 UnitedStack Inc.
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
16from oslo_log import log as logging
17import pecan
18import wsme
19from wsme import types as wtypes
20
21from magnum.api.controllers import base
22from magnum.api.controllers.v1 import collection
23from magnum.api import expose
24from magnum.api import utils as api_utils
25from magnum.common import exception
26from magnum.common import policy
27from magnum.i18n import _
28from magnum import objects
29from magnum.objects import fields
30
31LOG = logging.getLogger(__name__)
32
33
34class Quota(base.APIBase):
35 """API representation of a project Quota.
36
37 This class enforces type checking and value constraints, and converts
38 between the internal object model and the API representation of Quota.
39 """
40 id = wsme.wsattr(wtypes.IntegerType(minimum=1))
41 """unique id"""
42
43 hard_limit = wsme.wsattr(wtypes.IntegerType(minimum=1), default=1)
44 """The hard limit for total number of clusters. Default to 1 if not set"""
45
46 project_id = wsme.wsattr(wtypes.StringType(min_length=1, max_length=255),
47 default=None)
48 """The project id"""
49
50 resource = wsme.wsattr(wtypes.Enum(str, *fields.QuotaResourceName.ALL),
51 default='Cluster')
52 """The resource name"""
53
54 def __init__(self, **kwargs):
55 super(Quota, self).__init__()
56 self.fields = []
57 for field in objects.Quota.fields:
58 # Skip fields we do not expose.
59 if not hasattr(self, field):
60 continue
61 self.fields.append(field)
62 setattr(self, field, kwargs.get(field, wtypes.Unset))
63
64 @classmethod
65 def convert(cls, quota):
66 return Quota(**quota.as_dict())
67
68
69class QuotaCollection(collection.Collection):
70 """API representation of a collection of quotas."""
71
72 quotas = [Quota]
73 """A list containing quota objects"""
74
75 def __init__(self, **kwargs):
76 self._type = 'quotas'
77
78 @staticmethod
79 def convert(quotas, limit, **kwargs):
80 collection = QuotaCollection()
81 collection.quotas = [Quota.convert(p) for p in quotas]
82 collection.next = collection.get_next(limit,
83 marker_attribute='id',
84 **kwargs)
85 return collection
86
87
88class QuotaController(base.Controller):
89 """REST controller for Quotas."""
90
91 def __init__(self):
92 super(QuotaController, self).__init__()
93
94 _custom_actions = {
95 'detail': ['GET'],
96 }
97
98 def _get_quota_collection(self, marker, limit, sort_key, sort_dir,
99 filters):
100
101 limit = api_utils.validate_limit(limit)
102 sort_dir = api_utils.validate_sort_dir(sort_dir)
103
104 marker_obj = None
105 if marker:
106 marker_obj = objects.Quota.get_by_id(pecan.request.context,
107 marker)
108
109 quotas = objects.Quota.list(pecan.request.context,
110 limit,
111 marker_obj,
112 sort_key=sort_key,
113 sort_dir=sort_dir,
114 filters=filters)
115
116 return QuotaCollection.convert(quotas,
117 limit,
118 sort_key=sort_key,
119 sort_dir=sort_dir)
120
121 @expose.expose(QuotaCollection, int, int, wtypes.text, wtypes.text, bool)
122 def get_all(self, marker=None, limit=None, sort_key='id',
123 sort_dir='asc', all_tenants=False):
124 """Retrieve a list of quotas.
125
126 :param marker: pagination marker for large data sets.
127 :param limit: maximum number of resources to return in a single result.
128 :param sort_key: column to sort results by. Default: id.
129 :param sort_dir: direction to sort. "asc" or "desc". Default: asc.
130 :param all_tenants: a flag to indicate all or current tenant.
131 """
132 context = pecan.request.context
133 policy.enforce(context, 'quota:get_all',
134 action='quota:get_all')
135
136 filters = {}
137 if not context.is_admin or not all_tenants:
138 filters = {"project_id": context.project_id}
139
140 return self._get_quota_collection(marker,
141 limit,
142 sort_key,
143 sort_dir,
144 filters)
145
146 @expose.expose(Quota, wtypes.text, wtypes.text)
147 def get_one(self, project_id, resource):
148 """Retrieve Quota information for the given project_id.
149
150 :param id: project id.
151 :param resource: resource name.
152 """
153 context = pecan.request.context
154 policy.enforce(context, 'quota:get', action='quota:get')
155
156 if not context.is_admin and project_id != context.project_id:
157 raise exception.NotAuthorized()
158
159 quota = objects.Quota.get_quota_by_project_id_resource(context,
160 project_id,
161 resource)
162 return Quota.convert(quota)
163
164 @expose.expose(Quota, body=Quota, status_code=201)
165 def post(self, quota):
166 """Create Quota.
167
168 :param quota: a json document to create this Quota.
169 """
170
171 context = pecan.request.context
172 policy.enforce(context, 'quota:create', action='quota:create')
173
174 quota_dict = quota.as_dict()
175 if 'project_id'not in quota_dict or not quota_dict['project_id']:
176 msg = _('Must provide a valid project ID.')
177 raise exception.InvalidParameterValue(message=msg)
178
179 new_quota = objects.Quota(context, **quota_dict)
180 new_quota.create()
181 return Quota.convert(new_quota)
182
183 @expose.expose(Quota, wtypes.text, wtypes.text, body=Quota,
184 status_code=202)
185 def patch(self, project_id, resource, quotapatch):
186 """Update Quota for a given project_id.
187
188 :param project_id: project id.
189 :param resource: resource name.
190 :param quotapatch: a json document to update Quota.
191 """
192
193 context = pecan.request.context
194 policy.enforce(context, 'quota:update', action='quota:update')
195 quota_dict = quotapatch.as_dict()
196 quota_dict['project_id'] = project_id
197 quota_dict['resource'] = resource
198 db_quota = objects.Quota.update_quota(context, project_id, quota_dict)
199 return Quota.convert(db_quota)
200
201 @expose.expose(None, wtypes.text, wtypes.text, status_code=204)
202 def delete(self, project_id, resource):
203 """Delete Quota for a given project_id and resource.
204
205 :param project_id: project id.
206 :param resource: resource name.
207 """
208
209 context = pecan.request.context
210 policy.enforce(context, 'quota:delete', action='quota:delete')
211 quota_dict = {"project_id": project_id, "resource": resource}
212 quota = objects.Quota(context, **quota_dict)
213 quota.delete()
diff --git a/magnum/api/controllers/v1/stats.py b/magnum/api/controllers/v1/stats.py
new file mode 100644
index 0000000..9461ffe
--- /dev/null
+++ b/magnum/api/controllers/v1/stats.py
@@ -0,0 +1,72 @@
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
13import pecan
14from wsme import types as wtypes
15
16from magnum.api.controllers import base
17from magnum.api import expose
18from magnum.common import exception
19from magnum.common import policy
20from magnum.i18n import _
21from magnum import objects
22
23
24class Stats(base.APIBase):
25
26 clusters = wtypes.IntegerType(minimum=0)
27 nodes = wtypes.IntegerType(minimum=0)
28
29 def __init__(self, **kwargs):
30 self.fields = []
31 for field in objects.Stats.fields:
32 # Skip fields we do not expose.
33 if not hasattr(self, field):
34 continue
35 self.fields.append(field)
36 setattr(self, field, kwargs.get(field, wtypes.Unset))
37
38 @classmethod
39 def convert(cls, rpc_stats):
40 return Stats(**rpc_stats.as_dict())
41
42
43class StatsController(base.Controller):
44 """REST controller for Stats."""
45 def __init__(self, **kwargs):
46 super(StatsController, self).__init__()
47
48 @expose.expose(Stats, wtypes.text, wtypes.text)
49 def get_all(self, project_id=None, type="cluster"):
50 """Retrieve magnum stats.
51
52 """
53 context = pecan.request.context
54 policy.enforce(context, 'stats:get_all', action='stats:get_all')
55 allowed_stats = ["cluster"]
56
57 if type.lower() not in allowed_stats:
58 msg = _("Invalid stats type. Allowed values are '%s'")
59 allowed_str = ','.join(allowed_stats)
60 raise exception.InvalidParameterValue(err=msg % allowed_str)
61
62 # 1.If the requester is not an admin and trying to request stats for
63 # different tenant, then reject the request
64 # 2.If the requester is not an admin and project_id was not provided,
65 # then return self stats
66 if not context.is_admin:
67 project_id = project_id if project_id else context.project_id
68 if project_id != context.project_id:
69 raise exception.NotAuthorized()
70
71 stats = objects.Stats.get_cluster_stats(context, project_id)
72 return Stats.convert(stats)
diff --git a/magnum/api/controllers/versions.py b/magnum/api/controllers/versions.py
index 6358388..67caafb 100644
--- a/magnum/api/controllers/versions.py
+++ b/magnum/api/controllers/versions.py
@@ -36,10 +36,13 @@ REST_API_VERSION_HISTORY = """REST API Version History:
36 * 1.1 - Initial version 36 * 1.1 - Initial version
37 * 1.2 - Async bay operations support 37 * 1.2 - Async bay operations support
38 * 1.3 - Add bay rollback support 38 * 1.3 - Add bay rollback support
39 * 1.4 - Add stats API
40 * 1.5 - Add cluster CA certificate rotation support
41 * 1.6 - Add quotas API
39""" 42"""
40 43
41BASE_VER = '1.1' 44BASE_VER = '1.1'
42CURRENT_MAX_VER = '1.3' 45CURRENT_MAX_VER = '1.6'
43 46
44 47
45class Version(object): 48class Version(object):
diff --git a/magnum/api/rest_api_version_history.rst b/magnum/api/rest_api_version_history.rst
index 7b3f3ff..a9aa04b 100644
--- a/magnum/api/rest_api_version_history.rst
+++ b/magnum/api/rest_api_version_history.rst
@@ -44,3 +44,34 @@ user documentation.
44 For example:- 44 For example:-
45 - http://XXX/v1/clusters/XXX/?rollback=True or 45 - http://XXX/v1/clusters/XXX/?rollback=True or
46 - http://XXX/v1/bays/XXX/?rollback=True 46 - http://XXX/v1/bays/XXX/?rollback=True
47
48
491.4
50---
51
52 Add stats API
53
54 An admin user can get total number of clusters and nodes for a specified
55 tenant or for all the tenants and also a non-admin user can get self stats.
56 For example:-
57 - http://XXX/v1/stats or
58 - http://XXX/v1/stats?project_id=<project-id> or
59 - http://XXX/v1/stats?project_id=<project-id>&type=<stats-type>
60
61
621.5
63---
64
65 Support for cluster CA certificate rotation
66
67 This gives admins a way to revoke access to an existing cluster once
68 a user has been granted access.
69
70
711.6
72---
73
74 Add quotas API
75
76 An admin user can set/update/delete/list quotas for the given tenant.
77 A non-admin user can get self quota information.
diff --git a/magnum/api/validation.py b/magnum/api/validation.py
index 0b16f16..234bba8 100644
--- a/magnum/api/validation.py
+++ b/magnum/api/validation.py
@@ -15,12 +15,12 @@
15 15
16import decorator 16import decorator
17 17
18from oslo_utils import uuidutils
19import pecan 18import pecan
20 19
21from magnum.api import utils as api_utils 20from magnum.api import utils as api_utils
22from magnum.common import exception 21from magnum.common import exception
23import magnum.conf 22import magnum.conf
23from magnum.drivers.common import driver
24from magnum.i18n import _ 24from magnum.i18n import _
25from magnum import objects 25from magnum import objects
26 26
@@ -29,33 +29,16 @@ CONF = magnum.conf.CONF
29cluster_update_allowed_properties = set(['node_count']) 29cluster_update_allowed_properties = set(['node_count'])
30 30
31 31
32def enforce_cluster_types(*cluster_types): 32def enforce_cluster_type_supported():
33 """Enforce that cluster_type is in supported list."""
34 @decorator.decorator 33 @decorator.decorator
35 def wrapper(func, *args, **kwargs): 34 def wrapper(func, *args, **kwargs):
36 # Note(eliqiao): This decorator has some assumptions 35 cluster = args[1]
37 # args[1] should be an APIBase instance or 36 cluster_template = objects.ClusterTemplate.get_by_uuid(
38 # args[2] should be a cluster_ident 37 pecan.request.context, cluster.cluster_template_id)
39 obj = args[1] 38 cluster_type = (cluster_template.server_type,
40 if hasattr(obj, 'cluster_uuid'): 39 cluster_template.cluster_distro,
41 cluster = objects.Cluster.get_by_uuid(pecan.request.context, 40 cluster_template.coe)
42 obj.cluster_uuid) 41 driver.Driver.get_driver(*cluster_type)
43 else:
44 cluster_ident = args[2]
45 if uuidutils.is_uuid_like(cluster_ident):
46 cluster = objects.Cluster.get_by_uuid(pecan.request.context,
47 cluster_ident)
48 else:
49 cluster = objects.Cluster.get_by_name(pecan.request.context,
50 cluster_ident)
51
52 if cluster.cluster_template.coe not in cluster_types:
53 raise exception.InvalidParameterValue(_(
54 'Cannot fulfill request with a %(cluster_type)s cluster, '
55 'expecting a %(supported_cluster_types)s cluster.') %
56 {'cluster_type': cluster.cluster_template.coe,
57 'supported_cluster_types': '/'.join(cluster_types)})
58
59 return func(*args, **kwargs) 42 return func(*args, **kwargs)
60 43
61 return wrapper 44 return wrapper
diff --git a/magnum/cmd/__init__.py b/magnum/cmd/__init__.py
index e69de29..277b2af 100644
--- a/magnum/cmd/__init__.py
+++ b/magnum/cmd/__init__.py
@@ -0,0 +1,20 @@
1# Copyright 2017 Fujitsu 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# NOTE(hieulq): we monkey patch all eventlet services for easier tracking/debug
17
18import eventlet
19
20eventlet.monkey_patch()
diff --git a/magnum/cmd/api.py b/magnum/cmd/api.py
index 92d7863..eaad02f 100644
--- a/magnum/cmd/api.py
+++ b/magnum/cmd/api.py
@@ -23,6 +23,7 @@ from oslo_reports import guru_meditation_report as gmr
23from werkzeug import serving 23from werkzeug import serving
24 24
25from magnum.api import app as api_app 25from magnum.api import app as api_app
26from magnum.common import profiler
26from magnum.common import service 27from magnum.common import service
27import magnum.conf 28import magnum.conf
28from magnum.i18n import _ 29from magnum.i18n import _
@@ -63,6 +64,9 @@ def main():
63 64
64 app = api_app.load_app() 65 app = api_app.load_app()
65 66
67 # Setup OSprofiler for WSGI service
68 profiler.setup('magnum-api', CONF.host)
69
66 # SSL configuration 70 # SSL configuration
67 use_ssl = CONF.api.enabled_ssl 71 use_ssl = CONF.api.enabled_ssl
68 72
diff --git a/magnum/common/cert_manager/barbican_cert_manager.py b/magnum/common/cert_manager/barbican_cert_manager.py
index d35b72d..865e423 100644
--- a/magnum/common/cert_manager/barbican_cert_manager.py
+++ b/magnum/common/cert_manager/barbican_cert_manager.py
@@ -13,12 +13,14 @@
13# under the License. 13# under the License.
14 14
15from barbicanclient import client as barbican_client 15from barbicanclient import client as barbican_client
16from barbicanclient import exceptions as barbican_exc
16from oslo_log import log as logging 17from oslo_log import log as logging
17from oslo_utils import excutils 18from oslo_utils import excutils
18 19
19from magnum.common.cert_manager import cert_manager 20from magnum.common.cert_manager import cert_manager
20from magnum.common import clients 21from magnum.common import clients
21from magnum.common import context 22from magnum.common import context
23from magnum.common import exception as magnum_exc
22from magnum.i18n import _ 24from magnum.i18n import _
23from magnum.i18n import _LE 25from magnum.i18n import _LE
24from magnum.i18n import _LI 26from magnum.i18n import _LI
@@ -134,7 +136,7 @@ class CertManager(cert_manager.CertManager):
134 # exceptions strangely -- this will catch anything that it raises and 136 # exceptions strangely -- this will catch anything that it raises and
135 # reraise the original exception, while also providing useful 137 # reraise the original exception, while also providing useful
136 # feedback in the logs for debugging 138 # feedback in the logs for debugging
137 except Exception: 139 except magnum_exc.CertificateStorageException:
138 for secret in [certificate_secret, private_key_secret, 140 for secret in [certificate_secret, private_key_secret,
139 intermediates_secret, pkp_secret]: 141 intermediates_secret, pkp_secret]:
140 if secret and secret.secret_ref: 142 if secret and secret.secret_ref:
@@ -183,7 +185,7 @@ class CertManager(cert_manager.CertManager):
183 url=resource_ref 185 url=resource_ref
184 ) 186 )
185 return Cert(cert_container) 187 return Cert(cert_container)
186 except Exception: 188 except barbican_exc.HTTPClientError:
187 with excutils.save_and_reraise_exception(): 189 with excutils.save_and_reraise_exception():
188 LOG.exception(_LE("Error getting {0}").format(cert_ref)) 190 LOG.exception(_LE("Error getting {0}").format(cert_ref))
189 191
@@ -209,7 +211,7 @@ class CertManager(cert_manager.CertManager):
209 certificate_container.private_key_passphrase.delete() 211 certificate_container.private_key_passphrase.delete()
210 certificate_container.private_key.delete() 212 certificate_container.private_key.delete()
211 certificate_container.delete() 213 certificate_container.delete()
212 except Exception: 214 except barbican_exc.HTTPClientError:
213 with excutils.save_and_reraise_exception(): 215 with excutils.save_and_reraise_exception():
214 LOG.exception(_LE( 216 LOG.exception(_LE(
215 "Error recursively deleting certificate container {0}" 217 "Error recursively deleting certificate container {0}"
diff --git a/magnum/common/clients.py b/magnum/common/clients.py
index ad980ba..29dac42 100644
--- a/magnum/common/clients.py
+++ b/magnum/common/clients.py
@@ -165,9 +165,10 @@ class OpenStackClients(object):
165 'insecure': self._get_client_option('nova', 'insecure') 165 'insecure': self._get_client_option('nova', 'insecure')
166 } 166 }
167 167
168 session = self.keystone().session
168 self._nova = novaclient.Client(novaclient_version, 169 self._nova = novaclient.Client(novaclient_version,
169 auth_token=self.auth_token, **args) 170 session=session,
170 self._nova.client.management_url = endpoint 171 endpoint_override=endpoint, **args)
171 return self._nova 172 return self._nova
172 173
173 @exception.wrap_keystone_exception 174 @exception.wrap_keystone_exception
diff --git a/magnum/common/config.py b/magnum/common/config.py
index f9f54e6..c846453 100644
--- a/magnum/common/config.py
+++ b/magnum/common/config.py
@@ -15,20 +15,22 @@
15# License for the specific language governing permissions and limitations 15# License for the specific language governing permissions and limitations
16# under the License. 16# under the License.
17 17
18from oslo_config import cfg
19from oslo_middleware import cors 18from oslo_middleware import cors
20 19
21from magnum.common import rpc 20from magnum.common import rpc
21import magnum.conf
22from magnum import version 22from magnum import version
23 23
24CONF = magnum.conf.CONF
25
24 26
25def parse_args(argv, default_config_files=None): 27def parse_args(argv, default_config_files=None):
26 rpc.set_defaults(control_exchange='magnum') 28 rpc.set_defaults(control_exchange='magnum')
27 cfg.CONF(argv[1:], 29 CONF(argv[1:],
28 project='magnum', 30 project='magnum',
29 version=version.version_info.release_string(), 31 version=version.version_info.release_string(),
30 default_config_files=default_config_files) 32 default_config_files=default_config_files)
31 rpc.init(cfg.CONF) 33 rpc.init(CONF)
32 34
33 35
34def set_config_defaults(): 36def set_config_defaults():
diff --git a/magnum/common/context.py b/magnum/common/context.py
index fc943ce..7a4e4ed 100644
--- a/magnum/common/context.py
+++ b/magnum/common/context.py
@@ -44,7 +44,8 @@ class RequestContext(context.RequestContext):
44 is_admin=is_admin, 44 is_admin=is_admin,
45 read_only=read_only, 45 read_only=read_only,
46 show_deleted=show_deleted, 46 show_deleted=show_deleted,
47 request_id=request_id) 47 request_id=request_id,
48 roles=roles)
48 49
49 self.user_name = user_name 50 self.user_name = user_name
50 self.user_id = user_id 51 self.user_id = user_id
@@ -54,7 +55,6 @@ class RequestContext(context.RequestContext):
54 self.domain_name = domain_name 55 self.domain_name = domain_name
55 self.user_domain_id = user_domain_id 56 self.user_domain_id = user_domain_id
56 self.user_domain_name = user_domain_name 57 self.user_domain_name = user_domain_name
57 self.roles = roles
58 self.auth_url = auth_url 58 self.auth_url = auth_url
59 self.auth_token_info = auth_token_info 59 self.auth_token_info = auth_token_info
60 self.trust_id = trust_id 60 self.trust_id = trust_id
@@ -142,3 +142,13 @@ def set_ctx(new_ctx):
142 if new_ctx: 142 if new_ctx:
143 setattr(_CTX_STORE, _CTX_KEY, new_ctx) 143 setattr(_CTX_STORE, _CTX_KEY, new_ctx)
144 setattr(context._request_store, 'context', new_ctx) 144 setattr(context._request_store, 'context', new_ctx)
145
146
147def get_admin_context(read_deleted="no"):
148 # NOTE(tovin07): This method should only be used when an admin context is
149 # necessary for the entirety of the context lifetime.
150 return RequestContext(user_id=None,
151 project_id=None,
152 is_admin=True,
153 read_deleted=read_deleted,
154 overwrite=False)
diff --git a/magnum/common/exception.py b/magnum/common/exception.py
index db6e94f..d398649 100644
--- a/magnum/common/exception.py
+++ b/magnum/common/exception.py
@@ -260,7 +260,7 @@ class NotSupported(MagnumException):
260 code = 400 260 code = 400
261 261
262 262
263class ClusterTypeNotSupported(MagnumException): 263class ClusterTypeNotSupported(NotSupported):
264 message = _("Cluster type (%(server_type)s, %(os)s, %(coe)s)" 264 message = _("Cluster type (%(server_type)s, %(os)s, %(coe)s)"
265 " not supported.") 265 " not supported.")
266 266
@@ -383,6 +383,14 @@ class QuotaAlreadyExists(Conflict):
383 "for resource %(resource)s.") 383 "for resource %(resource)s.")
384 384
385 385
386class QuotaNotFound(ResourceNotFound):
387 message = _("Quota could not be found: %(msg)s")
388
389
390class ResourceLimitExceeded(NotAuthorized):
391 message = _('Resource limit exceeded: %(msg)s')
392
393
386class RegionsListFailed(MagnumException): 394class RegionsListFailed(MagnumException):
387 message = _("Failed to list regions.") 395 message = _("Failed to list regions.")
388 396
diff --git a/magnum/common/profiler.py b/magnum/common/profiler.py
new file mode 100644
index 0000000..a529df4
--- /dev/null
+++ b/magnum/common/profiler.py
@@ -0,0 +1,86 @@
1# Copyright 2017 Fujitsu 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###
17# This code is taken from nova. Goal is minimal modification.
18###
19
20from oslo_log import log as logging
21from oslo_utils import importutils
22import webob.dec
23
24from magnum.common import context
25import magnum.conf
26from magnum.i18n import _LI
27
28profiler = importutils.try_import("osprofiler.profiler")
29profiler_initializer = importutils.try_import("osprofiler.initializer")
30profiler_web = importutils.try_import("osprofiler.web")
31
32
33CONF = magnum.conf.CONF
34
35LOG = logging.getLogger(__name__)
36
37
38class WsgiMiddleware(object):
39
40 def __init__(self, application, **kwargs):
41 self.application = application
42
43 @classmethod
44 def factory(cls, global_conf, **local_conf):
45 if profiler_web:
46 return profiler_web.WsgiMiddleware.factory(global_conf,
47 **local_conf)
48
49 def filter_(app):
50 return cls(app, **local_conf)
51
52 return filter_
53
54 @webob.dec.wsgify
55 def __call__(self, request):
56 return request.get_response(self.application)
57
58
59def setup(binary, host):
60 if CONF.profiler.enabled:
61 profiler_initializer.init_from_conf(
62 conf=CONF,
63 context=context.get_admin_context().to_dict(),
64 project="magnum",
65 service=binary,
66 host=host)
67 LOG.info(_LI("OSprofiler is enabled."))
68
69
70def trace_cls(name, **kwargs):
71 """Wrap the OSprofiler trace_cls.
72
73 Wrap the OSprofiler trace_cls decorator so that it will not try to
74 patch the class unless OSprofiler is present.
75
76 :param name: The name of action. For example, wsgi, rpc, db, ...
77 :param kwargs: Any other keyword args used by profiler.trace_cls
78 """
79
80 def decorator(cls):
81 if profiler and 'profiler' in CONF:
82 trace_decorator = profiler.trace_cls(name, kwargs)
83 return trace_decorator(cls)
84 return cls
85
86 return decorator
diff --git a/magnum/common/rpc.py b/magnum/common/rpc.py
index 22b2bc9..a864160 100644
--- a/magnum/common/rpc.py
+++ b/magnum/common/rpc.py
@@ -32,11 +32,13 @@ import socket
32 32
33import oslo_messaging as messaging 33import oslo_messaging as messaging
34from oslo_serialization import jsonutils 34from oslo_serialization import jsonutils
35from oslo_utils import importutils
35 36
36from magnum.common import context as magnum_context 37from magnum.common import context as magnum_context
37from magnum.common import exception 38from magnum.common import exception
38import magnum.conf 39import magnum.conf
39 40
41profiler = importutils.try_import("osprofiler.profiler")
40 42
41CONF = magnum.conf.CONF 43CONF = magnum.conf.CONF
42TRANSPORT = None 44TRANSPORT = None
@@ -121,22 +123,56 @@ class RequestContextSerializer(messaging.Serializer):
121 return magnum_context.RequestContext.from_dict(context) 123 return magnum_context.RequestContext.from_dict(context)
122 124
123 125
126class ProfilerRequestContextSerializer(RequestContextSerializer):
127 def serialize_context(self, context):
128 _context = super(ProfilerRequestContextSerializer,
129 self).serialize_context(context)
130
131 prof = profiler.get()
132 if prof:
133 trace_info = {
134 "hmac_key": prof.hmac_key,
135 "base_id": prof.get_base_id(),
136 "parent_id": prof.get_id()
137 }
138 _context.update({"trace_info": trace_info})
139
140 return _context
141
142 def deserialize_context(self, context):
143 trace_info = context.pop("trace_info", None)
144 if trace_info:
145 profiler.init(**trace_info)
146
147 return super(ProfilerRequestContextSerializer,
148 self).deserialize_context(context)
149
150
124def get_transport_url(url_str=None): 151def get_transport_url(url_str=None):
125 return messaging.TransportURL.parse(CONF, url_str, TRANSPORT_ALIASES) 152 return messaging.TransportURL.parse(CONF, url_str, TRANSPORT_ALIASES)
126 153
127 154
128def get_client(target, version_cap=None, serializer=None): 155def get_client(target, version_cap=None, serializer=None, timeout=None):
129 assert TRANSPORT is not None 156 assert TRANSPORT is not None
130 serializer = RequestContextSerializer(serializer) 157 if profiler:
158 serializer = ProfilerRequestContextSerializer(serializer)
159 else:
160 serializer = RequestContextSerializer(serializer)
161
131 return messaging.RPCClient(TRANSPORT, 162 return messaging.RPCClient(TRANSPORT,
132 target, 163 target,
133 version_cap=version_cap, 164 version_cap=version_cap,
134 serializer=serializer) 165 serializer=serializer,
166 timeout=timeout)
135 167
136 168
137def get_server(target, endpoints, serializer=None): 169def get_server(target, endpoints, serializer=None):
138 assert TRANSPORT is not None 170 assert TRANSPORT is not None
139 serializer = RequestContextSerializer(serializer) 171 if profiler:
172 serializer = ProfilerRequestContextSerializer(serializer)
173 else:
174 serializer = RequestContextSerializer(serializer)
175
140 return messaging.get_rpc_server(TRANSPORT, 176 return messaging.get_rpc_server(TRANSPORT,
141 target, 177 target,
142 endpoints, 178 endpoints,
diff --git a/magnum/common/rpc_service.py b/magnum/common/rpc_service.py
index 7e03c59..26d4f33 100644
--- a/magnum/common/rpc_service.py
+++ b/magnum/common/rpc_service.py
@@ -14,24 +14,17 @@
14 14
15"""Common RPC service and API tools for Magnum.""" 15"""Common RPC service and API tools for Magnum."""
16 16
17import eventlet
18import oslo_messaging as messaging 17import oslo_messaging as messaging
19from oslo_service import service 18from oslo_service import service
19from oslo_utils import importutils
20 20
21from magnum.common import profiler
21from magnum.common import rpc 22from magnum.common import rpc
22import magnum.conf 23import magnum.conf
23from magnum.objects import base as objects_base 24from magnum.objects import base as objects_base
24from magnum.service import periodic 25from magnum.service import periodic
25from magnum.servicegroup import magnum_service_periodic as servicegroup 26from magnum.servicegroup import magnum_service_periodic as servicegroup
26 27
27
28# NOTE(paulczar):
29# Ubuntu 14.04 forces librabbitmq when kombu is used
30# Unfortunately it forces a version that has a crash
31# bug. Calling eventlet.monkey_patch() tells kombu
32# to use libamqp instead.
33eventlet.monkey_patch()
34
35# NOTE(asalkeld): 28# NOTE(asalkeld):
36# The magnum.openstack.common.rpc entries are for compatibility 29# The magnum.openstack.common.rpc entries are for compatibility
37# with devstack rpc_backend configuration values. 30# with devstack rpc_backend configuration values.
@@ -41,15 +34,26 @@ TRANSPORT_ALIASES = {
41 'magnum.openstack.common.rpc.impl_zmq': 'zmq', 34 'magnum.openstack.common.rpc.impl_zmq': 'zmq',
42} 35}
43 36
37osprofiler = importutils.try_import("osprofiler.profiler")
38
44CONF = magnum.conf.CONF 39CONF = magnum.conf.CONF
45 40
46 41
42def _init_serializer():
43 serializer = rpc.RequestContextSerializer(
44 objects_base.MagnumObjectSerializer())
45 if osprofiler:
46 serializer = rpc.ProfilerRequestContextSerializer(serializer)
47 else:
48 serializer = rpc.RequestContextSerializer(serializer)
49 return serializer
50
51
47class Service(service.Service): 52class Service(service.Service):
48 53
49 def __init__(self, topic, server, handlers, binary): 54 def __init__(self, topic, server, handlers, binary):
50 super(Service, self).__init__() 55 super(Service, self).__init__()
51 serializer = rpc.RequestContextSerializer( 56 serializer = _init_serializer()
52 objects_base.MagnumObjectSerializer())
53 transport = messaging.get_transport(CONF, 57 transport = messaging.get_transport(CONF,
54 aliases=TRANSPORT_ALIASES) 58 aliases=TRANSPORT_ALIASES)
55 # TODO(asalkeld) add support for version='x.y' 59 # TODO(asalkeld) add support for version='x.y'
@@ -57,6 +61,7 @@ class Service(service.Service):
57 self._server = messaging.get_rpc_server(transport, target, handlers, 61 self._server = messaging.get_rpc_server(transport, target, handlers,
58 serializer=serializer) 62 serializer=serializer)
59 self.binary = binary 63 self.binary = binary
64 profiler.setup(binary, CONF.host)
60 65
61 def start(self): 66 def start(self):
62 # NOTE(suro-patz): The parent class has created a threadgroup, already 67 # NOTE(suro-patz): The parent class has created a threadgroup, already
@@ -80,8 +85,7 @@ class Service(service.Service):
80class API(object): 85class API(object):
81 def __init__(self, transport=None, context=None, topic=None, server=None, 86 def __init__(self, transport=None, context=None, topic=None, server=None,
82 timeout=None): 87 timeout=None):
83 serializer = rpc.RequestContextSerializer( 88 serializer = _init_serializer()
84 objects_base.MagnumObjectSerializer())
85 if transport is None: 89 if transport is None:
86 exmods = rpc.get_allowed_exmods() 90 exmods = rpc.get_allowed_exmods()
87 transport = messaging.get_transport(CONF, 91 transport = messaging.get_transport(CONF,
diff --git a/magnum/conductor/api.py b/magnum/conductor/api.py
index 4d70616..9295f31 100644
--- a/magnum/conductor/api.py
+++ b/magnum/conductor/api.py
@@ -12,6 +12,7 @@
12 12
13"""API for interfacing with Magnum Backend.""" 13"""API for interfacing with Magnum Backend."""
14 14
15from magnum.common import profiler
15from magnum.common import rpc_service 16from magnum.common import rpc_service
16import magnum.conf 17import magnum.conf
17 18
@@ -22,6 +23,7 @@ CONF = magnum.conf.CONF
22# API to trigger operations on the conductors 23# API to trigger operations on the conductors
23 24
24 25
26@profiler.trace_cls("rpc")
25class API(rpc_service.API): 27class API(rpc_service.API):
26 def __init__(self, transport=None, context=None, topic=None): 28 def __init__(self, transport=None, context=None, topic=None):
27 super(API, self).__init__(transport, context, 29 super(API, self).__init__(transport, context,
@@ -58,6 +60,9 @@ class API(rpc_service.API):
58 def get_ca_certificate(self, cluster): 60 def get_ca_certificate(self, cluster):
59 return self._call('get_ca_certificate', cluster=cluster) 61 return self._call('get_ca_certificate', cluster=cluster)
60 62
63 def rotate_ca_certificate(self, cluster):
64 return self._call('rotate_ca_certificate', cluster=cluster)
65
61 # Versioned Objects indirection API 66 # Versioned Objects indirection API
62 67
63 def object_class_action(self, context, objname, objmethod, objver, 68 def object_class_action(self, context, objname, objmethod, objver,
@@ -78,6 +83,7 @@ class API(rpc_service.API):
78 target_version=target_version) 83 target_version=target_version)
79 84
80 85
86@profiler.trace_cls("rpc")
81class ListenerAPI(rpc_service.API): 87class ListenerAPI(rpc_service.API):
82 def __init__(self, context=None, topic=None, server=None, timeout=None): 88 def __init__(self, context=None, topic=None, server=None, timeout=None):
83 super(ListenerAPI, self).__init__(context=context, topic=topic, 89 super(ListenerAPI, self).__init__(context=context, topic=topic,
diff --git a/magnum/conductor/handlers/ca_conductor.py b/magnum/conductor/handlers/ca_conductor.py
index da96979..eee89c9 100644
--- a/magnum/conductor/handlers/ca_conductor.py
+++ b/magnum/conductor/handlers/ca_conductor.py
@@ -15,11 +15,14 @@
15 15
16from oslo_log import log as logging 16from oslo_log import log as logging
17 17
18from magnum.common import profiler
18from magnum.conductor.handlers.common import cert_manager 19from magnum.conductor.handlers.common import cert_manager
20from magnum.drivers.common import driver
19from magnum import objects 21from magnum import objects
20LOG = logging.getLogger(__name__) 22LOG = logging.getLogger(__name__)
21 23
22 24
25@profiler.trace_cls("rpc")
23class Handler(object): 26class Handler(object):
24 """Magnum CA RPC handler. 27 """Magnum CA RPC handler.
25 28
@@ -45,3 +48,8 @@ class Handler(object):
45 certificate = objects.Certificate.from_object_cluster(cluster) 48 certificate = objects.Certificate.from_object_cluster(cluster)
46 certificate.pem = ca_cert.get_certificate() 49 certificate.pem = ca_cert.get_certificate()
47 return certificate 50 return certificate
51
52 def rotate_ca_certificate(self, context, cluster):
53 cluster_driver = driver.Driver.get_driver_for_cluster(context,
54 cluster)
55 cluster_driver.rotate_ca_certificate(context, cluster)
diff --git a/magnum/conductor/handlers/cluster_conductor.py b/magnum/conductor/handlers/cluster_conductor.py
index c531e78..2e1ec9d 100644
--- a/magnum/conductor/handlers/cluster_conductor.py
+++ b/magnum/conductor/handlers/cluster_conductor.py
@@ -19,6 +19,7 @@ import six
19 19
20from magnum.common import clients 20from magnum.common import clients
21from magnum.common import exception 21from magnum.common import exception
22from magnum.common import profiler
22from magnum.conductor.handlers.common import cert_manager 23from magnum.conductor.handlers.common import cert_manager
23from magnum.conductor.handlers.common import trust_manager 24from magnum.conductor.handlers.common import trust_manager
24from magnum.conductor import scale_manager 25from magnum.conductor import scale_manager
@@ -35,6 +36,7 @@ CONF = magnum.conf.CONF
35LOG = logging.getLogger(__name__) 36LOG = logging.getLogger(__name__)
36 37
37 38
39@profiler.trace_cls("rpc")
38class Handler(object): 40class Handler(object):
39 41
40 def __init__(self): 42 def __init__(self):
diff --git a/magnum/conductor/handlers/conductor_listener.py b/magnum/conductor/handlers/conductor_listener.py
index d343caa..fd7710a 100644
--- a/magnum/conductor/handlers/conductor_listener.py
+++ b/magnum/conductor/handlers/conductor_listener.py
@@ -10,7 +10,10 @@
10# See the License for the specific language governing permissions and 10# See the License for the specific language governing permissions and
11# limitations under the License. 11# limitations under the License.
12 12
13from magnum.common import profiler
13 14
15
16@profiler.trace_cls("rpc")
14class Handler(object): 17class Handler(object):
15 '''Listen on an AMQP queue named for the conductor. 18 '''Listen on an AMQP queue named for the conductor.
16 19
diff --git a/magnum/conductor/handlers/indirection_api.py b/magnum/conductor/handlers/indirection_api.py
index 6a76ed0..1671c98 100644
--- a/magnum/conductor/handlers/indirection_api.py
+++ b/magnum/conductor/handlers/indirection_api.py
@@ -12,9 +12,11 @@
12 12
13import oslo_messaging as messaging 13import oslo_messaging as messaging
14 14
15from magnum.common import profiler
15from magnum.objects import base 16from magnum.objects import base
16 17
17 18
19@profiler.trace_cls("rpc")
18class Handler(object): 20class Handler(object):
19 "Indirection API callbacks" 21 "Indirection API callbacks"
20 22
diff --git a/magnum/conductor/k8s_api.py b/magnum/conductor/k8s_api.py
index aa036ff..78e5ffe 100644
--- a/magnum/conductor/k8s_api.py
+++ b/magnum/conductor/k8s_api.py
@@ -14,8 +14,9 @@
14 14
15import tempfile 15import tempfile
16 16
17from k8sclient.client import api_client 17from kubernetes import client as k8s_config
18from k8sclient.client.apis import apiv_api 18from kubernetes.client import api_client
19from kubernetes.client.apis import core_v1_api
19from oslo_log import log as logging 20from oslo_log import log as logging
20 21
21from magnum.conductor.handlers.common.cert_manager import create_client_files 22from magnum.conductor.handlers.common.cert_manager import create_client_files
@@ -24,7 +25,7 @@ from magnum.i18n import _LE
24LOG = logging.getLogger(__name__) 25LOG = logging.getLogger(__name__)
25 26
26 27
27class K8sAPI(apiv_api.ApivApi): 28class K8sAPI(core_v1_api.CoreV1Api):
28 29
29 def _create_temp_file_with_content(self, content): 30 def _create_temp_file_with_content(self, content):
30 """Creates temp file and write content to the file. 31 """Creates temp file and write content to the file.
@@ -50,11 +51,14 @@ class K8sAPI(apiv_api.ApivApi):
50 (self.ca_file, self.key_file, 51 (self.ca_file, self.key_file,
51 self.cert_file) = create_client_files(cluster, context) 52 self.cert_file) = create_client_files(cluster, context)
52 53
54 config = k8s_config.ConfigurationObject()
55 config.host = cluster.api_address
56 config.ssl_ca_cert = self.ca_file.name
57 config.cert_file = self.cert_file.name
58 config.key_file = self.key_file.name
59
53 # build a connection with Kubernetes master 60 # build a connection with Kubernetes master
54 client = api_client.ApiClient(cluster.api_address, 61 client = api_client.ApiClient(config=config)
55 key_file=self.key_file.name,
56 cert_file=self.cert_file.name,
57 ca_certs=self.ca_file.name)
58 62
59 super(K8sAPI, self).__init__(client) 63 super(K8sAPI, self).__init__(client)
60 64
diff --git a/magnum/conductor/monitors.py b/magnum/conductor/monitors.py
index 7cc4a92..5360204 100644
--- a/magnum/conductor/monitors.py
+++ b/magnum/conductor/monitors.py
@@ -16,24 +16,19 @@
16import abc 16import abc
17 17
18from oslo_log import log 18from oslo_log import log
19from oslo_utils import importutils
20import six 19import six
21 20
21from magnum.common import profiler
22import magnum.conf 22import magnum.conf
23from magnum.objects import fields 23from magnum.drivers.common.driver import Driver
24 24
25 25
26LOG = log.getLogger(__name__) 26LOG = log.getLogger(__name__)
27 27
28CONF = magnum.conf.CONF 28CONF = magnum.conf.CONF
29 29
30COE_CLASS_PATH = {
31 fields.ClusterType.SWARM: 'magnum.conductor.swarm_monitor.SwarmMonitor',
32 fields.ClusterType.KUBERNETES: 'magnum.conductor.k8s_monitor.K8sMonitor',
33 fields.ClusterType.MESOS: 'magnum.conductor.mesos_monitor.MesosMonitor'
34}
35
36 30
31@profiler.trace_cls("rpc")
37@six.add_metaclass(abc.ABCMeta) 32@six.add_metaclass(abc.ABCMeta)
38class MonitorBase(object): 33class MonitorBase(object):
39 34
@@ -62,10 +57,10 @@ class MonitorBase(object):
62 57
63 58
64def create_monitor(context, cluster): 59def create_monitor(context, cluster):
65 if cluster.cluster_template.coe in COE_CLASS_PATH: 60 cluster_driver = Driver.get_driver_for_cluster(context, cluster)
66 coe_cls = importutils.import_class( 61 monitor = cluster_driver.get_monitor(context, cluster)
67 COE_CLASS_PATH[cluster.cluster_template.coe]) 62 if monitor:
68 return coe_cls(context, cluster) 63 return monitor
69 64
70 LOG.debug("Cannot create monitor with cluster type '%s'", 65 LOG.debug("Cannot create monitor with cluster type '%s'",
71 cluster.cluster_template.coe) 66 cluster.cluster_template.coe)
diff --git a/magnum/conductor/scale_manager.py b/magnum/conductor/scale_manager.py
index b2056d4..d78c035 100644
--- a/magnum/conductor/scale_manager.py
+++ b/magnum/conductor/scale_manager.py
@@ -13,11 +13,10 @@
13# under the License. 13# under the License.
14 14
15import abc 15import abc
16from marathon import MarathonClient
17from oslo_log import log as logging 16from oslo_log import log as logging
18 17
19from magnum.common import exception 18from magnum.common import exception
20from magnum.conductor import k8s_api as k8s 19from magnum.drivers.common.driver import Driver
21from magnum.i18n import _ 20from magnum.i18n import _
22from magnum.i18n import _LI 21from magnum.i18n import _LI
23from magnum.i18n import _LW 22from magnum.i18n import _LW
@@ -28,13 +27,9 @@ LOG = logging.getLogger(__name__)
28 27
29 28
30def get_scale_manager(context, osclient, cluster): 29def get_scale_manager(context, osclient, cluster):
31 manager = None 30 cluster_driver = Driver.get_driver_for_cluster(context, cluster)
32 coe = cluster.cluster_template.coe 31 manager = cluster_driver.get_scale_manager(context, osclient, cluster)
33 if coe == 'kubernetes': 32 if not manager:
34 manager = K8sScaleManager(context, osclient, cluster)
35 elif coe == 'mesos':
36 manager = MesosScaleManager(context, osclient, cluster)
37 else:
38 LOG.warning(_LW( 33 LOG.warning(_LW(
39 "Currently only kubernetes and mesos cluster scale manager " 34 "Currently only kubernetes and mesos cluster scale manager "
40 "are available")) 35 "are available"))
@@ -94,41 +89,3 @@ class ScaleManager(object):
94 def _get_hosts_with_container(self, context, cluster): 89 def _get_hosts_with_container(self, context, cluster):
95 """Return the hosts with container running on them.""" 90 """Return the hosts with container running on them."""
96 pass 91 pass
97
98
99class K8sScaleManager(ScaleManager):
100
101 def __init__(self, context, osclient, cluster):
102 super(K8sScaleManager, self).__init__(context, osclient, cluster)
103
104 def _get_hosts_with_container(self, context, cluster):
105 k8s_api = k8s.create_k8s_api(self.context, cluster)
106 hosts = set()
107 for pod in k8s_api.list_namespaced_pod(namespace='default').items:
108 hosts.add(pod.spec.node_name)
109
110 return hosts
111
112
113class MesosScaleManager(ScaleManager):
114 """When scaling a mesos cluster, MesosScaleManager will inspect the
115
116 nodes and find out those with containers on them. Thus we can
117 ask Heat to delete the nodes without containers. Note that this
118 is a best effort basis -- Magnum doesn't have any synchronization
119 with Marathon, so while Magnum is checking for the containers to
120 choose nodes to remove, new containers can be deployed on the
121 nodes to be removed.
122 """
123
124 def __init__(self, context, osclient, cluster):
125 super(MesosScaleManager, self).__init__(context, osclient, cluster)
126
127 def _get_hosts_with_container(self, context, cluster):
128 marathon_client = MarathonClient(
129 'http://' + cluster.api_address + ':8080')
130 hosts = set()
131 for task in marathon_client.list_tasks():
132 hosts.add(task.host)
133
134 return hosts
diff --git a/magnum/conf/__init__.py b/magnum/conf/__init__.py
index 8c0f6f6..35b4cb0 100644
--- a/magnum/conf/__init__.py
+++ b/magnum/conf/__init__.py
@@ -33,6 +33,8 @@ from magnum.conf import magnum_client
33from magnum.conf import neutron 33from magnum.conf import neutron
34from magnum.conf import nova 34from magnum.conf import nova
35from magnum.conf import paths 35from magnum.conf import paths
36from magnum.conf import profiler
37from magnum.conf import quota
36from magnum.conf import rpc 38from magnum.conf import rpc
37from magnum.conf import services 39from magnum.conf import services
38from magnum.conf import trust 40from magnum.conf import trust
@@ -59,8 +61,10 @@ magnum_client.register_opts(CONF)
59neutron.register_opts(CONF) 61neutron.register_opts(CONF)
60nova.register_opts(CONF) 62nova.register_opts(CONF)
61paths.register_opts(CONF) 63paths.register_opts(CONF)
64quota.register_opts(CONF)
62rpc.register_opts(CONF) 65rpc.register_opts(CONF)
63services.register_opts(CONF) 66services.register_opts(CONF)
64trust.register_opts(CONF) 67trust.register_opts(CONF)
65utils.register_opts(CONF) 68utils.register_opts(CONF)
66x509.register_opts(CONF) 69x509.register_opts(CONF)
70profiler.register_opts(CONF)
diff --git a/magnum/conf/profiler.py b/magnum/conf/profiler.py
new file mode 100644
index 0000000..d0f68c0
--- /dev/null
+++ b/magnum/conf/profiler.py
@@ -0,0 +1,27 @@
1# Licensed under the Apache License, Version 2.0 (the "License"); you may not
2# use this file except in compliance with the License. You may obtain a copy
3# 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,
9# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10# See the License for the specific language governing permissions and
11# limitations under the License.
12
13from oslo_utils import importutils
14
15
16profiler_opts = importutils.try_import('osprofiler.opts')
17
18
19def register_opts(conf):
20 if profiler_opts:
21 profiler_opts.set_defaults(conf)
22
23
24def list_opts():
25 return {
26 profiler_opts._profiler_opt_group: profiler_opts._PROFILER_OPTS
27 }
diff --git a/magnum/conf/quota.py b/magnum/conf/quota.py
new file mode 100644
index 0000000..d160976
--- /dev/null
+++ b/magnum/conf/quota.py
@@ -0,0 +1,38 @@
1# Licensed under the Apache License, Version 2.0 (the "License"); you may not
2# use this file except in compliance with the License. You may obtain a copy
3# 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,
9# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10# See the License for the specific language governing permissions and
11# limitations under the License.
12
13from oslo_config import cfg
14
15from magnum.i18n import _
16
17quotas_group = cfg.OptGroup(name='quotas',
18 title='Options for quota configuration')
19
20quotas_def_opts = [
21 cfg.IntOpt('max_clusters_per_project',
22 default=20,
23 help=_('Max number of clusters allowed per project. Admin can '
24 'override this default quota for a project by setting '
25 'explicit limit in quotas DB table (using /quotas REST '
26 'API endpoint).')),
27]
28
29
30def register_opts(conf):
31 conf.register_group(quotas_group)
32 conf.register_opts(quotas_def_opts, group=quotas_group)
33
34
35def list_opts():
36 return {
37 quotas_group: quotas_def_opts
38 }
diff --git a/magnum/db/api.py b/magnum/db/api.py
index 19b70e3..b2878de 100644
--- a/magnum/db/api.py
+++ b/magnum/db/api.py
@@ -21,6 +21,8 @@ from oslo_config import cfg
21from oslo_db import api as db_api 21from oslo_db import api as db_api
22import six 22import six
23 23
24from magnum.common import profiler
25
24 26
25_BACKEND_MAPPING = {'sqlalchemy': 'magnum.db.sqlalchemy.api'} 27_BACKEND_MAPPING = {'sqlalchemy': 'magnum.db.sqlalchemy.api'}
26IMPL = db_api.DBAPI.from_config(cfg.CONF, backend_mapping=_BACKEND_MAPPING, 28IMPL = db_api.DBAPI.from_config(cfg.CONF, backend_mapping=_BACKEND_MAPPING,
@@ -32,6 +34,7 @@ def get_instance():
32 return IMPL 34 return IMPL
33 35
34 36
37@profiler.trace_cls("db")
35@six.add_metaclass(abc.ABCMeta) 38@six.add_metaclass(abc.ABCMeta)
36class Connection(object): 39class Connection(object):
37 """Base class for storage system connections.""" 40 """Base class for storage system connections."""
@@ -107,6 +110,24 @@ class Connection(object):
107 """ 110 """
108 111
109 @abc.abstractmethod 112 @abc.abstractmethod
113 def get_cluster_stats(self, context, project_id):
114 """Return clusters stats for the given project.
115
116 :param context: The security context
117 :param project_id: The project id.
118 :returns: clusters, nodes count.
119 """
120
121 @abc.abstractmethod
122 def get_cluster_count_all(self, context, filters=None):
123 """Get count of matching clusters.
124
125 :param context: The security context
126 :param filters: Filters to apply. Defaults to None.
127 :returns: Count of matching clusters.
128 """
129
130 @abc.abstractmethod
110 def destroy_cluster(self, cluster_id): 131 def destroy_cluster(self, cluster_id):
111 """Destroy a cluster and all associated interfaces. 132 """Destroy a cluster and all associated interfaces.
112 133
@@ -295,10 +316,9 @@ class Connection(object):
295 """ 316 """
296 317
297 @abc.abstractmethod 318 @abc.abstractmethod
298 def get_magnum_service_by_host_and_binary(self, context, host, binary): 319 def get_magnum_service_by_host_and_binary(self, host, binary):
299 """Return a magnum_service record. 320 """Return a magnum_service record.
300 321
301 :param context: The security context
302 :param host: The host where the binary is located. 322 :param host: The host where the binary is located.
303 :param binary: The name of the binary. 323 :param binary: The name of the binary.
304 :returns: A magnum_service record. 324 :returns: A magnum_service record.
@@ -314,14 +334,13 @@ class Connection(object):
314 """ 334 """
315 335
316 @abc.abstractmethod 336 @abc.abstractmethod
317 def get_magnum_service_list(self, context, disabled=None, limit=None, 337 def get_magnum_service_list(self, disabled=None, limit=None,
318 marker=None, sort_key=None, sort_dir=None): 338 marker=None, sort_key=None, sort_dir=None):
319 """Get matching magnum_service records. 339 """Get matching magnum_service records.
320 340
321 Return a list of the specified columns for all magnum_services 341 Return a list of the specified columns for all magnum_services
322 those match the specified filters. 342 those match the specified filters.
323 343
324 :param context: The security context
325 :param disabled: Filters disbaled services. Defaults to None. 344 :param disabled: Filters disbaled services. Defaults to None.
326 :param limit: Maximum number of magnum_services to return. 345 :param limit: Maximum number of magnum_services to return.
327 :param marker: the last item of the previous page; we return the next 346 :param marker: the last item of the previous page; we return the next
@@ -352,6 +371,62 @@ class Connection(object):
352 """ 371 """
353 372
354 @abc.abstractmethod 373 @abc.abstractmethod
374 def update_quota(self, project_id, values):
375 """Update quota record.
376
377 :param project_id: The project id.
378 :param values: A dict containing several items used to identify
379 and track quota for a resource in a project.
380
381 ::
382
383 {
384 'id': uuidutils.generate_uuid(),
385 'project_id': 'fake_project',
386 'resource': 'fake_resource',
387 'hard_limit': 'fake_hardlimit',
388 }
389 :returns: A quota record.
390 """
391
392 @abc.abstractmethod
393 def delete_quota(self, project_id, resource):
394 """Delete a quota.
395
396 :param project_id: Project id.
397 :param resource: resource name.
398 """
399
400 @abc.abstractmethod
401 def get_quota_by_id(self, context, quota_id):
402 """Return a quota.
403
404 :param context: The security context
405 :param quota_id: The id of a quota.
406 :returns: A quota.
407 """
408
409 @abc.abstractmethod
410 def get_quota_list(self, context, filters=None, limit=None,
411 marker=None, sort_key=None, sort_dir=None):
412 """Get quota list.
413
414 Return a list of the specified columns for all quotas that match the
415 specified filters.
416
417 :param context: The security context
418 :param filters: Filters to apply. Defaults to None.
419
420 :param limit: Maximum number of clusters to return.
421 :param marker: the last item of the previous page; we return the next
422 result set.
423 :param sort_key: Attribute by which results should be sorted.
424 :param sort_dir: direction in which results should be sorted.
425 (asc, desc)
426 :returns: A list of tuples of the specified columns.
427 """
428
429 @abc.abstractmethod
355 def quota_get_all_by_project_id(self, project_id): 430 def quota_get_all_by_project_id(self, project_id):
356 """Gets Quota record for all the resources in a project. 431 """Gets Quota record for all the resources in a project.
357 432
@@ -359,3 +434,13 @@ class Connection(object):
359 434
360 :returns: Quota record for all resources in a project. 435 :returns: Quota record for all resources in a project.
361 """ 436 """
437
438 @abc.abstractmethod
439 def get_quota_by_project_id_resource(self, project_id, resource):
440 """Gets quota record for the given quota id.
441
442 :param project_id: project id.
443 :param resource: resource name.
444
445 :returns: Quota record.
446 """
diff --git a/magnum/db/sqlalchemy/api.py b/magnum/db/sqlalchemy/api.py
index 4404758..fd1857b 100644
--- a/magnum/db/sqlalchemy/api.py
+++ b/magnum/db/sqlalchemy/api.py
@@ -17,11 +17,14 @@
17from oslo_db import exception as db_exc 17from oslo_db import exception as db_exc
18from oslo_db.sqlalchemy import session as db_session 18from oslo_db.sqlalchemy import session as db_session
19from oslo_db.sqlalchemy import utils as db_utils 19from oslo_db.sqlalchemy import utils as db_utils
20from oslo_utils import importutils
20from oslo_utils import strutils 21from oslo_utils import strutils
21from oslo_utils import timeutils 22from oslo_utils import timeutils
22from oslo_utils import uuidutils 23from oslo_utils import uuidutils
24import sqlalchemy as sa
23from sqlalchemy.orm.exc import MultipleResultsFound 25from sqlalchemy.orm.exc import MultipleResultsFound
24from sqlalchemy.orm.exc import NoResultFound 26from sqlalchemy.orm.exc import NoResultFound
27from sqlalchemy.sql import func
25 28
26from magnum.common import exception 29from magnum.common import exception
27import magnum.conf 30import magnum.conf
@@ -29,6 +32,8 @@ from magnum.db import api
29from magnum.db.sqlalchemy import models 32from magnum.db.sqlalchemy import models
30from magnum.i18n import _ 33from magnum.i18n import _
31 34
35profiler_sqlalchemy = importutils.try_import('osprofiler.sqlalchemy')
36
32CONF = magnum.conf.CONF 37CONF = magnum.conf.CONF
33 38
34 39
@@ -39,6 +44,10 @@ def _create_facade_lazily():
39 global _FACADE 44 global _FACADE
40 if _FACADE is None: 45 if _FACADE is None:
41 _FACADE = db_session.EngineFacade.from_config(CONF) 46 _FACADE = db_session.EngineFacade.from_config(CONF)
47 if profiler_sqlalchemy:
48 if CONF.profiler.enabled and CONF.profiler.trace_sqlalchemy:
49 profiler_sqlalchemy.add_tracing(sa, _FACADE.get_engine(), "db")
50
42 return _FACADE 51 return _FACADE
43 52
44 53
@@ -190,6 +199,29 @@ class Connection(api.Connection):
190 except NoResultFound: 199 except NoResultFound:
191 raise exception.ClusterNotFound(cluster=cluster_uuid) 200 raise exception.ClusterNotFound(cluster=cluster_uuid)
192 201
202 def get_cluster_stats(self, context, project_id=None):
203 query = model_query(models.Cluster)
204 node_count_col = models.Cluster.node_count
205 master_count_col = models.Cluster.master_count
206 ncfunc = func.sum(node_count_col + master_count_col)
207
208 if project_id:
209 query = query.filter_by(project_id=project_id)
210 nquery = query.session.query(ncfunc.label("nodes")).filter_by(
211 project_id=project_id)
212 else:
213 nquery = query.session.query(ncfunc.label("nodes"))
214
215 clusters = query.count()
216 nodes = int(nquery.one()[0]) if nquery.one()[0] else 0
217 return clusters, nodes
218
219 def get_cluster_count_all(self, context, filters=None):
220 query = model_query(models.Cluster)
221 query = self._add_tenant_filters(context, query)
222 query = self._add_clusters_filters(query, filters)
223 return query.count()
224
193 def destroy_cluster(self, cluster_id): 225 def destroy_cluster(self, cluster_id):
194 session = get_session() 226 session = get_session()
195 with session.begin(): 227 with session.begin():
@@ -221,9 +253,6 @@ class Connection(api.Connection):
221 except NoResultFound: 253 except NoResultFound:
222 raise exception.ClusterNotFound(cluster=cluster_id) 254 raise exception.ClusterNotFound(cluster=cluster_id)
223 255
224 if 'provision_state' in values:
225 values['provision_updated_at'] = timeutils.utcnow()
226
227 ref.update(values) 256 ref.update(values)
228 return ref 257 return ref
229 258
@@ -425,9 +454,6 @@ class Connection(api.Connection):
425 except NoResultFound: 454 except NoResultFound:
426 raise exception.X509KeyPairNotFound(x509keypair=x509keypair_id) 455 raise exception.X509KeyPairNotFound(x509keypair=x509keypair_id)
427 456
428 if 'provision_state' in values:
429 values['provision_updated_at'] = timeutils.utcnow()
430
431 ref.update(values) 457 ref.update(values)
432 return ref 458 return ref
433 459
@@ -478,7 +504,7 @@ class Connection(api.Connection):
478 ref.update(values) 504 ref.update(values)
479 return ref 505 return ref
480 506
481 def get_magnum_service_by_host_and_binary(self, context, host, binary): 507 def get_magnum_service_by_host_and_binary(self, host, binary):
482 query = model_query(models.MagnumService) 508 query = model_query(models.MagnumService)
483 query = query.filter_by(host=host, binary=binary) 509 query = query.filter_by(host=host, binary=binary)
484 try: 510 try:
@@ -495,7 +521,7 @@ class Connection(api.Connection):
495 raise exception.MagnumServiceAlreadyExists(id=magnum_service['id']) 521 raise exception.MagnumServiceAlreadyExists(id=magnum_service['id'])
496 return magnum_service 522 return magnum_service
497 523
498 def get_magnum_service_list(self, context, disabled=None, limit=None, 524 def get_magnum_service_list(self, disabled=None, limit=None,
499 marker=None, sort_key=None, sort_dir=None 525 marker=None, sort_key=None, sort_dir=None
500 ): 526 ):
501 query = model_query(models.MagnumService) 527 query = model_query(models.MagnumService)
@@ -515,8 +541,81 @@ class Connection(api.Connection):
515 resource=values['resource']) 541 resource=values['resource'])
516 return quotas 542 return quotas
517 543
544 def _add_quota_filters(self, query, filters):
545 if filters is None:
546 filters = {}
547
548 possible_filters = ["resource", "project_id"]
549
550 filter_names = set(filters).intersection(possible_filters)
551 filter_dict = {filter_name: filters[filter_name]
552 for filter_name in filter_names}
553
554 query = query.filter_by(**filter_dict)
555 return query
556
557 def get_quota_list(self, context, filters=None, limit=None, marker=None,
558 sort_key=None, sort_dir=None):
559 query = model_query(models.Quota)
560 query = self._add_quota_filters(query, filters)
561 return _paginate_query(models.Quota, limit, marker,
562 sort_key, sort_dir, query)
563
564 def update_quota(self, project_id, values):
565 session = get_session()
566 with session.begin():
567 query = model_query(models.Quota, session=session)
568 resource = values['resource']
569 try:
570 query = query.filter_by(project_id=project_id).filter_by(
571 resource=resource)
572 ref = query.with_lockmode('update').one()
573 except NoResultFound:
574 msg = (_('project_id %(project_id)s resource %(resource)s.') %
575 {'project_id': project_id, 'resource': resource})
576 raise exception.QuotaNotFound(msg=msg)
577
578 ref.update(values)
579 return ref
580
581 def delete_quota(self, project_id, resource):
582 session = get_session()
583 with session.begin():
584 query = model_query(models.Quota, session=session)
585
586 try:
587 query.filter_by(project_id=project_id).filter_by(
588 resource=resource).one()
589 except NoResultFound:
590 msg = (_('project_id %(project_id)s resource %(resource)s.') %
591 {'project_id': project_id, 'resource': resource})
592 raise exception.QuotaNotFound(msg=msg)
593
594 query.delete()
595
596 def get_quota_by_id(self, context, quota_id):
597 query = model_query(models.Quota)
598 query = query.filter_by(id=quota_id)
599 try:
600 return query.one()
601 except NoResultFound:
602 msg = _('quota id %s .') % quota_id
603 raise exception.QuotaNotFound(msg=msg)
604
518 def quota_get_all_by_project_id(self, project_id): 605 def quota_get_all_by_project_id(self, project_id):
519 query = model_query(models.Quota) 606 query = model_query(models.Quota)
520 result = query.filter_by(project_id=project_id).all() 607 result = query.filter_by(project_id=project_id).all()
521 608
522 return result 609 return result
610
611 def get_quota_by_project_id_resource(self, project_id, resource):
612 query = model_query(models.Quota)
613 query = query.filter_by(project_id=project_id).filter_by(
614 resource=resource)
615
616 try:
617 return query.one()
618 except NoResultFound:
619 msg = (_('project_id %(project_id)s resource %(resource)s.') %
620 {'project_id': project_id, 'resource': resource})
621 raise exception.QuotaNotFound(msg=msg)
diff --git a/magnum/drivers/common/driver.py b/magnum/drivers/common/driver.py
index 2f8a049..204a91f 100644
--- a/magnum/drivers/common/driver.py
+++ b/magnum/drivers/common/driver.py
@@ -174,3 +174,17 @@ class Driver(object):
174 def delete_cluster(self, context, cluster): 174 def delete_cluster(self, context, cluster):
175 raise NotImplementedError("Subclasses must implement " 175 raise NotImplementedError("Subclasses must implement "
176 "'delete_cluster'.") 176 "'delete_cluster'.")
177
178 def get_monitor(self, context, cluster):
179 """return the monitor with container data for this driver."""
180
181 return None
182
183 def get_scale_manager(self, context, osclient, cluster):
184 """return the scale manager for this driver."""
185
186 return None
187
188 def rotate_ca_certificate(self, context, cluster):
189 raise exception.NotSupported(
190 "'rotate_ca_certificate' is not supported by this driver.")
diff --git a/magnum/conductor/k8s_monitor.py b/magnum/drivers/common/k8s_monitor.py
index 0de63a9..c6ac147 100644
--- a/magnum/conductor/k8s_monitor.py
+++ b/magnum/drivers/common/k8s_monitor.py
@@ -40,7 +40,7 @@ class K8sMonitor(monitors.MonitorBase):
40 40
41 def pull_data(self): 41 def pull_data(self):
42 k8s_api = k8s.create_k8s_api(self.context, self.cluster) 42 k8s_api = k8s.create_k8s_api(self.context, self.cluster)
43 nodes = k8s_api.list_namespaced_node() 43 nodes = k8s_api.list_node()
44 self.data['nodes'] = self._parse_node_info(nodes) 44 self.data['nodes'] = self._parse_node_info(nodes)
45 pods = k8s_api.list_namespaced_pod('default') 45 pods = k8s_api.list_namespaced_pod('default')
46 self.data['pods'] = self._parse_pod_info(pods) 46 self.data['pods'] = self._parse_pod_info(pods)
@@ -158,7 +158,7 @@ class K8sMonitor(monitors.MonitorBase):
158 # Output of node.status.capacity is strong 158 # Output of node.status.capacity is strong
159 # for example: 159 # for example:
160 # capacity = "{'cpu': '1', 'memory': '1000Ki'}" 160 # capacity = "{'cpu': '1', 'memory': '1000Ki'}"
161 capacity = ast.literal_eval(node.status.capacity) 161 capacity = node.status.capacity
162 memory = utils.get_k8s_quantity(capacity['memory']) 162 memory = utils.get_k8s_quantity(capacity['memory'])
163 cpu = int(capacity['cpu']) 163 cpu = int(capacity['cpu'])
164 parsed_nodes.append({'Memory': memory, 'Cpu': cpu}) 164 parsed_nodes.append({'Memory': memory, 'Cpu': cpu})
diff --git a/magnum/drivers/common/k8s_scale_manager.py b/magnum/drivers/common/k8s_scale_manager.py
new file mode 100644
index 0000000..854f574
--- /dev/null
+++ b/magnum/drivers/common/k8s_scale_manager.py
@@ -0,0 +1,33 @@
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
13from oslo_log import log as logging
14
15from magnum.conductor import k8s_api as k8s
16from magnum.conductor.scale_manager import ScaleManager
17
18
19LOG = logging.getLogger(__name__)
20
21
22class K8sScaleManager(ScaleManager):
23
24 def __init__(self, context, osclient, cluster):
25 super(K8sScaleManager, self).__init__(context, osclient, cluster)
26
27 def _get_hosts_with_container(self, context, cluster):
28 k8s_api = k8s.create_k8s_api(self.context, cluster)
29 hosts = set()
30 for pod in k8s_api.list_namespaced_pod(namespace='default').items:
31 hosts.add(pod.spec.node_name)
32
33 return hosts
diff --git a/magnum/drivers/common/templates/environments/no_private_network.yaml b/magnum/drivers/common/templates/environments/no_private_network.yaml
new file mode 100644
index 0000000..59e0e62
--- /dev/null
+++ b/magnum/drivers/common/templates/environments/no_private_network.yaml
@@ -0,0 +1,8 @@
1resource_registry:
2 "Magnum::NetworkSwitcher": ../fragments/network_switcher_existing.yaml
3
4 # Cluster template
5 "Magnum::Optional::Neutron::Subnet": "OS::Heat::None"
6 "Magnum::Optional::Neutron::Net": "OS::Heat::None"
7 "Magnum::Optional::Neutron::Router": "OS::Heat::None"
8 "Magnum::Optional::Neutron::RouterInterface": "OS::Heat::None"
diff --git a/magnum/drivers/common/templates/environments/with_private_network.yaml b/magnum/drivers/common/templates/environments/with_private_network.yaml
new file mode 100644
index 0000000..5a4b415
--- /dev/null
+++ b/magnum/drivers/common/templates/environments/with_private_network.yaml
@@ -0,0 +1,8 @@
1resource_registry:
2 "Magnum::NetworkSwitcher": ../fragments/network_switcher_private.yaml
3
4 # Cluster template
5 "Magnum::Optional::Neutron::Subnet": "OS::Neutron::Subnet"
6 "Magnum::Optional::Neutron::Net": "OS::Neutron::Net"
7 "Magnum::Optional::Neutron::Router": "OS::Neutron::Router"
8 "Magnum::Optional::Neutron::RouterInterface": "OS::Neutron::RouterInterface"
diff --git a/magnum/drivers/common/templates/fragments/configure_docker_storage_driver_atomic.sh b/magnum/drivers/common/templates/fragments/configure_docker_storage_driver_atomic.sh
index ec289fb..9d92a66 100644
--- a/magnum/drivers/common/templates/fragments/configure_docker_storage_driver_atomic.sh
+++ b/magnum/drivers/common/templates/fragments/configure_docker_storage_driver_atomic.sh
@@ -1,9 +1,17 @@
1# This file contains docker storage drivers configuration for fedora 1# This file contains docker storage drivers configuration for fedora
2# atomic hosts. Currently, devicemapper and overlay are supported. 2# atomic hosts. Currently, devicemapper and overlay are supported.
3 3
4# Remove any existing docker-storage configuration. In case of an 4# * Remove any existing docker-storage configuration. In case of an
5# existing configuration, docker-storage-setup will fail. 5# existing configuration, docker-storage-setup will fail.
6clear_docker_storage_configuration () { 6# * Remove docker storage graph
7clear_docker_storage () {
8 # stop docker
9 systemctl stop docker
10 # clear storage graph
11 rm -rf /var/lib/docker/*
12 # remove current LVs
13 docker-storage-setup --reset
14
7 if [ -f /etc/sysconfig/docker-storage ]; then 15 if [ -f /etc/sysconfig/docker-storage ]; then
8 sed -i "/^DOCKER_STORAGE_OPTIONS=/ s/=.*/=/" /etc/sysconfig/docker-storage 16 sed -i "/^DOCKER_STORAGE_OPTIONS=/ s/=.*/=/" /etc/sysconfig/docker-storage
9 fi 17 fi
@@ -11,9 +19,7 @@ clear_docker_storage_configuration () {
11 19
12# Configure docker storage with xfs as backing filesystem. 20# Configure docker storage with xfs as backing filesystem.
13configure_overlay () { 21configure_overlay () {
14 clear_docker_storage_configuration 22 clear_docker_storage
15
16 rm -rf /var/lib/docker/*
17 23
18 if [ -n "$DOCKER_VOLUME_SIZE" ] && [ "$DOCKER_VOLUME_SIZE" -gt 0 ]; then 24 if [ -n "$DOCKER_VOLUME_SIZE" ] && [ "$DOCKER_VOLUME_SIZE" -gt 0 ]; then
19 mkfs.xfs -f ${device_path} 25 mkfs.xfs -f ${device_path}
@@ -23,20 +29,29 @@ configure_overlay () {
23 29
24 echo "STORAGE_DRIVER=overlay" > /etc/sysconfig/docker-storage-setup 30 echo "STORAGE_DRIVER=overlay" > /etc/sysconfig/docker-storage-setup
25 31
26 # SELinux must be enabled and in enforcing mode on the physical 32 docker-storage-setup
27 # machine, but must be disabled in the container when performing 33
28 # container separation 34 local lvname=$(lvdisplay | grep "LV\ Path" | awk '{print $3}')
29 sed -i "/^OPTIONS=/ s/--selinux-enabled/--selinux-enabled=false/" /etc/sysconfig/docker 35 local pvname=$(pvdisplay | grep "PV\ Name" | awk '{print $3}')
36 lvextend -r $lvname $pvname
30} 37}
31 38
32# Configure docker storage with devicemapper using direct LVM 39# Configure docker storage with devicemapper using direct LVM
33configure_devicemapper () { 40configure_devicemapper () {
34 clear_docker_storage_configuration 41 clear_docker_storage
42
43 echo "GROWROOT=True" > /etc/sysconfig/docker-storage-setup
44 echo "ROOT_SIZE=5GB" >> /etc/sysconfig/docker-storage-setup
35 45
36 if [ -n "$DOCKER_VOLUME_SIZE" ] && [ "$DOCKER_VOLUME_SIZE" -gt 0 ]; then 46 if [ -n "$DOCKER_VOLUME_SIZE" ] && [ "$DOCKER_VOLUME_SIZE" -gt 0 ]; then
47
37 pvcreate -f ${device_path} 48 pvcreate -f ${device_path}
38 vgcreate docker ${device_path} 49 vgcreate docker ${device_path}
39 50
40 echo "VG=docker" > /etc/sysconfig/docker-storage-setup 51 echo "VG=docker" >> /etc/sysconfig/docker-storage-setup
52 else
53 echo "DATA_SIZE=95%FREE" >> /etc/sysconfig/docker-storage-setup
41 fi 54 fi
55
56 docker-storage-setup
42} 57}
diff --git a/magnum/drivers/common/templates/fragments/network_switcher_existing.yaml b/magnum/drivers/common/templates/fragments/network_switcher_existing.yaml
new file mode 100644
index 0000000..e3f14fa
--- /dev/null
+++ b/magnum/drivers/common/templates/fragments/network_switcher_existing.yaml
@@ -0,0 +1,27 @@
1heat_template_version: 2014-10-16
2
3parameters:
4
5 private_network:
6 type: string
7 default: ""
8
9 existing_network:
10 type: string
11 default: ""
12
13 private_subnet:
14 type: string
15 default: ""
16
17 existing_subnet:
18 type: string
19 default: ""
20
21outputs:
22
23 network:
24 value: {get_param: existing_network}
25
26 subnet:
27 value: {get_param: existing_subnet}
diff --git a/magnum/drivers/common/templates/fragments/network_switcher_private.yaml b/magnum/drivers/common/templates/fragments/network_switcher_private.yaml
new file mode 100644
index 0000000..107dd43
--- /dev/null
+++ b/magnum/drivers/common/templates/fragments/network_switcher_private.yaml
@@ -0,0 +1,27 @@
1heat_template_version: 2014-10-16
2
3parameters:
4
5 private_network:
6 type: string
7 default: ""
8
9 existing_network:
10 type: string
11 default: ""
12
13 private_subnet:
14 type: string
15 default: ""
16
17 existing_subnet:
18 type: string
19 default: ""
20
21outputs:
22
23 network:
24 value: {get_param: private_network}
25
26 subnet:
27 value: {get_param: private_subnet}
diff --git a/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-master.sh b/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-master.sh
index 681315e..0b6fd77 100644
--- a/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-master.sh
+++ b/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-master.sh
@@ -25,12 +25,17 @@ else
25 KUBE_API_ARGS="$KUBE_API_ARGS --client-ca-file=/srv/kubernetes/ca.crt" 25 KUBE_API_ARGS="$KUBE_API_ARGS --client-ca-file=/srv/kubernetes/ca.crt"
26fi 26fi
27 27
28KUBE_ADMISSION_CONTROL=""
29if [ -n "${ADMISSION_CONTROL_LIST}" ] && [ "${TLS_DISABLED}" == "False" ]; then
30 KUBE_ADMISSION_CONTROL="--admission-control=${ADMISSION_CONTROL_LIST}"
31fi
32
28sed -i ' 33sed -i '
29 /^KUBE_API_ADDRESS=/ s/=.*/='"${KUBE_API_ADDRESS}"'/ 34 /^KUBE_API_ADDRESS=/ s/=.*/="'"${KUBE_API_ADDRESS}"'"/
30 /^KUBE_SERVICE_ADDRESSES=/ s|=.*|="--service-cluster-ip-range='"$PORTAL_NETWORK_CIDR"'"| 35 /^KUBE_SERVICE_ADDRESSES=/ s|=.*|="--service-cluster-ip-range='"$PORTAL_NETWORK_CIDR"'"|
31 /^KUBE_API_ARGS=/ s/KUBE_API_ARGS.// 36 /^KUBE_API_ARGS=/ s/KUBE_API_ARGS.//
32 /^KUBE_ETCD_SERVERS=/ s/=.*/="--etcd-servers=http:\/\/127.0.0.1:2379"/ 37 /^KUBE_ETCD_SERVERS=/ s/=.*/="--etcd-servers=http:\/\/127.0.0.1:2379"/
33 /^KUBE_ADMISSION_CONTROL=/ s/=.*/=""/ 38 /^KUBE_ADMISSION_CONTROL=/ s/=.*/="'"${KUBE_ADMISSION_CONTROL}"'"/
34' /etc/kubernetes/apiserver 39' /etc/kubernetes/apiserver
35cat << _EOC_ >> /etc/kubernetes/apiserver 40cat << _EOC_ >> /etc/kubernetes/apiserver
36#Uncomment the following line to disable Load Balancer feature 41#Uncomment the following line to disable Load Balancer feature
@@ -39,10 +44,19 @@ KUBE_API_ARGS="$KUBE_API_ARGS"
39#KUBE_API_ARGS="$KUBE_API_ARGS --cloud-config=/etc/sysconfig/kube_openstack_config --cloud-provider=openstack" 44#KUBE_API_ARGS="$KUBE_API_ARGS --cloud-config=/etc/sysconfig/kube_openstack_config --cloud-provider=openstack"
40_EOC_ 45_EOC_
41 46
47# Add controller manager args
48KUBE_CONTROLLER_MANAGER_ARGS=""
49if [ -n "${ADMISSION_CONTROL_LIST}" ] && [ "${TLS_DISABLED}" == "False" ]; then
50 KUBE_CONTROLLER_MANAGER_ARGS="--service-account-private-key-file=/srv/kubernetes/server.key"
51fi
42sed -i ' 52sed -i '
43 /^KUBELET_ADDRESSES=/ s/=.*/="--machines='""'"/ 53 /^KUBELET_ADDRESSES=/ s/=.*/="--machines='""'"/
44 /^KUBE_CONTROLLER_MANAGER_ARGS=/ s/KUBE_CONTROLLER_MANAGER_ARGS.*/#Uncomment the following line to enable Kubernetes Load Balancer feature \n#KUBE_CONTROLLER_MANAGER_ARGS="--cloud-config=\/etc\/sysconfig\/kube_openstack_config --cloud-provider=openstack"/ 54 /^KUBE_CONTROLLER_MANAGER_ARGS=/ s#\(KUBE_CONTROLLER_MANAGER_ARGS\).*#\1="'"${KUBE_CONTROLLER_MANAGER_ARGS}"'"#
45' /etc/kubernetes/controller-manager 55' /etc/kubernetes/controller-manager
56cat << _EOC_ >> /etc/kubernetes/controller-manager
57#Uncomment the following line to enable Kubernetes Load Balancer feature
58#KUBE_CONTROLLER_MANAGER_ARGS="\$KUBE_CONTROLLER_MANAGER_ARGS --cloud-config=/etc/sysconfig/kube_openstack_config --cloud-provider=openstack"
59_EOC_
46 60
47KUBELET_ARGS="--register-node=true --register-schedulable=false --config=/etc/kubernetes/manifests --hostname-override=$KUBE_NODE_IP" 61KUBELET_ARGS="--register-node=true --register-schedulable=false --config=/etc/kubernetes/manifests --hostname-override=$KUBE_NODE_IP"
48 62
diff --git a/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-minion.sh b/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-minion.sh
index 56071c4..d48cbb4 100644
--- a/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-minion.sh
+++ b/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-minion.sh
@@ -73,13 +73,13 @@ sed -i '
73 73
74if [ "$NETWORK_DRIVER" = "flannel" ]; then 74if [ "$NETWORK_DRIVER" = "flannel" ]; then
75 sed -i ' 75 sed -i '
76 /^FLANNEL_ETCD=/ s|=.*|="'"$PROTOCOL"'://'"$ETCD_SERVER_IP"':2379"| 76 /^FLANNEL_ETCD_ENDPOINTS=/ s|=.*|="'"$PROTOCOL"'://'"$ETCD_SERVER_IP"':2379"|
77 ' $FLANNELD_CONFIG 77 ' $FLANNELD_CONFIG
78 78
79 # Make sure etcd has a flannel configuration 79 # Make sure etcd has a flannel configuration
80 . $FLANNELD_CONFIG 80 . $FLANNELD_CONFIG
81 until curl -sf $ETCD_CURL_OPTIONS \ 81 until curl -sf $ETCD_CURL_OPTIONS \
82 "$FLANNEL_ETCD/v2/keys${FLANNEL_ETCD_KEY}/config?quorum=false&recursive=false&sorted=false" 82 "$FLANNEL_ETCD_ENDPOINTS/v2/keys${FLANNEL_ETCD_PREFIX}/config?quorum=false&recursive=false&sorted=false"
83 do 83 do
84 echo "Waiting for flannel configuration in etcd..." 84 echo "Waiting for flannel configuration in etcd..."
85 sleep 5 85 sleep 5
diff --git a/magnum/drivers/common/templates/kubernetes/fragments/enable-kube-controller-manager-scheduler.sh b/magnum/drivers/common/templates/kubernetes/fragments/enable-kube-controller-manager-scheduler.sh
index 3474ea8..aca388f 100644
--- a/magnum/drivers/common/templates/kubernetes/fragments/enable-kube-controller-manager-scheduler.sh
+++ b/magnum/drivers/common/templates/kubernetes/fragments/enable-kube-controller-manager-scheduler.sh
@@ -8,15 +8,28 @@ else
8 HYPERKUBE_IMAGE="gcr.io/google_containers/hyperkube:${KUBE_VERSION}" 8 HYPERKUBE_IMAGE="gcr.io/google_containers/hyperkube:${KUBE_VERSION}"
9fi 9fi
10 10
11# vars also used by the Kubernetes config files
12unset KUBE_API_PORT
13unset KUBE_ALLOW_PRIV
14
15# this function generate a list of args (one per line) from a list of possibly nested args
16# the first parameter is the prefix to be added before each arg
17# empty args are ignored
18generate_pod_args() {
19 prefix=$1
20
21 for var in "${@:2}" ; do
22 for arg in "$var" ; do
23 echo "$prefix$arg"
24 done
25 done
26}
27
11 28
12init_templates () { 29init_templates () {
13 local SERVICE_ACCOUNT_PRIVATE_KEY_FILE=/etc/kubernetes/ssl/server.key 30 . /etc/kubernetes/config
14 local ROOT_CA_FILE=/etc/kubernetes/ssl/ca.crt
15 31
16 if [ "${TLS_DISABLED}" = "True" ]; then 32 . /etc/kubernetes/controller-manager
17 SERVICE_ACCOUNT_PRIVATE_KEY_FILE=
18 ROOT_CA_FILE=
19 fi
20 33
21 local TEMPLATE=/etc/kubernetes/manifests/kube-controller-manager.yaml 34 local TEMPLATE=/etc/kubernetes/manifests/kube-controller-manager.yaml
22 [ -f ${TEMPLATE} ] || { 35 [ -f ${TEMPLATE} ] || {
@@ -29,47 +42,47 @@ metadata:
29 name: kube-controller-manager 42 name: kube-controller-manager
30 namespace: kube-system 43 namespace: kube-system
31spec: 44spec:
45 hostNetwork: true
32 containers: 46 containers:
33 - name: kube-controller-manager 47 - name: kube-controller-manager
34 image: ${HYPERKUBE_IMAGE} 48 image: ${HYPERKUBE_IMAGE}
35 command: 49 command:
36 - /hyperkube 50 - /hyperkube
37 - controller-manager 51 - controller-manager
38 - --master=http://127.0.0.1:8080
39 - --leader-elect=true 52 - --leader-elect=true
40 - --service-account-private-key-file=${SERVICE_ACCOUNT_PRIVATE_KEY_FILE} 53$(generate_pod_args " - " $KUBE_LOGTOSTDERR $KUBE_LOG_LEVEL $KUBE_MASTER $KUBE_CONTROLLER_MANAGER_ARGS)
41 - --root-ca-file=${ROOT_CA_FILE}
42 livenessProbe: 54 livenessProbe:
43 httpGet: 55 httpGet:
44 host: 127.0.0.1 56 host: 127.0.0.1
45 path: /healthz 57 path: /healthz
46 port: 10252 58 port: 10252
47 initialDelaySeconds: 15 59 initialDelaySeconds: ${SYSTEM_PODS_INITIAL_DELAY}
48 timeoutSeconds: 1 60 timeoutSeconds: ${SYSTEM_PODS_TIMEOUT}
49 volumeMounts: 61 volumeMounts:
50 - mountPath: /etc/kubernetes/ssl
51 name: ssl-certs-kubernetes
52 readOnly: true
53 - mountPath: /etc/ssl/certs 62 - mountPath: /etc/ssl/certs
54 name: ssl-certs-host 63 name: ssl-certs-host
55 readOnly: true 64 readOnly: true
65 - mountPath: /srv/kubernetes
66 name: kubernetes-config
67 readOnly: true
56 - mountPath: /etc/sysconfig 68 - mountPath: /etc/sysconfig
57 name: sysconfig 69 name: sysconfig
58 readOnly: true 70 readOnly: true
59 hostNetwork: true
60 volumes: 71 volumes:
61 - hostPath: 72 - hostPath:
62 path: /srv/kubernetes
63 name: ssl-certs-kubernetes
64 - hostPath:
65 path: /etc/ssl/certs 73 path: /etc/ssl/certs
66 name: ssl-certs-host 74 name: ssl-certs-host
67 - hostPath: 75 - hostPath:
76 path: /srv/kubernetes
77 name: kubernetes-config
78 - hostPath:
68 path: /etc/sysconfig 79 path: /etc/sysconfig
69 name: sysconfig 80 name: sysconfig
70EOF 81EOF
71 } 82 }
72 83
84 . /etc/kubernetes/scheduler
85
73 local TEMPLATE=/etc/kubernetes/manifests/kube-scheduler.yaml 86 local TEMPLATE=/etc/kubernetes/manifests/kube-scheduler.yaml
74 [ -f ${TEMPLATE} ] || { 87 [ -f ${TEMPLATE} ] || {
75 echo "TEMPLATE: $TEMPLATE" 88 echo "TEMPLATE: $TEMPLATE"
@@ -88,15 +101,35 @@ spec:
88 command: 101 command:
89 - /hyperkube 102 - /hyperkube
90 - scheduler 103 - scheduler
91 - --master=http://127.0.0.1:8080
92 - --leader-elect=true 104 - --leader-elect=true
105$(generate_pod_args " - " $KUBE_LOGTOSTDERR $KUBE_LOG_LEVEL $KUBE_MASTER $KUBE_SCHEDULER_ARGS)
93 livenessProbe: 106 livenessProbe:
94 httpGet: 107 httpGet:
95 host: 127.0.0.1 108 host: 127.0.0.1
96 path: /healthz 109 path: /healthz
97 port: 10251 110 port: 10251
98 initialDelaySeconds: 15 111 initialDelaySeconds: ${SYSTEM_PODS_INITIAL_DELAY}
99 timeoutSeconds: 1 112 timeoutSeconds: ${SYSTEM_PODS_TIMEOUT}
113 volumeMounts:
114 - mountPath: /etc/ssl/certs
115 name: ssl-certs-host
116 readOnly: true
117 - mountPath: /srv/kubernetes
118 name: kubernetes-config
119 readOnly: true
120 - mountPath: /etc/sysconfig
121 name: sysconfig
122 readOnly: true
123 volumes:
124 - hostPath:
125 path: /etc/ssl/certs
126 name: ssl-certs-host
127 - hostPath:
128 path: /srv/kubernetes
129 name: kubernetes-config
130 - hostPath:
131 path: /etc/sysconfig
132 name: sysconfig
100EOF 133EOF
101 } 134 }
102} 135}
diff --git a/magnum/drivers/common/templates/kubernetes/fragments/make-cert-client.sh b/magnum/drivers/common/templates/kubernetes/fragments/make-cert-client.sh
index 6dcdd3a..34d05ba 100644
--- a/magnum/drivers/common/templates/kubernetes/fragments/make-cert-client.sh
+++ b/magnum/drivers/common/templates/kubernetes/fragments/make-cert-client.sh
@@ -70,6 +70,7 @@ USER_TOKEN=`curl -k -s -i -X POST -H "$content_type" -d "$auth_json" $url \
70# Get CA certificate for this cluster 70# Get CA certificate for this cluster
71curl -k -X GET \ 71curl -k -X GET \
72 -H "X-Auth-Token: $USER_TOKEN" \ 72 -H "X-Auth-Token: $USER_TOKEN" \
73 -H "OpenStack-API-Version: container-infra latest" \
73 $MAGNUM_URL/certificates/$CLUSTER_UUID | python -c 'import sys, json; print json.load(sys.stdin)["pem"]' > $CA_CERT 74 $MAGNUM_URL/certificates/$CLUSTER_UUID | python -c 'import sys, json; print json.load(sys.stdin)["pem"]' > $CA_CERT
74 75
75# Create config for client's csr 76# Create config for client's csr
@@ -103,6 +104,7 @@ openssl req -new -days 1000 \
103csr_req=$(python -c "import json; fp = open('${CLIENT_CSR}'); print json.dumps({'cluster_uuid': '$CLUSTER_UUID', 'csr': fp.read()}); fp.close()") 104csr_req=$(python -c "import json; fp = open('${CLIENT_CSR}'); print json.dumps({'cluster_uuid': '$CLUSTER_UUID', 'csr': fp.read()}); fp.close()")
104curl -k -X POST \ 105curl -k -X POST \
105 -H "X-Auth-Token: $USER_TOKEN" \ 106 -H "X-Auth-Token: $USER_TOKEN" \
107 -H "OpenStack-API-Version: container-infra latest" \
106 -H "Content-Type: application/json" \ 108 -H "Content-Type: application/json" \
107 -d "$csr_req" \ 109 -d "$csr_req" \
108 $MAGNUM_URL/certificates | python -c 'import sys, json; print json.load(sys.stdin)["pem"]' > ${CLIENT_CERT} 110 $MAGNUM_URL/certificates | python -c 'import sys, json; print json.load(sys.stdin)["pem"]' > ${CLIENT_CERT}
diff --git a/magnum/drivers/common/templates/kubernetes/fragments/make-cert.sh b/magnum/drivers/common/templates/kubernetes/fragments/make-cert.sh
index bf91246..3dd2c71 100644
--- a/magnum/drivers/common/templates/kubernetes/fragments/make-cert.sh
+++ b/magnum/drivers/common/templates/kubernetes/fragments/make-cert.sh
@@ -92,6 +92,7 @@ USER_TOKEN=`curl -k -s -i -X POST -H "$content_type" -d "$auth_json" $url \
92# Get CA certificate for this cluster 92# Get CA certificate for this cluster
93curl -k -X GET \ 93curl -k -X GET \
94 -H "X-Auth-Token: $USER_TOKEN" \ 94 -H "X-Auth-Token: $USER_TOKEN" \
95 -H "OpenStack-API-Version: container-infra latest" \
95 $MAGNUM_URL/certificates/$CLUSTER_UUID | python -c 'import sys, json; print json.load(sys.stdin)["pem"]' > ${CA_CERT} 96 $MAGNUM_URL/certificates/$CLUSTER_UUID | python -c 'import sys, json; print json.load(sys.stdin)["pem"]' > ${CA_CERT}
96 97
97# Create config for server's csr 98# Create config for server's csr
@@ -120,6 +121,7 @@ openssl req -new -days 1000 \
120csr_req=$(python -c "import json; fp = open('${SERVER_CSR}'); print json.dumps({'cluster_uuid': '$CLUSTER_UUID', 'csr': fp.read()}); fp.close()") 121csr_req=$(python -c "import json; fp = open('${SERVER_CSR}'); print json.dumps({'cluster_uuid': '$CLUSTER_UUID', 'csr': fp.read()}); fp.close()")
121curl -k -X POST \ 122curl -k -X POST \
122 -H "X-Auth-Token: $USER_TOKEN" \ 123 -H "X-Auth-Token: $USER_TOKEN" \
124 -H "OpenStack-API-Version: container-infra latest" \
123 -H "Content-Type: application/json" \ 125 -H "Content-Type: application/json" \
124 -d "$csr_req" \ 126 -d "$csr_req" \
125 $MAGNUM_URL/certificates | python -c 'import sys, json; print json.load(sys.stdin)["pem"]' > ${SERVER_CERT} 127 $MAGNUM_URL/certificates | python -c 'import sys, json; print json.load(sys.stdin)["pem"]' > ${SERVER_CERT}
diff --git a/magnum/drivers/common/templates/kubernetes/fragments/network-config-service.sh b/magnum/drivers/common/templates/kubernetes/fragments/network-config-service.sh
index 1cac6b5..643179c 100644
--- a/magnum/drivers/common/templates/kubernetes/fragments/network-config-service.sh
+++ b/magnum/drivers/common/templates/kubernetes/fragments/network-config-service.sh
@@ -41,14 +41,14 @@ if ! [ -f "$FLANNEL_JSON" ]; then
41 exit 1 41 exit 1
42fi 42fi
43 43
44if ! [ "$FLANNEL_ETCD" ] && [ "$FLANNEL_ETCD_KEY" ]; then 44if [ -z "$FLANNEL_ETCD_ENDPOINTS" ] || [ -z "$FLANNEL_ETCD_PREFIX" ]; then
45 echo "ERROR: missing required configuration" >&2 45 echo "ERROR: missing required configuration" >&2
46 exit 1 46 exit 1
47fi 47fi
48 48
49echo "creating flanneld config in etcd" 49echo "creating flanneld config in etcd"
50while ! curl -sf -L $ETCD_CURL_OPTIONS \ 50while ! curl -sf -L $ETCD_CURL_OPTIONS \
51 $FLANNEL_ETCD/v2/keys${FLANNEL_ETCD_KEY}/config \ 51 $FLANNEL_ETCD_ENDPOINTS/v2/keys${FLANNEL_ETCD_PREFIX}/config \
52 -X PUT --data-urlencode value@${FLANNEL_JSON}; do 52 -X PUT --data-urlencode value@${FLANNEL_JSON}; do
53 echo "waiting for etcd" 53 echo "waiting for etcd"
54 sleep 1 54 sleep 1
diff --git a/magnum/drivers/common/templates/kubernetes/fragments/write-heat-params-master.yaml b/magnum/drivers/common/templates/kubernetes/fragments/write-heat-params-master.yaml
index 4651ab3..249d3d4 100644
--- a/magnum/drivers/common/templates/kubernetes/fragments/write-heat-params-master.yaml
+++ b/magnum/drivers/common/templates/kubernetes/fragments/write-heat-params-master.yaml
@@ -20,6 +20,7 @@ write_files:
20 FLANNEL_NETWORK_SUBNETLEN="$FLANNEL_NETWORK_SUBNETLEN" 20 FLANNEL_NETWORK_SUBNETLEN="$FLANNEL_NETWORK_SUBNETLEN"
21 FLANNEL_BACKEND="$FLANNEL_BACKEND" 21 FLANNEL_BACKEND="$FLANNEL_BACKEND"
22 PORTAL_NETWORK_CIDR="$PORTAL_NETWORK_CIDR" 22 PORTAL_NETWORK_CIDR="$PORTAL_NETWORK_CIDR"
23 ADMISSION_CONTROL_LIST="$ADMISSION_CONTROL_LIST"
23 ETCD_DISCOVERY_URL="$ETCD_DISCOVERY_URL" 24 ETCD_DISCOVERY_URL="$ETCD_DISCOVERY_URL"
24 USERNAME="$USERNAME" 25 USERNAME="$USERNAME"
25 PASSWORD="$PASSWORD" 26 PASSWORD="$PASSWORD"
@@ -38,3 +39,5 @@ write_files:
38 TRUST_ID="$TRUST_ID" 39 TRUST_ID="$TRUST_ID"
39 AUTH_URL="$AUTH_URL" 40 AUTH_URL="$AUTH_URL"
40 INSECURE_REGISTRY_URL="$INSECURE_REGISTRY_URL" 41 INSECURE_REGISTRY_URL="$INSECURE_REGISTRY_URL"
42 SYSTEM_PODS_INITIAL_DELAY="$SYSTEM_PODS_INITIAL_DELAY"
43 SYSTEM_PODS_TIMEOUT="$SYSTEM_PODS_TIMEOUT"
diff --git a/magnum/drivers/common/templates/kubernetes/fragments/write-network-config.sh b/magnum/drivers/common/templates/kubernetes/fragments/write-network-config.sh
index f2f36ee..3b2c54b 100644
--- a/magnum/drivers/common/templates/kubernetes/fragments/write-network-config.sh
+++ b/magnum/drivers/common/templates/kubernetes/fragments/write-network-config.sh
@@ -12,7 +12,7 @@ FLANNEL_JSON=/etc/sysconfig/flannel-network.json
12FLANNELD_CONFIG=/etc/sysconfig/flanneld 12FLANNELD_CONFIG=/etc/sysconfig/flanneld
13 13
14sed -i ' 14sed -i '
15 /^FLANNEL_ETCD=/ s/=.*/="http:\/\/127.0.0.1:2379"/ 15 /^FLANNEL_ETCD_ENDPOINTS=/ s/=.*/="http:\/\/127.0.0.1:2379"/
16' /etc/sysconfig/flanneld 16' /etc/sysconfig/flanneld
17 17
18# Generate a flannel configuration that we will 18# Generate a flannel configuration that we will
diff --git a/magnum/drivers/common/templates/lb.yaml b/magnum/drivers/common/templates/lb.yaml
index fb928d5..ef4c65e 100644
--- a/magnum/drivers/common/templates/lb.yaml
+++ b/magnum/drivers/common/templates/lb.yaml
@@ -27,7 +27,7 @@ resources:
27 type: Magnum::Optional::Neutron::LBaaS::Listener 27 type: Magnum::Optional::Neutron::LBaaS::Listener
28 properties: 28 properties:
29 loadbalancer: {get_resource: loadbalancer} 29 loadbalancer: {get_resource: loadbalancer}
30 protocol: {get_param: loadbalancing_protocol} 30 protocol: {get_param: protocol}
31 protocol_port: {get_param: port} 31 protocol_port: {get_param: port}
32 32
33 pool: 33 pool:
@@ -35,7 +35,7 @@ resources:
35 properties: 35 properties:
36 lb_algorithm: ROUND_ROBIN 36 lb_algorithm: ROUND_ROBIN
37 listener: {get_resource: listener} 37 listener: {get_resource: listener}
38 protocol: {get_param: loadbalancing_protocol} 38 protocol: {get_param: protocol}
39 39
40 monitor: 40 monitor:
41 type: Magnum::Optional::Neutron::LBaaS::HealthMonitor 41 type: Magnum::Optional::Neutron::LBaaS::HealthMonitor