/usr/lib/python2.7/dist-packages/quickstart/watchers.py is in juju-quickstart 1.3.1-0ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | # This file is part of the Juju Quickstart Plugin, which lets users set up a
# Juju environment in very few steps (https://launchpad.net/juju-quickstart).
# Copyright (C) 2014 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License version 3, as published by
# the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Juju Quickstart utilities for watching Juju environments."""
from __future__ import (
print_function,
unicode_literals,
)
IPV6_ADDRESS = 'ipv6'
NETWORK_PUBLIC = 'public'
NETWORK_UNKNOWN = ''
def retrieve_public_adddress(addresses):
"""Parse the given addresses and return a public address if available.
The addresses argument is a list of address dictionaries.
Cloud addresses look like the following:
[
{'NetworkName': '',
'NetworkScope': 'public',
'Type': 'hostname',
'Value': 'eu-west-1.compute.example.com'},
{'NetworkName': '',
'NetworkScope': 'local-cloud',
'Type': 'hostname',
'Value': 'eu-west-1.example.internal'},
{'NetworkName': '',
'NetworkScope': 'public',
'Type': 'ipv4',
'Value': '444.222.444.222'},
{'NetworkName': '',
'NetworkScope': 'local-cloud',
'Type': 'ipv4',
'Value': '10.42.47.10'},
{'NetworkName': '',
'NetworkScope': '',
'Type': 'ipv6',
'Value': 'fe80::92b8:d0ff:fe94:8f8c'},
]
When using the local provider, LXC addresses are like the following:
[
{'NetworkName': '',
'NetworkScope': '',
'Type': 'ipv4',
'Value': '10.0.3.42'},
{'NetworkName': '',
'NetworkScope': '',
'Type': 'ipv6',
'Value': 'fe80::216:3eff:fefd:787e'},
]
If the addresses list is empty, or if no public/reachable addresses can be
found, this function returns None.
"""
# This implementation reflects how the public address is retrieved in Juju:
# see juju-core/instance/address.go:SelectPublicAddress.
public_address = None
for address in addresses:
value = address['Value']
# Exclude empty values and ipv6 addresses.
if value and (address['Type'] != IPV6_ADDRESS):
scope = address['NetworkScope']
# If the scope is public then we have found the address.
if scope == NETWORK_PUBLIC:
return value
# If the scope is unknown then store the value. This way the last
# address with unknown scope will be returned, and we are able to
# return the right LXC address.
if scope == NETWORK_UNKNOWN:
public_address = value
return public_address
def parse_machine_change(action, data, current_status, address):
"""Parse the given machine change.
The change is represented by the given action/data pair.
Also receive the last known machine status and address, which can be empty
strings if those pieces of information are unknown.
Output a human readable message each time a relevant change is found.
Return the machine status and the address.
Raise a ValueError if the machine is removed or in an error state.
"""
machine_id = data['Id']
status = data['Status']
# Exit with an error if the machine is removed.
if action == 'remove':
msg = 'machine {} unexpectedly removed'.format(machine_id)
raise ValueError(msg.encode('utf-8'))
if 'error' in status:
msg = 'machine {} is in an error state: {}: {}'.format(
machine_id, status, data['StatusInfo'])
raise ValueError(msg.encode('utf-8'))
# Notify when the machine becomes reachable. Starting from juju-core 1.18,
# the mega-watcher for machines includes addresses for each machine. This
# info is the preferred source where to look to retrieve the public address
# of units hosted by a specific machine.
if not address:
addresses = data.get('Addresses', [])
public_address = retrieve_public_adddress(addresses)
if public_address is not None:
address = public_address
print('unit placed on {}'.format(address))
# Notify status changes.
if status != current_status:
if status == 'pending':
print('machine {} provisioning is pending'.format(
machine_id))
elif status == 'started':
print('machine {} is started'.format(machine_id))
return status, address
def parse_unit_change(action, data, current_status, address):
"""Parse the given unit change.
The change is represented by the given action/data pair.
Also receive the last known unit status and address, which can be empty
strings if those pieces of information are unknown.
Output a human readable message each time a relevant change is found.
Return the unit status, address and machine identifier.
Raise a ValueError if the service unit is removed or in an error state.
"""
unit_name = data['Name']
# Exit with an error if the unit is removed.
if action == 'remove':
msg = '{} unexpectedly removed'.format(unit_name)
raise ValueError(msg.encode('utf-8'))
# Exit with an error if the unit is in an error state.
status = data['Status']
if 'error' in status:
msg = '{} is in an error state: {}: {}'.format(
unit_name, status, data['StatusInfo'])
raise ValueError(msg.encode('utf-8'))
# Notify when the unit becomes reachable. Up to juju-core 1.18, the
# mega-watcher for units includes the public address for each unit. This
# info is likely to be deprecated in favor of addresses as included in the
# mega-watcher for machines, but we still try to retrieve the address here
# for backward compatibility.
if not address:
address = data.get('PublicAddress', '')
if address:
print('{} placed on {}'.format(unit_name, address))
# Notify status changes.
if status != current_status:
if status == 'pending':
print('{} deployment is pending'.format(unit_name))
elif status == 'installed':
print('{} is installed'.format(unit_name))
elif status == 'started':
print('{} is ready on machine {}'.format(
unit_name, data['MachineId']))
return status, address, data.get('MachineId', '')
def unit_machine_changes(changeset):
"""Parse the changeset and return the units and machines related changes.
Changes to units and machines are grouped into two lists, e.g.:
unit_changes, machine_changes = unit_machine_changes(changeset)
Each list includes (action, data) tuples, in which:
- action is he change type (e.g. "change", "remove");
- data is the actual information about the changed entity (as a dict).
This function is intended to be used as a processor callable for the
watch_changes method of quickstart.juju.Environment.
"""
unit_changes = []
machine_changes = []
for entity, action, data in changeset:
if entity == 'unit':
unit_changes.append((action, data))
elif entity == 'machine':
machine_changes.append((action, data))
return unit_changes, machine_changes
|