-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add fallback for unit.get_public_address()
Due to bug [1], it seems that there is a issue with libjuju communicating with the Juju controller which causes a permanent model disconnect that libjuju doesn't resolve. Thus, for zaza, this patch wraps unit.get_public.address() with a wrapper that can choose, based on the environment variable `ZAZA_FEATURE_BUG472` to use a subprocess shell call to the `juju status` command to get the public address. This should always succeed. The feature environment variable `ZAZA_FEATURE_BUG472` was added so that the library can switch between the native libjuju function and the fallback wrapper to enable testing of the issue as libjuju continues to evolve. By default, the wrapper function is used, to enable zaza to interoperate with libjuju with Juju 2.9 on OpenStack providers. The implementation is slightly complicated because an async version of the wrapper `get_unit_public_address()` is needed as it is called from async code. [1]: juju/python-libjuju#615
- Loading branch information
1 parent
5457dc3
commit 0d7212a
Showing
10 changed files
with
444 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Scripts sub-directory | ||
|
||
The scripts in this directory are for regression/manual testing of the | ||
libjuju unit.get_public_address() function. The tox target 'third' can | ||
also be used for regression testing. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
#!/bin/bash | ||
|
||
_dir="$( cd "$(dirname "${BASH_SOURCE[0]}" )" && pwd)" | ||
runner="${_dir}/fetch1.py" | ||
|
||
# loop 10 times and fetch an instance address | ||
i=0 | ||
while [ $i -ne 10 ]; | ||
do | ||
printf "\n\n\n!!!!!!" | ||
printf "\n\n\nDoing number $i" | ||
printf "\n\n" | ||
$runner $i | ||
i=$(($i+1)) | ||
done |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
#!/usr/bin/env python3 | ||
|
||
from juju.model import Model | ||
import asyncio | ||
import sys | ||
|
||
# set to use something other than the current model | ||
MODEL=None | ||
|
||
|
||
async def get_units(): | ||
model = Model() | ||
if MODEL is None: | ||
await model.connect() | ||
else: | ||
await model.connect_model(MODEL) | ||
units = sorted(model.applications['ubuntu'].units, key=lambda u: u.name) | ||
await model.disconnect() | ||
return units | ||
|
||
|
||
async def get_address(unit): | ||
model = Model() | ||
await model.connect_model(MODEL) | ||
print("{} Address: .public_address {}".format(unit.name, unit.public_address)) | ||
while True: | ||
try: | ||
print("{} Address: get_public_address() {}".format(unit.name, await unit.get_public_address())) | ||
break | ||
except Exception as e: | ||
print("Exception was: %s", e) | ||
await asyncio.sleep(.25) | ||
await ensure_model_connected(model) | ||
print("{} Address: .public_address {}".format(unit.name, unit.public_address)) | ||
print("\n") | ||
await model.disconnect() | ||
|
||
|
||
def run_it(step): | ||
loop = asyncio.get_event_loop() | ||
task = loop.create_task(step) | ||
loop.run_until_complete(asyncio.wait([task], loop=loop)) | ||
result = task.result() | ||
return result | ||
|
||
|
||
async def get_unit(n): | ||
units = await get_units() | ||
print("units", units) | ||
await get_address(units[n]) | ||
|
||
|
||
def is_model_disconnected(model): | ||
"""Return True if the model is disconnected. | ||
:param model: the model to check | ||
:type model: :class:'juju.Model' | ||
:returns: True if disconnected | ||
:rtype: bool | ||
""" | ||
print("is_model_disconnected?: %s, %s", model.is_connected(), model.connection().is_open) | ||
return not (model.is_connected() and model.connection().is_open) | ||
|
||
|
||
async def ensure_model_connected(model): | ||
"""Ensure that the model is connected. | ||
If model is disconnected then reconnect it. | ||
:param model: the model to check | ||
:type model: :class:'juju.Model' | ||
""" | ||
if is_model_disconnected(model): | ||
model_name = model.info.name | ||
print( | ||
"model: %s has disconnected, forcing full disconnection " | ||
"and then reconnecting ...", model_name) | ||
try: | ||
await model.disconnect() | ||
except Exception: | ||
# We don't care if disconnect fails; we're much more | ||
# interested in re-connecting, and this is just to clean up | ||
# anything that might be left over (i.e. | ||
# model.is_connected() might be true, but | ||
# model.connection().is_open may be false | ||
pass | ||
print("Attempting to reconnect model %s", model_name) | ||
await model.connect_model(model_name) | ||
|
||
|
||
if __name__ == '__main__': | ||
unit_num = 0 | ||
if len(sys.argv) > 1: | ||
unit_num = int(sys.argv[1]) | ||
|
||
run_it(get_unit(unit_num)) | ||
asyncio.get_event_loop().close() | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
#!/usr/bin/env python3 | ||
|
||
from juju.model import Model | ||
import asyncio | ||
|
||
# set to use something other than the current model | ||
MODEL=None | ||
|
||
|
||
async def get_units(): | ||
model = Model() | ||
if MODEL is None: | ||
await model.connect() | ||
else: | ||
await model.connect_model(MODEL) | ||
units = sorted(model.applications['ubuntu'].units, key=lambda u: u.name) | ||
await model.disconnect() | ||
return units | ||
|
||
|
||
async def get_address(unit): | ||
model = Model() | ||
await model.connect_model(MODEL) | ||
print("{} Address: .public_address {}".format(unit.name, unit.public_address)) | ||
print("{} Address: get_public_address() {}".format(unit.name, await unit.get_public_address())) | ||
print("{} Address: .public_address {}".format(unit.name, unit.public_address)) | ||
print("\n") | ||
await model.disconnect() | ||
|
||
|
||
def run_it(step): | ||
loop = asyncio.get_event_loop() | ||
task = loop.create_task(step) | ||
loop.run_until_complete(asyncio.wait([task], loop=loop)) | ||
result = task.result() | ||
return result | ||
|
||
|
||
def get_all_units(): | ||
units = run_it(get_units()) | ||
print("units", units) | ||
for unit in units: | ||
run_it(get_address(unit)) | ||
|
||
|
||
get_all_units() | ||
asyncio.get_event_loop().close() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.