diff --git a/examples/ces/create_alarm.py b/examples/ces/create_alarm.py new file mode 100644 index 000000000..bdbc35e86 --- /dev/null +++ b/examples/ces/create_alarm.py @@ -0,0 +1,89 @@ +#!/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 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" + }, + "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", + "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,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 \ +'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/examples/ces/create_metric_data.py b/examples/ces/create_metric_data.py new file mode 100644 index 000000000..c5c92f9c3 --- /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') + +# 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": "%" + } +] + + +data = conn.ces.create_metric_data(**attrs) +print(data) 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/list_alarms.py b/examples/ces/list_alarms.py new file mode 100644 index 000000000..2d68be2b3 --- /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(): + print(alarm) diff --git a/examples/ces/list_event_data.py b/examples/ces/list_event_data.py new file mode 100644 index 000000000..d7e89b414 --- /dev/null +++ b/examples/ces/list_event_data.py @@ -0,0 +1,32 @@ +#!/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') + + +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 +} + +for data in conn.ces.event_data(**query): + print(data) diff --git a/examples/ces/list_metric_data.py b/examples/ces/list_metric_data.py new file mode 100644 index 000000000..229cdd312 --- /dev/null +++ b/examples/ces/list_metric_data.py @@ -0,0 +1,34 @@ +#!/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') + + +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/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/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/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/__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..8e0c36be2 --- /dev/null +++ b/otcextensions/osclient/ces/v1/alarm.py @@ -0,0 +1,399 @@ +# 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.cli import parseractions +from osc_lib.command import command + +from otcextensions.i18n import _ +from otcextensions.common import sdk_utils + +LOG = logging.getLogger(__name__) + + +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, + '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. + '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, + 'state': obj.alarm_state, + } + 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(undefined): Implement query arguments -> SDK not working +class ListAlarms(command.Lister): + _description = _('List CES alarms') + columns = ( + 'id', + 'name', + 'namespace', + 'dimensions.name', + 'dimensions.value', + 'alarm_level', + 'enabled', + 'action_enabled', + 'state' + ) + + def get_parser(self, prog_name): + parser = super(ListAlarms, 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_dict_properties( + _flatten_output(s), self.columns + ) for s in data)) + return table + + +class ShowAlarm(command.ShowOne): + _description = _('Show CloudEye alarm rule 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 rule.') + ) + 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 = _('Switch Alarm status.') + + def get_parser(self, prog_name): + parser = super(SetAlarm, 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: + client.switch_alarm_state( + 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) + + +class CreateAlarm(command.ShowOne): + _description = _('Create CloudEye alarm rule') + + 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, + type=bool, + 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=False, + type=bool, + 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') + ) + + # AlarmActions + parser.add_argument( + '--alarm-action-type', + metavar='', + 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.\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.\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.\n' + 'URN example structure:\n' + 'urn:smn:region:68438a86d98e427e907e0097b7e35d48:sd\n' + 'The parameter can be given multiple times to ' + 'notify multiple targets.') + ) + + # ConditionSpec + parser.add_argument( + '--comparison-operator', + metavar='', + required=True, + 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.\n' + 'Value range: 1 to 5') + ) + 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='', + type=int, + required=True, + help=_('Indicates the interval (in seconds) for checking ' + 'whether the configured alarm rules are met.') + ) + parser.add_argument( + '--unit', + metavar='', + help=_('Specifies data unit.') + ) + parser.add_argument( + '--value', + metavar='', + type=int, + required=True, + help=_('Specifies the alarm threshold.\n' + 'Values: 0 to max(int)') + ) + + # DimensionsSpec for Metrics + parser.add_argument( + '--dimension', + metavar='name=,value=', + action=parseractions.MultiKeyValueAction, + dest='dimensions', + required=True, + required_keys=['name', 'value'], + help=_('Example: \n' + '--dimension name=instance_id,value=123456-bfdba93d4123\n' + 'Repeat option to provide multiple dimensions.') + ) + + # 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:\n' + 'SYS.ECS, SYS.AS') + ) + + return parser + + def take_action(self, parsed_args): + + client = self.app.client_manager.ces + + attrs = {} + + attrs['name'] = parsed_args.name + if parsed_args.enabled: + attrs['alarm_enabled'] = parsed_args.enabled + if parsed_args.description: + attrs['alarm_description'] = parsed_args.description + 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: + 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, + '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.append({ + 'type': parsed_args.alarm_action_type, + 'notificationList': nl + }) + attrs['alarm_actions'] = alarm_actions + + condition = { + '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 + attrs['condition'] = condition + + metric = { + 'dimensions': parsed_args.dimensions, + 'metric_name': parsed_args.metric_name, + 'namespace': parsed_args.namespace + } + attrs['metric'] = metric + + obj = client.create_alarm( + **attrs + ) + + display_columns, columns = _get_columns(obj) + data = utils.get_item_properties(obj, columns) + + return (display_columns, 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..1edc7c661 --- /dev/null +++ b/otcextensions/osclient/ces/v1/event_data.py @@ -0,0 +1,87 @@ +# 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 _ + +LOG = logging.getLogger(__name__) + + +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/osclient/ces/v1/metric.py b/otcextensions/osclient/ces/v1/metric.py new file mode 100644 index 000000000..fe9bddf1c --- /dev/null +++ b/otcextensions/osclient/ces/v1/metric.py @@ -0,0 +1,93 @@ +# 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 _ + +LOG = logging.getLogger(__name__) + + +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) + + # 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): + 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 = [] + + table = (self.table_columns, big_list) + + return table diff --git a/otcextensions/osclient/ces/v1/metric_data.py b/otcextensions/osclient/ces/v1/metric_data.py new file mode 100644 index 000000000..f61841a18 --- /dev/null +++ b/otcextensions/osclient/ces/v1/metric_data.py @@ -0,0 +1,87 @@ +# 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 _ + +LOG = logging.getLogger(__name__) + + +class ListMetricData(command.Lister): + _description = _('List CES event data') + columns = ( + 'timestamp', + 'type', + 'value' + ) + + def get_parser(self, prog_name): + parser = super(ListMetricData, 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/osclient/ces/v1/quota.py b/otcextensions/osclient/ces/v1/quota.py new file mode 100644 index 000000000..cf55095df --- /dev/null +++ b/otcextensions/osclient/ces/v1/quota.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. +# +'''CES Alarm v1 action implementations''' +import logging + +from osc_lib import utils +from osc_lib.command import command + +from otcextensions.i18n import _ + +LOG = logging.getLogger(__name__) + + +def _flatten_output(obj): + data = { + 'type': obj.resources[0].type, + 'quota': obj.resources[0].quota, + 'used': obj.resources[0].used + } + return data + + +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/__init__.py b/otcextensions/sdk/__init__.py index 7da252974..79571b0a2 100644 --- a/otcextensions/sdk/__init__.py +++ b/otcextensions/sdk/__init__.py @@ -61,6 +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, + # }, 'cts': { 'service_type': 'cts', }, diff --git a/otcextensions/sdk/ces/v1/_proxy.py b/otcextensions/sdk/ces/v1/_proxy.py index 38cfb42e8..9b5bc565b 100644 --- a/otcextensions/sdk/ces/v1/_proxy.py +++ b/otcextensions/sdk/ces/v1/_proxy.py @@ -1,17 +1,148 @@ -# 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 proxy - - -class Proxy(proxy.Proxy): - - skip_discovery = True +# 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 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 +from otcextensions.sdk.ces.v1 import metric_data as _metric_data +from otcextensions.sdk.ces.v1 import quota as _quota + + +class Proxy(proxy.Proxy): + + skip_discovery = True + + # ======== Alarms ======== + 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` + """ + return self._list(_alarm.Alarm, **query) + + 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, 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, + ignore_missing=ignore_missing) + + def find_alarm(self, name_or_id, ignore_missing=True): + """Find a single 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 switch_alarm_state(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: None + """ + alarm = self._get_resource(_alarm.Alarm, alarm) + alarm.change_alarm_status(self) + + # ======== 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 + + :param kwargs query: Optional query parameters to be sent to limit + the resources being returned. + :returns: A generator of metric objects + :rtype: :class:`~otcextensions.sdk.ces.v1.metric.Metric` + """ + 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) + + # 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): + """Return a generator of quotas + + :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/alarm.py b/otcextensions/sdk/ces/v1/alarm.py new file mode 100644 index 000000000..0a8fdd5cc --- /dev/null +++ b/otcextensions/sdk/ces/v1/alarm.py @@ -0,0 +1,236 @@ +# 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 exceptions +from openstack import resource +from openstack import utils + + +# 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 + type = resource.Body('type') + + +class OkActionsSpec(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 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('count', type=int) + # indicates the data rollup method + # values: max, min, average, sum, variance + filter = 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) + # Specifies the metric name + 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_commit = True + allow_create = True + allow_fetch = True + allow_delete = True + allow_list = True + + _query_mapping = resource.QueryParameters( + 'limit', 'order', 'start' + ) + + # Properties + # Specifies the action triggered by 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', alternate_id=True) + # alarm severity + # values: 1: critical, 2: major, 3: minor, 4: informational alarm + alarm_level = resource.Body('alarm_level', type=int) + # 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=OkActionsSpec) + # Describes alarm triggering condititon + condition = resource.Body('condition', type=ConditionSpec) + # 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') + + 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()) + + def _action(self, session, body): + """Perform actions given the message body. + + """ + url = utils.urljoin(self.base_path, self.id, "action") + response = session.put( + url, + json=body) + exceptions.raise_from_response(response) + return response + + def change_alarm_status(self, session): + body = { + "alarm_enabled": True + } + current_status = self.get('alarm_enabled') + if current_status is True: + body.update({'alarm_enabled': False}) + 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)) diff --git a/otcextensions/sdk/ces/v1/event_data.py b/otcextensions/sdk/ces/v1/event_data.py new file mode 100644 index 000000000..a7d0ac92a --- /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.0', '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.py b/otcextensions/sdk/ces/v1/metric.py new file mode 100644 index 000000000..fe8059c67 --- /dev/null +++ b/otcextensions/sdk/ces/v1/metric.py @@ -0,0 +1,47 @@ +# 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 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', 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..662c73e37 --- /dev/null +++ b/otcextensions/sdk/ces/v1/metric_data.py @@ -0,0 +1,49 @@ +# 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 + # 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 = '/metric-data' + + # capabilities + allow_list = True + + _query_mapping = resource.QueryParameters( + 'namespace', 'period', 'filter', 'metric_name', 'dim.0', 'from', + 'to') + + # 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') diff --git a/otcextensions/sdk/ces/v1/quota.py b/otcextensions/sdk/ces/v1/quota.py new file mode 100644 index 000000000..530fb1442 --- /dev/null +++ b/otcextensions/sdk/ces/v1/quota.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 ResourcesSpec(resource.Resource): + + # Properties + # quota type + type = 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=list, + list_type=ResourcesSpec) 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/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_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_proxy.py b/otcextensions/tests/unit/sdk/ces/v1/test_proxy.py index 55f837575..6519cfe02 100644 --- a/otcextensions/tests/unit/sdk/ces/v1/test_proxy.py +++ b/otcextensions/tests/unit/sdk/ces/v1/test_proxy.py @@ -1,22 +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. - -from otcextensions.sdk.ces.v1 import _proxy - -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) +# 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.switch_alarm_state, + 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) 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) diff --git a/otcextensions/tests/unit/sdk/nat/v2/test_dnat.py b/otcextensions/tests/unit/sdk/nat/v2/test_dnat.py index 655773526..6df4f1f3b 100644 --- a/otcextensions/tests/unit/sdk/nat/v2/test_dnat.py +++ b/otcextensions/tests/unit/sdk/nat/v2/test_dnat.py @@ -1,67 +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', - 'project_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['project_id'], sot.project_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) +# 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', + 'project_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['project_id'], sot.project_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 index 327231eae..c3fd7c374 100644 --- a/otcextensions/tests/unit/sdk/nat/v2/test_gateway.py +++ b/otcextensions/tests/unit/sdk/nat/v2/test_gateway.py @@ -1,60 +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, - 'project_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['project_id'], sot.project_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) +# 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, + 'project_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['project_id'], sot.project_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_snat.py b/otcextensions/tests/unit/sdk/nat/v2/test_snat.py index 21cf7ee60..57a3dbbc9 100644 --- a/otcextensions/tests/unit/sdk/nat/v2/test_snat.py +++ b/otcextensions/tests/unit/sdk/nat/v2/test_snat.py @@ -1,61 +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, - 'project_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['project_id'], sot.project_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) +# 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, + 'project_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['project_id'], sot.project_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) diff --git a/setup.cfg b/setup.cfg index 82cff272f..2b0879dc5 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 @@ -214,6 +215,17 @@ 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:ListAlarms + ces_alarm_show = otcextensions.osclient.ces.v1.alarm:ShowAlarm + 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 + ces_quota_list = otcextensions.osclient.ces.v1.quota:ListQuotas + ces_event_data_list = otcextensions.osclient.ces.v1.event_data:ListEventData + + openstack.load_balancer.v1 = loadbalancer_list = otcextensions.osclient.load_balancer.v1.load_balancer:ListLoadBalancer loadbalancer_show = otcextensions.osclient.load_balancer.v1.load_balancer:ShowLoadBalancer