From 539bae9d3ea5f7a6fb56bc80572f9efd47ed787d Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Tue, 12 Nov 2019 10:05:37 +0000 Subject: [PATCH 01/42] initial nat gateway branche --- otcextensions/sdk/__init__.py | 4 ++ otcextensions/sdk/nat/__init__.py | 0 otcextensions/sdk/nat/nat_service.py | 22 ++++++++++ otcextensions/sdk/nat/v2/__init__.py | 0 otcextensions/sdk/nat/v2/_proxy.py | 36 +++++++++++++++++ otcextensions/sdk/nat/v2/nat_gateway.py | 54 +++++++++++++++++++++++++ 6 files changed, 116 insertions(+) create mode 100644 otcextensions/sdk/nat/__init__.py create mode 100644 otcextensions/sdk/nat/nat_service.py create mode 100644 otcextensions/sdk/nat/v2/__init__.py create mode 100644 otcextensions/sdk/nat/v2/_proxy.py create mode 100644 otcextensions/sdk/nat/v2/nat_gateway.py diff --git a/otcextensions/sdk/__init__.py b/otcextensions/sdk/__init__.py index f9c76b8e2..c153dac6c 100644 --- a/otcextensions/sdk/__init__.py +++ b/otcextensions/sdk/__init__.py @@ -82,6 +82,10 @@ 'service_type': 'kms', 'append_project_id': True, }, + 'nat': { + 'service_type': 'nat', + 'append_project_id': True, + }, 'obs': { 'service_type': 'obs', 'require_ak': True, diff --git a/otcextensions/sdk/nat/__init__.py b/otcextensions/sdk/nat/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/otcextensions/sdk/nat/nat_service.py b/otcextensions/sdk/nat/nat_service.py new file mode 100644 index 000000000..d5fbc32f1 --- /dev/null +++ b/otcextensions/sdk/nat/nat_service.py @@ -0,0 +1,22 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from openstack import service_description + +from otcextensions.sdk.nat.v2 import _proxy + + +class NatService(service_description.ServiceDescription): + """The NAT service.""" + + supported_versions = { + '2': _proxy.Proxy + } diff --git a/otcextensions/sdk/nat/v2/__init__.py b/otcextensions/sdk/nat/v2/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/otcextensions/sdk/nat/v2/_proxy.py b/otcextensions/sdk/nat/v2/_proxy.py new file mode 100644 index 000000000..f7ace7ca1 --- /dev/null +++ b/otcextensions/sdk/nat/v2/_proxy.py @@ -0,0 +1,36 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from otcextensions.sdk.nat.v2 import nat_gateway as _nat_gateway + +from openstack import proxy + + +class Proxy(proxy.Proxy): + + def gateways(self, **attrs): + return self._list(_nat_gateway.NatGateway, **attrs) + + def create_gateway(self, **attrs): + """Create a new gateway from attributes + + :param dict attrs: Keyword arguments which will be used to create + a :class:`~otcextensions.sdk.nat.v2.nat_gateway.NatGateway + """ + return self._create(_nat_gateway.NatGateway, **attrs) + + def delete_gateway(self, id): + """Create a new gateway from attributes + + :param dict attrs: Keyword arguments which will be used to create + a :class:`~otcextensions.sdk.nat.v2.nat_gateway.NatGateway + """ + return self._delete(_nat_gateway.NatGateway, id=id) diff --git a/otcextensions/sdk/nat/v2/nat_gateway.py b/otcextensions/sdk/nat/v2/nat_gateway.py new file mode 100644 index 000000000..d87da1924 --- /dev/null +++ b/otcextensions/sdk/nat/v2/nat_gateway.py @@ -0,0 +1,54 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from openstack import resource + +class NatGateway(resource.Resource): + resources_key = 'nat_gateways' + base_path = '/nat_gateways' + + # capabilities + allow_create = True + allow_delete = True + allow_list = True + #allow_get = True + + # Properties + #: Specifies the ID of the NAT gateway. + id = resource.Body('id') + #: Specifies the project ID + tenant_id = resource.Body('tenant_id') + #: Specifies the name of the NAT gateway. + #: Contains only digits, letters, underscores and hyphens + name = resource.Body('name') + #: Provides description of NAT Gateway + description = resource.Body('description') + #: Specifies the type of the NAT gateway. + #: *1:* small type, supports up to 10,000 SNAT connections + #: *2:* medium type, supports up to 50,000 SNAT connections + #: *3:* large type, supports up to 200,000 SNAT connections + #: *4:* extra-large type, supports up to 1,000,000 SNAT connections + spec = resource.Body('spec', type=int) + #: Specifies the router ID + router_id = resource.Body('router_id') + #: Specifies the network ID of the downstream interface + internal_network_id = resource.Body('internal_network_id') + #: Specifies the status + status = resource.Body('status') + #: Specifies whether GW is up or down + #: *true:* Gw is up + #: *false:* GW is down + admin_state_up = resource.Body('admin_state_up', type=bool) + #: Specifies when GW was is created + #: format is *yyyy-mm-dd hh:mm:ss* + created_at = resource.Body('created_at') + + From adf6243b4fdb264af5f6782c3f53d420f3d5f22c Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Tue, 12 Nov 2019 14:03:38 +0000 Subject: [PATCH 02/42] added snat and dnat resource --- otcextensions/sdk/nat/v2/_proxy.py | 44 ++++++++++++++++--- otcextensions/sdk/nat/v2/dnat.py | 57 +++++++++++++++++++++++++ otcextensions/sdk/nat/v2/nat_gateway.py | 5 ++- otcextensions/sdk/nat/v2/snat.py | 54 +++++++++++++++++++++++ 4 files changed, 153 insertions(+), 7 deletions(-) create mode 100644 otcextensions/sdk/nat/v2/dnat.py create mode 100644 otcextensions/sdk/nat/v2/snat.py diff --git a/otcextensions/sdk/nat/v2/_proxy.py b/otcextensions/sdk/nat/v2/_proxy.py index f7ace7ca1..36cd1f64e 100644 --- a/otcextensions/sdk/nat/v2/_proxy.py +++ b/otcextensions/sdk/nat/v2/_proxy.py @@ -13,17 +13,15 @@ from openstack import proxy - class Proxy(proxy.Proxy): - def gateways(self, **attrs): - return self._list(_nat_gateway.NatGateway, **attrs) +# NAT Gateway def create_gateway(self, **attrs): """Create a new gateway from attributes :param dict attrs: Keyword arguments which will be used to create - a :class:`~otcextensions.sdk.nat.v2.nat_gateway.NatGateway + a :class:`~otcextensions.sdk.nat.v2.nat_gateway.NatGateway` """ return self._create(_nat_gateway.NatGateway, **attrs) @@ -31,6 +29,42 @@ def delete_gateway(self, id): """Create a new gateway from attributes :param dict attrs: Keyword arguments which will be used to create - a :class:`~otcextensions.sdk.nat.v2.nat_gateway.NatGateway + a :class:`~otcextensions.sdk.nat.v2.nat_gateway.NatGateway` """ return self._delete(_nat_gateway.NatGateway, id=id) + + def gateways(self, **attrs): + """Return a generator of NAT Gateways + + :param kwargs query: Optional query parameters to be sent to limit + the resources being returned. + :returns: A generator of NAT Gateway objects + :rtype: :class:`~otcextensions.sdk.nat.v2.nat_gateway.NatGateway` + """ + return self._list(_nat_gateway.NatGateway, **attrs) + + def get_nat_gateway(self, nat_gateway): + """Get a single Nat Gateway + + :param nat_gateway: The value can be the ID of a NAT Gatway or a + :class:`~otcextensions.sdk.nat.v2.nat_gateway.NatGateway` + instance. + + :returns: One :class:`~otcextensions.sdk.nat.v2.nat_gateway.NatGateway` + :raises: :class:`~openstack.exceptions.ResourceNotFound` + when no resource can be found. + """ + return self._get(_nat_gateway.NatGateway, nat_gateway) + + def update_nat_gateway(self, nat_gateway, **attrs): + """Update a NAT Gateway + + :param nat_gateway: Either the ID of a NAT Gateway or a + :class:`~otcextensions.sdk.nat.v2.nat_gateway.NatGateway` instance. + :attrs kwargs: The attributes to update on the server represented + by ``server``. + + :returns: The updated NAT Gateway + :rtype: :class:`~otcextensions.sdk.nat.v2.nat_gateway.NatGateway` + """ + return self._update(_server.Server, server, **attrs) diff --git a/otcextensions/sdk/nat/v2/dnat.py b/otcextensions/sdk/nat/v2/dnat.py new file mode 100644 index 000000000..35a46c40b --- /dev/null +++ b/otcextensions/sdk/nat/v2/dnat.py @@ -0,0 +1,57 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from openstack import resource + +class Dnat(resource.Resource): + resources_key = 'dnat_rules' + base_path = '/dnat_rules' + + # capabilities + allow_create = True + allow_fetch = True + allow_delete = True + allow_list = True + + # Properties + id = resource.Body('id') + #: Specifies the ID of the DNAT rule. + tenant_id = resource.Body('tenant_id') + #: Specifies the project ID. + nat_gateway_id = resource.Body('nat_gateway_id') + #: Specifies the NAT Gateway ID. + port_id = resource.Body('port_id') + #: Specifies the port ID of an ECS or BMS + #: Parameter is used in the VPC scenario. + #: This parameter is an alternative to private_ip + private_ip = resource.Body('private_ip') + #: Specifies the IP address of a Direct Connect connection. + #: Parameter is used in the Direct Connect scenario. + #: This parameter is an alternative to port_id. + internal_service_port = resource.Body('internal_service_port', type=int) + #: Specifies port used by ECS/BMS to provide services for external systems + floating_ip_id = resource.Body('floating_ip_id') + #: Specifies the EIP ID + floating_ip_address = resource.Body('floating_ip_address') + #: Specifies the EIP + external_service_port = resource.Body('external_service_port') + #: Specifies the port for providing external services. + protocol = resource.Body('protocol') + #: Specifies the protocol type. Currently TCP(6), UDP(17) and ANY(0) + status = resource.Body('status') + #: Specifies the status of the DNAT rule + admin_state_up = resource.Body('admin_state_up', type=bool) + #: Specifies whether DNAT rule is enabled / disabled + #: *true:* DNAT rule is enabled + #: *false:* DNAT rule is disabled + created_at = resource.Body('created_at') + #: Specifies when the rule is created. + #: The format is yyyy-mm-dd hh:mm:ss. diff --git a/otcextensions/sdk/nat/v2/nat_gateway.py b/otcextensions/sdk/nat/v2/nat_gateway.py index d87da1924..b33776bf7 100644 --- a/otcextensions/sdk/nat/v2/nat_gateway.py +++ b/otcextensions/sdk/nat/v2/nat_gateway.py @@ -17,10 +17,11 @@ class NatGateway(resource.Resource): # capabilities allow_create = True + allow_fetch = True + allow_commit = True allow_delete = True allow_list = True - #allow_get = True - + # Properties #: Specifies the ID of the NAT gateway. id = resource.Body('id') diff --git a/otcextensions/sdk/nat/v2/snat.py b/otcextensions/sdk/nat/v2/snat.py new file mode 100644 index 000000000..b9f49619e --- /dev/null +++ b/otcextensions/sdk/nat/v2/snat.py @@ -0,0 +1,54 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from openstack import resource + +class Snat(resource.Resource): + resources_key = 'snat_rules' + base_path = '/snat_rules' + + # capabilities + allow_create = True + allow_fetch = True + allow_delete = True + allow_list = True + + # Properties + id = resource.Body('id') + #: Specifies the ID of the SNAT rule. + tenant_id = resource.Body('tenant_id') + #: Specifies the project ID. + nat_gateway_id = resource.Body('nat_gateway_id') + #: Specifies the NAT Gateway ID. + network_id = resource.Body('network_id') + #: Specifies the network ID + cidr = resource.Body('cidr') + #: Specifies a subset of the VPC subnet CIDR block or a + #: CIDR block of Direct Connect connection. + source_type = resource.Body('source_type', type=int) + #: *0:* Either network_id or cidr can be specified in VPC + #: *1:* only cidr can be specified over a Direct Connect connection + #: Default: 0 + floating_ip_id = resource.Body('floating_ip_id') + #: Specifies the EIP ID + #: Multiple EIPs are separated using commas + floating_ip_address = resource.Body('floating_ip_address') + #: Specifies the EIP + #: Multiple EIPs are separated using commas + status = resource.Body('status') + #: Specifies the status of the SNAT rule + admin_state_up = resource.Body('admin_state_up', type=bool) + #: Specifies whether SNAT rule is enabled / disabled + #: *true:* SNAT rule is enabled + #: *false:* SNAT rule is disabled + created_at = resource.Body('created_at') + #: Specifies when the rule is created. + #: The format is yyyy-mm-dd hh:mm:ss. \ No newline at end of file From 4ad264490c0bebd54355085021c1ac6701591057 Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Tue, 12 Nov 2019 14:51:25 +0000 Subject: [PATCH 03/42] dnat and snat proxy functions added --- otcextensions/sdk/nat/v2/_proxy.py | 90 +++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 3 deletions(-) diff --git a/otcextensions/sdk/nat/v2/_proxy.py b/otcextensions/sdk/nat/v2/_proxy.py index 36cd1f64e..3a21b133a 100644 --- a/otcextensions/sdk/nat/v2/_proxy.py +++ b/otcextensions/sdk/nat/v2/_proxy.py @@ -10,6 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. from otcextensions.sdk.nat.v2 import nat_gateway as _nat_gateway +from otcextensions.sdk.nat.v2 import snat as _snat +from otcextensions.sdk.nat.v2 import dnat as _dnat from openstack import proxy @@ -26,7 +28,7 @@ def create_gateway(self, **attrs): return self._create(_nat_gateway.NatGateway, **attrs) def delete_gateway(self, id): - """Create a new gateway from attributes + """Delete a NAT gateway :param dict attrs: Keyword arguments which will be used to create a :class:`~otcextensions.sdk.nat.v2.nat_gateway.NatGateway` @@ -61,10 +63,92 @@ def update_nat_gateway(self, nat_gateway, **attrs): :param nat_gateway: Either the ID of a NAT Gateway or a :class:`~otcextensions.sdk.nat.v2.nat_gateway.NatGateway` instance. - :attrs kwargs: The attributes to update on the server represented + :attrs attrs: The attributes to update on the server represented by ``server``. :returns: The updated NAT Gateway :rtype: :class:`~otcextensions.sdk.nat.v2.nat_gateway.NatGateway` """ - return self._update(_server.Server, server, **attrs) + return self._update(_nat_gateway, nat_gateway, **attrs) + +# SNAT rules + + def create_snat_rule(self, **attrs): + """Create a new SNAT rule from attributes + + :param dict attrs: Keyword arguments which will be used to create + a :class:`~otcextensions.sdk.nat.v2.snat.Snat` + """ + return self._create(_snat.Snat, **attrs) + + def delete_snat_rule(self, id): + """Delete a SNAT rule + + :param dict attrs: Keyword arguments which will be used to create + a :class:`~otcextensions.sdk.nat.v2.snat.Snat` + """ + return self._delete(_snat.Snat, id=id) + + def get_snat_rule(self, snat_rule): + """Get a single SNAT rule + + :param snat_rule: The value can be the ID of a SNAT rule or a + :class:`~otcextensions.sdk.nat.v2.snat.Snat` + instance. + + :returns: One :class:`~otcextensions.sdk.nat.v2.snat.Snat` + :raises: :class:`~openstack.exceptions.ResourceNotFound` + when no resource can be found. + """ + return self._get(_snat.Snat, snat_rule) + + def snat_rules(self, **attrs): + """Return a generator of SNAT rules + + :param attrs query: Optional query parameters to be sent to limit + the resources being returned. + :returns: A generator of NAT Gateway objects + :rtype: :class:`~otcextensions.sdk.nat.v2.snat.SNAT` + """ + return self._list(_snat.Snat, **attrs) + +# DNAT rules + + def create_dnat_rule(self, **attrs): + """Create a new DNAT rule from attributes + + :param dict attrs: Keyword arguments which will be used to create + a :class:`~otcextensions.sdk.nat.v2.dnat.Dnat` + """ + return self._create(_dnat.Dnat, **attrs) + + def delete_dnat_rule(self, id): + """Delete a DNAT rule + + :param dict attrs: Keyword arguments which will be used to create + a :class:`~otcextensions.sdk.nat.v2.dnat.Dnat` + """ + return self._delete(_dnat.Dnat, id=id) + + def get_dnat_rule(self, dnat_rule): + """Get a single DNAT rule + + :param dnat_rule: The value can be the ID of a DNAT rule or a + :class:`~otcextensions.sdk.nat.v2.dnat.Dnat` + instance. + + :returns: One :class:`~otcextensions.sdk.nat.v2.dnat.Dnat` + :raises: :class:`~openstack.exceptions.ResourceNotFound` + when no resource can be found. + """ + return self._get(_dnat.Dnat, dnat_rule) + + def dnat_rules(self, **attrs): + """Return a generator of DNAT rules + + :param attrs query: Optional query parameters to be sent to limit + the resources being returned. + :returns: A generator of DNAT rules objects + :rtype: :class:`~otcextensions.sdk.nat.v2.dnat.Dnat` + """ + return self._list(_dnat.Dnat, **attrs) \ No newline at end of file From dcf62914fcc7243ea604a7ca6ab3e514155a8021 Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Fri, 15 Nov 2019 13:36:12 +0000 Subject: [PATCH 04/42] first running state --- otcextensions/sdk/__init__.py | 3 +-- otcextensions/sdk/nat/v2/_proxy.py | 2 ++ otcextensions/sdk/nat/v2/nat_gateway.py | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/otcextensions/sdk/__init__.py b/otcextensions/sdk/__init__.py index c153dac6c..bcf865dec 100644 --- a/otcextensions/sdk/__init__.py +++ b/otcextensions/sdk/__init__.py @@ -83,8 +83,7 @@ 'append_project_id': True, }, 'nat': { - 'service_type': 'nat', - 'append_project_id': True, + 'service_type': 'nat' }, 'obs': { 'service_type': 'obs', diff --git a/otcextensions/sdk/nat/v2/_proxy.py b/otcextensions/sdk/nat/v2/_proxy.py index 3a21b133a..080ffe71e 100644 --- a/otcextensions/sdk/nat/v2/_proxy.py +++ b/otcextensions/sdk/nat/v2/_proxy.py @@ -17,6 +17,8 @@ class Proxy(proxy.Proxy): + skip_discovery = True + # NAT Gateway def create_gateway(self, **attrs): diff --git a/otcextensions/sdk/nat/v2/nat_gateway.py b/otcextensions/sdk/nat/v2/nat_gateway.py index b33776bf7..a7951d81c 100644 --- a/otcextensions/sdk/nat/v2/nat_gateway.py +++ b/otcextensions/sdk/nat/v2/nat_gateway.py @@ -13,6 +13,7 @@ class NatGateway(resource.Resource): resources_key = 'nat_gateways' + resource_key = 'nat_gateway' base_path = '/nat_gateways' # capabilities @@ -37,7 +38,7 @@ class NatGateway(resource.Resource): #: *2:* medium type, supports up to 50,000 SNAT connections #: *3:* large type, supports up to 200,000 SNAT connections #: *4:* extra-large type, supports up to 1,000,000 SNAT connections - spec = resource.Body('spec', type=int) + spec = resource.Body('spec') #: Specifies the router ID router_id = resource.Body('router_id') #: Specifies the network ID of the downstream interface From ecb94c8a418c88f8c03ee3f2e40ff268df18929d Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Fri, 15 Nov 2019 15:07:14 +0000 Subject: [PATCH 05/42] gateway fixed --- otcextensions/sdk/nat/v2/_proxy.py | 50 +++++++++---------- otcextensions/sdk/nat/v2/dnat.py | 48 +++++++++--------- .../sdk/nat/v2/{nat_gateway.py => gateway.py} | 40 +++++++-------- otcextensions/sdk/nat/v2/snat.py | 44 ++++++++-------- 4 files changed, 90 insertions(+), 92 deletions(-) rename otcextensions/sdk/nat/v2/{nat_gateway.py => gateway.py} (87%) diff --git a/otcextensions/sdk/nat/v2/_proxy.py b/otcextensions/sdk/nat/v2/_proxy.py index 080ffe71e..8c19bc932 100644 --- a/otcextensions/sdk/nat/v2/_proxy.py +++ b/otcextensions/sdk/nat/v2/_proxy.py @@ -9,7 +9,7 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -from otcextensions.sdk.nat.v2 import nat_gateway as _nat_gateway +from otcextensions.sdk.nat.v2 import gateway as _gateway from otcextensions.sdk.nat.v2 import snat as _snat from otcextensions.sdk.nat.v2 import dnat as _dnat @@ -19,59 +19,59 @@ class Proxy(proxy.Proxy): skip_discovery = True -# NAT Gateway +# Gateway def create_gateway(self, **attrs): """Create a new gateway from attributes :param dict attrs: Keyword arguments which will be used to create - a :class:`~otcextensions.sdk.nat.v2.nat_gateway.NatGateway` + a :class:`~otcextensions.sdk.nat.v2.gateway.Gateway` """ - return self._create(_nat_gateway.NatGateway, **attrs) + return self._create(_gateway.Gateway, **attrs) def delete_gateway(self, id): - """Delete a NAT gateway + """Delete a gateway :param dict attrs: Keyword arguments which will be used to create - a :class:`~otcextensions.sdk.nat.v2.nat_gateway.NatGateway` + a :class:`~otcextensions.sdk.nat.v2.gateway.Gateway` """ - return self._delete(_nat_gateway.NatGateway, id=id) + return self._delete(_gateway.Gateway, id=id) def gateways(self, **attrs): - """Return a generator of NAT Gateways + """Return a generator of gateways :param kwargs query: Optional query parameters to be sent to limit the resources being returned. - :returns: A generator of NAT Gateway objects - :rtype: :class:`~otcextensions.sdk.nat.v2.nat_gateway.NatGateway` + :returns: A generator of gateway objects + :rtype: :class:`~otcextensions.sdk.nat.v2.gateway.Gateway` """ - return self._list(_nat_gateway.NatGateway, **attrs) + return self._list(_gateway.Gateway, **attrs) - def get_nat_gateway(self, nat_gateway): - """Get a single Nat Gateway + def get_gateway(self, gateway): + """Get a single gateway - :param nat_gateway: The value can be the ID of a NAT Gatway or a - :class:`~otcextensions.sdk.nat.v2.nat_gateway.NatGateway` + :param gateway: The value can be the ID of a NAT Gatway or a + :class:`~otcextensions.sdk.nat.v2.gateway.Gateway` instance. - :returns: One :class:`~otcextensions.sdk.nat.v2.nat_gateway.NatGateway` + :returns: One :class:`~otcextensions.sdk.nat.v2.gateway.Gateway` :raises: :class:`~openstack.exceptions.ResourceNotFound` when no resource can be found. """ - return self._get(_nat_gateway.NatGateway, nat_gateway) + return self._get(_gateway.Gateway, gateway) - def update_nat_gateway(self, nat_gateway, **attrs): - """Update a NAT Gateway + def update_gateway(self, gateway, **attrs): + """Update a gateway - :param nat_gateway: Either the ID of a NAT Gateway or a - :class:`~otcextensions.sdk.nat.v2.nat_gateway.NatGateway` instance. + :param gateway: Either the ID of a gateway or a + :class:`~otcextensions.sdk.nat.v2.gateway.Gateway` instance. :attrs attrs: The attributes to update on the server represented by ``server``. - :returns: The updated NAT Gateway - :rtype: :class:`~otcextensions.sdk.nat.v2.nat_gateway.NatGateway` + :returns: The updated gateway + :rtype: :class:`~otcextensions.sdk.nat.v2.gateway.Gateway` """ - return self._update(_nat_gateway, nat_gateway, **attrs) + return self._update(_gateway.Gateway, gateway, **attrs) # SNAT rules @@ -109,7 +109,7 @@ def snat_rules(self, **attrs): :param attrs query: Optional query parameters to be sent to limit the resources being returned. - :returns: A generator of NAT Gateway objects + :returns: A generator of gateway objects :rtype: :class:`~otcextensions.sdk.nat.v2.snat.SNAT` """ return self._list(_snat.Snat, **attrs) diff --git a/otcextensions/sdk/nat/v2/dnat.py b/otcextensions/sdk/nat/v2/dnat.py index 35a46c40b..4cada2e6f 100644 --- a/otcextensions/sdk/nat/v2/dnat.py +++ b/otcextensions/sdk/nat/v2/dnat.py @@ -22,36 +22,36 @@ class Dnat(resource.Resource): allow_list = True # Properties - id = resource.Body('id') + #: Specifies whether DNAT rule is enabled / disabled + #: *true:* DNAT rule is enabled + #: *false:* DNAT rule is disabled + admin_state_up = resource.Body('admin_state_up', type=bool) + #: Specifies when the rule is created. + #: The format is yyyy-mm-dd hh:mm:ss. + created_at = resource.Body('created_at') + #: Specifies the port for providing external services. + external_service_port = resource.Body('external_service_port') + #: Specifies the EIP + floating_ip_address = resource.Body('floating_ip_address') + #: Specifies the EIP ID + floating_ip_id = resource.Body('floating_ip_id') + #: Specifies the gateway ID. + gateway_id = resource.Body('gateway_id') #: Specifies the ID of the DNAT rule. - tenant_id = resource.Body('tenant_id') - #: Specifies the project ID. - nat_gateway_id = resource.Body('nat_gateway_id') - #: Specifies the NAT Gateway ID. - port_id = resource.Body('port_id') + id = resource.Body('id') + #: Specifies port used by ECS/BMS to provide services for external systems + internal_service_port = resource.Body('internal_service_port', type=int) #: Specifies the port ID of an ECS or BMS #: Parameter is used in the VPC scenario. #: This parameter is an alternative to private_ip - private_ip = resource.Body('private_ip') + port_id = resource.Body('port_id') #: Specifies the IP address of a Direct Connect connection. #: Parameter is used in the Direct Connect scenario. #: This parameter is an alternative to port_id. - internal_service_port = resource.Body('internal_service_port', type=int) - #: Specifies port used by ECS/BMS to provide services for external systems - floating_ip_id = resource.Body('floating_ip_id') - #: Specifies the EIP ID - floating_ip_address = resource.Body('floating_ip_address') - #: Specifies the EIP - external_service_port = resource.Body('external_service_port') - #: Specifies the port for providing external services. - protocol = resource.Body('protocol') + private_ip = resource.Body('private_ip') #: Specifies the protocol type. Currently TCP(6), UDP(17) and ANY(0) - status = resource.Body('status') + protocol = resource.Body('protocol') #: Specifies the status of the DNAT rule - admin_state_up = resource.Body('admin_state_up', type=bool) - #: Specifies whether DNAT rule is enabled / disabled - #: *true:* DNAT rule is enabled - #: *false:* DNAT rule is disabled - created_at = resource.Body('created_at') - #: Specifies when the rule is created. - #: The format is yyyy-mm-dd hh:mm:ss. + status = resource.Body('status') + #: Specifies the project ID. + tenant_id = resource.Body('tenant_id') \ No newline at end of file diff --git a/otcextensions/sdk/nat/v2/nat_gateway.py b/otcextensions/sdk/nat/v2/gateway.py similarity index 87% rename from otcextensions/sdk/nat/v2/nat_gateway.py rename to otcextensions/sdk/nat/v2/gateway.py index a7951d81c..cf5dc99f4 100644 --- a/otcextensions/sdk/nat/v2/nat_gateway.py +++ b/otcextensions/sdk/nat/v2/gateway.py @@ -11,7 +11,7 @@ # under the License. from openstack import resource -class NatGateway(resource.Resource): +class Gateway(resource.Resource): resources_key = 'nat_gateways' resource_key = 'nat_gateway' base_path = '/nat_gateways' @@ -24,33 +24,31 @@ class NatGateway(resource.Resource): allow_list = True # Properties - #: Specifies the ID of the NAT gateway. + #: Specifies whether GW is up or down + #: *true:* Gw is up + #: *false:* GW is down + admin_state_up = resource.Body('admin_state_up', type=bool) + #: Specifies when GW was is created + #: format is *yyyy-mm-dd hh:mm:ss* + created_at = resource.Body('created_at') + #: Provides description of gateway + description = resource.Body('description') + #: Specifies the ID of the gateway. id = resource.Body('id') - #: Specifies the project ID - tenant_id = resource.Body('tenant_id') - #: Specifies the name of the NAT gateway. + #: Specifies the network ID of the downstream interface + internal_network_id = resource.Body('internal_network_id') + #: Specifies the name of the gateway. #: Contains only digits, letters, underscores and hyphens name = resource.Body('name') - #: Provides description of NAT Gateway - description = resource.Body('description') - #: Specifies the type of the NAT gateway. + #: Specifies the router ID + router_id = resource.Body('router_id') + #: Specifies the type of the gateway. #: *1:* small type, supports up to 10,000 SNAT connections #: *2:* medium type, supports up to 50,000 SNAT connections #: *3:* large type, supports up to 200,000 SNAT connections #: *4:* extra-large type, supports up to 1,000,000 SNAT connections spec = resource.Body('spec') - #: Specifies the router ID - router_id = resource.Body('router_id') - #: Specifies the network ID of the downstream interface - internal_network_id = resource.Body('internal_network_id') #: Specifies the status status = resource.Body('status') - #: Specifies whether GW is up or down - #: *true:* Gw is up - #: *false:* GW is down - admin_state_up = resource.Body('admin_state_up', type=bool) - #: Specifies when GW was is created - #: format is *yyyy-mm-dd hh:mm:ss* - created_at = resource.Body('created_at') - - + #: Specifies the project ID + tenant_id = resource.Body('tenant_id') \ No newline at end of file diff --git a/otcextensions/sdk/nat/v2/snat.py b/otcextensions/sdk/nat/v2/snat.py index b9f49619e..53467da1b 100644 --- a/otcextensions/sdk/nat/v2/snat.py +++ b/otcextensions/sdk/nat/v2/snat.py @@ -22,33 +22,33 @@ class Snat(resource.Resource): allow_list = True # Properties - id = resource.Body('id') - #: Specifies the ID of the SNAT rule. - tenant_id = resource.Body('tenant_id') - #: Specifies the project ID. - nat_gateway_id = resource.Body('nat_gateway_id') - #: Specifies the NAT Gateway ID. - network_id = resource.Body('network_id') - #: Specifies the network ID - cidr = resource.Body('cidr') + #: Specifies the status of the SNAT rule + admin_state_up = resource.Body('admin_state_up', type=bool) #: Specifies a subset of the VPC subnet CIDR block or a #: CIDR block of Direct Connect connection. - source_type = resource.Body('source_type', type=int) - #: *0:* Either network_id or cidr can be specified in VPC - #: *1:* only cidr can be specified over a Direct Connect connection - #: Default: 0 - floating_ip_id = resource.Body('floating_ip_id') - #: Specifies the EIP ID + cidr = resource.Body('cidr') + #: Specifies when the rule is created. + #: The format is yyyy-mm-dd hh:mm:ss. + created_at = resource.Body('created_at') + #: Specifies the EIP #: Multiple EIPs are separated using commas floating_ip_address = resource.Body('floating_ip_address') - #: Specifies the EIP + #: Specifies the EIP ID #: Multiple EIPs are separated using commas - status = resource.Body('status') - #: Specifies the status of the SNAT rule - admin_state_up = resource.Body('admin_state_up', type=bool) + floating_ip_id = resource.Body('floating_ip_id') + #: Specifies the gateway ID. + gateway_id = resource.Body('gateway_id') + #: Specifies the ID of the SNAT rule. + id = resource.Body('id') + #: Specifies the network ID + network_id = resource.Body('network_id') + #: *0:* Either network_id or cidr can be specified in VPC + #: *1:* only cidr can be specified over a Direct Connect connection + #: Default: 0 + source_type = resource.Body('source_type', type=int) #: Specifies whether SNAT rule is enabled / disabled #: *true:* SNAT rule is enabled #: *false:* SNAT rule is disabled - created_at = resource.Body('created_at') - #: Specifies when the rule is created. - #: The format is yyyy-mm-dd hh:mm:ss. \ No newline at end of file + status = resource.Body('status') + #: Specifies the project ID. + tenant_id = resource.Body('tenant_id') \ No newline at end of file From de41e076ab88a04474ff8e2a9aa477cf9c813772 Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Fri, 15 Nov 2019 15:22:44 +0000 Subject: [PATCH 06/42] linting corrections --- otcextensions/sdk/__init__.py | 2 +- otcextensions/sdk/nat/v2/_proxy.py | 6 ++++-- otcextensions/sdk/nat/v2/dnat.py | 5 +++-- otcextensions/sdk/nat/v2/gateway.py | 5 +++-- otcextensions/sdk/nat/v2/snat.py | 9 +++++---- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/otcextensions/sdk/__init__.py b/otcextensions/sdk/__init__.py index bcf865dec..a02385686 100644 --- a/otcextensions/sdk/__init__.py +++ b/otcextensions/sdk/__init__.py @@ -84,7 +84,7 @@ }, 'nat': { 'service_type': 'nat' - }, + }, 'obs': { 'service_type': 'obs', 'require_ak': True, diff --git a/otcextensions/sdk/nat/v2/_proxy.py b/otcextensions/sdk/nat/v2/_proxy.py index 8c19bc932..cca88af90 100644 --- a/otcextensions/sdk/nat/v2/_proxy.py +++ b/otcextensions/sdk/nat/v2/_proxy.py @@ -15,6 +15,7 @@ from openstack import proxy + class Proxy(proxy.Proxy): skip_discovery = True @@ -64,7 +65,8 @@ def update_gateway(self, gateway, **attrs): """Update a gateway :param gateway: Either the ID of a gateway or a - :class:`~otcextensions.sdk.nat.v2.gateway.Gateway` instance. + :class:`~otcextensions.sdk.nat.v2.gateway.Gateway` + instance. :attrs attrs: The attributes to update on the server represented by ``server``. @@ -153,4 +155,4 @@ def dnat_rules(self, **attrs): :returns: A generator of DNAT rules objects :rtype: :class:`~otcextensions.sdk.nat.v2.dnat.Dnat` """ - return self._list(_dnat.Dnat, **attrs) \ No newline at end of file + return self._list(_dnat.Dnat, **attrs) diff --git a/otcextensions/sdk/nat/v2/dnat.py b/otcextensions/sdk/nat/v2/dnat.py index 4cada2e6f..c26f928ac 100644 --- a/otcextensions/sdk/nat/v2/dnat.py +++ b/otcextensions/sdk/nat/v2/dnat.py @@ -11,6 +11,7 @@ # under the License. from openstack import resource + class Dnat(resource.Resource): resources_key = 'dnat_rules' base_path = '/dnat_rules' @@ -20,7 +21,7 @@ class Dnat(resource.Resource): allow_fetch = True allow_delete = True allow_list = True - + # Properties #: Specifies whether DNAT rule is enabled / disabled #: *true:* DNAT rule is enabled @@ -54,4 +55,4 @@ class Dnat(resource.Resource): #: Specifies the status of the DNAT rule status = resource.Body('status') #: Specifies the project ID. - tenant_id = resource.Body('tenant_id') \ No newline at end of file + tenant_id = resource.Body('tenant_id') diff --git a/otcextensions/sdk/nat/v2/gateway.py b/otcextensions/sdk/nat/v2/gateway.py index cf5dc99f4..8080521f9 100644 --- a/otcextensions/sdk/nat/v2/gateway.py +++ b/otcextensions/sdk/nat/v2/gateway.py @@ -11,6 +11,7 @@ # under the License. from openstack import resource + class Gateway(resource.Resource): resources_key = 'nat_gateways' resource_key = 'nat_gateway' @@ -22,7 +23,7 @@ class Gateway(resource.Resource): allow_commit = True allow_delete = True allow_list = True - + # Properties #: Specifies whether GW is up or down #: *true:* Gw is up @@ -51,4 +52,4 @@ class Gateway(resource.Resource): #: Specifies the status status = resource.Body('status') #: Specifies the project ID - tenant_id = resource.Body('tenant_id') \ No newline at end of file + tenant_id = resource.Body('tenant_id') diff --git a/otcextensions/sdk/nat/v2/snat.py b/otcextensions/sdk/nat/v2/snat.py index 53467da1b..a2e556de3 100644 --- a/otcextensions/sdk/nat/v2/snat.py +++ b/otcextensions/sdk/nat/v2/snat.py @@ -11,6 +11,7 @@ # under the License. from openstack import resource + class Snat(resource.Resource): resources_key = 'snat_rules' base_path = '/snat_rules' @@ -20,16 +21,16 @@ class Snat(resource.Resource): allow_fetch = True allow_delete = True allow_list = True - + # Properties #: Specifies the status of the SNAT rule admin_state_up = resource.Body('admin_state_up', type=bool) - #: Specifies a subset of the VPC subnet CIDR block or a + #: Specifies a subset of the VPC subnet CIDR block or a #: CIDR block of Direct Connect connection. cidr = resource.Body('cidr') #: Specifies when the rule is created. #: The format is yyyy-mm-dd hh:mm:ss. - created_at = resource.Body('created_at') + created_at = resource.Body('created_at') #: Specifies the EIP #: Multiple EIPs are separated using commas floating_ip_address = resource.Body('floating_ip_address') @@ -51,4 +52,4 @@ class Snat(resource.Resource): #: *false:* SNAT rule is disabled status = resource.Body('status') #: Specifies the project ID. - tenant_id = resource.Body('tenant_id') \ No newline at end of file + tenant_id = resource.Body('tenant_id') From 2d10ad3c6c381db3035ad9f34e63b75f541defb4 Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Mon, 18 Nov 2019 15:46:18 +0000 Subject: [PATCH 07/42] Minor changes, tested successfully --- otcextensions/sdk/nat/v2/dnat.py | 13 ++++++++++++- otcextensions/sdk/nat/v2/gateway.py | 6 ++++++ otcextensions/sdk/nat/v2/snat.py | 15 +++++++++++---- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/otcextensions/sdk/nat/v2/dnat.py b/otcextensions/sdk/nat/v2/dnat.py index c26f928ac..fc7f842dd 100644 --- a/otcextensions/sdk/nat/v2/dnat.py +++ b/otcextensions/sdk/nat/v2/dnat.py @@ -14,6 +14,7 @@ class Dnat(resource.Resource): resources_key = 'dnat_rules' + resource_key = 'dnat_rule' base_path = '/dnat_rules' # capabilities @@ -22,6 +23,14 @@ class Dnat(resource.Resource): allow_delete = True allow_list = True + _query_mapping = resource.QueryParameters( + 'admin_state_up', 'cidr', 'created_at', 'external_service_port', + 'floating_ip_address', 'floating_ip_id', 'id', + 'internal_service_port', 'limit', 'nat_gateway_id', 'network_id', + 'port_id', 'private_id', 'protocol', 'source_type', 'status', + 'tenant_id' + ) + # Properties #: Specifies whether DNAT rule is enabled / disabled #: *true:* DNAT rule is enabled @@ -31,7 +40,7 @@ class Dnat(resource.Resource): #: The format is yyyy-mm-dd hh:mm:ss. created_at = resource.Body('created_at') #: Specifies the port for providing external services. - external_service_port = resource.Body('external_service_port') + external_service_port = resource.Body('external_service_port', type=int) #: Specifies the EIP floating_ip_address = resource.Body('floating_ip_address') #: Specifies the EIP ID @@ -42,6 +51,8 @@ class Dnat(resource.Resource): id = resource.Body('id') #: Specifies port used by ECS/BMS to provide services for external systems internal_service_port = resource.Body('internal_service_port', type=int) + #: Specifies the ID of the NAT gateway. + nat_gateway_id = resource.Body('nat_gateway_id') #: Specifies the port ID of an ECS or BMS #: Parameter is used in the VPC scenario. #: This parameter is an alternative to private_ip diff --git a/otcextensions/sdk/nat/v2/gateway.py b/otcextensions/sdk/nat/v2/gateway.py index 8080521f9..0b42f1172 100644 --- a/otcextensions/sdk/nat/v2/gateway.py +++ b/otcextensions/sdk/nat/v2/gateway.py @@ -24,6 +24,12 @@ class Gateway(resource.Resource): allow_delete = True allow_list = True + _query_mapping = resource.QueryParameters( + 'admin_state_up', 'created_at', 'description', 'id', + 'internal_network_id', 'limit', 'name', 'router_id', + 'spec', 'status', 'tenant_id' + ) + # Properties #: Specifies whether GW is up or down #: *true:* Gw is up diff --git a/otcextensions/sdk/nat/v2/snat.py b/otcextensions/sdk/nat/v2/snat.py index a2e556de3..dc8afcc7e 100644 --- a/otcextensions/sdk/nat/v2/snat.py +++ b/otcextensions/sdk/nat/v2/snat.py @@ -14,6 +14,7 @@ class Snat(resource.Resource): resources_key = 'snat_rules' + resource_key = 'snat_rule' base_path = '/snat_rules' # capabilities @@ -22,6 +23,12 @@ class Snat(resource.Resource): allow_delete = True allow_list = True + _query_mapping = resource.QueryParameters( + 'admin_state_up', 'cidr', 'created_at', 'floating_ip_address', + 'floating_ip_id', 'id', 'limit', 'nat_gateway_id', 'network_id', + 'source_type', 'status', 'tenant_id' + ) + # Properties #: Specifies the status of the SNAT rule admin_state_up = resource.Body('admin_state_up', type=bool) @@ -37,12 +44,14 @@ class Snat(resource.Resource): #: Specifies the EIP ID #: Multiple EIPs are separated using commas floating_ip_id = resource.Body('floating_ip_id') - #: Specifies the gateway ID. - gateway_id = resource.Body('gateway_id') #: Specifies the ID of the SNAT rule. id = resource.Body('id') + #: Specifies the gateway ID. + nat_gateway_id = resource.Body('nat_gateway_id') #: Specifies the network ID network_id = resource.Body('network_id') + #: Specifies the project ID. + tenant_id = resource.Body('tenant_id') #: *0:* Either network_id or cidr can be specified in VPC #: *1:* only cidr can be specified over a Direct Connect connection #: Default: 0 @@ -51,5 +60,3 @@ class Snat(resource.Resource): #: *true:* SNAT rule is enabled #: *false:* SNAT rule is disabled status = resource.Body('status') - #: Specifies the project ID. - tenant_id = resource.Body('tenant_id') From e1b927ff96e5d0b86116ab01bcd716b875b65c3e Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Tue, 19 Nov 2019 15:28:52 +0000 Subject: [PATCH 08/42] unittest nat.test_snat nat.test_dnat and nat.test_gateway added --- otcextensions/tests/unit/sdk/nat/__init__.py | 0 .../tests/unit/sdk/nat/v2/test_dnat.py | 67 +++++++++++++++++++ .../tests/unit/sdk/nat/v2/test_gateway.py | 60 +++++++++++++++++ .../tests/unit/sdk/nat/v2/test_proxy.py | 0 .../tests/unit/sdk/nat/v2/test_snat.py | 61 +++++++++++++++++ 5 files changed, 188 insertions(+) create mode 100644 otcextensions/tests/unit/sdk/nat/__init__.py create mode 100644 otcextensions/tests/unit/sdk/nat/v2/test_dnat.py create mode 100644 otcextensions/tests/unit/sdk/nat/v2/test_gateway.py create mode 100644 otcextensions/tests/unit/sdk/nat/v2/test_proxy.py create mode 100644 otcextensions/tests/unit/sdk/nat/v2/test_snat.py diff --git a/otcextensions/tests/unit/sdk/nat/__init__.py b/otcextensions/tests/unit/sdk/nat/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/otcextensions/tests/unit/sdk/nat/v2/test_dnat.py b/otcextensions/tests/unit/sdk/nat/v2/test_dnat.py new file mode 100644 index 000000000..60f2c0ac2 --- /dev/null +++ b/otcextensions/tests/unit/sdk/nat/v2/test_dnat.py @@ -0,0 +1,67 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from openstack.tests.unit import base + +from otcextensions.sdk.nat.v2 import dnat + + +INSTANCE_ID = '5b95c675-69c2-4656-ba06-58ff72e1d338' +EXAMPLE = { + 'floating_ip_id': 'bdc10a4c-d81a-41ec-adf7-de857f7c812a', + 'status': 'ACTIVE', + 'nat_gateway_id': 'a78fb3eb-1654-4710-8742-3fc49d5f04f8', + 'admin_state_up': True, + 'port_id': '9a469561-daac-4c94-88f5-39366e5ea193', + 'internal_service_port': 993, + 'protocol': 'TCP', + 'tenant_id': '27e25061336f4af590faeabeb7fcd9a3', + 'created_at': '2017-11-18 07:54:21.665430', + 'id': INSTANCE_ID, + 'floating_ip_address': '5.21.11.226', + 'external_service_port': 242, + 'private_ip': "", +} + + +class TestDnat(base.TestCase): + + def test_basic(self): + sot = dnat.Dnat() + self.assertEqual('dnat_rule', sot.resource_key) + self.assertEqual('dnat_rules', sot.resources_key) + path = '/dnat_rules' + self.assertEqual(path, sot.base_path) + self.assertTrue(sot.allow_list) + self.assertTrue(sot.allow_create) + self.assertTrue(sot.allow_fetch) + self.assertFalse(sot.allow_commit) + self.assertTrue(sot.allow_delete) + + def test_make_it(self): + sot = dnat.Dnat(**EXAMPLE) + self.assertEqual(EXAMPLE['floating_ip_id'], sot.floating_ip_id) + self.assertEqual(EXAMPLE['status'], sot.status) + self.assertEqual(EXAMPLE['nat_gateway_id'], sot.nat_gateway_id) + self.assertEqual(EXAMPLE['admin_state_up'], sot.admin_state_up) + self.assertEqual(EXAMPLE['port_id'], sot.port_id) + self.assertEqual(EXAMPLE['internal_service_port'], + sot.internal_service_port) + self.assertEqual(EXAMPLE['protocol'], sot.protocol) + self.assertEqual(EXAMPLE['tenant_id'], sot.tenant_id) + self.assertEqual(EXAMPLE['created_at'], sot.created_at) + self.assertEqual(EXAMPLE['id'], sot.id) + self.assertEqual(EXAMPLE['floating_ip_address'], + sot.floating_ip_address) + self.assertEqual(EXAMPLE['external_service_port'], + sot.external_service_port) + self.assertEqual(EXAMPLE['private_ip'], sot.private_ip) diff --git a/otcextensions/tests/unit/sdk/nat/v2/test_gateway.py b/otcextensions/tests/unit/sdk/nat/v2/test_gateway.py new file mode 100644 index 000000000..ae462908b --- /dev/null +++ b/otcextensions/tests/unit/sdk/nat/v2/test_gateway.py @@ -0,0 +1,60 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from openstack.tests.unit import base + +from otcextensions.sdk.nat.v2 import gateway + + +INSTANCE_NAME = 'GATEWAYNAME' +INSTANCE_ID = 'a78fb3eb-1654-4710-8742-3fc49d5f04f8' +EXAMPLE = { + 'router_id': 'd84f345c-80a1-4fa2-a39c-d0d397c3f09a', + 'status': 'PENDING_CREATE', + 'description': 'Test Gateway Response', + 'admin_state_up': True, + 'tenant_id': '27e25061336f4af590faeabeb7fcd9a3', + 'created_at': '2017-11-18 07:34:32.203044', + 'spec': '2', + 'internal_network_id': '89d66639-aacb-4929-969d-07080b0f9fd9', + 'id': INSTANCE_ID, + 'name': INSTANCE_NAME +} + + +class TestGateway(base.TestCase): + + def test_basic(self): + sot = gateway.Gateway() + self.assertEqual('nat_gateway', sot.resource_key) + self.assertEqual('nat_gateways', sot.resources_key) + path = '/nat_gateways' + self.assertEqual(path, sot.base_path) + self.assertTrue(sot.allow_list) + self.assertTrue(sot.allow_create) + self.assertTrue(sot.allow_fetch) + self.assertTrue(sot.allow_commit) + self.assertTrue(sot.allow_delete) + + def test_make_it(self): + sot = gateway.Gateway(**EXAMPLE) + self.assertEqual(EXAMPLE['router_id'], sot.router_id) + self.assertEqual(EXAMPLE['status'], sot.status) + self.assertEqual(EXAMPLE['description'], sot.description) + self.assertEqual(EXAMPLE['admin_state_up'], sot.admin_state_up) + self.assertEqual(EXAMPLE['tenant_id'], sot.tenant_id) + self.assertEqual(EXAMPLE['created_at'], sot.created_at) + self.assertEqual(EXAMPLE['spec'], sot.spec) + self.assertEqual(EXAMPLE['internal_network_id'], + sot.internal_network_id) + self.assertEqual(EXAMPLE['id'], sot.id) + self.assertEqual(EXAMPLE['name'], sot.name) diff --git a/otcextensions/tests/unit/sdk/nat/v2/test_proxy.py b/otcextensions/tests/unit/sdk/nat/v2/test_proxy.py new file mode 100644 index 000000000..e69de29bb diff --git a/otcextensions/tests/unit/sdk/nat/v2/test_snat.py b/otcextensions/tests/unit/sdk/nat/v2/test_snat.py new file mode 100644 index 000000000..7a801a63f --- /dev/null +++ b/otcextensions/tests/unit/sdk/nat/v2/test_snat.py @@ -0,0 +1,61 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from openstack.tests.unit import base + +from otcextensions.sdk.nat.v2 import snat + + +INSTANCE_ID = '5b95c675-69c2-4656-ba06-58ff72e1d338' +EXAMPLE = { + 'floating_ip_id': 'bdc10a4c-d81a-41ec-adf7-de857f7c812a', + 'status': 'PENDING_CREATE', + 'nat_gateway_id': 'a78fb3eb-1654-4710-8742-3fc49d5f04f8', + 'admin_state_up': True, + 'network_id': 'eaad9cd6-2372-4be1-9535-9bd37210ae7b', + 'cidr': None, + 'source_type': 0, + 'tenant_id': '27e25061336f4af590faeabeb7fcd9a3', + 'created_at': '2017-11-18 07:54:21.665430', + 'id': INSTANCE_ID, + 'floating_ip_address': '5.21.11.226' +} + + +class TestSnat(base.TestCase): + + def test_basic(self): + sot = snat.Snat() + self.assertEqual('snat_rule', sot.resource_key) + self.assertEqual('snat_rules', sot.resources_key) + path = '/snat_rules' + self.assertEqual(path, sot.base_path) + self.assertTrue(sot.allow_list) + self.assertTrue(sot.allow_create) + self.assertTrue(sot.allow_fetch) + self.assertFalse(sot.allow_commit) + self.assertTrue(sot.allow_delete) + + def test_make_it(self): + sot = snat.Snat(**EXAMPLE) + self.assertEqual(EXAMPLE['floating_ip_id'], sot.floating_ip_id) + self.assertEqual(EXAMPLE['status'], sot.status) + self.assertEqual(EXAMPLE['nat_gateway_id'], sot.nat_gateway_id) + self.assertEqual(EXAMPLE['admin_state_up'], sot.admin_state_up) + self.assertEqual(EXAMPLE['network_id'], sot.network_id) + self.assertIsNone(sot.cidr) + self.assertEqual(EXAMPLE['source_type'], sot.source_type) + self.assertEqual(EXAMPLE['tenant_id'], sot.tenant_id) + self.assertEqual(EXAMPLE['created_at'], sot.created_at) + self.assertEqual(EXAMPLE['id'], sot.id) + self.assertEqual(EXAMPLE['floating_ip_address'], + sot.floating_ip_address) From 03b6891a574b09eb9b3ca1fddedab03e417275f5 Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Wed, 27 Nov 2019 14:16:21 +0000 Subject: [PATCH 09/42] smn initialization --- otcextensions/sdk/nat/v2/_proxy.py | 12 +-- otcextensions/sdk/smn/__init_.py | 0 .../tests/unit/sdk/nat/v2/test_proxy.py | 90 +++++++++++++++++++ 3 files changed, 96 insertions(+), 6 deletions(-) create mode 100644 otcextensions/sdk/smn/__init_.py diff --git a/otcextensions/sdk/nat/v2/_proxy.py b/otcextensions/sdk/nat/v2/_proxy.py index cca88af90..c13c7668b 100644 --- a/otcextensions/sdk/nat/v2/_proxy.py +++ b/otcextensions/sdk/nat/v2/_proxy.py @@ -30,13 +30,13 @@ def create_gateway(self, **attrs): """ return self._create(_gateway.Gateway, **attrs) - def delete_gateway(self, id): + def delete_gateway(self, id, ignore_missing=True): """Delete a gateway :param dict attrs: Keyword arguments which will be used to create a :class:`~otcextensions.sdk.nat.v2.gateway.Gateway` """ - return self._delete(_gateway.Gateway, id=id) + return self._delete(_gateway.Gateway, id=id, ignore_missing=ignore_missing) def gateways(self, **attrs): """Return a generator of gateways @@ -85,13 +85,13 @@ def create_snat_rule(self, **attrs): """ return self._create(_snat.Snat, **attrs) - def delete_snat_rule(self, id): + def delete_snat_rule(self, id, ignore_missing=True): """Delete a SNAT rule :param dict attrs: Keyword arguments which will be used to create a :class:`~otcextensions.sdk.nat.v2.snat.Snat` """ - return self._delete(_snat.Snat, id=id) + return self._delete(_snat.Snat, id=id, ignore_missing=ignore_missing) def get_snat_rule(self, snat_rule): """Get a single SNAT rule @@ -126,13 +126,13 @@ def create_dnat_rule(self, **attrs): """ return self._create(_dnat.Dnat, **attrs) - def delete_dnat_rule(self, id): + def delete_dnat_rule(self, id, ignore_missing=True): """Delete a DNAT rule :param dict attrs: Keyword arguments which will be used to create a :class:`~otcextensions.sdk.nat.v2.dnat.Dnat` """ - return self._delete(_dnat.Dnat, id=id) + return self._delete(_dnat.Dnat, id=id, ignore_missing=ignore_missing) def get_dnat_rule(self, dnat_rule): """Get a single DNAT rule diff --git a/otcextensions/sdk/smn/__init_.py b/otcextensions/sdk/smn/__init_.py new file mode 100644 index 000000000..e69de29bb diff --git a/otcextensions/tests/unit/sdk/nat/v2/test_proxy.py b/otcextensions/tests/unit/sdk/nat/v2/test_proxy.py index e69de29bb..9f5140483 100644 --- a/otcextensions/tests/unit/sdk/nat/v2/test_proxy.py +++ b/otcextensions/tests/unit/sdk/nat/v2/test_proxy.py @@ -0,0 +1,90 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from otcextensions.sdk.nat.v2 import _proxy +from otcextensions.sdk.nat.v2 import snat +from otcextensions.sdk.nat.v2 import dnat +from otcextensions.sdk.nat.v2 import gateway + +from openstack.tests.unit import test_proxy_base + + +class TestNatProxy(test_proxy_base.TestProxyBase): + def setUp(self): + super(TestNatProxy, self).setUp() + self.proxy = _proxy.Proxy(self.session) + + +class TestNatGateway(TestNatProxy): + def test_gateway_create(self): + self.verify_create(self.proxy.create_gateway, gateway.Gateway, + method_kwargs={'name': 'id'}, + expected_kwargs={'name': 'id'}) + + def test_gateway_delete(self): + self.verify_delete(self.proxy.delete_gateway, + gateway.Gateway, True) + + def test_gateway_get(self): + self.verify_get(self.proxy.get_gateway, gateway.Gateway) + + def test_gateways(self): + self.verify_list(self.proxy.gateways, gateway.Gateway) + + def test_gateway_update(self): + self.verify_update(self.proxy.update_gateway, gateway.Gateway) + + +class TestNatSnatRule(TestNatProxy): + def test_snat_rule_create(self): + self.verify_create(self.proxy.create_snat_rule, snat.Snat, + method_kwargs={'name': 'id'}, + expected_kwargs={'name': 'id'}) + + def test_snat_rule_delete(self): + self.verify_delete(self.proxy.delete_snat_rule, + snat.Snat, True) + + def test_snat_rule_get(self): + self.verify_get(self.proxy.get_snat_rule, snat.Snat) + + def test_snat_rules(self): + self.verify_list(self.proxy.snat_rules, snat.Snat) + + +class TestNatDnatRule(TestNatProxy): + def test_dnat_rule_create(self): + self.verify_create(self.proxy.create_dnat_rule, dnat.Dnat, + method_kwargs={'name': 'id'}, + expected_kwargs={'name': 'id'}) + + def test_dnat_rule_delete(self): + self.verify_delete(self.proxy.delete_dnat_rule, + dnat.Dnat, True) + + def test_dnat_rule_get(self): + self.verify_get(self.proxy.get_dnat_rule, dnat.Dnat) + + def test_dnat_rules(self): + self.verify_list(self.proxy.dnat_rules, dnat.Dnat) From b19763152b8b098f263bd0411bf67bb53982736a Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Wed, 27 Nov 2019 15:58:35 +0000 Subject: [PATCH 10/42] ces initalisation --- otcextensions/sdk/__init__.py | 4 +++ otcextensions/sdk/ces/__init__.py | 0 otcextensions/sdk/ces/ces_service.py | 23 +++++++++++++++++ otcextensions/sdk/ces/v2/__init__.py | 0 otcextensions/sdk/ces/v2/metric.py | 37 ++++++++++++++++++++++++++++ 5 files changed, 64 insertions(+) create mode 100644 otcextensions/sdk/ces/__init__.py create mode 100644 otcextensions/sdk/ces/ces_service.py create mode 100644 otcextensions/sdk/ces/v2/__init__.py create mode 100644 otcextensions/sdk/ces/v2/metric.py diff --git a/otcextensions/sdk/__init__.py b/otcextensions/sdk/__init__.py index a02385686..aba8345fc 100644 --- a/otcextensions/sdk/__init__.py +++ b/otcextensions/sdk/__init__.py @@ -52,6 +52,10 @@ 'endpoint_service_type': 'ccev2.0', # 'append_project_id': False, }, + 'ces': { + 'service_type': 'ces', + 'append_project_id': True + } 'cts': { 'service_type': 'cts', # 'append_project_id': True, diff --git a/otcextensions/sdk/ces/__init__.py b/otcextensions/sdk/ces/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/otcextensions/sdk/ces/ces_service.py b/otcextensions/sdk/ces/ces_service.py new file mode 100644 index 000000000..1f301bd93 --- /dev/null +++ b/otcextensions/sdk/ces/ces_service.py @@ -0,0 +1,23 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from openstack import service_description + +from otcextensions.sdk.ces.v2 import _proxy + + +class CesService(service_description.ServiceDescription): + """The Cloud Eye service.""" + + supported_versions = { + '2': _proxy.Proxy + } + diff --git a/otcextensions/sdk/ces/v2/__init__.py b/otcextensions/sdk/ces/v2/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/otcextensions/sdk/ces/v2/metric.py b/otcextensions/sdk/ces/v2/metric.py new file mode 100644 index 000000000..0840f7e6e --- /dev/null +++ b/otcextensions/sdk/ces/v2/metric.py @@ -0,0 +1,37 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from openstack import resource + + +class Metric(resource.Resource): + resource_key = 'metric' + resources_key = 'metrics' + base_path = '/metrics' + + # capabilities + allow_list = True + + _query_mapping = resource.QueryParameters( + 'dim', 'limit', 'metric_name', 'namespace', 'order', 'start' + ) + + # Properties + #: List of metric dimensions + #: dimension.name: object type e.g. ECS (instance_id) + #: dimension.value: object id e.g. ECS ID + dimensions = resource.Body('dimensions') + #: Shows the metric name + metric_name = resource.Body('metric_name') + #: Indicates the metric namespaces + namespace = resource.Body('namespace') + #: Indicates the metric unit + unit = resource.Body('unit') From baf788783c4c65c7f9523c4fa13aeb3dd8e2151a Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Mon, 2 Dec 2019 10:23:08 +0000 Subject: [PATCH 11/42] new ordering, alarm resource created --- otcextensions/sdk/__init__.py | 5 +- otcextensions/sdk/ces/ces_service.py | 4 +- otcextensions/sdk/ces/{v2 => v1}/__init__.py | 0 otcextensions/sdk/ces/v1/_proxy.py | 28 +++++ otcextensions/sdk/ces/v1/alarm.py | 121 +++++++++++++++++++ otcextensions/sdk/ces/v1/favorit_metric.py | 41 +++++++ otcextensions/sdk/ces/{v2 => v1}/metric.py | 13 +- 7 files changed, 207 insertions(+), 5 deletions(-) rename otcextensions/sdk/ces/{v2 => v1}/__init__.py (100%) create mode 100644 otcextensions/sdk/ces/v1/_proxy.py create mode 100644 otcextensions/sdk/ces/v1/alarm.py create mode 100644 otcextensions/sdk/ces/v1/favorit_metric.py rename otcextensions/sdk/ces/{v2 => v1}/metric.py (77%) diff --git a/otcextensions/sdk/__init__.py b/otcextensions/sdk/__init__.py index aba8345fc..c8c5e7470 100644 --- a/otcextensions/sdk/__init__.py +++ b/otcextensions/sdk/__init__.py @@ -54,8 +54,9 @@ }, 'ces': { 'service_type': 'ces', - 'append_project_id': True - } + 'endpoint_service_type': 'cesv1', + 'append_project_id': False, + }, 'cts': { 'service_type': 'cts', # 'append_project_id': True, diff --git a/otcextensions/sdk/ces/ces_service.py b/otcextensions/sdk/ces/ces_service.py index 1f301bd93..5565d2822 100644 --- a/otcextensions/sdk/ces/ces_service.py +++ b/otcextensions/sdk/ces/ces_service.py @@ -11,13 +11,13 @@ # under the License. from openstack import service_description -from otcextensions.sdk.ces.v2 import _proxy +from otcextensions.sdk.ces.v1 import _proxy class CesService(service_description.ServiceDescription): """The Cloud Eye service.""" supported_versions = { - '2': _proxy.Proxy + '1': _proxy.Proxy } diff --git a/otcextensions/sdk/ces/v2/__init__.py b/otcextensions/sdk/ces/v1/__init__.py similarity index 100% rename from otcextensions/sdk/ces/v2/__init__.py rename to otcextensions/sdk/ces/v1/__init__.py diff --git a/otcextensions/sdk/ces/v1/_proxy.py b/otcextensions/sdk/ces/v1/_proxy.py new file mode 100644 index 000000000..2e5692142 --- /dev/null +++ b/otcextensions/sdk/ces/v1/_proxy.py @@ -0,0 +1,28 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from otcextensions.sdk.ces.v1 import metric as _metric + +from openstack import proxy + +class Proxy(proxy.Proxy): + + # Metrics + def metric(self, **query): + """Return a generator of metrics + + :param kwargs query: Optional query parameters to be sent to limit + the resources being returned. + :returns: A generator of metric objects + :rtype: :class:`~openstack.database.v1.metric.Metric` + """ + return self._list(_metric.Metric, **query) + diff --git a/otcextensions/sdk/ces/v1/alarm.py b/otcextensions/sdk/ces/v1/alarm.py new file mode 100644 index 000000000..1672babe1 --- /dev/null +++ b/otcextensions/sdk/ces/v1/alarm.py @@ -0,0 +1,121 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from openstack import resource + + +#class MetaDataSpec(resource.Resource): + + # Properties + # Number of returned results / alarms +# count = resource.Body('count') + # Indicates pagination marker +# marker = resource.Body('marker') + # Number of total queried results / alarms +# total = resource.Body('total') + +class AlarmActionsSpec(resource.Resource): + + # Properties + # notification list ID + notificationList = resource.Body('notificationList') + # Indicates the type of action triggered by an alarm. + # Value can be notication or autoscaling + typestring = resource.Body('type') + + +class ConditionSpec(resource.Resource): + + # Properties + # indicates the comparison operator + # values can be <,=,>,>= or <= + comparison_operator = resource.Body('comparison_operator') + # Indicates how many consecutive times an alarm has + # been generated + count = resource.Body('unit', type=int) + # indicates the data rollup method + # values: Max., Min., average, Sum. or Variance + filterstring = resource.Body('filter') + # Indicates the interval (in seconds) for checking + # whether the configured alarm rules are met + period = resource.Body('period', type=int) + # Data unit + unit = resource.Body('unit') + # Alarm threshold + value = resource.Body('value', type=int) + + +class DimensionsSpec(resource.Resource): + + # Properties + #: dimension.name: object type e.g. ECS (instance_id) + name = resource.Body('name') + #: dimension.value: object id e.g. ECS ID + value = resource.Body('value') + + +class MetricSpec(resource.Resource): + + # Properties + # List of metric dimensions + dimensions = resource.Body('dimensions', type=list, list_type=DimensionsSpec) + # Metric name, such as cpu_util in ECS metrics + metric_name = resource.Body('metric_name') + # Metric Namespace + namespace = resource.Body('namespace') + + +class Alarm(resource.Resource): + resources_key = 'metric_alarms' + base_path = '/alarms' + + # capabilities + allow_list = True + + _query_mapping = resource.QueryParameters( + 'limit', 'order', 'start' + ) + + # Properties + # Indicates the action triggered by clearing an alarm + alarm_actions = resource.Body('alarm_actions', type=AlarmActionsSpec) + # Indicates whether an action will be triggered by an alarm + # True: action will be triggered + # False: action will not be triggered + alarm_action_enabled = resource.Body('alarm_action_enabled', type=bool) + # Description of the alarm + alarm_description = resource.Body('alarm_description') + # Alarm is enabled (True) or disabled (False) + alarm_enabled = resource.Body('alarm_enabled', type=bool) + # alarm rule ID + alarm_id = resource.Body('alarm_id') + # alarm severity + # values: 1: critical, 2: major, 3: minor, 4: informational alarm + alarm_level = resource.Body('alarm_level', type=int) + # Name of the alarm + alarm_name = resource.Body('alarm_name') + # Alarm status + # ok: alarm status is normal + # alarm: an alarm is generated + # insufficient_data: required data is insufficient + alarm_state = resource.Body('alarm_state') + # Describes alarm triggering condititon + condititon = resource.Body('condition', type=ConditionSpec) + # Metadata of query results and paging information + #meta_data = resource.Body('meta_data', type=MetaDataSpec) + # List of alarm objects + #metric_alarms = resource.Body('metric_alarms', type=list, list_type=MetricAlarmsSpec) + # Specification of specific alarm + metric = resource.Body('metric', type=MetricSpec) + # Time when alarm status changed + # UNIX timestamp in ms + update_time = resource.Body('update_time') + diff --git a/otcextensions/sdk/ces/v1/favorit_metric.py b/otcextensions/sdk/ces/v1/favorit_metric.py new file mode 100644 index 000000000..a110a4ab9 --- /dev/null +++ b/otcextensions/sdk/ces/v1/favorit_metric.py @@ -0,0 +1,41 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from openstack import resource + +class DimensionsSpec(resource.Resource): + + # Properties + #: dimension.name: object type e.g. ECS (instance_id) + name = resource.Body('name') + #: dimension.value: object id e.g. ECS ID + value = resource.Body('value') + + +class FavoriteMetric(resource.Resource): + resources_key = 'metrics' + base_path = '/favorite-metrics' + + # capabilities + allow_list = True + + + # Properties + #: List of metric dimensions + #: dimension.name: object type e.g. ECS (instance_id) + #: dimension.value: object id e.g. ECS ID + dimensions = resource.Body('dimensions', type=DimensionsSpec) + #: Shows the metric name + metric_name = resource.Body('metric_name') + #: Indicates the metric namespaces + namespace = resource.Body('namespace') + #: Indicates the metric unit + unit = resource.Body('unit') diff --git a/otcextensions/sdk/ces/v2/metric.py b/otcextensions/sdk/ces/v1/metric.py similarity index 77% rename from otcextensions/sdk/ces/v2/metric.py rename to otcextensions/sdk/ces/v1/metric.py index 0840f7e6e..cf4a9e6ab 100644 --- a/otcextensions/sdk/ces/v2/metric.py +++ b/otcextensions/sdk/ces/v1/metric.py @@ -12,6 +12,15 @@ from openstack import resource +class DimensionsSpec(resource.Resource): + + # Properties + #: dimension.name: object type e.g. ECS (instance_id) + name = resource.Body('name') + #: dimension.value: object id e.g. ECS ID + value = resource.Body('value') + + class Metric(resource.Resource): resource_key = 'metric' resources_key = 'metrics' @@ -28,10 +37,12 @@ class Metric(resource.Resource): #: List of metric dimensions #: dimension.name: object type e.g. ECS (instance_id) #: dimension.value: object id e.g. ECS ID - dimensions = resource.Body('dimensions') + dimensions = resource.Body('dimensions', type=list, list_type=DimensionsSpec) #: Shows the metric name metric_name = resource.Body('metric_name') #: Indicates the metric namespaces namespace = resource.Body('namespace') #: Indicates the metric unit unit = resource.Body('unit') + + From a7119136a4b1bc0976351d69ed6b0b7733e9fe95 Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Tue, 3 Dec 2019 14:40:02 +0000 Subject: [PATCH 12/42] quota added and flake8 corrections --- otcextensions/sdk/ces/ces_service.py | 1 - otcextensions/sdk/ces/v1/_proxy.py | 12 +++++++- otcextensions/sdk/ces/v1/alarm.py | 26 +++++++--------- otcextensions/sdk/ces/v1/favorit_metric.py | 2 +- otcextensions/sdk/ces/v1/metric.py | 5 ++- otcextensions/sdk/ces/v1/metric_data.py | 23 ++++++++++++++ otcextensions/sdk/ces/v1/quota.py | 36 ++++++++++++++++++++++ 7 files changed, 84 insertions(+), 21 deletions(-) create mode 100644 otcextensions/sdk/ces/v1/metric_data.py create mode 100644 otcextensions/sdk/ces/v1/quota.py diff --git a/otcextensions/sdk/ces/ces_service.py b/otcextensions/sdk/ces/ces_service.py index 5565d2822..e964dd8ec 100644 --- a/otcextensions/sdk/ces/ces_service.py +++ b/otcextensions/sdk/ces/ces_service.py @@ -20,4 +20,3 @@ class CesService(service_description.ServiceDescription): supported_versions = { '1': _proxy.Proxy } - diff --git a/otcextensions/sdk/ces/v1/_proxy.py b/otcextensions/sdk/ces/v1/_proxy.py index 2e5692142..3c0a5d55a 100644 --- a/otcextensions/sdk/ces/v1/_proxy.py +++ b/otcextensions/sdk/ces/v1/_proxy.py @@ -10,9 +10,11 @@ # License for the specific language governing permissions and limitations # under the License. from otcextensions.sdk.ces.v1 import metric as _metric +from otcextensions.sdk.ces.v1 import quota as _quota from openstack import proxy + class Proxy(proxy.Proxy): # Metrics @@ -25,4 +27,12 @@ def metric(self, **query): :rtype: :class:`~openstack.database.v1.metric.Metric` """ return self._list(_metric.Metric, **query) - + + # Quotas + def quotas(self): + """Return a generator of quotas + + :returns: A generator of metric objects + :rtype: :class:`~openstack.database.v1.quotas.Quotas` + """ + return self._list(_quota.Quota) diff --git a/otcextensions/sdk/ces/v1/alarm.py b/otcextensions/sdk/ces/v1/alarm.py index 1672babe1..5ad80a8d9 100644 --- a/otcextensions/sdk/ces/v1/alarm.py +++ b/otcextensions/sdk/ces/v1/alarm.py @@ -12,15 +12,15 @@ from openstack import resource -#class MetaDataSpec(resource.Resource): +# class MetaDataSpec(resource.Resource): - # Properties - # Number of returned results / alarms -# count = resource.Body('count') - # Indicates pagination marker -# marker = resource.Body('marker') - # Number of total queried results / alarms -# total = resource.Body('total') +# Properties +# Number of returned results / alarms +# count = resource.Body('count') +# Indicates pagination marker +# marker = resource.Body('marker') +# Number of total queried results / alarms +# total = resource.Body('total') class AlarmActionsSpec(resource.Resource): @@ -42,7 +42,7 @@ class ConditionSpec(resource.Resource): # been generated count = resource.Body('unit', type=int) # indicates the data rollup method - # values: Max., Min., average, Sum. or Variance + # values: max, min, average, sum, variance filterstring = resource.Body('filter') # Indicates the interval (in seconds) for checking # whether the configured alarm rules are met @@ -66,7 +66,8 @@ class MetricSpec(resource.Resource): # Properties # List of metric dimensions - dimensions = resource.Body('dimensions', type=list, list_type=DimensionsSpec) + dimensions = resource.Body('dimensions', type=list, + list_type=DimensionsSpec) # Metric name, such as cpu_util in ECS metrics metric_name = resource.Body('metric_name') # Metric Namespace @@ -109,13 +110,8 @@ class Alarm(resource.Resource): alarm_state = resource.Body('alarm_state') # Describes alarm triggering condititon condititon = resource.Body('condition', type=ConditionSpec) - # Metadata of query results and paging information - #meta_data = resource.Body('meta_data', type=MetaDataSpec) - # List of alarm objects - #metric_alarms = resource.Body('metric_alarms', type=list, list_type=MetricAlarmsSpec) # Specification of specific alarm metric = resource.Body('metric', type=MetricSpec) # Time when alarm status changed # UNIX timestamp in ms update_time = resource.Body('update_time') - diff --git a/otcextensions/sdk/ces/v1/favorit_metric.py b/otcextensions/sdk/ces/v1/favorit_metric.py index a110a4ab9..49204cc28 100644 --- a/otcextensions/sdk/ces/v1/favorit_metric.py +++ b/otcextensions/sdk/ces/v1/favorit_metric.py @@ -11,6 +11,7 @@ # under the License. from openstack import resource + class DimensionsSpec(resource.Resource): # Properties @@ -27,7 +28,6 @@ class FavoriteMetric(resource.Resource): # capabilities allow_list = True - # Properties #: List of metric dimensions #: dimension.name: object type e.g. ECS (instance_id) diff --git a/otcextensions/sdk/ces/v1/metric.py b/otcextensions/sdk/ces/v1/metric.py index cf4a9e6ab..fe8059c67 100644 --- a/otcextensions/sdk/ces/v1/metric.py +++ b/otcextensions/sdk/ces/v1/metric.py @@ -37,12 +37,11 @@ class Metric(resource.Resource): #: List of metric dimensions #: dimension.name: object type e.g. ECS (instance_id) #: dimension.value: object id e.g. ECS ID - dimensions = resource.Body('dimensions', type=list, list_type=DimensionsSpec) + dimensions = resource.Body('dimensions', type=list, + list_type=DimensionsSpec) #: Shows the metric name metric_name = resource.Body('metric_name') #: Indicates the metric namespaces namespace = resource.Body('namespace') #: Indicates the metric unit unit = resource.Body('unit') - - diff --git a/otcextensions/sdk/ces/v1/metric_data.py b/otcextensions/sdk/ces/v1/metric_data.py new file mode 100644 index 000000000..ff083faac --- /dev/null +++ b/otcextensions/sdk/ces/v1/metric_data.py @@ -0,0 +1,23 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from openstack import resource + + +class MetricData(resource.Resource): + resource_key = '' + resources_key = '' + base_path = '/metrics-data' + + # capabilities + allow_list = True + + # Properties diff --git a/otcextensions/sdk/ces/v1/quota.py b/otcextensions/sdk/ces/v1/quota.py new file mode 100644 index 000000000..d34a29ce6 --- /dev/null +++ b/otcextensions/sdk/ces/v1/quota.py @@ -0,0 +1,36 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from openstack import resource + + +class ResourcesSpec(resource.Body): + + # Properties + # quota type + typeprop = resource.Body('type') + # total amount of quota + quota = resource.Body('quota', type=int) + # quota unit + unit = resource.Body('unit') + # used amount of quota + used = resource.Body('used', type=int) + + +class Quota(resource.Resource): + resources_key = 'quotas' + base_path = '/quotas' + + # capabilities + allow_list = True + + # Properties + resources = resource.Body('resources', type=ResourcesSpec) From ba70444534d3f05f5e331ceb9ced40e1ca557e57 Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Tue, 3 Dec 2019 15:09:23 +0000 Subject: [PATCH 13/42] flake8 issue --- otcextensions/sdk/nat/v2/_proxy.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/otcextensions/sdk/nat/v2/_proxy.py b/otcextensions/sdk/nat/v2/_proxy.py index c13c7668b..8b1fa71e9 100644 --- a/otcextensions/sdk/nat/v2/_proxy.py +++ b/otcextensions/sdk/nat/v2/_proxy.py @@ -36,7 +36,8 @@ def delete_gateway(self, id, ignore_missing=True): :param dict attrs: Keyword arguments which will be used to create a :class:`~otcextensions.sdk.nat.v2.gateway.Gateway` """ - return self._delete(_gateway.Gateway, id=id, ignore_missing=ignore_missing) + return self._delete(_gateway.Gateway, id=id, + ignore_missing=ignore_missing) def gateways(self, **attrs): """Return a generator of gateways From af7d5041a1a912429719a1464d53fee57430120c Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Thu, 5 Dec 2019 13:16:49 +0000 Subject: [PATCH 14/42] minor changes to service --- otcextensions/sdk/__init__.py | 3 +-- otcextensions/sdk/ces/v1/_proxy.py | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/otcextensions/sdk/__init__.py b/otcextensions/sdk/__init__.py index c8c5e7470..eb0c6de22 100644 --- a/otcextensions/sdk/__init__.py +++ b/otcextensions/sdk/__init__.py @@ -54,8 +54,7 @@ }, 'ces': { 'service_type': 'ces', - 'endpoint_service_type': 'cesv1', - 'append_project_id': False, + 'endpoint_service_type': 'cesv1' }, 'cts': { 'service_type': 'cts', diff --git a/otcextensions/sdk/ces/v1/_proxy.py b/otcextensions/sdk/ces/v1/_proxy.py index 3c0a5d55a..313cf3a4a 100644 --- a/otcextensions/sdk/ces/v1/_proxy.py +++ b/otcextensions/sdk/ces/v1/_proxy.py @@ -17,6 +17,8 @@ class Proxy(proxy.Proxy): + skip_discovery = True + # Metrics def metric(self, **query): """Return a generator of metrics From 136de7c0145442fcdda42d534c99b76d483424af Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Thu, 5 Dec 2019 14:36:29 +0000 Subject: [PATCH 15/42] fix service discovery --- otcextensions/sdk/__init__.py | 4 +++- otcextensions/sdk/ces/v1/quota.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/otcextensions/sdk/__init__.py b/otcextensions/sdk/__init__.py index eb0c6de22..4eed4d63f 100644 --- a/otcextensions/sdk/__init__.py +++ b/otcextensions/sdk/__init__.py @@ -54,7 +54,9 @@ }, 'ces': { 'service_type': 'ces', - 'endpoint_service_type': 'cesv1' + 'endpoint_service_type': 'cesv1', + 'set_endpoint_override': True + # 'append_project_id': False, }, 'cts': { 'service_type': 'cts', diff --git a/otcextensions/sdk/ces/v1/quota.py b/otcextensions/sdk/ces/v1/quota.py index d34a29ce6..bb8a1e490 100644 --- a/otcextensions/sdk/ces/v1/quota.py +++ b/otcextensions/sdk/ces/v1/quota.py @@ -12,7 +12,7 @@ from openstack import resource -class ResourcesSpec(resource.Body): +class ResourcesSpec(resource.Resource): # Properties # quota type From 5c9183ad6a5b866455c0eb284135602942fd9a9b Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Mon, 9 Dec 2019 15:29:19 +0000 Subject: [PATCH 16/42] correcting some stuff --- otcextensions/sdk/ces/v1/_proxy.py | 20 +++++++++++++++----- otcextensions/sdk/ces/v1/alarm.py | 4 ++-- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/otcextensions/sdk/ces/v1/_proxy.py b/otcextensions/sdk/ces/v1/_proxy.py index 313cf3a4a..c924f9788 100644 --- a/otcextensions/sdk/ces/v1/_proxy.py +++ b/otcextensions/sdk/ces/v1/_proxy.py @@ -11,6 +11,7 @@ # under the License. from otcextensions.sdk.ces.v1 import metric as _metric from otcextensions.sdk.ces.v1 import quota as _quota +from otcextensions.sdk.ces.v1 import alarm as _alarm from openstack import proxy @@ -19,22 +20,31 @@ class Proxy(proxy.Proxy): skip_discovery = True - # Metrics - def metric(self, **query): + # ======== Metrics ======== + def metrics(self, **query): """Return a generator of metrics :param kwargs query: Optional query parameters to be sent to limit the resources being returned. :returns: A generator of metric objects - :rtype: :class:`~openstack.database.v1.metric.Metric` + :rtype: :class:`~otcextensions.sdk.ces.v1.metric.Metric` """ return self._list(_metric.Metric, **query) - # Quotas + # ======== Quotas ======== def quotas(self): """Return a generator of quotas :returns: A generator of metric objects - :rtype: :class:`~openstack.database.v1.quotas.Quotas` + :rtype: :class:`~otcextensions.sdk.ces.v1.quota.Quota` """ return self._list(_quota.Quota) + + # ======== Alarms ======== + def alarms(self): + """Return a generator of alarms + + :returns: A generator of alarm objects + :rtype: :class:`~otcextensions.sdk.ces.v1.alarm.Alarm` + """ + return self._list(_alarm.Alarm) \ No newline at end of file diff --git a/otcextensions/sdk/ces/v1/alarm.py b/otcextensions/sdk/ces/v1/alarm.py index 5ad80a8d9..0798f79b0 100644 --- a/otcextensions/sdk/ces/v1/alarm.py +++ b/otcextensions/sdk/ces/v1/alarm.py @@ -48,9 +48,9 @@ class ConditionSpec(resource.Resource): # whether the configured alarm rules are met period = resource.Body('period', type=int) # Data unit - unit = resource.Body('unit') + xunit = resource.Body('unit', type=str) # Alarm threshold - value = resource.Body('value', type=int) + value = resource.Body('value') class DimensionsSpec(resource.Resource): From ef9f29e47578622d33b4b62d4b507e7da57f7365 Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Mon, 9 Dec 2019 15:30:08 +0000 Subject: [PATCH 17/42] correcting some stuff --- otcextensions/sdk/ces/v1/alarm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otcextensions/sdk/ces/v1/alarm.py b/otcextensions/sdk/ces/v1/alarm.py index 0798f79b0..ef706e149 100644 --- a/otcextensions/sdk/ces/v1/alarm.py +++ b/otcextensions/sdk/ces/v1/alarm.py @@ -48,7 +48,7 @@ class ConditionSpec(resource.Resource): # whether the configured alarm rules are met period = resource.Body('period', type=int) # Data unit - xunit = resource.Body('unit', type=str) + unit = resource.Body('unit', type=str) # Alarm threshold value = resource.Body('value') From 3cbc6077026641440c4c6f9136d2bc2bfe0369bf Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Mon, 9 Dec 2019 15:32:12 +0000 Subject: [PATCH 18/42] delete favorit_metric --- otcextensions/sdk/ces/v1/favorit_metric.py | 41 ---------------------- 1 file changed, 41 deletions(-) delete mode 100644 otcextensions/sdk/ces/v1/favorit_metric.py diff --git a/otcextensions/sdk/ces/v1/favorit_metric.py b/otcextensions/sdk/ces/v1/favorit_metric.py deleted file mode 100644 index 49204cc28..000000000 --- a/otcextensions/sdk/ces/v1/favorit_metric.py +++ /dev/null @@ -1,41 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -from openstack import resource - - -class DimensionsSpec(resource.Resource): - - # Properties - #: dimension.name: object type e.g. ECS (instance_id) - name = resource.Body('name') - #: dimension.value: object id e.g. ECS ID - value = resource.Body('value') - - -class FavoriteMetric(resource.Resource): - resources_key = 'metrics' - base_path = '/favorite-metrics' - - # capabilities - allow_list = True - - # Properties - #: List of metric dimensions - #: dimension.name: object type e.g. ECS (instance_id) - #: dimension.value: object id e.g. ECS ID - dimensions = resource.Body('dimensions', type=DimensionsSpec) - #: Shows the metric name - metric_name = resource.Body('metric_name') - #: Indicates the metric namespaces - namespace = resource.Body('namespace') - #: Indicates the metric unit - unit = resource.Body('unit') From 526889ab2fac846654795086db635db16f651b7c Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Mon, 9 Dec 2019 17:11:59 +0000 Subject: [PATCH 19/42] fix alarm.py --- otcextensions/sdk/ces/v1/alarm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/otcextensions/sdk/ces/v1/alarm.py b/otcextensions/sdk/ces/v1/alarm.py index ef706e149..756fe6cc2 100644 --- a/otcextensions/sdk/ces/v1/alarm.py +++ b/otcextensions/sdk/ces/v1/alarm.py @@ -40,7 +40,7 @@ class ConditionSpec(resource.Resource): comparison_operator = resource.Body('comparison_operator') # Indicates how many consecutive times an alarm has # been generated - count = resource.Body('unit', type=int) + count = resource.Body('count', type=int) # indicates the data rollup method # values: max, min, average, sum, variance filterstring = resource.Body('filter') @@ -48,7 +48,7 @@ class ConditionSpec(resource.Resource): # whether the configured alarm rules are met period = resource.Body('period', type=int) # Data unit - unit = resource.Body('unit', type=str) + unit = resource.Body('unit') # Alarm threshold value = resource.Body('value') From 2467ef69f312fe481736f0aa6cf7de8319f9d1b0 Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Wed, 11 Dec 2019 13:55:00 +0000 Subject: [PATCH 20/42] event data added and proxy first draft --- otcextensions/sdk/ces/v1/_proxy.py | 99 ++++++++++++++++++++++--- otcextensions/sdk/ces/v1/alarm.py | 5 ++ otcextensions/sdk/ces/v1/event_data.py | 41 ++++++++++ otcextensions/sdk/ces/v1/metric_data.py | 25 ++++++- 4 files changed, 159 insertions(+), 11 deletions(-) create mode 100644 otcextensions/sdk/ces/v1/event_data.py diff --git a/otcextensions/sdk/ces/v1/_proxy.py b/otcextensions/sdk/ces/v1/_proxy.py index c924f9788..2250076d4 100644 --- a/otcextensions/sdk/ces/v1/_proxy.py +++ b/otcextensions/sdk/ces/v1/_proxy.py @@ -9,9 +9,12 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +from otcextensions.sdk.ces.v1 import alarm as _alarm +from otcextensions.sdk.ces.v1 import event_data as _event_data from otcextensions.sdk.ces.v1 import metric as _metric +from otcextensions.sdk.ces.v1 import metric_data as _metric_data from otcextensions.sdk.ces.v1 import quota as _quota -from otcextensions.sdk.ces.v1 import alarm as _alarm + from openstack import proxy @@ -20,6 +23,72 @@ class Proxy(proxy.Proxy): skip_discovery = True + # ======== Alarms ======== + def alarms(self): + """Return a generator of alarms + + :returns: A generator of alarm objects + :rtype: :class:`~otcextensions.sdk.ces.v1.alarm.Alarm` + """ + return self._list(_alarm.Alarm) + + def get_alarm(self, alarm): + """Return a single alarm + + :param alarm: The value can be the ID of a alarm or a + :class:`~otcextensions.sdk.ces.v1.alarm.Alarm` + instance. + :returns: A generator of alarm objects + :rtype: :class:`~otcextensions.sdk.ces.v1.alarm.Alarm` + """ + return self._get(_alarm.Alarm) + + def create_alarm(self, **attrs): + """Create a new Alarm from attributes + + :param dict attrs: Keyword arguments which will be used to create + a :class:`otcextensions.sdk.ces.v1.alarm.Alarm` + """ + return self._create(_alarm.Alarm, **attrs) + + def delete_alarm(self, alarm, ignore_missing=True): + """Delete a Alarm + + :param alarm: key id or an instance of + :class:`otcextensions.sdk.ces.v1.alarm.Alarm` + :param bool ignore_missing: When set to ``False`` + :class:`~openstack.exceptions.ResourceNotFound` will be raised when + the alarm does not exist. + When set to ``True``, no exception will be set when attempting to + delete a nonexistent alarm. + :returns: Alarm been deleted + :rtype: :class:`otcextensions.sdk.ces.v1.alarm.Alarm` + """ + return self._delete(_alarm.Alarm, alarm=alarm, + ignore_missing=ignore_missing) + + def update_alarm(self, alarm, **attrs): + """Update an Alarm from attributes + + : param alarm: An id or an instance of + :class:`otcextensions.sdk.ces.v1.alarm.Alarm` + :param dict attrs: Keyword arguments which will be used to update + a :class:`otcextensions.sdk.ces.v1.alarm.Alarm` + """ + return self._update(_alarm.Alarm, alarm, **attrs) + + # ======== Event-Data ======== + def event_data(self, **query): + """Return a generator of host configurations for a specified + event type in a specified period of time. + + :param kwargs query: Optional query parameters to be sent to limit + the resources being returned. + :returns: A generator of event data objects + :rtype: :class:`~otcextensions.sdk.ces.v1.event_data.EventData` + """ + return self._list(_event_data.EventData, **query) + # ======== Metrics ======== def metrics(self, **query): """Return a generator of metrics @@ -31,6 +100,25 @@ def metrics(self, **query): """ return self._list(_metric.Metric, **query) + # ======== Metric-Data ======== + def metric_data(self, **query): + """Return a generator of Metric Data + + :param kwargs query: Optional query parameters to be sent to limit + the resources being returned. + :returns: A generator of metric data objects + :rtype: :class:`~otcextensions.sdk.ces.v1.metric_data.MetricData` + """ + return self._list(_metric_data.MetricData, **query) + + # def create_metric_data(self, **attrs): + # """Create a new Alarm from attributes + + # :param dict attrs: Keyword arguments which will be used to create + # a :class:`~otcextensions.sdk.ces.v1.metric_data.MetricData` + # """ + # return self._create(_metric_data.MetricData, **attrs) + # ======== Quotas ======== def quotas(self): """Return a generator of quotas @@ -39,12 +127,3 @@ def quotas(self): :rtype: :class:`~otcextensions.sdk.ces.v1.quota.Quota` """ return self._list(_quota.Quota) - - # ======== Alarms ======== - def alarms(self): - """Return a generator of alarms - - :returns: A generator of alarm objects - :rtype: :class:`~otcextensions.sdk.ces.v1.alarm.Alarm` - """ - return self._list(_alarm.Alarm) \ No newline at end of file diff --git a/otcextensions/sdk/ces/v1/alarm.py b/otcextensions/sdk/ces/v1/alarm.py index 756fe6cc2..a538fd3e7 100644 --- a/otcextensions/sdk/ces/v1/alarm.py +++ b/otcextensions/sdk/ces/v1/alarm.py @@ -75,10 +75,15 @@ class MetricSpec(resource.Resource): class Alarm(resource.Resource): + resources_key = 'metric_alarms' base_path = '/alarms' # capabilities + allow_commit = True + allow_create = True + allow_fetch = True + allow_delete = True allow_list = True _query_mapping = resource.QueryParameters( diff --git a/otcextensions/sdk/ces/v1/event_data.py b/otcextensions/sdk/ces/v1/event_data.py new file mode 100644 index 000000000..b7d0d9929 --- /dev/null +++ b/otcextensions/sdk/ces/v1/event_data.py @@ -0,0 +1,41 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from openstack import resource + + +class DatapointsSpec(resource.Resource): + + # Properties + # time of the metric collection + timestamp = resource.Body('timestamp') + # Indicates the event type e.g. instance_host_info + typespec = resource.Body('type') + # host configuration information + value = resource.Body('value') + + +class EventData(resource.Resource): + + base_path = '/event-data' + + # capabilities + allow_list = True + # allow_create = True + + _query_mapping = resource.QueryParameters( + 'dim', 'from', 'to', 'namespace', 'type' + ) + + # Properties + # Datapoints retrieve the metrics data list + datapoints = resource.Body('datapoints', type=list, + list_type=DatapointsSpec) diff --git a/otcextensions/sdk/ces/v1/metric_data.py b/otcextensions/sdk/ces/v1/metric_data.py index ff083faac..f718ab43f 100644 --- a/otcextensions/sdk/ces/v1/metric_data.py +++ b/otcextensions/sdk/ces/v1/metric_data.py @@ -12,12 +12,35 @@ from openstack import resource +class DatapointsSpec(resource.Resource): + + # Properties + # Metric Value, the value is the same as that of + # parameter filter + average = resource.Body('average') + maximum = resource.Body('max') + minimum = resource.Body('min') + sumspec = resource.Body('sum') + variance = resource.Body('variance') + # time of the metric collection + timestamp = resource.Body('timestamp') + # indicates the metric unit + unit = resource.Body('unit') + + class MetricData(resource.Resource): + resource_key = '' resources_key = '' - base_path = '/metrics-data' + base_path = '/metric-data' # capabilities allow_list = True + # allow_create = True # Properties + # Datapoints retrieve the metrics data list + datapoints = resource.Body('datapoints', type=list, + list_type=DatapointsSpec) + # Metric Name like 'cpu_util' + metric_name = resource.Body('metric_name') From 1ea033b4e5cb4114dabd068bd445b1d23c1ca5fe Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Fri, 13 Dec 2019 14:45:43 +0000 Subject: [PATCH 21/42] translate response and fixed get_alarm --- otcextensions/sdk/ces/v1/_proxy.py | 4 ++-- otcextensions/sdk/ces/v1/alarm.py | 35 +++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/otcextensions/sdk/ces/v1/_proxy.py b/otcextensions/sdk/ces/v1/_proxy.py index 2250076d4..f3d4426a6 100644 --- a/otcextensions/sdk/ces/v1/_proxy.py +++ b/otcextensions/sdk/ces/v1/_proxy.py @@ -41,7 +41,7 @@ def get_alarm(self, alarm): :returns: A generator of alarm objects :rtype: :class:`~otcextensions.sdk.ces.v1.alarm.Alarm` """ - return self._get(_alarm.Alarm) + return self._get(_alarm.Alarm, alarm) def create_alarm(self, **attrs): """Create a new Alarm from attributes @@ -64,7 +64,7 @@ def delete_alarm(self, alarm, ignore_missing=True): :returns: Alarm been deleted :rtype: :class:`otcextensions.sdk.ces.v1.alarm.Alarm` """ - return self._delete(_alarm.Alarm, alarm=alarm, + return self._delete(_alarm.Alarm, alarm, ignore_missing=ignore_missing) def update_alarm(self, alarm, **attrs): diff --git a/otcextensions/sdk/ces/v1/alarm.py b/otcextensions/sdk/ces/v1/alarm.py index a538fd3e7..fee046423 100644 --- a/otcextensions/sdk/ces/v1/alarm.py +++ b/otcextensions/sdk/ces/v1/alarm.py @@ -9,6 +9,7 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +from openstack import exceptions from openstack import resource @@ -102,7 +103,7 @@ class Alarm(resource.Resource): # Alarm is enabled (True) or disabled (False) alarm_enabled = resource.Body('alarm_enabled', type=bool) # alarm rule ID - alarm_id = resource.Body('alarm_id') + alarm_id = resource.Body('alarm_id', alternate_id=True) # alarm severity # values: 1: critical, 2: major, 3: minor, 4: informational alarm alarm_level = resource.Body('alarm_level', type=int) @@ -120,3 +121,35 @@ class Alarm(resource.Resource): # Time when alarm status changed # UNIX timestamp in ms update_time = resource.Body('update_time') + + def _translate_response(self, response, has_body=None, error_message=None): + """Given a KSA response, inflate this instance with its data + + DELETE operations don't return a body, so only try to work + with a body when has_body is True. + + This method updates attributes that correspond to headers + and body on this instance and clears the dirty set. + """ + if has_body is None: + has_body = self.has_body + exceptions.raise_from_response(response, error_message=error_message) + if has_body: + try: + body = response.json() + if self.resources_key and self.resources_key in body: + body = body[self.resources_key][0] + body_attrs = self._consume_body_attrs(body) + self._body.attributes.update(body_attrs) + self._body.clean() + + except ValueError: + # Server returned not parse-able response (202, 204, etc) + # Do simply nothing + pass + + headers = self._consume_header_attrs(response.headers) + self._header.attributes.update(headers) + self._header.clean() + self._update_location() + dict.update(self, self.to_dict()) From 7abb59c2b5123e7f25c6b64c97e3076621e01aa9 Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Fri, 20 Dec 2019 14:26:59 +0000 Subject: [PATCH 22/42] ces final sdk implementation and flake8 corrections --- otcextensions/sdk/ces/v1/_proxy.py | 36 ++++++++++----- otcextensions/sdk/ces/v1/alarm.py | 72 ++++++++++++++++++++++++++++-- 2 files changed, 95 insertions(+), 13 deletions(-) diff --git a/otcextensions/sdk/ces/v1/_proxy.py b/otcextensions/sdk/ces/v1/_proxy.py index f3d4426a6..f3720da15 100644 --- a/otcextensions/sdk/ces/v1/_proxy.py +++ b/otcextensions/sdk/ces/v1/_proxy.py @@ -9,6 +9,8 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +from openstack import proxy + from otcextensions.sdk.ces.v1 import alarm as _alarm from otcextensions.sdk.ces.v1 import event_data as _event_data from otcextensions.sdk.ces.v1 import metric as _metric @@ -16,9 +18,6 @@ from otcextensions.sdk.ces.v1 import quota as _quota -from openstack import proxy - - class Proxy(proxy.Proxy): skip_discovery = True @@ -67,15 +66,32 @@ def delete_alarm(self, alarm, ignore_missing=True): return self._delete(_alarm.Alarm, alarm, ignore_missing=ignore_missing) - def update_alarm(self, alarm, **attrs): - """Update an Alarm from attributes + def find_alarm(self, name_or_id, ignore_missing=True): + """Find a single alarm - : param alarm: An id or an instance of - :class:`otcextensions.sdk.ces.v1.alarm.Alarm` - :param dict attrs: Keyword arguments which will be used to update - a :class:`otcextensions.sdk.ces.v1.alarm.Alarm` + :param name_or_id: The name or ID of a alarm + :param bool ignore_missing: When set to ``False`` + :class:`~openstack.exceptions.ResourceNotFound` will be raised + when the alarm does not exist. + When set to ``True``, no exception will be set when attempting + to delete a nonexistent alarm. + + :returns: ``None`` + """ + return self._find(_alarm.Alarm, name_or_id, + ignore_missing=ignore_missing) + + def update_alarm_enabled(self, alarm): + """Enables or disables Alarm + + :param alarm: The value can be the ID of an alarm + or a :class:`~otcextensions.sdk.ces.v1.alarm.Alarm` instance. + :returns: updater instance of + :class:`~otcextensions.sdk.ces.v1.alarm.Alarm` + :rtype: :class:`~otcextensions.sdk.ces.v1.alarm.Alarm` """ - return self._update(_alarm.Alarm, alarm, **attrs) + alarm = self._get_resource(_alarm.Alarm, alarm) + return alarm.change_alarm_status(self, alarm) # ======== Event-Data ======== def event_data(self, **query): diff --git a/otcextensions/sdk/ces/v1/alarm.py b/otcextensions/sdk/ces/v1/alarm.py index fee046423..bf3bdbf3e 100644 --- a/otcextensions/sdk/ces/v1/alarm.py +++ b/otcextensions/sdk/ces/v1/alarm.py @@ -11,6 +11,7 @@ # under the License. from openstack import exceptions from openstack import resource +from openstack import utils # class MetaDataSpec(resource.Resource): @@ -30,7 +31,7 @@ class AlarmActionsSpec(resource.Resource): notificationList = resource.Body('notificationList') # Indicates the type of action triggered by an alarm. # Value can be notication or autoscaling - typestring = resource.Body('type') + type = resource.Body('type') class ConditionSpec(resource.Resource): @@ -44,7 +45,7 @@ class ConditionSpec(resource.Resource): count = resource.Body('count', type=int) # indicates the data rollup method # values: max, min, average, sum, variance - filterstring = resource.Body('filter') + filter = resource.Body('filter') # Indicates the interval (in seconds) for checking # whether the configured alarm rules are met period = resource.Body('period', type=int) @@ -108,7 +109,7 @@ class Alarm(resource.Resource): # values: 1: critical, 2: major, 3: minor, 4: informational alarm alarm_level = resource.Body('alarm_level', type=int) # Name of the alarm - alarm_name = resource.Body('alarm_name') + name = resource.Body('alarm_name') # Alarm status # ok: alarm status is normal # alarm: an alarm is generated @@ -153,3 +154,68 @@ def _translate_response(self, response, has_body=None, error_message=None): self._header.clean() self._update_location() dict.update(self, self.to_dict()) + + def _action(self, session, body): + """Perform actions given the message body. + + """ + url = utils.urljoin(self.base_path, self.id, "action") + return session.put( + url, + json=body) + + def change_alarm_status(self, session, alarm): + body = { + "alarm_enabled": True + } + current_status = alarm.get('alarm_enabled') + if current_status is True: + body.update({'alarm_enabled': False}) + return self._action(session, body) + + @classmethod + def find(cls, session, name_or_id, ignore_missing=True, **params): + """Find a resource by its name or id. + + :param session: The session to use for making this request. + :type session: :class:`~keystoneauth1.adapter.Adapter` + :param name_or_id: This resource's identifier, if needed by + the request. The default is ``None``. + :param bool ignore_missing: When set to ``False`` + :class:`~openstack.exceptions.ResourceNotFound` will be + raised when the resource does not exist. + When set to ``True``, None will be returned when + attempting to find a nonexistent resource. + :param dict params: Any additional parameters to be passed into + underlying methods, such as to + :meth:`~openstack.resource.Resource.existing` + in order to pass on URI parameters. + + :return: The :class:`Resource` object matching the given name or id + or None if nothing matches. + :raises: :class:`openstack.exceptions.DuplicateResource` if more + than one resource is found for this request. + :raises: :class:`openstack.exceptions.ResourceNotFound` if nothing + is found and ignore_missing is ``False``. + """ + session = cls._get_session(session) + # Try to short-circuit by looking directly for a matching ID. + try: + match = cls.existing( + id=name_or_id, + connection=session._get_connection(), + **params) + return match.fetch(session, **params) + except exceptions.SDKException: + pass + + data = cls.list(session, **params) + + result = cls._get_one_match(name_or_id, data) + if result is not None: + return result + + if ignore_missing: + return None + raise exceptions.ResourceNotFound( + "No %s found for %s" % (cls.__name__, name_or_id)) From 727b92eb20df99ef6e4d4e8cfdd39babaa9ce285 Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Fri, 10 Jan 2020 11:44:51 +0000 Subject: [PATCH 23/42] unittests ces --- otcextensions/sdk/ces/v1/_proxy.py | 6 +- otcextensions/sdk/ces/v1/alarm.py | 12 +-- otcextensions/tests/unit/sdk/ces/__init__.py | 0 .../tests/unit/sdk/ces/v1/__init__.py | 0 .../tests/unit/sdk/ces/v1/test_alarm.py | 80 +++++++++++++++++ .../tests/unit/sdk/ces/v1/test_proxy.py | 86 +++++++++++++++++++ 6 files changed, 175 insertions(+), 9 deletions(-) create mode 100644 otcextensions/tests/unit/sdk/ces/__init__.py create mode 100644 otcextensions/tests/unit/sdk/ces/v1/__init__.py create mode 100644 otcextensions/tests/unit/sdk/ces/v1/test_alarm.py create mode 100644 otcextensions/tests/unit/sdk/ces/v1/test_proxy.py diff --git a/otcextensions/sdk/ces/v1/_proxy.py b/otcextensions/sdk/ces/v1/_proxy.py index f3720da15..7088bc613 100644 --- a/otcextensions/sdk/ces/v1/_proxy.py +++ b/otcextensions/sdk/ces/v1/_proxy.py @@ -86,12 +86,10 @@ def update_alarm_enabled(self, alarm): :param alarm: The value can be the ID of an alarm or a :class:`~otcextensions.sdk.ces.v1.alarm.Alarm` instance. - :returns: updater instance of - :class:`~otcextensions.sdk.ces.v1.alarm.Alarm` - :rtype: :class:`~otcextensions.sdk.ces.v1.alarm.Alarm` + :returns: None """ alarm = self._get_resource(_alarm.Alarm, alarm) - return alarm.change_alarm_status(self, alarm) + alarm.change_alarm_status(self) # ======== Event-Data ======== def event_data(self, **query): diff --git a/otcextensions/sdk/ces/v1/alarm.py b/otcextensions/sdk/ces/v1/alarm.py index bf3bdbf3e..de3c30903 100644 --- a/otcextensions/sdk/ces/v1/alarm.py +++ b/otcextensions/sdk/ces/v1/alarm.py @@ -116,7 +116,7 @@ class Alarm(resource.Resource): # insufficient_data: required data is insufficient alarm_state = resource.Body('alarm_state') # Describes alarm triggering condititon - condititon = resource.Body('condition', type=ConditionSpec) + condition = resource.Body('condition', type=ConditionSpec) # Specification of specific alarm metric = resource.Body('metric', type=MetricSpec) # Time when alarm status changed @@ -160,18 +160,20 @@ def _action(self, session, body): """ url = utils.urljoin(self.base_path, self.id, "action") - return session.put( + response = session.put( url, json=body) + exceptions.raise_from_response(response) + return response - def change_alarm_status(self, session, alarm): + def change_alarm_status(self, session): body = { "alarm_enabled": True } - current_status = alarm.get('alarm_enabled') + current_status = self.get('alarm_enabled') if current_status is True: body.update({'alarm_enabled': False}) - return self._action(session, body) + self._action(session, body) @classmethod def find(cls, session, name_or_id, ignore_missing=True, **params): diff --git a/otcextensions/tests/unit/sdk/ces/__init__.py b/otcextensions/tests/unit/sdk/ces/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/otcextensions/tests/unit/sdk/ces/v1/__init__.py b/otcextensions/tests/unit/sdk/ces/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/otcextensions/tests/unit/sdk/ces/v1/test_alarm.py b/otcextensions/tests/unit/sdk/ces/v1/test_alarm.py new file mode 100644 index 000000000..21f7fa9ab --- /dev/null +++ b/otcextensions/tests/unit/sdk/ces/v1/test_alarm.py @@ -0,0 +1,80 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from openstack.tests.unit import base + +from otcextensions.sdk.ces.v1 import alarm + + +EXAMPLE = { + 'alarm_name': 'alarm-qht9', + 'alarm_action_enabled': False, + 'alarm_description': 'Test description', + 'alarm_enabled': True, + 'metric': { + 'namespace': 'SYS.ECS', + 'metric_name': 'cpu_util', + 'dimensions': [ + { + 'name': 'instance_id', + 'value': 'ccb27344-1ec4-423e-a4d9-f3a885a23e72' + } + ] + }, + 'condition': { + 'period': 1, + 'filter': 'average', + 'comparison_operator': '>=', + 'value': 80, + 'unit': '%', + 'count': 3 + } +} + + +class TestAlarm(base.TestCase): + + def test_basic(self): + sot = alarm.Alarm() + self.assertEqual('metric_alarms', sot.resources_key) + path = '/alarms' + self.assertEqual(path, sot.base_path) + self.assertTrue(sot.allow_list) + self.assertTrue(sot.allow_create) + self.assertTrue(sot.allow_fetch) + self.assertTrue(sot.allow_commit) + self.assertTrue(sot.allow_delete) + + def test_make_it(self): + sot = alarm.Alarm(**EXAMPLE) + self.assertEqual(EXAMPLE['alarm_name'], sot.name) + self.assertEqual(EXAMPLE['alarm_action_enabled'], + sot.alarm_action_enabled) + self.assertEqual(EXAMPLE['alarm_description'], + sot.alarm_description) + self.assertEqual(EXAMPLE['alarm_enabled'], sot.alarm_enabled) + self.assertEqual(EXAMPLE['metric']['namespace'], sot.metric.namespace) + self.assertEqual(EXAMPLE['metric']['metric_name'], + sot.metric.metric_name) + self.assertEqual(EXAMPLE['metric']['dimensions'][0]['name'], + sot.metric.dimensions[0].name) + self.assertEqual(EXAMPLE['metric']['dimensions'][0]['value'], + sot.metric.dimensions[0].value) + self.assertEqual(EXAMPLE['condition']['period'], + sot.condition.period) + self.assertEqual(EXAMPLE['condition']['filter'], + sot.condition.filter) + self.assertEqual(EXAMPLE['condition']['comparison_operator'], + sot.condition.comparison_operator) + self.assertEqual(EXAMPLE['condition']['value'], sot.condition.value) + self.assertEqual(EXAMPLE['condition']['unit'], sot.condition.unit) + self.assertEqual(EXAMPLE['condition']['count'], sot.condition.count) diff --git a/otcextensions/tests/unit/sdk/ces/v1/test_proxy.py b/otcextensions/tests/unit/sdk/ces/v1/test_proxy.py new file mode 100644 index 000000000..d449978e9 --- /dev/null +++ b/otcextensions/tests/unit/sdk/ces/v1/test_proxy.py @@ -0,0 +1,86 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from otcextensions.sdk.ces.v1 import _proxy +from otcextensions.sdk.ces.v1 import alarm +from otcextensions.sdk.ces.v1 import event_data +from otcextensions.sdk.ces.v1 import metric_data +from otcextensions.sdk.ces.v1 import metric +from otcextensions.sdk.ces.v1 import quota + +from openstack.tests.unit import test_proxy_base + + +class TestCesProxy(test_proxy_base.TestProxyBase): + def setUp(self): + super(TestCesProxy, self).setUp() + self.proxy = _proxy.Proxy(self.session) + + +class TestCesAlarm(TestCesProxy): + def test_alarm_create(self): + self.verify_create(self.proxy.create_alarm, alarm.Alarm, + method_kwargs={'name': 'id'}, + expected_kwargs={'name': 'id'}) + + def test_alarm_delete(self): + self.verify_delete(self.proxy.delete_alarm, + alarm.Alarm, True) + + def test_alarm_get(self): + self.verify_get(self.proxy.get_alarm, alarm.Alarm) + + def test_alarms(self): + self.verify_list(self.proxy.alarms, alarm.Alarm) + + def test_update_alarm_enabled(self): + self._verify( + 'otcextensions.sdk.ces.v1.alarm.Alarm.change_alarm_status', + self.proxy.update_alarm_enabled, + method_args=["alarm"], + expected_args=[] + ) + + def test_alarm_find(self): + self.verify_find(self.proxy.find_alarm, alarm.Alarm) + + +class TestCesEventData(TestCesProxy): + def test_event_data(self): + self.verify_list(self.proxy.event_data, event_data.EventData) + + +class TestCesMetric(TestCesProxy): + def test_metrics(self): + self.verify_list(self.proxy.metrics, metric.Metric) + + +class TestCesMetricData(TestCesProxy): + def test_metric_data(self): + self.verify_list(self.proxy.metric_data, metric_data.MetricData) + + +class TestCesQuota(TestCesProxy): + def test_quotas(self): + self.verify_list(self.proxy.quotas, quota.Quota) From 113488b4db86658141d95211fe7727c397bfb8d7 Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Wed, 15 Jan 2020 16:38:04 +0000 Subject: [PATCH 24/42] ces init for OpenstackClient and alarm resource started --- otcextensions/osclient/ces/__init__.py | 0 otcextensions/osclient/ces/client.py | 44 +++ otcextensions/osclient/ces/v1/__init__.py | 0 otcextensions/osclient/ces/v1/alarm.py | 370 ++++++++++++++++++++++ setup.cfg | 8 + 5 files changed, 422 insertions(+) create mode 100644 otcextensions/osclient/ces/__init__.py create mode 100644 otcextensions/osclient/ces/client.py create mode 100644 otcextensions/osclient/ces/v1/__init__.py create mode 100644 otcextensions/osclient/ces/v1/alarm.py diff --git a/otcextensions/osclient/ces/__init__.py b/otcextensions/osclient/ces/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/otcextensions/osclient/ces/client.py b/otcextensions/osclient/ces/client.py new file mode 100644 index 000000000..7da23ee68 --- /dev/null +++ b/otcextensions/osclient/ces/client.py @@ -0,0 +1,44 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +import logging + +from otcextensions import sdk + + +LOG = logging.getLogger(__name__) + +DEFAULT_API_VERSION = '1' +API_VERSION_OPTION = 'os_ces_api_version' +API_NAME = "ces" +API_VERSIONS = { + "1.0": "openstack.connection.Connection", + "1": "openstack.connection.Connection", +} + + +def make_client(instance): + """Returns a CES proxy""" + + conn = instance.sdk_connection + + # register unconditionally, since we need to override default CES + sdk.register_otc_extensions(conn) + + LOG.debug('ces client initialized using OpenStack OTC SDK: %s', + conn.ces) + return conn.ces + + +def build_option_parser(parser): + """Hook to add global options""" + return parser diff --git a/otcextensions/osclient/ces/v1/__init__.py b/otcextensions/osclient/ces/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/otcextensions/osclient/ces/v1/alarm.py b/otcextensions/osclient/ces/v1/alarm.py new file mode 100644 index 000000000..f698eeb6f --- /dev/null +++ b/otcextensions/osclient/ces/v1/alarm.py @@ -0,0 +1,370 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +'''CES Alarm v1 action implementations''' +import argparse +import logging + +from osc_lib import utils +from osc_lib.command import command + +from otcextensions.i18n import _ +from otcextensions.common import sdk_utils + +LOG = logging.getLogger(__name__) + +""" +ZONE_TYPES = ['private', 'public'] + + +_formatters = { + # 'traffic_limited_list': sdk_utils.ListOfDictColumn, + # 'http_limited_list': sdk_utils.ListOfDictColumn, + # 'connection_limited_list': sdk_utils.ListOfDictColumn, +} + + + +""" + +def _get_columns(item): + column_map = { + } + hidden = ['location', 'links'] + return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map, + hidden) + + +class ListAlarm(command.Lister): + _description = _('List CES alarms') + columns = ( + 'id', 'name', 'alarm_enabled', 'alarm_action_enabled', 'alarm_state', 'alarm_level' + ) + + def get_parser(self, prog_name): + parser = super(ListAlarm, self).get_parser(prog_name) + + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.ces + + data = client.alarms() + + table = (self.columns, + (utils.get_item_properties( + s, self.columns + ) for s in data)) + return table + + +class ShowAlarm(command.ShowOne): + _description = _('Show the alarm details') + + def get_parser(self, prog_name): + parser = super(ShowAlarm, self).get_parser(prog_name) + + parser.add_argument( + 'alarm', + metavar='', + help=_('UUID or name of the alarm.') + ) + + return parser + + def take_action(self, parsed_args): + + client = self.app.client_manager.ces + + obj = client.find_alarm( + parsed_args.alarm, + ignore_missing=False + ) + + display_columns, columns = _get_columns(obj) + data = utils.get_item_properties(obj, columns) + + return (display_columns, data) + + +class DeleteAlarm(command.Command): + _description = _('Delete CES alarm') + + def get_parser(self, prog_name): + parser = super(DeleteAlarm, self).get_parser(prog_name) + + parser.add_argument( + 'alarm', + metavar='', + nargs='+', + help=_('UUID or name of the alarm.') + ) + + return parser + + def take_action(self, parsed_args): + if parsed_args.alarm: + client = self.app.client_manager.ces + for alarm in parsed_args.alarm: + alarm = client.find_alarm(alarm, ignore_missing=False) + client.delete_alarm(alarm=alarm) + + +class SetAlarm(command.ShowOne): + _description = _('Change alarm_status_enabled to the opposite value of true / false.') + + def get_parser(self, prog_name): + parser = super(ShowAlarm, self).get_parser(prog_name) + + parser.add_argument( + 'alarm', + metavar='', + help=_('UUID or name of the alarm.') + ) + + return parser + + def take_action(self, parsed_args): + + client = self.app.client_manager.ces + + alarm = client.find_alarm(parsed_args.alarm, ignore_missing=False) + + if alarm: + obj = client.update_alarm_enabled( + alarm=alarm + ) + + display_columns, columns = _get_columns(obj) + data = utils.get_item_properties(obj, columns) + + return (display_columns, data) + +""" + +class CreateZone(command.ShowOne): + _description = _('Create zone') + + def get_parser(self, prog_name): + parser = super(CreateZone, self).get_parser(prog_name) + + parser.add_argument( + 'name', + metavar='', + help=_('DNS Name for the zone.') + ) + parser.add_argument( + '--email', + metavar='', + help=_('E-mail for the zone. Used in SOA records for the zone.') + ) + parser.add_argument( + '--description', + metavar='', + help=_('Description for this zone.') + ) + parser.add_argument( + '--type', + metavar='{' + ','.join(ZONE_TYPES) + '}', + type=lambda s: s.lower(), + choices=ZONE_TYPES, + help=_('Domain name type, the value of which can be ' + '`public` or `private` .') + ) + parser.add_argument( + '--ttl', + metavar='<300-2147483647>', + type=int, + # NOTE: py2 does not support such big int, skip unless py3-only + # choices=range(300, 2147483647), + help=_('TTL (Time to Live) for the zone.') + ) + parser.add_argument( + '--router_id', + metavar='', + help=_('Router ID (VPC ID) for the private zone.') + ) + parser.add_argument( + '--router_region', + metavar='', + help=_('Router region for the private zone.') + ) + + return parser + + def take_action(self, parsed_args): + + client = self.app.client_manager.dns + + attrs = {} + + if parsed_args.name: + attrs['name'] = parsed_args.name + if parsed_args.email: + attrs['email'] = parsed_args.email + if parsed_args.description: + attrs['description'] = parsed_args.description + if parsed_args.type: + attrs['zone_type'] = parsed_args.type + if parsed_args.ttl: + attrs['ttl'] = parsed_args.ttl + if parsed_args.type and parsed_args.type == 'private': + if not parsed_args.router_id: + msg = _('router_id is required for a private zone') + raise argparse.ArgumentTypeError(msg) + router = { + 'router_id': parsed_args.router_id + } + if parsed_args.router_region: + router['router_region'] = parsed_args.router_region + attrs['router'] = router + + obj = client.create_zone( + **attrs + ) + + display_columns, columns = _get_columns(obj) + data = utils.get_item_properties(obj, columns) + + return (display_columns, data) + + + + + +class AssociateRouterToZone(command.ShowOne): + _description = _('Associate router with a private zone') + + def get_parser(self, prog_name): + parser = super(AssociateRouterToZone, self).get_parser(prog_name) + + parser.add_argument( + 'zone', + metavar='', + help=_('UUID or name of the zone.') + ) + parser.add_argument( + '--router_id', + metavar='', + help=_('Router ID (VPC ID) for the private zone.') + ) + parser.add_argument( + '--router_region', + metavar='', + help=_('Router region for the private zone.') + ) + + return parser + + def take_action(self, parsed_args): + + client = self.app.client_manager.dns + + router = { + 'router_id': parsed_args.router_id + } + if parsed_args.router_region: + router['router_region'] = parsed_args.router_region + + zone = client.find_zone(parsed_args.zone, ignore_missing=False) + + if zone: + obj = client.add_router_to_zone( + zone=zone, + **router + ) + + display_columns, columns = _get_columns(obj) + data = utils.get_item_properties(obj, columns) + + return (display_columns, data) + + +class DisassociateRouterToZone(command.ShowOne): + _description = _('Disassociate router with a private zone') + + def get_parser(self, prog_name): + parser = super(DisassociateRouterToZone, self).get_parser(prog_name) + + parser.add_argument( + 'zone', + metavar='', + help=_('UUID or name of the zone.') + ) + parser.add_argument( + '--router_id', + metavar='', + help=_('Router ID (VPC ID) for the private zone.') + ) + parser.add_argument( + '--router_region', + metavar='', + help=_('Router region for the private zone.') + ) + + return parser + + def take_action(self, parsed_args): + + client = self.app.client_manager.dns + + router = { + 'router_id': parsed_args.router_id + } + if parsed_args.router_region: + router['router_region'] = parsed_args.router_region + + zone = client.find_zone(parsed_args.zone, ignore_missing=False) + + if zone: + obj = client.remove_router_from_zone( + zone=zone, + **router + ) + + display_columns, columns = _get_columns(obj) + data = utils.get_item_properties(obj, columns) + + return (display_columns, data) + + +class ListNameserver(command.Lister): + _description = _('List DNS zone nameservers') + columns = ( + 'address', 'hostname', 'priority' + ) + + def get_parser(self, prog_name): + parser = super(ListNameserver, self).get_parser(prog_name) + + parser.add_argument( + 'zone', + metavar='', + help=_('UUID or name of the zone.') + ) + + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.dns + + zone = client.find_zone(parsed_args.zone, ignore_missing=False) + + if zone: + data = client.nameservers(zone=zone) + + table = (self.columns, + (utils.get_item_properties( + s, self.columns, formatters=_formatters + ) for s in data)) + return table +""" \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 65555cce7..353f2a49b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -32,6 +32,7 @@ openstack.cli.extension = auto_scaling = otcextensions.osclient.auto_scaling.client kms = otcextensions.osclient.kms.client cce = otcextensions.osclient.cce.client + ces = otcextensions.osclient.ces.client load_balancer = otcextensions.osclient.load_balancer.client volume_backup = otcextensions.osclient.volume_backup.client dms = otcextensions.osclient.dms.client @@ -257,6 +258,13 @@ openstack.dns.v2 = dns_ptr_record_set = otcextensions.osclient.dns.v2.ptr:SetPTR dns_ptr_record_unset = otcextensions.osclient.dns.v2.ptr:DeletePTR +openstack.ces.v1 = + ces_alarm_list = otcextensions.osclient.ces.v1.alarm:ListAlarm + ces_alarm_create = otcextensions.osclient.ces.v1.alarm:CreateAlarm + ces_alarm_delete = otcextensions.osclient.ces.v1.alarm:DeleteAlarm + ces_alarm_show = otcextensions.osclient.ces.v1.alarm:ShowAlarm + ces_alarm_set = otcextensions.osclient.ces.v1.alarm:SetAlarm + openstack.deh.v1 = deh_host_list = otcextensions.osclient.deh.v1.host:ListHost deh_host_show = otcextensions.osclient.deh.v1.host:ShowHost From 3dbffed8626b0414857726165c61a30a67ac1445 Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Thu, 13 Feb 2020 13:39:51 +0000 Subject: [PATCH 25/42] cli alarm improvements --- otcextensions/osclient/ces/v1/alarm.py | 108 ++++++++++++++++++++----- setup.cfg | 14 ++-- 2 files changed, 94 insertions(+), 28 deletions(-) diff --git a/otcextensions/osclient/ces/v1/alarm.py b/otcextensions/osclient/ces/v1/alarm.py index f698eeb6f..2a103497d 100644 --- a/otcextensions/osclient/ces/v1/alarm.py +++ b/otcextensions/osclient/ces/v1/alarm.py @@ -22,27 +22,6 @@ LOG = logging.getLogger(__name__) -""" -ZONE_TYPES = ['private', 'public'] - - -_formatters = { - # 'traffic_limited_list': sdk_utils.ListOfDictColumn, - # 'http_limited_list': sdk_utils.ListOfDictColumn, - # 'connection_limited_list': sdk_utils.ListOfDictColumn, -} - - - -""" - -def _get_columns(item): - column_map = { - } - hidden = ['location', 'links'] - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map, - hidden) - class ListAlarm(command.Lister): _description = _('List CES alarms') @@ -149,6 +128,93 @@ def take_action(self, parsed_args): return (display_columns, data) + +class CreateAlarm(command.ShowOne): + _description = _('Create Alarm') + + def get_parser(self, prog_name): + parser = super(CreateAlarm, self).get_parser(prog_name) + + parser.add_argument( + 'name', + metavar='', + help=_('Alarm name') + ) + parser.add_argument( + '--enabled', + metavar='', + default=True, + help=_('State of the alarm.\n' + 'True: enable alarm (default)\n' + 'False: disable alarm\n') + ) + parser.add_argument( + '--description', + metavar='', + help=_('Description of the alarm') + ) + parser.add_argument( + '--action-enabled', + default=True, + required=True, + help=_('Specifies whether the alarm action is triggered') + ) + parser.add_argument( + '--level', + metavar='', + type=int, + help=_('Indicates the alarm level\n' + '1: critical\n' + '2: major\n' + '3: minor\n' + '4: informational') + ) + parser.add_argument( + '--alarm-action-type', + metavar='', + help=_('Specifies the alarms action type' + 'notification: notification will be sent to user' + 'autoscaling: scaling action will be triggered') + ) + parser.add_argument( + '--alarm-action-notification-list', + metavar='', + action='append', + help=_('Specifies the list of objects being notified when\n' + 'alarm status changes.' + 'URN example structure:\n' + 'urn:smn:region:68438a86d98e427e907e0097b7e35d48:sd\n' + 'The parameter can be given multiple times to\n' + 'notify multiple targets') + ) + parser.add_argument( + '--comparison-operator', + metavar='', + help=_('Specifies the conditions comparison operator\n' + 'Values: >, =, <, ≥, or ≤') + ) + parser.add_argument( + '--count', + metavar='', + type=int, + help=_('Specifies how many times the alarm condition has to\n' + 'triggered until Alarm raises\n' + 'Value range: 1 to 5') + ) + parser.add_argument( + '--filter', + metavar='', + help=_('Specifies the data rollup method.\n' + 'Values: max, min, average, sum, variance') + ) + parser.add_argument( + '--period', + metavar='', + help=_('Specifies the data rollup method.\n' + 'Values: max, min, average, sum, variance') + ) + + """ class CreateZone(command.ShowOne): diff --git a/setup.cfg b/setup.cfg index 353f2a49b..914998460 100644 --- a/setup.cfg +++ b/setup.cfg @@ -187,6 +187,13 @@ openstack.cce.v3 = cce_cluster_node_create = otcextensions.osclient.cce.v2.cluster_node:CreateCCEClusterNode cce_cluster_node_delete = otcextensions.osclient.cce.v2.cluster_node:DeleteCCEClusterNode +openstack.ces.v1 = + ces_alarm_list = otcextensions.osclient.ces.v1.alarm:ListAlarm + ces_alarm_show = otcextensions.osclient.ces.v1.alarm:ShowAlarm + ces_alarm_create = otcextensions.osclient.ces.v1.alarm:CreateAlarm + ces_alarm_change_state = otcextensions.osclient.ces.v1.alarm:SetAlarm + ces_alarm_delete = otcextensions.osclient.ces.v1.alarm:DeleteAlarm + openstack.load_balancer.v1 = loadbalancer_list = otcextensions.osclient.load_balancer.v1.load_balancer:ListLoadBalancer loadbalancer_show = otcextensions.osclient.load_balancer.v1.load_balancer:ShowLoadBalancer @@ -258,13 +265,6 @@ openstack.dns.v2 = dns_ptr_record_set = otcextensions.osclient.dns.v2.ptr:SetPTR dns_ptr_record_unset = otcextensions.osclient.dns.v2.ptr:DeletePTR -openstack.ces.v1 = - ces_alarm_list = otcextensions.osclient.ces.v1.alarm:ListAlarm - ces_alarm_create = otcextensions.osclient.ces.v1.alarm:CreateAlarm - ces_alarm_delete = otcextensions.osclient.ces.v1.alarm:DeleteAlarm - ces_alarm_show = otcextensions.osclient.ces.v1.alarm:ShowAlarm - ces_alarm_set = otcextensions.osclient.ces.v1.alarm:SetAlarm - openstack.deh.v1 = deh_host_list = otcextensions.osclient.deh.v1.host:ListHost deh_host_show = otcextensions.osclient.deh.v1.host:ShowHost From 01cae7cbf3ce1367c7fcaef029183c22028d0923 Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Fri, 14 Feb 2020 11:33:26 +0000 Subject: [PATCH 26/42] some extensions --- otcextensions/osclient/ces/v1/alarm.py | 231 +++++++++++-------------- otcextensions/sdk/ces/v1/alarm.py | 14 +- 2 files changed, 106 insertions(+), 139 deletions(-) diff --git a/otcextensions/osclient/ces/v1/alarm.py b/otcextensions/osclient/ces/v1/alarm.py index 2a103497d..556a08e16 100644 --- a/otcextensions/osclient/ces/v1/alarm.py +++ b/otcextensions/osclient/ces/v1/alarm.py @@ -169,6 +169,7 @@ def get_parser(self, prog_name): '3: minor\n' '4: informational') ) + # AlarmActions parser.add_argument( '--alarm-action-type', metavar='', @@ -187,6 +188,26 @@ def get_parser(self, prog_name): 'The parameter can be given multiple times to\n' 'notify multiple targets') ) + # OkActions + parser.add_argument( + '--ok-action-type', + metavar='', + help=_('Specifies the alarms action type' + 'notification: notification will be sent to user' + 'autoscaling: scaling action will be triggered') + ) + parser.add_argument( + '--ok-action-notification-list', + metavar='', + action='append', + help=_('Specifies the list of objects being notified when\n' + 'alarm status changes.' + 'URN example structure:\n' + 'urn:smn:region:68438a86d98e427e907e0097b7e35d48:sd\n' + 'The parameter can be given multiple times to\n' + 'notify multiple targets') + ) + # ConditionSpec parser.add_argument( '--comparison-operator', metavar='', @@ -210,9 +231,84 @@ def get_parser(self, prog_name): parser.add_argument( '--period', metavar='', - help=_('Specifies the data rollup method.\n' - 'Values: max, min, average, sum, variance') + help=_('Indicates the interval (in seconds) for checking\n' + 'whether the configured alarm rules are met') + ) + parser.add_argument( + '--unit', + metavar='', + help=_('Specifies data unit\n' + 'Values: B/s') + ) + parser.add_argument( + '--value', + metavar='', + help=_('Specifies the alarm threshold\n' + 'Values: 0 to max(int)') + ) + # MetricSpec + parser.add_argument( + '--metric-name', + metavar='', + help=_('Specifies the metric name') + ) + parser.add_argument( + '--namespace', + metavar='', + help=_('Specifies the namespace of the metric such as SYS.ECS') ) + # DimensionsSpec for Metrics + # This is a list of dictionaries + # IMPROVEMENT NEEDED + parser.add_argument( + '--dimension-name', + action='append', + metavar='', + help=_('dimension.name: object type e.g. ECS') + ) + parser.add_argument( + '--dimension-value', + action='append', + metavar='', + help=_('dimension.value: object id e.g. ECS ID') + ) + + def take_action(self, parsed_args): + + client = self.app.client_manager.ces + + attrs = {} + + if parsed_args.name: + attrs['name'] = parsed_args.name + if parsed_args.email: + attrs['email'] = parsed_args.email + if parsed_args.description: + attrs['description'] = parsed_args.description + if parsed_args.type: + attrs['zone_type'] = parsed_args.type + if parsed_args.ttl: + attrs['ttl'] = parsed_args.ttl + if parsed_args.type and parsed_args.type == 'private': + if not parsed_args.router_id: + msg = _('router_id is required for a private zone') + raise argparse.ArgumentTypeError(msg) + router = { + 'router_id': parsed_args.router_id + } + if parsed_args.router_region: + router['router_region'] = parsed_args.router_region + attrs['router'] = router + + obj = client.create_zone( + **attrs + ) + + display_columns, columns = _get_columns(obj) + data = utils.get_item_properties(obj, columns) + + return (display_columns, data) + """ @@ -302,135 +398,4 @@ def take_action(self, parsed_args): data = utils.get_item_properties(obj, columns) return (display_columns, data) - - - - - -class AssociateRouterToZone(command.ShowOne): - _description = _('Associate router with a private zone') - - def get_parser(self, prog_name): - parser = super(AssociateRouterToZone, self).get_parser(prog_name) - - parser.add_argument( - 'zone', - metavar='', - help=_('UUID or name of the zone.') - ) - parser.add_argument( - '--router_id', - metavar='', - help=_('Router ID (VPC ID) for the private zone.') - ) - parser.add_argument( - '--router_region', - metavar='', - help=_('Router region for the private zone.') - ) - - return parser - - def take_action(self, parsed_args): - - client = self.app.client_manager.dns - - router = { - 'router_id': parsed_args.router_id - } - if parsed_args.router_region: - router['router_region'] = parsed_args.router_region - - zone = client.find_zone(parsed_args.zone, ignore_missing=False) - - if zone: - obj = client.add_router_to_zone( - zone=zone, - **router - ) - - display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns) - - return (display_columns, data) - - -class DisassociateRouterToZone(command.ShowOne): - _description = _('Disassociate router with a private zone') - - def get_parser(self, prog_name): - parser = super(DisassociateRouterToZone, self).get_parser(prog_name) - - parser.add_argument( - 'zone', - metavar='', - help=_('UUID or name of the zone.') - ) - parser.add_argument( - '--router_id', - metavar='', - help=_('Router ID (VPC ID) for the private zone.') - ) - parser.add_argument( - '--router_region', - metavar='', - help=_('Router region for the private zone.') - ) - - return parser - - def take_action(self, parsed_args): - - client = self.app.client_manager.dns - - router = { - 'router_id': parsed_args.router_id - } - if parsed_args.router_region: - router['router_region'] = parsed_args.router_region - - zone = client.find_zone(parsed_args.zone, ignore_missing=False) - - if zone: - obj = client.remove_router_from_zone( - zone=zone, - **router - ) - - display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns) - - return (display_columns, data) - - -class ListNameserver(command.Lister): - _description = _('List DNS zone nameservers') - columns = ( - 'address', 'hostname', 'priority' - ) - - def get_parser(self, prog_name): - parser = super(ListNameserver, self).get_parser(prog_name) - - parser.add_argument( - 'zone', - metavar='', - help=_('UUID or name of the zone.') - ) - - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.dns - - zone = client.find_zone(parsed_args.zone, ignore_missing=False) - - if zone: - data = client.nameservers(zone=zone) - - table = (self.columns, - (utils.get_item_properties( - s, self.columns, formatters=_formatters - ) for s in data)) - return table """ \ No newline at end of file diff --git a/otcextensions/sdk/ces/v1/alarm.py b/otcextensions/sdk/ces/v1/alarm.py index de3c30903..c986a8aa0 100644 --- a/otcextensions/sdk/ces/v1/alarm.py +++ b/otcextensions/sdk/ces/v1/alarm.py @@ -24,7 +24,7 @@ # Number of total queried results / alarms # total = resource.Body('total') -class AlarmActionsSpec(resource.Resource): +class ActionsSpec(resource.Resource): # Properties # notification list ID @@ -70,7 +70,7 @@ class MetricSpec(resource.Resource): # List of metric dimensions dimensions = resource.Body('dimensions', type=list, list_type=DimensionsSpec) - # Metric name, such as cpu_util in ECS metrics + # Specifies the metric name metric_name = resource.Body('metric_name') # Metric Namespace namespace = resource.Body('namespace') @@ -93,8 +93,8 @@ class Alarm(resource.Resource): ) # Properties - # Indicates the action triggered by clearing an alarm - alarm_actions = resource.Body('alarm_actions', type=AlarmActionsSpec) + # Specifies the action triggered by an alarm. + alarm_actions = resource.Body('alarm_actions', type=ActionsSpec) # Indicates whether an action will be triggered by an alarm # True: action will be triggered # False: action will not be triggered @@ -108,13 +108,15 @@ class Alarm(resource.Resource): # alarm severity # values: 1: critical, 2: major, 3: minor, 4: informational alarm alarm_level = resource.Body('alarm_level', type=int) - # Name of the alarm - name = resource.Body('alarm_name') # Alarm status # ok: alarm status is normal # alarm: an alarm is generated # insufficient_data: required data is insufficient alarm_state = resource.Body('alarm_state') + # Name of the alarm + name = resource.Body('alarm_name') + # Indicates the action triggered by clearing an alarm + ok_actions = resource.Body('ok_actions', type=ActionsSpec) # Describes alarm triggering condititon condition = resource.Body('condition', type=ConditionSpec) # Specification of specific alarm From 5006a8ebdf111167840ad8cff00fe6ecc12a7831 Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Wed, 29 Jul 2020 11:33:40 +0000 Subject: [PATCH 27/42] Cleanup an minor changes to OSC alarm module --- examples/ces/list_alarms.py | 23 +++++ otcextensions/osclient/ces/v1/alarm.py | 134 +++++++------------------ otcextensions/sdk/ces/v1/_proxy.py | 4 +- setup.cfg | 2 +- 4 files changed, 62 insertions(+), 101 deletions(-) create mode 100644 examples/ces/list_alarms.py diff --git a/examples/ces/list_alarms.py b/examples/ces/list_alarms.py new file mode 100644 index 000000000..c37b537e3 --- /dev/null +++ b/examples/ces/list_alarms.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +""" +List all CloudEye alarm rules +""" +import openstack + + +openstack.enable_logging(True) +conn = openstack.connect(cloud='otc') + +for alarm in conn.ces.alarms(limit=1): + print(alarm) diff --git a/otcextensions/osclient/ces/v1/alarm.py b/otcextensions/osclient/ces/v1/alarm.py index 556a08e16..99606de60 100644 --- a/otcextensions/osclient/ces/v1/alarm.py +++ b/otcextensions/osclient/ces/v1/alarm.py @@ -22,16 +22,45 @@ LOG = logging.getLogger(__name__) - -class ListAlarm(command.Lister): +def _translate_alarm_level(level): + case = { + 1: '1: Critical', + 2: '2: Major', + 3: '3: Minor', + 4: '4: Informational' + } + return case.get(level) + +def _flatten_output(obj): + data = { + 'id': obj.id, + 'name': obj.name, + 'metric.resource_type': obj.metric.namespace, + # The return value of obj.metric.dimensions is a list. The list has + # only one value. It is not possible to have several items inside. + 'metric.monitored_obj': obj.metric.dimensions[0].value, + 'alarm_level': _translate_alarm_level(obj.alarm_level), + 'enabled': obj.alarm_enabled, + 'action_enabled': obj.alarm_action_enabled, + 'state': obj.alarm_state, + } + return data + +class ListAlarms(command.Lister): _description = _('List CES alarms') columns = ( - 'id', 'name', 'alarm_enabled', 'alarm_action_enabled', 'alarm_state', 'alarm_level' + 'id', + 'name', + 'metric.resource_type', + 'metric.monitored_obj', + 'alarm_level', + 'enabled', + 'action_enabled', + 'state' ) def get_parser(self, prog_name): - parser = super(ListAlarm, self).get_parser(prog_name) - + parser = super(ListAlarms, self).get_parser(prog_name) return parser def take_action(self, parsed_args): @@ -40,8 +69,8 @@ def take_action(self, parsed_args): data = client.alarms() table = (self.columns, - (utils.get_item_properties( - s, self.columns + (utils.get_dict_properties( + _flatten_output(s), self.columns ) for s in data)) return table @@ -308,94 +337,3 @@ def take_action(self, parsed_args): data = utils.get_item_properties(obj, columns) return (display_columns, data) - - - -""" - -class CreateZone(command.ShowOne): - _description = _('Create zone') - - def get_parser(self, prog_name): - parser = super(CreateZone, self).get_parser(prog_name) - - parser.add_argument( - 'name', - metavar='', - help=_('DNS Name for the zone.') - ) - parser.add_argument( - '--email', - metavar='', - help=_('E-mail for the zone. Used in SOA records for the zone.') - ) - parser.add_argument( - '--description', - metavar='', - help=_('Description for this zone.') - ) - parser.add_argument( - '--type', - metavar='{' + ','.join(ZONE_TYPES) + '}', - type=lambda s: s.lower(), - choices=ZONE_TYPES, - help=_('Domain name type, the value of which can be ' - '`public` or `private` .') - ) - parser.add_argument( - '--ttl', - metavar='<300-2147483647>', - type=int, - # NOTE: py2 does not support such big int, skip unless py3-only - # choices=range(300, 2147483647), - help=_('TTL (Time to Live) for the zone.') - ) - parser.add_argument( - '--router_id', - metavar='', - help=_('Router ID (VPC ID) for the private zone.') - ) - parser.add_argument( - '--router_region', - metavar='', - help=_('Router region for the private zone.') - ) - - return parser - - def take_action(self, parsed_args): - - client = self.app.client_manager.dns - - attrs = {} - - if parsed_args.name: - attrs['name'] = parsed_args.name - if parsed_args.email: - attrs['email'] = parsed_args.email - if parsed_args.description: - attrs['description'] = parsed_args.description - if parsed_args.type: - attrs['zone_type'] = parsed_args.type - if parsed_args.ttl: - attrs['ttl'] = parsed_args.ttl - if parsed_args.type and parsed_args.type == 'private': - if not parsed_args.router_id: - msg = _('router_id is required for a private zone') - raise argparse.ArgumentTypeError(msg) - router = { - 'router_id': parsed_args.router_id - } - if parsed_args.router_region: - router['router_region'] = parsed_args.router_region - attrs['router'] = router - - obj = client.create_zone( - **attrs - ) - - display_columns, columns = _get_columns(obj) - data = utils.get_item_properties(obj, columns) - - return (display_columns, data) -""" \ No newline at end of file diff --git a/otcextensions/sdk/ces/v1/_proxy.py b/otcextensions/sdk/ces/v1/_proxy.py index 7088bc613..18cb92047 100644 --- a/otcextensions/sdk/ces/v1/_proxy.py +++ b/otcextensions/sdk/ces/v1/_proxy.py @@ -23,13 +23,13 @@ class Proxy(proxy.Proxy): skip_discovery = True # ======== Alarms ======== - def alarms(self): + def alarms(self, **query): """Return a generator of alarms :returns: A generator of alarm objects :rtype: :class:`~otcextensions.sdk.ces.v1.alarm.Alarm` """ - return self._list(_alarm.Alarm) + return self._list(_alarm.Alarm, **query) def get_alarm(self, alarm): """Return a single alarm diff --git a/setup.cfg b/setup.cfg index a5dd41b55..676b17d61 100644 --- a/setup.cfg +++ b/setup.cfg @@ -216,7 +216,7 @@ openstack.cce.v3 = cce_cluster_node_delete = otcextensions.osclient.cce.v2.cluster_node:DeleteCCEClusterNode openstack.ces.v1 = - ces_alarm_list = otcextensions.osclient.ces.v1.alarm:ListAlarm + ces_alarm_list = otcextensions.osclient.ces.v1.alarm:ListAlarms ces_alarm_show = otcextensions.osclient.ces.v1.alarm:ShowAlarm ces_alarm_create = otcextensions.osclient.ces.v1.alarm:CreateAlarm ces_alarm_change_state = otcextensions.osclient.ces.v1.alarm:SetAlarm From 30869af13413ac0f01119e4d82bda0a2fdd07f84 Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Thu, 30 Jul 2020 15:16:04 +0000 Subject: [PATCH 28/42] OSC alarm rule update --- otcextensions/osclient/ces/v1/alarm.py | 32 ++++++++++++++++++-------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/otcextensions/osclient/ces/v1/alarm.py b/otcextensions/osclient/ces/v1/alarm.py index 99606de60..2eb773dda 100644 --- a/otcextensions/osclient/ces/v1/alarm.py +++ b/otcextensions/osclient/ces/v1/alarm.py @@ -46,6 +46,14 @@ def _flatten_output(obj): } return data +def _get_columns(item): + column_map = { + } + inv_columns = [''] + return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map, + inv_columns) + +# TODO: Implement query arguments -> SDK not working class ListAlarms(command.Lister): _description = _('List CES alarms') columns = ( @@ -76,7 +84,7 @@ def take_action(self, parsed_args): class ShowAlarm(command.ShowOne): - _description = _('Show the alarm details') + _description = _('Show CloudEye alarm rule details') def get_parser(self, prog_name): parser = super(ShowAlarm, self).get_parser(prog_name) @@ -84,9 +92,8 @@ def get_parser(self, prog_name): parser.add_argument( 'alarm', metavar='', - help=_('UUID or name of the alarm.') + help=_('UUID or name of the alarm rule.') ) - return parser def take_action(self, parsed_args): @@ -131,7 +138,7 @@ class SetAlarm(command.ShowOne): _description = _('Change alarm_status_enabled to the opposite value of true / false.') def get_parser(self, prog_name): - parser = super(ShowAlarm, self).get_parser(prog_name) + parser = super(SetAlarm, self).get_parser(prog_name) parser.add_argument( 'alarm', @@ -148,13 +155,18 @@ def take_action(self, parsed_args): alarm = client.find_alarm(parsed_args.alarm, ignore_missing=False) if alarm: - obj = client.update_alarm_enabled( + client.update_alarm_enabled( alarm=alarm ) - + + # instance of alarm needs to be found again due to missing + # return body of alarm rule update function + obj = client.find_alarm( + parsed_args.alarm, + ignore_missing=False + ) display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns) - return (display_columns, data) @@ -315,12 +327,12 @@ def take_action(self, parsed_args): if parsed_args.description: attrs['description'] = parsed_args.description if parsed_args.type: - attrs['zone_type'] = parsed_args.type + attrs['alarm_type'] = parsed_args.type if parsed_args.ttl: attrs['ttl'] = parsed_args.ttl if parsed_args.type and parsed_args.type == 'private': if not parsed_args.router_id: - msg = _('router_id is required for a private zone') + msg = _('router_id is required for a private alarm') raise argparse.ArgumentTypeError(msg) router = { 'router_id': parsed_args.router_id @@ -329,7 +341,7 @@ def take_action(self, parsed_args): router['router_region'] = parsed_args.router_region attrs['router'] = router - obj = client.create_zone( + obj = client.create_alarm( **attrs ) From 24b6d8f30bb131c26dfc48b74e6ab769107646ad Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Fri, 31 Jul 2020 13:13:01 +0000 Subject: [PATCH 29/42] enhance OCS alarm rule --- otcextensions/osclient/ces/v1/alarm.py | 127 +++++++++++++++++-------- otcextensions/sdk/__init__.py | 12 +-- otcextensions/sdk/ces/v1/alarm.py | 17 +++- 3 files changed, 108 insertions(+), 48 deletions(-) diff --git a/otcextensions/osclient/ces/v1/alarm.py b/otcextensions/osclient/ces/v1/alarm.py index 2eb773dda..6bd42756f 100644 --- a/otcextensions/osclient/ces/v1/alarm.py +++ b/otcextensions/osclient/ces/v1/alarm.py @@ -11,7 +11,6 @@ # under the License. # '''CES Alarm v1 action implementations''' -import argparse import logging from osc_lib import utils @@ -22,6 +21,7 @@ LOG = logging.getLogger(__name__) + def _translate_alarm_level(level): case = { 1: '1: Critical', @@ -31,6 +31,7 @@ def _translate_alarm_level(level): } return case.get(level) + def _flatten_output(obj): data = { 'id': obj.id, @@ -46,6 +47,7 @@ def _flatten_output(obj): } return data + def _get_columns(item): column_map = { } @@ -53,7 +55,8 @@ def _get_columns(item): return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map, inv_columns) -# TODO: Implement query arguments -> SDK not working + +# TODO(undefined): Implement query arguments -> SDK not working class ListAlarms(command.Lister): _description = _('List CES alarms') columns = ( @@ -68,7 +71,7 @@ class ListAlarms(command.Lister): ) def get_parser(self, prog_name): - parser = super(ListAlarms, self).get_parser(prog_name) + parser = super(ListAlarms, self).get_parser(prog_name) return parser def take_action(self, parsed_args): @@ -135,7 +138,7 @@ def take_action(self, parsed_args): class SetAlarm(command.ShowOne): - _description = _('Change alarm_status_enabled to the opposite value of true / false.') + _description = _('Switch Alarm status.') def get_parser(self, prog_name): parser = super(SetAlarm, self).get_parser(prog_name) @@ -158,7 +161,7 @@ def take_action(self, parsed_args): client.update_alarm_enabled( alarm=alarm ) - + # instance of alarm needs to be found again due to missing # return body of alarm rule update function obj = client.find_alarm( @@ -171,7 +174,7 @@ def take_action(self, parsed_args): class CreateAlarm(command.ShowOne): - _description = _('Create Alarm') + _description = _('Create CloudEye alarm rule') def get_parser(self, prog_name): parser = super(CreateAlarm, self).get_parser(prog_name) @@ -184,7 +187,6 @@ def get_parser(self, prog_name): parser.add_argument( '--enabled', metavar='', - default=True, help=_('State of the alarm.\n' 'True: enable alarm (default)\n' 'False: disable alarm\n') @@ -196,7 +198,7 @@ def get_parser(self, prog_name): ) parser.add_argument( '--action-enabled', - default=True, + default=False, required=True, help=_('Specifies whether the alarm action is triggered') ) @@ -210,6 +212,7 @@ def get_parser(self, prog_name): '3: minor\n' '4: informational') ) + # AlarmActions parser.add_argument( '--alarm-action-type', @@ -229,6 +232,7 @@ def get_parser(self, prog_name): 'The parameter can be given multiple times to\n' 'notify multiple targets') ) + # OkActions parser.add_argument( '--ok-action-type', @@ -248,10 +252,12 @@ def get_parser(self, prog_name): 'The parameter can be given multiple times to\n' 'notify multiple targets') ) + # ConditionSpec parser.add_argument( '--comparison-operator', metavar='', + required=True, help=_('Specifies the conditions comparison operator\n' 'Values: >, =, <, ≥, or ≤') ) @@ -259,6 +265,7 @@ def get_parser(self, prog_name): '--count', metavar='', type=int, + required=True, help=_('Specifies how many times the alarm condition has to\n' 'triggered until Alarm raises\n' 'Value range: 1 to 5') @@ -266,12 +273,14 @@ def get_parser(self, prog_name): parser.add_argument( '--filter', metavar='', + required=True, help=_('Specifies the data rollup method.\n' 'Values: max, min, average, sum, variance') ) parser.add_argument( '--period', metavar='', + required=True, help=_('Indicates the interval (in seconds) for checking\n' 'whether the configured alarm rules are met') ) @@ -279,25 +288,16 @@ def get_parser(self, prog_name): '--unit', metavar='', help=_('Specifies data unit\n' - 'Values: B/s') + 'Values: B/s, %') ) parser.add_argument( '--value', metavar='', + required=True, help=_('Specifies the alarm threshold\n' 'Values: 0 to max(int)') ) - # MetricSpec - parser.add_argument( - '--metric-name', - metavar='', - help=_('Specifies the metric name') - ) - parser.add_argument( - '--namespace', - metavar='', - help=_('Specifies the namespace of the metric such as SYS.ECS') - ) + # DimensionsSpec for Metrics # This is a list of dictionaries # IMPROVEMENT NEEDED @@ -305,41 +305,90 @@ def get_parser(self, prog_name): '--dimension-name', action='append', metavar='', + required=True, help=_('dimension.name: object type e.g. ECS') ) parser.add_argument( '--dimension-value', action='append', + required=True, metavar='', help=_('dimension.value: object id e.g. ECS ID') ) - + + # MetricSpec + parser.add_argument( + '--metric-name', + metavar='', + required=True, + help=_('Specifies the metric name') + ) + parser.add_argument( + '--namespace', + metavar='', + required=True, + help=_('Specifies the namespace of the metric such as SYS.ECS') + ) + def take_action(self, parsed_args): client = self.app.client_manager.ces attrs = {} - if parsed_args.name: - attrs['name'] = parsed_args.name - if parsed_args.email: - attrs['email'] = parsed_args.email + attrs['name'] = parsed_args.name + if parsed_args.enabled: + attrs['alarm_enabled'] = parsed_args.enabled if parsed_args.description: - attrs['description'] = parsed_args.description - if parsed_args.type: - attrs['alarm_type'] = parsed_args.type - if parsed_args.ttl: - attrs['ttl'] = parsed_args.ttl - if parsed_args.type and parsed_args.type == 'private': - if not parsed_args.router_id: - msg = _('router_id is required for a private alarm') - raise argparse.ArgumentTypeError(msg) - router = { - 'router_id': parsed_args.router_id + attrs['alarm_description'] = parsed_args.description + attrs['alarm_action_enabled'] = parsed_args.action_enabled + if parsed_args.level: + attrs['alarm_level'] = parsed_args.level + + if parsed_args.action_enabled is True: + if (parsed_args.ok_action_type + and parsed_args.ok_action_notification_list): + + nl = parsed_args.ok_action_notification_list + ok_actions = { + 'type': parsed_args.ok_action_type, + 'notificationList': nl + } + attrs['ok_actions'] = ok_actions + + if (parsed_args.alarm_action_type + and parsed_args.alarm_action_notification_list): + + nl = parsed_args.alarm_action_notification_list + alarm_actions = { + 'type': parsed_args.alarm_action_type, + 'notificationList': nl + } + attrs['alarm_actions'] = alarm_actions + + condition = { + 'comparison_operatur': parsed_args.comparison_operator, + 'count': parsed_args.count, + 'filter': parsed_args.filter, + 'period': parsed_args.period, + 'value': parsed_args.value + } + if parsed_args.unit: + condition = { + 'unit': parsed_args.unit } - if parsed_args.router_region: - router['router_region'] = parsed_args.router_region - attrs['router'] = router + attrs['condition'] = condition + + dimensions = { + 'name': parsed_args.dimension_name, + 'value': parsed_args.dimension_value + } + metric = { + 'dimensions': dimensions, + 'metric_name': parsed_args.metric_name, + 'namespace': parsed_args.namespace + } + attrs['metric'] = metric obj = client.create_alarm( **attrs diff --git a/otcextensions/sdk/__init__.py b/otcextensions/sdk/__init__.py index c31bd8005..79571b0a2 100644 --- a/otcextensions/sdk/__init__.py +++ b/otcextensions/sdk/__init__.py @@ -61,12 +61,12 @@ 'service_type': 'ces', 'append_project_id': True, }, - 'ces': { - 'service_type': 'ces', - 'endpoint_service_type': 'cesv1', - 'set_endpoint_override': True - # 'append_project_id': False, - }, + # 'ces': { + # 'service_type': 'ces', + # 'endpoint_service_type': 'cesv1', + # 'set_endpoint_override': True + # 'append_project_id': False, + # }, 'cts': { 'service_type': 'cts', }, diff --git a/otcextensions/sdk/ces/v1/alarm.py b/otcextensions/sdk/ces/v1/alarm.py index c986a8aa0..e0e94b903 100644 --- a/otcextensions/sdk/ces/v1/alarm.py +++ b/otcextensions/sdk/ces/v1/alarm.py @@ -24,7 +24,18 @@ # Number of total queried results / alarms # total = resource.Body('total') -class ActionsSpec(resource.Resource): + +class AlarmActionsSpec(resource.Resource): + + # Properties + # notification list ID + notificationList = resource.Body('notificationList') + # Indicates the type of action triggered by an alarm. + # Value can be notication or autoscaling + type = resource.Body('type') + + +class OkActionsSpec(resource.Resource): # Properties # notification list ID @@ -94,7 +105,7 @@ class Alarm(resource.Resource): # Properties # Specifies the action triggered by an alarm. - alarm_actions = resource.Body('alarm_actions', type=ActionsSpec) + alarm_actions = resource.Body('alarm_actions', type=AlarmActionsSpec) # Indicates whether an action will be triggered by an alarm # True: action will be triggered # False: action will not be triggered @@ -116,7 +127,7 @@ class Alarm(resource.Resource): # Name of the alarm name = resource.Body('alarm_name') # Indicates the action triggered by clearing an alarm - ok_actions = resource.Body('ok_actions', type=ActionsSpec) + ok_actions = resource.Body('ok_actions', type=OkActionsSpec) # Describes alarm triggering condititon condition = resource.Body('condition', type=ConditionSpec) # Specification of specific alarm From be997525fb585934c9f3105ff25db5cd4a55faf9 Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Tue, 4 Aug 2020 08:28:34 +0000 Subject: [PATCH 30/42] first error solved --- otcextensions/osclient/ces/v1/alarm.py | 46 ++++++++++++++------------ 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/otcextensions/osclient/ces/v1/alarm.py b/otcextensions/osclient/ces/v1/alarm.py index 6bd42756f..a48883aa4 100644 --- a/otcextensions/osclient/ces/v1/alarm.py +++ b/otcextensions/osclient/ces/v1/alarm.py @@ -187,9 +187,9 @@ def get_parser(self, prog_name): parser.add_argument( '--enabled', metavar='', - help=_('State of the alarm.\n' - 'True: enable alarm (default)\n' - 'False: disable alarm\n') + help=_('State of the alarm.' + 'True: enable alarm (default)' + 'False: disable alarm') ) parser.add_argument( '--description', @@ -206,10 +206,10 @@ def get_parser(self, prog_name): '--level', metavar='', type=int, - help=_('Indicates the alarm level\n' - '1: critical\n' - '2: major\n' - '3: minor\n' + help=_('Indicates the alarm level' + '1: critical' + '2: major' + '3: minor' '4: informational') ) @@ -225,11 +225,11 @@ def get_parser(self, prog_name): '--alarm-action-notification-list', metavar='', action='append', - help=_('Specifies the list of objects being notified when\n' + help=_('Specifies the list of objects being notified when' 'alarm status changes.' - 'URN example structure:\n' - 'urn:smn:region:68438a86d98e427e907e0097b7e35d48:sd\n' - 'The parameter can be given multiple times to\n' + 'URN example structure:' + 'urn:smn:region:68438a86d98e427e907e0097b7e35d48:sd' + 'The parameter can be given multiple times to' 'notify multiple targets') ) @@ -245,11 +245,11 @@ def get_parser(self, prog_name): '--ok-action-notification-list', metavar='', action='append', - help=_('Specifies the list of objects being notified when\n' + help=_('Specifies the list of objects being notified when' 'alarm status changes.' - 'URN example structure:\n' - 'urn:smn:region:68438a86d98e427e907e0097b7e35d48:sd\n' - 'The parameter can be given multiple times to\n' + 'URN example structure:' + 'urn:smn:region:68438a86d98e427e907e0097b7e35d48:sd' + 'The parameter can be given multiple times to' 'notify multiple targets') ) @@ -258,7 +258,7 @@ def get_parser(self, prog_name): '--comparison-operator', metavar='', required=True, - help=_('Specifies the conditions comparison operator\n' + help=_('Specifies the conditions comparison operator' 'Values: >, =, <, ≥, or ≤') ) parser.add_argument( @@ -266,35 +266,35 @@ def get_parser(self, prog_name): metavar='', type=int, required=True, - help=_('Specifies how many times the alarm condition has to\n' - 'triggered until Alarm raises\n' + help=_('Specifies how many times the alarm condition has to' + 'triggered until Alarm raises' 'Value range: 1 to 5') ) parser.add_argument( '--filter', metavar='', required=True, - help=_('Specifies the data rollup method.\n' + help=_('Specifies the data rollup method.' 'Values: max, min, average, sum, variance') ) parser.add_argument( '--period', metavar='', required=True, - help=_('Indicates the interval (in seconds) for checking\n' + help=_('Indicates the interval (in seconds) for checking' 'whether the configured alarm rules are met') ) parser.add_argument( '--unit', metavar='', - help=_('Specifies data unit\n' + help=_('Specifies data unit' 'Values: B/s, %') ) parser.add_argument( '--value', metavar='', required=True, - help=_('Specifies the alarm threshold\n' + help=_('Specifies the alarm threshold' 'Values: 0 to max(int)') ) @@ -330,6 +330,8 @@ def get_parser(self, prog_name): help=_('Specifies the namespace of the metric such as SYS.ECS') ) + return parser + def take_action(self, parsed_args): client = self.app.client_manager.ces From 10c883ba71524f80704394a4a58ad86a670a3c18 Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Tue, 4 Aug 2020 10:27:44 +0000 Subject: [PATCH 31/42] minor fixes, starting documentation --- examples/ces/delete_alarm.py | 25 +++++++ examples/ces/find_alarm.py | 24 +++++++ examples/ces/get_alarm.py | 24 +++++++ examples/ces/switch_alarm_state.py | 25 +++++++ otcextensions/osclient/ces/v1/alarm.py | 69 +++++++++---------- otcextensions/sdk/ces/v1/_proxy.py | 2 +- .../tests/unit/sdk/ces/v1/test_proxy.py | 2 +- setup.cfg | 2 +- 8 files changed, 135 insertions(+), 38 deletions(-) create mode 100644 examples/ces/delete_alarm.py create mode 100644 examples/ces/find_alarm.py create mode 100644 examples/ces/get_alarm.py create mode 100644 examples/ces/switch_alarm_state.py diff --git a/examples/ces/delete_alarm.py b/examples/ces/delete_alarm.py new file mode 100644 index 000000000..f2b97694d --- /dev/null +++ b/examples/ces/delete_alarm.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +""" +Delete CloudEye alarm rule +""" +import openstack + +openstack.enable_logging(True) +conn = openstack.connect(cloud='otc') + + +alarm = 'al1596533022051EZVV2nlZ8' +alarm = conn.ces.find_alarm(alarm) +alarm = conn.ces.delete_alarm(alarm) +print(alarm) diff --git a/examples/ces/find_alarm.py b/examples/ces/find_alarm.py new file mode 100644 index 000000000..0408111ca --- /dev/null +++ b/examples/ces/find_alarm.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +""" +Find CloudEye alarm rule by name or ID +""" +import openstack + +openstack.enable_logging(True) +conn = openstack.connect(cloud='otc') + + +alarm = 'alarm_id_or_name' +alarm = conn.ces.find_alarm(alarm) +print(alarm) diff --git a/examples/ces/get_alarm.py b/examples/ces/get_alarm.py new file mode 100644 index 000000000..e2b9a907a --- /dev/null +++ b/examples/ces/get_alarm.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +""" +Find CloudEye alarm rule by ID +""" +import openstack + +openstack.enable_logging(True) +conn = openstack.connect(cloud='otc') + + +alarm = 'alarm_id' +alarm = conn.ces.get_alarm(alarm) +print(alarm) diff --git a/examples/ces/switch_alarm_state.py b/examples/ces/switch_alarm_state.py new file mode 100644 index 000000000..4f04734eb --- /dev/null +++ b/examples/ces/switch_alarm_state.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +""" +Disable / Enable CloudEye alarm state +""" +import openstack + +openstack.enable_logging(True) +conn = openstack.connect(cloud='otc') + + +alarm = 'alarm_name_or_id' +alarm = conn.ces.find_alarm(alarm) +alarm = conn.ces.switch_alarm_state(alarm) +print(alarm) diff --git a/otcextensions/osclient/ces/v1/alarm.py b/otcextensions/osclient/ces/v1/alarm.py index a48883aa4..14ca23735 100644 --- a/otcextensions/osclient/ces/v1/alarm.py +++ b/otcextensions/osclient/ces/v1/alarm.py @@ -158,7 +158,7 @@ def take_action(self, parsed_args): alarm = client.find_alarm(parsed_args.alarm, ignore_missing=False) if alarm: - client.update_alarm_enabled( + client.switch_alarm_state( alarm=alarm ) @@ -187,9 +187,9 @@ def get_parser(self, prog_name): parser.add_argument( '--enabled', metavar='', - help=_('State of the alarm.' - 'True: enable alarm (default)' - 'False: disable alarm') + help=_('State of the alarm.\n' + 'True: enable alarm (default)\n' + 'False: disable alarm\n') ) parser.add_argument( '--description', @@ -206,10 +206,10 @@ def get_parser(self, prog_name): '--level', metavar='', type=int, - help=_('Indicates the alarm level' - '1: critical' - '2: major' - '3: minor' + help=_('Indicates the alarm level\n' + '1: critical\n' + '2: major\n' + '3: minor\n' '4: informational') ) @@ -217,40 +217,40 @@ def get_parser(self, prog_name): parser.add_argument( '--alarm-action-type', metavar='', - help=_('Specifies the alarms action type' - 'notification: notification will be sent to user' + help=_('Specifies the alarms action type.\n' + 'notification: notification will be sent to user\n' 'autoscaling: scaling action will be triggered') ) parser.add_argument( '--alarm-action-notification-list', metavar='', action='append', - help=_('Specifies the list of objects being notified when' - 'alarm status changes.' - 'URN example structure:' - 'urn:smn:region:68438a86d98e427e907e0097b7e35d48:sd' - 'The parameter can be given multiple times to' - 'notify multiple targets') + help=_('Specifies the list of objects being notified when ' + 'alarm status changes.\n' + 'URN example structure:\n' + 'urn:smn:region:68438a86d98e427e907e0097b7e35d48:sd\n' + 'The parameter can be given multiple times to ' + 'notify multiple targets.') ) # OkActions parser.add_argument( '--ok-action-type', metavar='', - help=_('Specifies the alarms action type' - 'notification: notification will be sent to user' + help=_('Specifies the alarms action type.\n' + 'notification: notification will be sent to user\n' 'autoscaling: scaling action will be triggered') ) parser.add_argument( '--ok-action-notification-list', metavar='', action='append', - help=_('Specifies the list of objects being notified when' - 'alarm status changes.' - 'URN example structure:' - 'urn:smn:region:68438a86d98e427e907e0097b7e35d48:sd' - 'The parameter can be given multiple times to' - 'notify multiple targets') + help=_('Specifies the list of objects being notified when ' + 'alarm status changes.\n' + 'URN example structure:\n' + 'urn:smn:region:68438a86d98e427e907e0097b7e35d48:sd\n' + 'The parameter can be given multiple times to ' + 'notify multiple targets.') ) # ConditionSpec @@ -258,43 +258,41 @@ def get_parser(self, prog_name): '--comparison-operator', metavar='', required=True, - help=_('Specifies the conditions comparison operator' - 'Values: >, =, <, ≥, or ≤') + help=_('Specifies the conditions comparison operator') ) parser.add_argument( '--count', metavar='', type=int, required=True, - help=_('Specifies how many times the alarm condition has to' - 'triggered until Alarm raises' + help=_('Specifies how many times the alarm condition has to ' + 'triggered until Alarm raises.\n' 'Value range: 1 to 5') ) parser.add_argument( '--filter', metavar='', required=True, - help=_('Specifies the data rollup method.' + help=_('Specifies the data rollup method.\n' 'Values: max, min, average, sum, variance') ) parser.add_argument( '--period', metavar='', required=True, - help=_('Indicates the interval (in seconds) for checking' - 'whether the configured alarm rules are met') + help=_('Indicates the interval (in seconds) for checking ' + 'whether the configured alarm rules are met.') ) parser.add_argument( '--unit', metavar='', - help=_('Specifies data unit' - 'Values: B/s, %') + help=_('Specifies data unit.') ) parser.add_argument( '--value', metavar='', required=True, - help=_('Specifies the alarm threshold' + help=_('Specifies the alarm threshold.\n' 'Values: 0 to max(int)') ) @@ -327,7 +325,8 @@ def get_parser(self, prog_name): '--namespace', metavar='', required=True, - help=_('Specifies the namespace of the metric such as SYS.ECS') + help=_('Specifies the namespace of the metric such as:\n' + 'SYS.ECS, SYS.AS') ) return parser diff --git a/otcextensions/sdk/ces/v1/_proxy.py b/otcextensions/sdk/ces/v1/_proxy.py index 18cb92047..35db97556 100644 --- a/otcextensions/sdk/ces/v1/_proxy.py +++ b/otcextensions/sdk/ces/v1/_proxy.py @@ -81,7 +81,7 @@ def find_alarm(self, name_or_id, ignore_missing=True): return self._find(_alarm.Alarm, name_or_id, ignore_missing=ignore_missing) - def update_alarm_enabled(self, alarm): + def switch_alarm_state(self, alarm): """Enables or disables Alarm :param alarm: The value can be the ID of an alarm diff --git a/otcextensions/tests/unit/sdk/ces/v1/test_proxy.py b/otcextensions/tests/unit/sdk/ces/v1/test_proxy.py index d449978e9..6519cfe02 100644 --- a/otcextensions/tests/unit/sdk/ces/v1/test_proxy.py +++ b/otcextensions/tests/unit/sdk/ces/v1/test_proxy.py @@ -57,7 +57,7 @@ def test_alarms(self): def test_update_alarm_enabled(self): self._verify( 'otcextensions.sdk.ces.v1.alarm.Alarm.change_alarm_status', - self.proxy.update_alarm_enabled, + self.proxy.switch_alarm_state, method_args=["alarm"], expected_args=[] ) diff --git a/setup.cfg b/setup.cfg index 676b17d61..15bcd5aa1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -219,7 +219,7 @@ openstack.ces.v1 = ces_alarm_list = otcextensions.osclient.ces.v1.alarm:ListAlarms ces_alarm_show = otcextensions.osclient.ces.v1.alarm:ShowAlarm ces_alarm_create = otcextensions.osclient.ces.v1.alarm:CreateAlarm - ces_alarm_change_state = otcextensions.osclient.ces.v1.alarm:SetAlarm + ces_alarm_switch_state = otcextensions.osclient.ces.v1.alarm:SetAlarm ces_alarm_delete = otcextensions.osclient.ces.v1.alarm:DeleteAlarm openstack.load_balancer.v1 = From 35aa98225463a15219519a8c5a910b051c3642ee Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Thu, 6 Aug 2020 15:31:08 +0000 Subject: [PATCH 32/42] reworking OCS alarm resource --- examples/ces/create_alarm.py | 58 ++++++++++++++++++++++++++ examples/ces/list_alarms.py | 2 +- otcextensions/osclient/ces/v1/alarm.py | 33 ++++++++------- otcextensions/sdk/ces/v1/alarm.py | 5 +-- 4 files changed, 79 insertions(+), 19 deletions(-) create mode 100644 examples/ces/create_alarm.py diff --git a/examples/ces/create_alarm.py b/examples/ces/create_alarm.py new file mode 100644 index 000000000..b9c958306 --- /dev/null +++ b/examples/ces/create_alarm.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# Licensed under the Apache License, Version 2.0 (the 'License'); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +''' +Create CloudEye alarm rule +''' +import openstack + +openstack.enable_logging(True) +conn = openstack.connect(cloud='otc') + + +attrs = { + "alarm_name": "alarm-test", + "alarm_description": "Test Alarm", + "metric": { + "namespace": "SYS.ECS", + "dimensions": [ + { + "name": "instance_id", + "value": "33328f02-3814-422e-b688-bfdba93d4051" + } + ], + "metric_name": "network_outgoing" + }, + "condition": { + "period": 300, + "filter": "average", + "comparison_operator": ">=", + "value": 6, + "unit": "B/s", + "count": 1 + }, + "alarm_enabled": True, + "alarm_action_enabled": True, + "alarm_level": 2, + "ok_actions": [ + { + "type": "notification", + "notificationList": ["urn:smn:region:68438a86d98e427e907e0097b7e35d48:sd"] + } + ] +} + + + + +alarm = conn.ces.create_alarm(**attrs) +print(alarm) diff --git a/examples/ces/list_alarms.py b/examples/ces/list_alarms.py index c37b537e3..2d68be2b3 100644 --- a/examples/ces/list_alarms.py +++ b/examples/ces/list_alarms.py @@ -19,5 +19,5 @@ openstack.enable_logging(True) conn = openstack.connect(cloud='otc') -for alarm in conn.ces.alarms(limit=1): +for alarm in conn.ces.alarms(): print(alarm) diff --git a/otcextensions/osclient/ces/v1/alarm.py b/otcextensions/osclient/ces/v1/alarm.py index 14ca23735..19c1e461d 100644 --- a/otcextensions/osclient/ces/v1/alarm.py +++ b/otcextensions/osclient/ces/v1/alarm.py @@ -187,6 +187,8 @@ def get_parser(self, prog_name): parser.add_argument( '--enabled', metavar='', + default=True, + type=bool, help=_('State of the alarm.\n' 'True: enable alarm (default)\n' 'False: disable alarm\n') @@ -199,6 +201,7 @@ def get_parser(self, prog_name): parser.add_argument( '--action-enabled', default=False, + type=bool, required=True, help=_('Specifies whether the alarm action is triggered') ) @@ -279,6 +282,7 @@ def get_parser(self, prog_name): parser.add_argument( '--period', metavar='', + type=int, required=True, help=_('Indicates the interval (in seconds) for checking ' 'whether the configured alarm rules are met.') @@ -291,6 +295,7 @@ def get_parser(self, prog_name): parser.add_argument( '--value', metavar='', + type=int, required=True, help=_('Specifies the alarm threshold.\n' 'Values: 0 to max(int)') @@ -301,14 +306,12 @@ def get_parser(self, prog_name): # IMPROVEMENT NEEDED parser.add_argument( '--dimension-name', - action='append', metavar='', required=True, help=_('dimension.name: object type e.g. ECS') ) parser.add_argument( '--dimension-value', - action='append', required=True, metavar='', help=_('dimension.value: object id e.g. ECS ID') @@ -345,45 +348,45 @@ def take_action(self, parsed_args): attrs['alarm_action_enabled'] = parsed_args.action_enabled if parsed_args.level: attrs['alarm_level'] = parsed_args.level + + ok_actions = [] + alarm_actions = [] - if parsed_args.action_enabled is True: + if parsed_args.action_enabled: if (parsed_args.ok_action_type and parsed_args.ok_action_notification_list): nl = parsed_args.ok_action_notification_list - ok_actions = { + ok_actions.append({ 'type': parsed_args.ok_action_type, 'notificationList': nl - } + }) attrs['ok_actions'] = ok_actions if (parsed_args.alarm_action_type and parsed_args.alarm_action_notification_list): nl = parsed_args.alarm_action_notification_list - alarm_actions = { + alarm_actions.append({ 'type': parsed_args.alarm_action_type, 'notificationList': nl - } + }) attrs['alarm_actions'] = alarm_actions condition = { - 'comparison_operatur': parsed_args.comparison_operator, + 'comparison_operator': parsed_args.comparison_operator, 'count': parsed_args.count, 'filter': parsed_args.filter, 'period': parsed_args.period, 'value': parsed_args.value } if parsed_args.unit: - condition = { - 'unit': parsed_args.unit - } + condition['unit'] = parsed_args.unit attrs['condition'] = condition - dimensions = { - 'name': parsed_args.dimension_name, - 'value': parsed_args.dimension_value - } + dimensions = [] + dimensions.append({'name': parsed_args.dimension_name, + 'value': parsed_args.dimension_value}) metric = { 'dimensions': dimensions, 'metric_name': parsed_args.metric_name, diff --git a/otcextensions/sdk/ces/v1/alarm.py b/otcextensions/sdk/ces/v1/alarm.py index e0e94b903..fabd53fd5 100644 --- a/otcextensions/sdk/ces/v1/alarm.py +++ b/otcextensions/sdk/ces/v1/alarm.py @@ -63,7 +63,7 @@ class ConditionSpec(resource.Resource): # Data unit unit = resource.Body('unit') # Alarm threshold - value = resource.Body('value') + value = resource.Body('value', type=int) class DimensionsSpec(resource.Resource): @@ -79,8 +79,7 @@ class MetricSpec(resource.Resource): # Properties # List of metric dimensions - dimensions = resource.Body('dimensions', type=list, - list_type=DimensionsSpec) + dimensions = resource.Body('dimensions', type=DimensionsSpec) # Specifies the metric name metric_name = resource.Body('metric_name') # Metric Namespace From 061d582b6cc7497830849716088ff3f47afc6fcc Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Fri, 7 Aug 2020 09:18:55 +0000 Subject: [PATCH 33/42] finish alarm.py --- examples/ces/create_alarm.py | 40 +++++++++++++++++++++++--- otcextensions/osclient/ces/v1/alarm.py | 29 +++++++++++++++---- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/examples/ces/create_alarm.py b/examples/ces/create_alarm.py index b9c958306..8204d54fd 100644 --- a/examples/ces/create_alarm.py +++ b/examples/ces/create_alarm.py @@ -21,13 +21,17 @@ attrs = { "alarm_name": "alarm-test", - "alarm_description": "Test Alarm", + "alarm_description": "Test Alarm description", "metric": { "namespace": "SYS.ECS", "dimensions": [ { "name": "instance_id", "value": "33328f02-3814-422e-b688-bfdba93d4051" + }, + { + "name": "instance_id", + "value": "04ab9572-8c9c-41b6-bcc8-51068463b123" } ], "metric_name": "network_outgoing" @@ -46,13 +50,41 @@ "ok_actions": [ { "type": "notification", - "notificationList": ["urn:smn:region:68438a86d98e427e907e0097b7e35d48:sd"] + "notificationList": [ + "urn:smn:region:68438a86d98e427e907e0097b7e35d48:sd", + "urn:smn:eu-de:16d53a84a13b49529d2e2c3646691222:Error"] + } + ], + "alarm_actions": [ + { + "type": "notification", + "notificationList": [ + "urn:smn:region:68438a86d98e427e907e0097b7e35d48:sd", + "urn:smn:eu-de:16d53a84a13b49529d2e2c3646691222:Error"] } ] } - - alarm = conn.ces.create_alarm(**attrs) print(alarm) + +# OSC command +''' +openstack --os-cloud otc ces alarm create --description "Test Alarm" \ +--namespace SYS.ECS --dimension-name instance_id --dimension-value \ +33328f02-3814-422e-b688-bfdba93d4123 --dimension-name instance_id \ +--dimension-value 33328f02-3814-422e-b688-bfdba93d4052 --metric-name \ +"network_outgoing" --period '300' --filter average \ +--comparison-operator '>=' --value '6' --unit 'B/s' --count '1' \ +--enabled True --action-enabled True --level 2 --ok-action-type notification \ +--ok-action-notification-list \ +'urn:smn:region:68438a86d98e427e907e0097b7e35d48:sd' \ +--ok-action-notification-list \ +'urn:smn:eu-de:16d53a84a13b49529d2e2c3646691222:Error' \ +--alarm-action-type notification --alarm-action-notification-list \ +'urn:smn:region:68438a86d98e427e907e0097b7e35d48:sd' \ +--alarm-action-notification-list \ +'urn:smn:eu-de:16d53a84a13b49529d2e2c3646691222:Error' alarm-test + +''' diff --git a/otcextensions/osclient/ces/v1/alarm.py b/otcextensions/osclient/ces/v1/alarm.py index 19c1e461d..344d56d2b 100644 --- a/otcextensions/osclient/ces/v1/alarm.py +++ b/otcextensions/osclient/ces/v1/alarm.py @@ -13,9 +13,11 @@ '''CES Alarm v1 action implementations''' import logging +from osc_lib import exceptions from osc_lib import utils from osc_lib.command import command + from otcextensions.i18n import _ from otcextensions.common import sdk_utils @@ -308,13 +310,23 @@ def get_parser(self, prog_name): '--dimension-name', metavar='', required=True, - help=_('dimension.name: object type e.g. ECS') + action='append', + help=_('dimension.name: object type e.g. instance_id\n' + 'Provide --dimension-name always in pair with' + '--dimension-value .\n' + 'Both values can be provided multiple times (equal number)' + 'to generate a list of monitored objects.') ) parser.add_argument( '--dimension-value', - required=True, metavar='', - help=_('dimension.value: object id e.g. ECS ID') + required=True, + action='append', + help=_('dimension.value: object id e.g. ECS ID\n' + 'Provide --dimension-name always in pair with' + '--dimension-value .\n' + 'Both values can be provided multiple times (equal number)' + 'to generate a list of monitored objects.') ) # MetricSpec @@ -355,7 +367,6 @@ def take_action(self, parsed_args): if parsed_args.action_enabled: if (parsed_args.ok_action_type and parsed_args.ok_action_notification_list): - nl = parsed_args.ok_action_notification_list ok_actions.append({ 'type': parsed_args.ok_action_type, @@ -385,8 +396,14 @@ def take_action(self, parsed_args): attrs['condition'] = condition dimensions = [] - dimensions.append({'name': parsed_args.dimension_name, - 'value': parsed_args.dimension_value}) + if len(parsed_args.dimension_name) == len(parsed_args.dimension_value): + for i in range(len(parsed_args.dimension_name)): + dimensions.append({'name': parsed_args.dimension_name[i-1], + 'value': parsed_args.dimension_value[i-1]}) + else: + msg = _('--dimension-name not in pair with --dimension-value') + raise exceptions.Conflict(msg) + metric = { 'dimensions': dimensions, 'metric_name': parsed_args.metric_name, From 86a3cd5ccad1e033970b6efa18248aea5789fc5f Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Fri, 7 Aug 2020 14:49:56 +0000 Subject: [PATCH 34/42] CES metric OCS impl. started --- examples/ces/list_event_data.py | 23 +++++ examples/ces/list_metrics.py | 23 +++++ otcextensions/osclient/ces/v1/metric.py | 123 ++++++++++++++++++++++++ setup.cfg | 1 + 4 files changed, 170 insertions(+) create mode 100644 examples/ces/list_event_data.py create mode 100644 examples/ces/list_metrics.py create mode 100644 otcextensions/osclient/ces/v1/metric.py diff --git a/examples/ces/list_event_data.py b/examples/ces/list_event_data.py new file mode 100644 index 000000000..d2a919408 --- /dev/null +++ b/examples/ces/list_event_data.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +""" +List all CloudEye event data +""" +import openstack + + +openstack.enable_logging(True) +conn = openstack.connect(cloud='otc') + +for data in conn.ces.event_data(): + print(data) diff --git a/examples/ces/list_metrics.py b/examples/ces/list_metrics.py new file mode 100644 index 000000000..888441533 --- /dev/null +++ b/examples/ces/list_metrics.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +""" +List all CloudEye metrics +""" +import openstack + + +openstack.enable_logging(True) +conn = openstack.connect(cloud='otc') + +for metric in conn.ces.metrics(): + print(metric) diff --git a/otcextensions/osclient/ces/v1/metric.py b/otcextensions/osclient/ces/v1/metric.py new file mode 100644 index 000000000..00fa2c17e --- /dev/null +++ b/otcextensions/osclient/ces/v1/metric.py @@ -0,0 +1,123 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +'''CES Alarm v1 action implementations''' +import logging + +from osc_lib import utils +from osc_lib.command import command + + +from otcextensions.i18n import _ +from otcextensions.common import sdk_utils + +LOG = logging.getLogger(__name__) + + +def _get_columns(item): + column_map = { + } + inv_columns = [''] + return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map, + inv_columns) + + +class ListMetrics(command.Lister): + _description = _('List CES alarms') + columns = ( + 'namespace', + 'dimensions', + 'metric_name', + 'unit', + ) + + table_columns = ( + 'namespace', + 'dimensions.name', + 'dimensions.value', + 'metric_name', + 'unit', + ) + + def get_parser(self, prog_name): + parser = super(ListMetrics, self).get_parser(prog_name) + + parser.add_argument( + '--namespace', + metavar='', + help=_('Namespace of the monitored object, e.g.\n' + 'SYS.ECS, SYS.VPC') + ) + parser.add_argument( + '--metric-name', + metavar='', + help=_('Name of the alarm rule object.') + ) + parser.add_argument( + '--unit', + metavar='', + help=_('Unit which is measured.') + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.ces + + query = {} + + if parsed_args.namespace: + query['namespace'] = parsed_args.namespace + if parsed_args.metric_name: + query['metric_name'] = parsed_args.metric_name + if parsed_args.unit: + query['unit'] = parsed_args.unit + + data = client.metrics(**query) + ''' + table = (self.table_columns, + (utils.get_dict_properties( + s, self.columns + ) for s in data)) + + new_table = [] + for s in data: + d = utils.get_dict_properties(s, self.columns) + print(d[1][0].name) + new_table.append(d[0]) + new_table.append(d[1][0].name) + for t in new_table: + print(t) + ''' + table = (self.table_columns,) + temp_list = [] + big_list = [] + for s in data: + for item in utils.get_dict_properties(s, self.columns): + print(item) + temp_list.append(item) + print(temp_list) + big_list.append(tuple(temp_list)) + temp_list = [] + + print('Big List Entries: ') + print(big_list[0]) + print(big_list[1]) + print(big_list[2]) + + + + + table = (self.columns, big_list) + + + return table + diff --git a/setup.cfg b/setup.cfg index 15bcd5aa1..436ed4473 100644 --- a/setup.cfg +++ b/setup.cfg @@ -221,6 +221,7 @@ openstack.ces.v1 = ces_alarm_create = otcextensions.osclient.ces.v1.alarm:CreateAlarm ces_alarm_switch_state = otcextensions.osclient.ces.v1.alarm:SetAlarm ces_alarm_delete = otcextensions.osclient.ces.v1.alarm:DeleteAlarm + ces_metric_list = otcextensions.osclient.ces.v1.metric:ListMetrics openstack.load_balancer.v1 = loadbalancer_list = otcextensions.osclient.load_balancer.v1.load_balancer:ListLoadBalancer From d8e86a68bb61ffe626b9d62a9ca8694c695fcad2 Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Tue, 18 Aug 2020 15:00:23 +0000 Subject: [PATCH 35/42] metric finished --- otcextensions/osclient/ces/v1/metric.py | 40 +++++++------------------ 1 file changed, 10 insertions(+), 30 deletions(-) diff --git a/otcextensions/osclient/ces/v1/metric.py b/otcextensions/osclient/ces/v1/metric.py index 00fa2c17e..5393a3987 100644 --- a/otcextensions/osclient/ces/v1/metric.py +++ b/otcextensions/osclient/ces/v1/metric.py @@ -73,7 +73,7 @@ def take_action(self, parsed_args): client = self.app.client_manager.ces query = {} - + if parsed_args.namespace: query['namespace'] = parsed_args.namespace if parsed_args.metric_name: @@ -82,42 +82,22 @@ def take_action(self, parsed_args): query['unit'] = parsed_args.unit data = client.metrics(**query) - ''' - table = (self.table_columns, - (utils.get_dict_properties( - s, self.columns - ) for s in data)) - new_table = [] - for s in data: - d = utils.get_dict_properties(s, self.columns) - print(d[1][0].name) - new_table.append(d[0]) - new_table.append(d[1][0].name) - for t in new_table: - print(t) - ''' - table = (self.table_columns,) + # Modify table output to provide a better metric overview. + # Given data set is taken, splitted and flattened to build the table. + table = () temp_list = [] big_list = [] for s in data: for item in utils.get_dict_properties(s, self.columns): - print(item) - temp_list.append(item) - print(temp_list) + if isinstance(item, (list)): + temp_list.append(item[0].name) + temp_list.append(item[0].value) + else: + temp_list.append(item) big_list.append(tuple(temp_list)) temp_list = [] - - print('Big List Entries: ') - print(big_list[0]) - print(big_list[1]) - print(big_list[2]) - - - - table = (self.columns, big_list) - + table = (self.table_columns, big_list) return table - From 5766c76714b23151b3adbcc77ca59b01d92da4fc Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Wed, 19 Aug 2020 13:09:07 +0000 Subject: [PATCH 36/42] OSC quota implementation --- examples/ces/list_metric_data.py | 23 ++++++++ examples/ces/list_quotas.py | 23 ++++++++ otcextensions/osclient/ces/v1/quota.py | 72 ++++++++++++++++++++++++++ otcextensions/sdk/ces/v1/_proxy.py | 2 +- otcextensions/sdk/ces/v1/quota.py | 5 +- setup.cfg | 2 + 6 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 examples/ces/list_metric_data.py create mode 100644 examples/ces/list_quotas.py create mode 100644 otcextensions/osclient/ces/v1/quota.py diff --git a/examples/ces/list_metric_data.py b/examples/ces/list_metric_data.py new file mode 100644 index 000000000..2e6dfc924 --- /dev/null +++ b/examples/ces/list_metric_data.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +""" +List all CloudEye metric data +""" +import openstack + + +openstack.enable_logging(True) +conn = openstack.connect(cloud='otc') + +for data in conn.ces.metric_data(): + print(data) diff --git a/examples/ces/list_quotas.py b/examples/ces/list_quotas.py new file mode 100644 index 000000000..33f4e1616 --- /dev/null +++ b/examples/ces/list_quotas.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +""" +List all CloudEye quotas +""" +import openstack + + +openstack.enable_logging(True) +conn = openstack.connect(cloud='otc') + +for quota in conn.ces.quotas(): + print(quota) diff --git a/otcextensions/osclient/ces/v1/quota.py b/otcextensions/osclient/ces/v1/quota.py new file mode 100644 index 000000000..2c8194252 --- /dev/null +++ b/otcextensions/osclient/ces/v1/quota.py @@ -0,0 +1,72 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +'''CES Alarm v1 action implementations''' +import logging + +from osc_lib import utils +from osc_lib.command import command + + +from otcextensions.i18n import _ +from otcextensions.common import sdk_utils + +LOG = logging.getLogger(__name__) + + +def _get_columns(item): + column_map = { + } + inv_columns = [''] + return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map, + inv_columns) + + +def _flatten_output(obj): + data = { + 'type': obj.resources[0].type, + 'quota': obj.resources[0].quota, + 'used': obj.resources[0].used + } + return data + + +def _get_columns(item): + column_map = { + } + inv_columns = [''] + return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map, + inv_columns) + + +class ListQuotas(command.Lister): + _description = _('List CES alarm quota') + columns = ( + 'type', + 'quota', + 'used' + ) + + def get_parser(self, prog_name): + parser = super(ListQuotas, self).get_parser(prog_name) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.ces + + data = client.quotas() + + table = (self.columns, + (utils.get_dict_properties( + _flatten_output(s), self.columns + ) for s in data)) + return table diff --git a/otcextensions/sdk/ces/v1/_proxy.py b/otcextensions/sdk/ces/v1/_proxy.py index 35db97556..5043cb745 100644 --- a/otcextensions/sdk/ces/v1/_proxy.py +++ b/otcextensions/sdk/ces/v1/_proxy.py @@ -137,7 +137,7 @@ def metric_data(self, **query): def quotas(self): """Return a generator of quotas - :returns: A generator of metric objects + :returns: A generator of quota objects :rtype: :class:`~otcextensions.sdk.ces.v1.quota.Quota` """ return self._list(_quota.Quota) diff --git a/otcextensions/sdk/ces/v1/quota.py b/otcextensions/sdk/ces/v1/quota.py index bb8a1e490..530fb1442 100644 --- a/otcextensions/sdk/ces/v1/quota.py +++ b/otcextensions/sdk/ces/v1/quota.py @@ -16,7 +16,7 @@ class ResourcesSpec(resource.Resource): # Properties # quota type - typeprop = resource.Body('type') + type = resource.Body('type') # total amount of quota quota = resource.Body('quota', type=int) # quota unit @@ -33,4 +33,5 @@ class Quota(resource.Resource): allow_list = True # Properties - resources = resource.Body('resources', type=ResourcesSpec) + resources = resource.Body('resources', type=list, + list_type=ResourcesSpec) diff --git a/setup.cfg b/setup.cfg index 436ed4473..a3dd3d3a2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -222,6 +222,8 @@ openstack.ces.v1 = ces_alarm_switch_state = otcextensions.osclient.ces.v1.alarm:SetAlarm ces_alarm_delete = otcextensions.osclient.ces.v1.alarm:DeleteAlarm ces_metric_list = otcextensions.osclient.ces.v1.metric:ListMetrics + ces_quota_list = otcextensions.osclient.ces.v1.quota:ListQuotas + openstack.load_balancer.v1 = loadbalancer_list = otcextensions.osclient.load_balancer.v1.load_balancer:ListLoadBalancer From 3ff7e908f1c044a7cccc938f29d87f6bfb19cb0b Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Thu, 20 Aug 2020 14:06:09 +0000 Subject: [PATCH 37/42] event data added to OCS --- examples/ces/list_event_data.py | 12 ++- otcextensions/osclient/ces/v1/event_data.py | 96 +++++++++++++++++++++ otcextensions/sdk/ces/v1/event_data.py | 2 +- setup.cfg | 1 + 4 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 otcextensions/osclient/ces/v1/event_data.py diff --git a/examples/ces/list_event_data.py b/examples/ces/list_event_data.py index d2a919408..98f0a621a 100644 --- a/examples/ces/list_event_data.py +++ b/examples/ces/list_event_data.py @@ -19,5 +19,15 @@ openstack.enable_logging(True) conn = openstack.connect(cloud='otc') -for data in conn.ces.event_data(): + +query = { + 'namespace': 'SYS.ECS', + 'type': 'instance_host_info', + 'dim.0': 'instance_id,6e83e6e7-3bf4-4b5b-b390-e80447ef1234', + 'from': '1596067200', + 'to': '1597929178' +} + + +for data in conn.ces.event_data(**query): print(data) diff --git a/otcextensions/osclient/ces/v1/event_data.py b/otcextensions/osclient/ces/v1/event_data.py new file mode 100644 index 000000000..6fa883a5c --- /dev/null +++ b/otcextensions/osclient/ces/v1/event_data.py @@ -0,0 +1,96 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +'''CES Alarm v1 action implementations''' +import logging + +from osc_lib import utils +from osc_lib.command import command + + +from otcextensions.i18n import _ +from otcextensions.common import sdk_utils + +LOG = logging.getLogger(__name__) + + +def _get_columns(item): + column_map = { + } + inv_columns = [''] + return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map, + inv_columns) + +class ListEventData(command.Lister): + _description = _('List CES event data') + columns = ( + 'timestamp', + 'type', + 'value' + ) + + def get_parser(self, prog_name): + parser = super(ListEventData, self).get_parser(prog_name) + parser.add_argument( + '--namespace', + metavar='', + required=True, + help=_('Specifies the namespace of the metric such as:\n' + 'SYS.ECS, SYS.AS') + ) + parser.add_argument( + '--type', + metavar='', + required=True, + help=_('Specifies the event type such as:\n' + 'instance_host_info') + ) + parser.add_argument( + '--dim', + metavar='', + required=True, + help=_('Specifies the monitoring dimension:\n' + 'dim.0=instance_id,123-456-789') + ) + parser.add_argument( + '--time-from', + metavar='', + required=True, + help=_('UNIX timestamp in ms from which the data is ' + 'collected.') + ) + parser.add_argument( + '--time-to', + metavar='', + required=True, + help=_('UNIX timestamp in ms to which the data is ' + 'collected.') + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.ces + + query = {} + query['namespace'] = parsed_args.namespace + query['type'] = parsed_args.type + query['dim.0'] = parsed_args.dim + query['from'] = parsed_args.time_from + query['to'] = parsed_args.time_to + + data = client.event_data(**query) + + table = (self.columns, + (utils.get_dict_properties( + s, self.columns + ) for s in data)) + return table diff --git a/otcextensions/sdk/ces/v1/event_data.py b/otcextensions/sdk/ces/v1/event_data.py index b7d0d9929..a7d0ac92a 100644 --- a/otcextensions/sdk/ces/v1/event_data.py +++ b/otcextensions/sdk/ces/v1/event_data.py @@ -32,7 +32,7 @@ class EventData(resource.Resource): # allow_create = True _query_mapping = resource.QueryParameters( - 'dim', 'from', 'to', 'namespace', 'type' + 'dim.0', 'from', 'to', 'namespace', 'type' ) # Properties diff --git a/setup.cfg b/setup.cfg index a3dd3d3a2..2b0879dc5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -223,6 +223,7 @@ openstack.ces.v1 = ces_alarm_delete = otcextensions.osclient.ces.v1.alarm:DeleteAlarm ces_metric_list = otcextensions.osclient.ces.v1.metric:ListMetrics ces_quota_list = otcextensions.osclient.ces.v1.quota:ListQuotas + ces_event_data_list = otcextensions.osclient.ces.v1.event_data:ListEventData openstack.load_balancer.v1 = From e5a46135a11438abfd04d4e495cf38d4c140fb25 Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Mon, 24 Aug 2020 13:46:18 +0000 Subject: [PATCH 38/42] begin of metric data impl. --- examples/ces/create_metric_data.py | 61 +++++++++++++ examples/ces/list_event_data.py | 6 +- examples/ces/list_metric_data.py | 13 ++- otcextensions/osclient/ces/v1/alarm.py | 10 +- otcextensions/osclient/ces/v1/metric_data.py | 96 ++++++++++++++++++++ otcextensions/sdk/ces/v1/alarm.py | 3 +- otcextensions/sdk/ces/v1/metric_data.py | 5 +- 7 files changed, 184 insertions(+), 10 deletions(-) create mode 100644 examples/ces/create_metric_data.py create mode 100644 otcextensions/osclient/ces/v1/metric_data.py diff --git a/examples/ces/create_metric_data.py b/examples/ces/create_metric_data.py new file mode 100644 index 000000000..c5e4c6035 --- /dev/null +++ b/examples/ces/create_metric_data.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +# Licensed under the Apache License, Version 2.0 (the 'License'); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +''' +Add CloudEye metric data +''' +import openstack + +openstack.enable_logging(True) +conn = openstack.connect(cloud='otc') + + +attrs = [ + { + "metric": { + "namespace": "MINE.APP", + "dimensions": [ + { + "name": "instance_id", + "value": "33328f02-3814-422e-b688-bfdba93d4050" + } + ], + "metric_name": "cpu_util" + }, + "ttl": 172800, + "collect_time": 1598266684000, + "type": "int", + "value": 60, + "unit": "%" + }, + { + "metric": { + "namespace": "MINE.APP", + "dimensions": [ + { + "name": "instance_id", + "value": "33328f02-3814-422e-b688-bfdba93d4050" + } + ], + "metric_name": "cpu_util" + }, + "ttl": 172800, + "collect_time": 1598266685000, + "type": "int", + "value": 70, + "unit": "%" + } +] + + +data = conn.ces.create_metric_data(**attrs) +print(data) diff --git a/examples/ces/list_event_data.py b/examples/ces/list_event_data.py index 98f0a621a..ff61a5e11 100644 --- a/examples/ces/list_event_data.py +++ b/examples/ces/list_event_data.py @@ -23,9 +23,9 @@ query = { 'namespace': 'SYS.ECS', 'type': 'instance_host_info', - 'dim.0': 'instance_id,6e83e6e7-3bf4-4b5b-b390-e80447ef1234', - 'from': '1596067200', - 'to': '1597929178' + 'dim.0': 'instance_id,6e83e6e7-3bf4-4b5b-b390-e80447ef1234', # key, value + 'from': '1596067200', # unix timestamp in ms + 'to': '1597929178' # unix timestamp in ms } diff --git a/examples/ces/list_metric_data.py b/examples/ces/list_metric_data.py index 2e6dfc924..0e674b9c5 100644 --- a/examples/ces/list_metric_data.py +++ b/examples/ces/list_metric_data.py @@ -19,5 +19,16 @@ openstack.enable_logging(True) conn = openstack.connect(cloud='otc') -for data in conn.ces.metric_data(): + +query = { + 'namespace': 'SYS.ECS', + 'metric_name': 'cpu_util', + 'from': '1556625600000', # unix timestamp in ms + 'to': '1556632800000', # unix timestamp in ms + 'period': 1, + 'filter': 'avg', + 'dim.0': 'instance_id,6e83e6e7-3bf4-4b5b-b390-e80447ef5733', # key, value +} + +for data in conn.ces.metric_data(**query): print(data) diff --git a/otcextensions/osclient/ces/v1/alarm.py b/otcextensions/osclient/ces/v1/alarm.py index 344d56d2b..aaacc3dbb 100644 --- a/otcextensions/osclient/ces/v1/alarm.py +++ b/otcextensions/osclient/ces/v1/alarm.py @@ -38,10 +38,11 @@ def _flatten_output(obj): data = { 'id': obj.id, 'name': obj.name, - 'metric.resource_type': obj.metric.namespace, + 'namespace': obj.metric.namespace, # The return value of obj.metric.dimensions is a list. The list has # only one value. It is not possible to have several items inside. - 'metric.monitored_obj': obj.metric.dimensions[0].value, + 'dimensions.name': obj.metric.dimensions[0].name, + 'dimensions.value': obj.metric.dimensions[0].value, 'alarm_level': _translate_alarm_level(obj.alarm_level), 'enabled': obj.alarm_enabled, 'action_enabled': obj.alarm_action_enabled, @@ -64,8 +65,9 @@ class ListAlarms(command.Lister): columns = ( 'id', 'name', - 'metric.resource_type', - 'metric.monitored_obj', + 'namespace', + 'dimensions.name', + 'dimensions.value', 'alarm_level', 'enabled', 'action_enabled', diff --git a/otcextensions/osclient/ces/v1/metric_data.py b/otcextensions/osclient/ces/v1/metric_data.py new file mode 100644 index 000000000..603f16307 --- /dev/null +++ b/otcextensions/osclient/ces/v1/metric_data.py @@ -0,0 +1,96 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +'''CES Alarm v1 action implementations''' +import logging + +from osc_lib import utils +from osc_lib.command import command + + +from otcextensions.i18n import _ +from otcextensions.common import sdk_utils + +LOG = logging.getLogger(__name__) + + +def _get_columns(item): + column_map = { + } + inv_columns = [''] + return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map, + inv_columns) + +class ListMetricData(command.Lister): + _description = _('List CES event data') + columns = ( + 'timestamp', + 'type', + 'value' + ) + + def get_parser(self, prog_name): + parser = super(ListEventData, self).get_parser(prog_name) + parser.add_argument( + '--namespace', + metavar='', + required=True, + help=_('Specifies the namespace of the metric such as:\n' + 'SYS.ECS, SYS.AS') + ) + parser.add_argument( + '--type', + metavar='', + required=True, + help=_('Specifies the event type such as:\n' + 'instance_host_info') + ) + parser.add_argument( + '--dim', + metavar='', + required=True, + help=_('Specifies the monitoring dimension:\n' + 'dim.0=instance_id,123-456-789') + ) + parser.add_argument( + '--time-from', + metavar='', + required=True, + help=_('UNIX timestamp in ms from which the data is ' + 'collected.') + ) + parser.add_argument( + '--time-to', + metavar='', + required=True, + help=_('UNIX timestamp in ms to which the data is ' + 'collected.') + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.ces + + query = {} + query['namespace'] = parsed_args.namespace + query['type'] = parsed_args.type + query['dim.0'] = parsed_args.dim + query['from'] = parsed_args.time_from + query['to'] = parsed_args.time_to + + data = client.event_data(**query) + + table = (self.columns, + (utils.get_dict_properties( + s, self.columns + ) for s in data)) + return table diff --git a/otcextensions/sdk/ces/v1/alarm.py b/otcextensions/sdk/ces/v1/alarm.py index fabd53fd5..0a8fdd5cc 100644 --- a/otcextensions/sdk/ces/v1/alarm.py +++ b/otcextensions/sdk/ces/v1/alarm.py @@ -79,7 +79,8 @@ class MetricSpec(resource.Resource): # Properties # List of metric dimensions - dimensions = resource.Body('dimensions', type=DimensionsSpec) + dimensions = resource.Body('dimensions', type=list, + list_type=DimensionsSpec) # Specifies the metric name metric_name = resource.Body('metric_name') # Metric Namespace diff --git a/otcextensions/sdk/ces/v1/metric_data.py b/otcextensions/sdk/ces/v1/metric_data.py index f718ab43f..3a8f0e18d 100644 --- a/otcextensions/sdk/ces/v1/metric_data.py +++ b/otcextensions/sdk/ces/v1/metric_data.py @@ -36,7 +36,10 @@ class MetricData(resource.Resource): # capabilities allow_list = True - # allow_create = True + + _query_mapping = resource.QueryParameters( + 'namespace', 'period', 'filter', 'metric_name', 'dim.0', 'from', + 'to') # Properties # Datapoints retrieve the metrics data list From 87ac62fc4da3db3dd7f70ca8e2f89702b022dd3f Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Tue, 25 Aug 2020 11:28:19 +0000 Subject: [PATCH 39/42] finish OSC part of CES --- examples/ces/create_metric_data.py | 2 +- examples/ces/list_event_data.py | 1 - otcextensions/sdk/ces/v1/_proxy.py | 19 +++++++++++-------- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/examples/ces/create_metric_data.py b/examples/ces/create_metric_data.py index c5e4c6035..7112d78e5 100644 --- a/examples/ces/create_metric_data.py +++ b/examples/ces/create_metric_data.py @@ -18,7 +18,7 @@ openstack.enable_logging(True) conn = openstack.connect(cloud='otc') - +# not working due to lag of compatible API: list instead of proper JSON attrs = [ { "metric": { diff --git a/examples/ces/list_event_data.py b/examples/ces/list_event_data.py index ff61a5e11..552b3ba29 100644 --- a/examples/ces/list_event_data.py +++ b/examples/ces/list_event_data.py @@ -28,6 +28,5 @@ 'to': '1597929178' # unix timestamp in ms } - for data in conn.ces.event_data(**query): print(data) diff --git a/otcextensions/sdk/ces/v1/_proxy.py b/otcextensions/sdk/ces/v1/_proxy.py index 5043cb745..5f263c1e4 100644 --- a/otcextensions/sdk/ces/v1/_proxy.py +++ b/otcextensions/sdk/ces/v1/_proxy.py @@ -124,14 +124,17 @@ def metric_data(self, **query): :rtype: :class:`~otcextensions.sdk.ces.v1.metric_data.MetricData` """ return self._list(_metric_data.MetricData, **query) - - # def create_metric_data(self, **attrs): - # """Create a new Alarm from attributes - - # :param dict attrs: Keyword arguments which will be used to create - # a :class:`~otcextensions.sdk.ces.v1.metric_data.MetricData` - # """ - # return self._create(_metric_data.MetricData, **attrs) + + # skipped due to lag of compliant API (resource is list not JSON) + ''' + def create_metric_data(self, **attrs): + """Create a new Alarm from attributes + + :param dict attrs: Keyword arguments which will be used to create + a :class:`~otcextensions.sdk.ces.v1.metric_data.MetricData` + """ + return self._create(_metric_data.MetricData, **attrs) + ''' # ======== Quotas ======== def quotas(self): From 72e2128688c7f9599355118251f051ff7227b109 Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Wed, 26 Aug 2020 06:31:52 +0000 Subject: [PATCH 40/42] CES SDK unit tests finished --- examples/ces/create_alarm.py | 43 ++++++----- examples/ces/create_metric_data.py | 72 +++++++++---------- examples/ces/list_event_data.py | 6 +- examples/ces/list_metric_data.py | 6 +- otcextensions/osclient/ces/v1/alarm.py | 11 +-- otcextensions/osclient/ces/v1/event_data.py | 1 + otcextensions/osclient/ces/v1/metric.py | 2 +- otcextensions/osclient/ces/v1/metric_data.py | 3 +- otcextensions/osclient/ces/v1/quota.py | 8 --- otcextensions/sdk/ces/v1/_proxy.py | 2 +- otcextensions/sdk/ces/v1/metric_data.py | 2 +- .../tests/unit/sdk/ces/v1/test_event_data.py | 27 +++++++ .../tests/unit/sdk/ces/v1/test_metric.py | 29 ++++++++ .../tests/unit/sdk/ces/v1/test_metric_data.py | 27 +++++++ .../tests/unit/sdk/ces/v1/test_quota.py | 28 ++++++++ 15 files changed, 186 insertions(+), 81 deletions(-) create mode 100644 otcextensions/tests/unit/sdk/ces/v1/test_event_data.py create mode 100644 otcextensions/tests/unit/sdk/ces/v1/test_metric.py create mode 100644 otcextensions/tests/unit/sdk/ces/v1/test_metric_data.py create mode 100644 otcextensions/tests/unit/sdk/ces/v1/test_quota.py diff --git a/examples/ces/create_alarm.py b/examples/ces/create_alarm.py index 8204d54fd..65fccadba 100644 --- a/examples/ces/create_alarm.py +++ b/examples/ces/create_alarm.py @@ -1,12 +1,12 @@ #!/usr/bin/env python3 -# Licensed under the Apache License, Version 2.0 (the 'License'); you may +# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. @@ -20,36 +20,36 @@ attrs = { - "alarm_name": "alarm-test", - "alarm_description": "Test Alarm description", + "alarm_name": "alarm-test", + "alarm_description": "Test Alarm description", "metric": { - "namespace": "SYS.ECS", + "namespace": "SYS.ECS", "dimensions": [ { - "name": "instance_id", + "name": "instance_id", "value": "33328f02-3814-422e-b688-bfdba93d4051" }, { - "name": "instance_id", + "name": "instance_id", "value": "04ab9572-8c9c-41b6-bcc8-51068463b123" } - ], + ], "metric_name": "network_outgoing" - }, + }, "condition": { - "period": 300, - "filter": "average", - "comparison_operator": ">=", - "value": 6, - "unit": "B/s", - "count": 1 - }, - "alarm_enabled": True, - "alarm_action_enabled": True, - "alarm_level": 2, + "period": 300, + "filter": "average", + "comparison_operator": ">=", + "value": 6, + "unit": "B/s", + "count": 1 + }, + "alarm_enabled": True, + "alarm_action_enabled": True, + "alarm_level": 2, "ok_actions": [ { - "type": "notification", + "type": "notification", "notificationList": [ "urn:smn:region:68438a86d98e427e907e0097b7e35d48:sd", "urn:smn:eu-de:16d53a84a13b49529d2e2c3646691222:Error"] @@ -57,7 +57,7 @@ ], "alarm_actions": [ { - "type": "notification", + "type": "notification", "notificationList": [ "urn:smn:region:68438a86d98e427e907e0097b7e35d48:sd", "urn:smn:eu-de:16d53a84a13b49529d2e2c3646691222:Error"] @@ -86,5 +86,4 @@ 'urn:smn:region:68438a86d98e427e907e0097b7e35d48:sd' \ --alarm-action-notification-list \ 'urn:smn:eu-de:16d53a84a13b49529d2e2c3646691222:Error' alarm-test - ''' diff --git a/examples/ces/create_metric_data.py b/examples/ces/create_metric_data.py index 7112d78e5..c5c92f9c3 100644 --- a/examples/ces/create_metric_data.py +++ b/examples/ces/create_metric_data.py @@ -1,12 +1,12 @@ #!/usr/bin/env python3 -# Licensed under the Apache License, Version 2.0 (the 'License'); you may +# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. @@ -20,40 +20,40 @@ # not working due to lag of compatible API: list instead of proper JSON attrs = [ - { - "metric": { - "namespace": "MINE.APP", - "dimensions": [ - { - "name": "instance_id", - "value": "33328f02-3814-422e-b688-bfdba93d4050" - } - ], - "metric_name": "cpu_util" - }, - "ttl": 172800, - "collect_time": 1598266684000, - "type": "int", - "value": 60, - "unit": "%" - }, - { - "metric": { - "namespace": "MINE.APP", - "dimensions": [ - { - "name": "instance_id", - "value": "33328f02-3814-422e-b688-bfdba93d4050" - } - ], - "metric_name": "cpu_util" - }, - "ttl": 172800, - "collect_time": 1598266685000, - "type": "int", - "value": 70, - "unit": "%" - } + { + "metric": { + "namespace": "MINE.APP", + "dimensions": [ + { + "name": "instance_id", + "value": "33328f02-3814-422e-b688-bfdba93d4050" + } + ], + "metric_name": "cpu_util" + }, + "ttl": 172800, + "collect_time": 1598266684000, + "type": "int", + "value": 60, + "unit": "%" + }, + { + "metric": { + "namespace": "MINE.APP", + "dimensions": [ + { + "name": "instance_id", + "value": "33328f02-3814-422e-b688-bfdba93d4050" + } + ], + "metric_name": "cpu_util" + }, + "ttl": 172800, + "collect_time": 1598266685000, + "type": "int", + "value": 70, + "unit": "%" + } ] diff --git a/examples/ces/list_event_data.py b/examples/ces/list_event_data.py index 552b3ba29..d7e89b414 100644 --- a/examples/ces/list_event_data.py +++ b/examples/ces/list_event_data.py @@ -23,9 +23,9 @@ query = { 'namespace': 'SYS.ECS', 'type': 'instance_host_info', - 'dim.0': 'instance_id,6e83e6e7-3bf4-4b5b-b390-e80447ef1234', # key, value - 'from': '1596067200', # unix timestamp in ms - 'to': '1597929178' # unix timestamp in ms + 'dim.0': 'instance_id,6e83e6e7-3bf4-4b5b-b390-e80447ef1234', # key, value + 'from': '1596067200', # unix timestamp in ms + 'to': '1597929178' # unix timestamp in ms } for data in conn.ces.event_data(**query): diff --git a/examples/ces/list_metric_data.py b/examples/ces/list_metric_data.py index 0e674b9c5..229cdd312 100644 --- a/examples/ces/list_metric_data.py +++ b/examples/ces/list_metric_data.py @@ -23,11 +23,11 @@ query = { 'namespace': 'SYS.ECS', 'metric_name': 'cpu_util', - 'from': '1556625600000', # unix timestamp in ms - 'to': '1556632800000', # unix timestamp in ms + 'from': '1556625600000', # unix timestamp in ms + 'to': '1556632800000', # unix timestamp in ms 'period': 1, 'filter': 'avg', - 'dim.0': 'instance_id,6e83e6e7-3bf4-4b5b-b390-e80447ef5733', # key, value + 'dim.0': 'instance_id,6e83e6e7-3bf4-4b5b-b390-e80447ef5733', # key, value } for data in conn.ces.metric_data(**query): diff --git a/otcextensions/osclient/ces/v1/alarm.py b/otcextensions/osclient/ces/v1/alarm.py index aaacc3dbb..5e4edf261 100644 --- a/otcextensions/osclient/ces/v1/alarm.py +++ b/otcextensions/osclient/ces/v1/alarm.py @@ -323,7 +323,7 @@ def get_parser(self, prog_name): '--dimension-value', metavar='', required=True, - action='append', + action='append', help=_('dimension.value: object id e.g. ECS ID\n' 'Provide --dimension-name always in pair with' '--dimension-value .\n' @@ -362,7 +362,7 @@ def take_action(self, parsed_args): attrs['alarm_action_enabled'] = parsed_args.action_enabled if parsed_args.level: attrs['alarm_level'] = parsed_args.level - + ok_actions = [] alarm_actions = [] @@ -400,12 +400,13 @@ def take_action(self, parsed_args): dimensions = [] if len(parsed_args.dimension_name) == len(parsed_args.dimension_value): for i in range(len(parsed_args.dimension_name)): - dimensions.append({'name': parsed_args.dimension_name[i-1], - 'value': parsed_args.dimension_value[i-1]}) + dimensions.append( + {'name': parsed_args.dimension_name[i - 1], + 'value': parsed_args.dimension_value[i - 1]}) else: msg = _('--dimension-name not in pair with --dimension-value') raise exceptions.Conflict(msg) - + metric = { 'dimensions': dimensions, 'metric_name': parsed_args.metric_name, diff --git a/otcextensions/osclient/ces/v1/event_data.py b/otcextensions/osclient/ces/v1/event_data.py index 6fa883a5c..88c753dcf 100644 --- a/otcextensions/osclient/ces/v1/event_data.py +++ b/otcextensions/osclient/ces/v1/event_data.py @@ -30,6 +30,7 @@ def _get_columns(item): return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map, inv_columns) + class ListEventData(command.Lister): _description = _('List CES event data') columns = ( diff --git a/otcextensions/osclient/ces/v1/metric.py b/otcextensions/osclient/ces/v1/metric.py index 5393a3987..b53eaaebc 100644 --- a/otcextensions/osclient/ces/v1/metric.py +++ b/otcextensions/osclient/ces/v1/metric.py @@ -50,7 +50,7 @@ class ListMetrics(command.Lister): def get_parser(self, prog_name): parser = super(ListMetrics, self).get_parser(prog_name) - + parser.add_argument( '--namespace', metavar='', diff --git a/otcextensions/osclient/ces/v1/metric_data.py b/otcextensions/osclient/ces/v1/metric_data.py index 603f16307..537e3f191 100644 --- a/otcextensions/osclient/ces/v1/metric_data.py +++ b/otcextensions/osclient/ces/v1/metric_data.py @@ -30,6 +30,7 @@ def _get_columns(item): return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map, inv_columns) + class ListMetricData(command.Lister): _description = _('List CES event data') columns = ( @@ -39,7 +40,7 @@ class ListMetricData(command.Lister): ) def get_parser(self, prog_name): - parser = super(ListEventData, self).get_parser(prog_name) + parser = super(ListMetricData, self).get_parser(prog_name) parser.add_argument( '--namespace', metavar='', diff --git a/otcextensions/osclient/ces/v1/quota.py b/otcextensions/osclient/ces/v1/quota.py index 2c8194252..a8fca2c3b 100644 --- a/otcextensions/osclient/ces/v1/quota.py +++ b/otcextensions/osclient/ces/v1/quota.py @@ -40,14 +40,6 @@ def _flatten_output(obj): return data -def _get_columns(item): - column_map = { - } - inv_columns = [''] - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map, - inv_columns) - - class ListQuotas(command.Lister): _description = _('List CES alarm quota') columns = ( diff --git a/otcextensions/sdk/ces/v1/_proxy.py b/otcextensions/sdk/ces/v1/_proxy.py index 5f263c1e4..d76ee6d42 100644 --- a/otcextensions/sdk/ces/v1/_proxy.py +++ b/otcextensions/sdk/ces/v1/_proxy.py @@ -124,7 +124,7 @@ def metric_data(self, **query): :rtype: :class:`~otcextensions.sdk.ces.v1.metric_data.MetricData` """ return self._list(_metric_data.MetricData, **query) - + # skipped due to lag of compliant API (resource is list not JSON) ''' def create_metric_data(self, **attrs): diff --git a/otcextensions/sdk/ces/v1/metric_data.py b/otcextensions/sdk/ces/v1/metric_data.py index 3a8f0e18d..662c73e37 100644 --- a/otcextensions/sdk/ces/v1/metric_data.py +++ b/otcextensions/sdk/ces/v1/metric_data.py @@ -36,7 +36,7 @@ class MetricData(resource.Resource): # capabilities allow_list = True - + _query_mapping = resource.QueryParameters( 'namespace', 'period', 'filter', 'metric_name', 'dim.0', 'from', 'to') diff --git a/otcextensions/tests/unit/sdk/ces/v1/test_event_data.py b/otcextensions/tests/unit/sdk/ces/v1/test_event_data.py new file mode 100644 index 000000000..75402beb5 --- /dev/null +++ b/otcextensions/tests/unit/sdk/ces/v1/test_event_data.py @@ -0,0 +1,27 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from openstack.tests.unit import base + +from otcextensions.sdk.ces.v1 import event_data + + +class TestEventData(base.TestCase): + + def test_basic(self): + sot = event_data.EventData() + self.assertEqual('/event-data', sot.base_path) + self.assertTrue(sot.allow_list) + self.assertFalse(sot.allow_create) + self.assertFalse(sot.allow_fetch) + self.assertFalse(sot.allow_commit) + self.assertFalse(sot.allow_delete) diff --git a/otcextensions/tests/unit/sdk/ces/v1/test_metric.py b/otcextensions/tests/unit/sdk/ces/v1/test_metric.py new file mode 100644 index 000000000..538380c89 --- /dev/null +++ b/otcextensions/tests/unit/sdk/ces/v1/test_metric.py @@ -0,0 +1,29 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from openstack.tests.unit import base + +from otcextensions.sdk.ces.v1 import metric + + +class TestMetric(base.TestCase): + + def test_basic(self): + sot = metric.Metric() + self.assertEqual('metrics', sot.resources_key) + self.assertEqual('metric', sot.resource_key) + self.assertEqual('/metrics', sot.base_path) + self.assertTrue(sot.allow_list) + self.assertFalse(sot.allow_create) + self.assertFalse(sot.allow_fetch) + self.assertFalse(sot.allow_commit) + self.assertFalse(sot.allow_delete) diff --git a/otcextensions/tests/unit/sdk/ces/v1/test_metric_data.py b/otcextensions/tests/unit/sdk/ces/v1/test_metric_data.py new file mode 100644 index 000000000..889d13feb --- /dev/null +++ b/otcextensions/tests/unit/sdk/ces/v1/test_metric_data.py @@ -0,0 +1,27 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from openstack.tests.unit import base + +from otcextensions.sdk.ces.v1 import metric_data + + +class TestMetricData(base.TestCase): + + def test_basic(self): + sot = metric_data.MetricData() + self.assertEqual('/metric-data', sot.base_path) + self.assertTrue(sot.allow_list) + self.assertFalse(sot.allow_create) + self.assertFalse(sot.allow_fetch) + self.assertFalse(sot.allow_commit) + self.assertFalse(sot.allow_delete) diff --git a/otcextensions/tests/unit/sdk/ces/v1/test_quota.py b/otcextensions/tests/unit/sdk/ces/v1/test_quota.py new file mode 100644 index 000000000..7129f8f1d --- /dev/null +++ b/otcextensions/tests/unit/sdk/ces/v1/test_quota.py @@ -0,0 +1,28 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from openstack.tests.unit import base + +from otcextensions.sdk.ces.v1 import quota + + +class TestQuota(base.TestCase): + + def test_basic(self): + sot = quota.Quota() + self.assertEqual('quotas', sot.resources_key) + self.assertEqual('/quotas', sot.base_path) + self.assertTrue(sot.allow_list) + self.assertFalse(sot.allow_create) + self.assertFalse(sot.allow_fetch) + self.assertFalse(sot.allow_commit) + self.assertFalse(sot.allow_delete) From 6fa919d7fe69b5ac47bda755886b768215586a79 Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Fri, 18 Sep 2020 10:10:59 +0000 Subject: [PATCH 41/42] Review changes --- examples/ces/create_alarm.py | 8 ++--- otcextensions/osclient/ces/v1/alarm.py | 37 ++++---------------- otcextensions/osclient/ces/v1/event_data.py | 10 ------ otcextensions/osclient/ces/v1/metric.py | 10 ------ otcextensions/osclient/ces/v1/metric_data.py | 10 ------ otcextensions/osclient/ces/v1/quota.py | 10 ------ otcextensions/sdk/ces/v1/_proxy.py | 2 ++ 7 files changed, 13 insertions(+), 74 deletions(-) diff --git a/examples/ces/create_alarm.py b/examples/ces/create_alarm.py index 65fccadba..bdbc35e86 100644 --- a/examples/ces/create_alarm.py +++ b/examples/ces/create_alarm.py @@ -72,10 +72,10 @@ # OSC command ''' openstack --os-cloud otc ces alarm create --description "Test Alarm" \ ---namespace SYS.ECS --dimension-name instance_id --dimension-value \ -33328f02-3814-422e-b688-bfdba93d4123 --dimension-name instance_id \ ---dimension-value 33328f02-3814-422e-b688-bfdba93d4052 --metric-name \ -"network_outgoing" --period '300' --filter average \ +--namespace SYS.ECS \ +--dimension name=instance_id,value=33328f02-3814-422e-b688-bfdba93d4123 \ +--dimension name=instance_id,value=33328f02-3814-422e-b688-bfdba93d4052 \ +--metric-name "network_outgoing" --period '300' --filter average \ --comparison-operator '>=' --value '6' --unit 'B/s' --count '1' \ --enabled True --action-enabled True --level 2 --ok-action-type notification \ --ok-action-notification-list \ diff --git a/otcextensions/osclient/ces/v1/alarm.py b/otcextensions/osclient/ces/v1/alarm.py index 5e4edf261..51fc692fb 100644 --- a/otcextensions/osclient/ces/v1/alarm.py +++ b/otcextensions/osclient/ces/v1/alarm.py @@ -13,11 +13,10 @@ '''CES Alarm v1 action implementations''' import logging -from osc_lib import exceptions from osc_lib import utils +from osc_lib.cli import parseractions from osc_lib.command import command - from otcextensions.i18n import _ from otcextensions.common import sdk_utils @@ -206,7 +205,6 @@ def get_parser(self, prog_name): '--action-enabled', default=False, type=bool, - required=True, help=_('Specifies whether the alarm action is triggered') ) parser.add_argument( @@ -306,30 +304,19 @@ def get_parser(self, prog_name): ) # DimensionsSpec for Metrics - # This is a list of dictionaries - # IMPROVEMENT NEEDED parser.add_argument( - '--dimension-name', - metavar='', + '--dimension', + metavar='name=,value=', + action=parseractions.MultiKeyValueAction, + dest='dimensions', required=True, - action='append', + required_keys=['name', 'value'], help=_('dimension.name: object type e.g. instance_id\n' 'Provide --dimension-name always in pair with' '--dimension-value .\n' 'Both values can be provided multiple times (equal number)' 'to generate a list of monitored objects.') ) - parser.add_argument( - '--dimension-value', - metavar='', - required=True, - action='append', - help=_('dimension.value: object id e.g. ECS ID\n' - 'Provide --dimension-name always in pair with' - '--dimension-value .\n' - 'Both values can be provided multiple times (equal number)' - 'to generate a list of monitored objects.') - ) # MetricSpec parser.add_argument( @@ -397,18 +384,8 @@ def take_action(self, parsed_args): condition['unit'] = parsed_args.unit attrs['condition'] = condition - dimensions = [] - if len(parsed_args.dimension_name) == len(parsed_args.dimension_value): - for i in range(len(parsed_args.dimension_name)): - dimensions.append( - {'name': parsed_args.dimension_name[i - 1], - 'value': parsed_args.dimension_value[i - 1]}) - else: - msg = _('--dimension-name not in pair with --dimension-value') - raise exceptions.Conflict(msg) - metric = { - 'dimensions': dimensions, + 'dimensions': parsed_args.dimensions, 'metric_name': parsed_args.metric_name, 'namespace': parsed_args.namespace } diff --git a/otcextensions/osclient/ces/v1/event_data.py b/otcextensions/osclient/ces/v1/event_data.py index 88c753dcf..1edc7c661 100644 --- a/otcextensions/osclient/ces/v1/event_data.py +++ b/otcextensions/osclient/ces/v1/event_data.py @@ -16,21 +16,11 @@ from osc_lib import utils from osc_lib.command import command - from otcextensions.i18n import _ -from otcextensions.common import sdk_utils LOG = logging.getLogger(__name__) -def _get_columns(item): - column_map = { - } - inv_columns = [''] - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map, - inv_columns) - - class ListEventData(command.Lister): _description = _('List CES event data') columns = ( diff --git a/otcextensions/osclient/ces/v1/metric.py b/otcextensions/osclient/ces/v1/metric.py index b53eaaebc..fe9bddf1c 100644 --- a/otcextensions/osclient/ces/v1/metric.py +++ b/otcextensions/osclient/ces/v1/metric.py @@ -16,21 +16,11 @@ from osc_lib import utils from osc_lib.command import command - from otcextensions.i18n import _ -from otcextensions.common import sdk_utils LOG = logging.getLogger(__name__) -def _get_columns(item): - column_map = { - } - inv_columns = [''] - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map, - inv_columns) - - class ListMetrics(command.Lister): _description = _('List CES alarms') columns = ( diff --git a/otcextensions/osclient/ces/v1/metric_data.py b/otcextensions/osclient/ces/v1/metric_data.py index 537e3f191..f61841a18 100644 --- a/otcextensions/osclient/ces/v1/metric_data.py +++ b/otcextensions/osclient/ces/v1/metric_data.py @@ -16,21 +16,11 @@ from osc_lib import utils from osc_lib.command import command - from otcextensions.i18n import _ -from otcextensions.common import sdk_utils LOG = logging.getLogger(__name__) -def _get_columns(item): - column_map = { - } - inv_columns = [''] - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map, - inv_columns) - - class ListMetricData(command.Lister): _description = _('List CES event data') columns = ( diff --git a/otcextensions/osclient/ces/v1/quota.py b/otcextensions/osclient/ces/v1/quota.py index a8fca2c3b..cf55095df 100644 --- a/otcextensions/osclient/ces/v1/quota.py +++ b/otcextensions/osclient/ces/v1/quota.py @@ -16,21 +16,11 @@ from osc_lib import utils from osc_lib.command import command - from otcextensions.i18n import _ -from otcextensions.common import sdk_utils LOG = logging.getLogger(__name__) -def _get_columns(item): - column_map = { - } - inv_columns = [''] - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map, - inv_columns) - - def _flatten_output(obj): data = { 'type': obj.resources[0].type, diff --git a/otcextensions/sdk/ces/v1/_proxy.py b/otcextensions/sdk/ces/v1/_proxy.py index d76ee6d42..9b5bc565b 100644 --- a/otcextensions/sdk/ces/v1/_proxy.py +++ b/otcextensions/sdk/ces/v1/_proxy.py @@ -26,6 +26,8 @@ class Proxy(proxy.Proxy): def alarms(self, **query): """Return a generator of alarms + :param dict query: Optional query parameters to be sent to limit the + resources being returned. :returns: A generator of alarm objects :rtype: :class:`~otcextensions.sdk.ces.v1.alarm.Alarm` """ From 2cbb6e9f5242470d8713db6c070aa15d0c35f884 Mon Sep 17 00:00:00 2001 From: "T. Schreiber" Date: Fri, 18 Sep 2020 10:23:01 +0000 Subject: [PATCH 42/42] minor fix in alarm rule description --- otcextensions/osclient/ces/v1/alarm.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/otcextensions/osclient/ces/v1/alarm.py b/otcextensions/osclient/ces/v1/alarm.py index 51fc692fb..8e0c36be2 100644 --- a/otcextensions/osclient/ces/v1/alarm.py +++ b/otcextensions/osclient/ces/v1/alarm.py @@ -311,11 +311,9 @@ def get_parser(self, prog_name): dest='dimensions', required=True, required_keys=['name', 'value'], - help=_('dimension.name: object type e.g. instance_id\n' - 'Provide --dimension-name always in pair with' - '--dimension-value .\n' - 'Both values can be provided multiple times (equal number)' - 'to generate a list of monitored objects.') + help=_('Example: \n' + '--dimension name=instance_id,value=123456-bfdba93d4123\n' + 'Repeat option to provide multiple dimensions.') ) # MetricSpec