1# Licensed under the Apache License, Version 2.0 (the "License"); you may
2# not use this file except in compliance with the License. You may obtain
3# a copy of the License at
4#
5#      http://www.apache.org/licenses/LICENSE-2.0
6#
7# Unless required by applicable law or agreed to in writing, software
8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10# License for the specific language governing permissions and limitations
11# under the License.
12
13from openstack import resource
14
15
16RETRIABLE_STATUS_CODES = [
17    # HTTP Conflict - happens if a node is locked
18    409,
19    # HTTP Service Unavailable happens if there's no free conductor
20    503
21]
22"""HTTP status codes that should be retried."""
23
24
25PROVISIONING_VERSIONS = {
26    'abort': 13,
27    'adopt': 17,
28    'clean': 15,
29    'inspect': 6,
30    'manage': 4,
31    'provide': 4,
32    'rescue': 38,
33    'unrescue': 38,
34}
35"""API microversions introducing provisioning verbs."""
36
37
38# Based on https://docs.openstack.org/ironic/latest/contributor/states.html
39EXPECTED_STATES = {
40    'active': 'active',
41    'adopt': 'available',
42    'clean': 'manageable',
43    'deleted': 'available',
44    'inspect': 'manageable',
45    'manage': 'manageable',
46    'provide': 'available',
47    'rebuild': 'active',
48    'rescue': 'rescue',
49}
50"""Mapping of provisioning actions to expected stable states."""
51
52EXPECTED_POWER_STATES = {
53    'power on': 'power on',
54    'power off': 'power off',
55    'rebooting': 'power on',
56    'soft power off': 'power off',
57    'soft rebooting': 'power on',
58}
59"""Mapping of target power states to expected power states."""
60
61STATE_VERSIONS = {
62    'enroll': '1.11',
63    'manageable': '1.4',
64}
65"""API versions when certain states were introduced."""
66
67VIF_VERSION = '1.28'
68"""API version in which the VIF operations were introduced."""
69
70CONFIG_DRIVE_REBUILD_VERSION = '1.35'
71"""API version in which rebuild accepts a configdrive."""
72
73RESET_INTERFACES_VERSION = '1.45'
74"""API version in which the reset_interfaces parameter was introduced."""
75
76CONFIG_DRIVE_DICT_VERSION = '1.56'
77"""API version in which configdrive can be a dictionary."""
78
79DEPLOY_STEPS_VERSION = '1.69'
80"""API version in which deploy_steps was added to node provisioning."""
81
82CHANGE_BOOT_MODE_VERSION = '1.76'
83"""API version in which boot_mode and secure_boot states can be changed"""
84
85
86class ListMixin:
87
88    @classmethod
89    def list(cls, session, details=False, **params):
90        """This method is a generator which yields resource objects.
91
92        This resource object list generator handles pagination and takes query
93        params for response filtering.
94
95        :param session: The session to use for making this request.
96        :type session: :class:`~keystoneauth1.adapter.Adapter`
97        :param bool details: Whether to return detailed node records
98        :param dict params: These keyword arguments are passed through the
99            :meth:`~openstack.resource.QueryParameter._transpose` method
100            to find if any of them match expected query parameters to be
101            sent in the *params* argument to
102            :meth:`~keystoneauth1.adapter.Adapter.get`.
103
104        :return: A generator of :class:`openstack.resource.Resource` objects.
105        :raises: :exc:`~openstack.exceptions.InvalidResourceQuery` if query
106                 contains invalid params.
107        """
108        base_path = cls.base_path
109        if details:
110            base_path += '/detail'
111        return super(ListMixin, cls).list(session, paginated=True,
112                                          base_path=base_path, **params)
113
114
115def comma_separated_list(value):
116    if value is None:
117        return None
118    else:
119        return ','.join(value)
120
121
122def fields_type(value, resource_type):
123    if value is None:
124        return None
125
126    resource_mapping = {
127        key: value.name
128        for key, value in resource_type.__dict__.items()
129        if isinstance(value, resource.Body)
130    }
131
132    return comma_separated_list(resource_mapping.get(x, x) for x in value)
133