~chris-gondolin/charms/trusty/keystone/ldap-fixes

« back to all changes in this revision

Viewing changes to charmhelpers/contrib/openstack/amulet/deployment.py

  • Committer: Edward Hope-Morley
  • Date: 2016-01-08 14:33:27 UTC
  • mfrom: (196 stable.remote)
  • mto: This revision was merged to the branch mainline in revision 197.
  • Revision ID: edward.hope-morley@canonical.com-20160108143327-czj7dusywrbuqz66
sync /next

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# You should have received a copy of the GNU Lesser General Public License
15
15
# along with charm-helpers.  If not, see <http://www.gnu.org/licenses/>.
16
16
 
 
17
import logging
 
18
import re
 
19
import sys
17
20
import six
18
21
from collections import OrderedDict
19
22
from charmhelpers.contrib.amulet.deployment import (
20
23
    AmuletDeployment
21
24
)
22
25
 
 
26
DEBUG = logging.DEBUG
 
27
ERROR = logging.ERROR
 
28
 
23
29
 
24
30
class OpenStackAmuletDeployment(AmuletDeployment):
25
31
    """OpenStack amulet deployment.
28
34
       that is specifically for use by OpenStack charms.
29
35
       """
30
36
 
31
 
    def __init__(self, series=None, openstack=None, source=None, stable=True):
 
37
    def __init__(self, series=None, openstack=None, source=None,
 
38
                 stable=True, log_level=DEBUG):
32
39
        """Initialize the deployment environment."""
33
40
        super(OpenStackAmuletDeployment, self).__init__(series)
 
41
        self.log = self.get_logger(level=log_level)
 
42
        self.log.info('OpenStackAmuletDeployment:  init')
34
43
        self.openstack = openstack
35
44
        self.source = source
36
45
        self.stable = stable
38
47
        # out.
39
48
        self.current_next = "trusty"
40
49
 
 
50
    def get_logger(self, name="deployment-logger", level=logging.DEBUG):
 
51
        """Get a logger object that will log to stdout."""
 
52
        log = logging
 
53
        logger = log.getLogger(name)
 
54
        fmt = log.Formatter("%(asctime)s %(funcName)s "
 
55
                            "%(levelname)s: %(message)s")
 
56
 
 
57
        handler = log.StreamHandler(stream=sys.stdout)
 
58
        handler.setLevel(level)
 
59
        handler.setFormatter(fmt)
 
60
 
 
61
        logger.addHandler(handler)
 
62
        logger.setLevel(level)
 
63
 
 
64
        return logger
 
65
 
41
66
    def _determine_branch_locations(self, other_services):
42
67
        """Determine the branch locations for the other services.
43
68
 
45
70
           stable or next (dev) branch, and based on this, use the corresonding
46
71
           stable or next branches for the other_services."""
47
72
 
 
73
        self.log.info('OpenStackAmuletDeployment:  determine branch locations')
 
74
 
48
75
        # Charms outside the lp:~openstack-charmers namespace
49
76
        base_charms = ['mysql', 'mongodb', 'nrpe']
50
77
 
82
109
 
83
110
    def _add_services(self, this_service, other_services):
84
111
        """Add services to the deployment and set openstack-origin/source."""
 
112
        self.log.info('OpenStackAmuletDeployment:  adding services')
 
113
 
85
114
        other_services = self._determine_branch_locations(other_services)
86
115
 
87
116
        super(OpenStackAmuletDeployment, self)._add_services(this_service,
95
124
                      'ceph-osd', 'ceph-radosgw']
96
125
 
97
126
        # Charms which can not use openstack-origin, ie. many subordinates
98
 
        no_origin = ['cinder-ceph', 'hacluster', 'neutron-openvswitch', 'nrpe']
 
127
        no_origin = ['cinder-ceph', 'hacluster', 'neutron-openvswitch', 'nrpe',
 
128
                     'openvswitch-odl', 'neutron-api-odl', 'odl-controller']
99
129
 
100
130
        if self.openstack:
101
131
            for svc in services:
111
141
 
112
142
    def _configure_services(self, configs):
113
143
        """Configure all of the services."""
 
144
        self.log.info('OpenStackAmuletDeployment:  configure services')
114
145
        for service, config in six.iteritems(configs):
115
146
            self.d.configure(service, config)
116
147
 
 
148
    def _auto_wait_for_status(self, message=None, exclude_services=None,
 
149
                              include_only=None, timeout=1800):
 
150
        """Wait for all units to have a specific extended status, except
 
151
        for any defined as excluded.  Unless specified via message, any
 
152
        status containing any case of 'ready' will be considered a match.
 
153
 
 
154
        Examples of message usage:
 
155
 
 
156
          Wait for all unit status to CONTAIN any case of 'ready' or 'ok':
 
157
              message = re.compile('.*ready.*|.*ok.*', re.IGNORECASE)
 
158
 
 
159
          Wait for all units to reach this status (exact match):
 
160
              message = re.compile('^Unit is ready and clustered$')
 
161
 
 
162
          Wait for all units to reach any one of these (exact match):
 
163
              message = re.compile('Unit is ready|OK|Ready')
 
164
 
 
165
          Wait for at least one unit to reach this status (exact match):
 
166
              message = {'ready'}
 
167
 
 
168
        See Amulet's sentry.wait_for_messages() for message usage detail.
 
169
        https://github.com/juju/amulet/blob/master/amulet/sentry.py
 
170
 
 
171
        :param message: Expected status match
 
172
        :param exclude_services: List of juju service names to ignore,
 
173
            not to be used in conjuction with include_only.
 
174
        :param include_only: List of juju service names to exclusively check,
 
175
            not to be used in conjuction with exclude_services.
 
176
        :param timeout: Maximum time in seconds to wait for status match
 
177
        :returns: None.  Raises if timeout is hit.
 
178
        """
 
179
        self.log.info('Waiting for extended status on units...')
 
180
 
 
181
        all_services = self.d.services.keys()
 
182
 
 
183
        if exclude_services and include_only:
 
184
            raise ValueError('exclude_services can not be used '
 
185
                             'with include_only')
 
186
 
 
187
        if message:
 
188
            if isinstance(message, re._pattern_type):
 
189
                match = message.pattern
 
190
            else:
 
191
                match = message
 
192
 
 
193
            self.log.debug('Custom extended status wait match: '
 
194
                           '{}'.format(match))
 
195
        else:
 
196
            self.log.debug('Default extended status wait match:  contains '
 
197
                           'READY (case-insensitive)')
 
198
            message = re.compile('.*ready.*', re.IGNORECASE)
 
199
 
 
200
        if exclude_services:
 
201
            self.log.debug('Excluding services from extended status match: '
 
202
                           '{}'.format(exclude_services))
 
203
        else:
 
204
            exclude_services = []
 
205
 
 
206
        if include_only:
 
207
            services = include_only
 
208
        else:
 
209
            services = list(set(all_services) - set(exclude_services))
 
210
 
 
211
        self.log.debug('Waiting up to {}s for extended status on services: '
 
212
                       '{}'.format(timeout, services))
 
213
        service_messages = {service: message for service in services}
 
214
        self.d.sentry.wait_for_messages(service_messages, timeout=timeout)
 
215
        self.log.info('OK')
 
216
 
117
217
    def _get_openstack_release(self):
118
218
        """Get openstack release.
119
219
 
125
225
         self.precise_havana, self.precise_icehouse,
126
226
         self.trusty_icehouse, self.trusty_juno, self.utopic_juno,
127
227
         self.trusty_kilo, self.vivid_kilo, self.trusty_liberty,
128
 
         self.wily_liberty) = range(12)
 
228
         self.wily_liberty, self.trusty_mitaka,
 
229
         self.xenial_mitaka) = range(14)
129
230
 
130
231
        releases = {
131
232
            ('precise', None): self.precise_essex,
137
238
            ('trusty', 'cloud:trusty-juno'): self.trusty_juno,
138
239
            ('trusty', 'cloud:trusty-kilo'): self.trusty_kilo,
139
240
            ('trusty', 'cloud:trusty-liberty'): self.trusty_liberty,
 
241
            ('trusty', 'cloud:trusty-mitaka'): self.trusty_mitaka,
140
242
            ('utopic', None): self.utopic_juno,
141
243
            ('vivid', None): self.vivid_kilo,
142
 
            ('wily', None): self.wily_liberty}
 
244
            ('wily', None): self.wily_liberty,
 
245
            ('xenial', None): self.xenial_mitaka}
143
246
        return releases[(self.series, self.openstack)]
144
247
 
145
248
    def _get_openstack_release_string(self):
156
259
            ('utopic', 'juno'),
157
260
            ('vivid', 'kilo'),
158
261
            ('wily', 'liberty'),
 
262
            ('xenial', 'mitaka'),
159
263
        ])
160
264
        if self.openstack:
161
265
            os_origin = self.openstack.split(':')[1]