35
35
from subprocess import CalledProcessError
38
from charmhelpers.cli import cmdline
39
except ImportError as e:
40
# due to the anti-pattern of partially synching charmhelpers directly
41
# into charms, it's possible that charmhelpers.cli is not available;
42
# if that's the case, they don't really care about using the cli anyway,
44
if str(e) == 'No module named cli':
45
class cmdline(object):
47
def subcommand(cls, *args, **kwargs):
56
39
from UserDict import UserDict
216
199
return os.environ.get('JUJU_REMOTE_UNIT', None)
219
@cmdline.subcommand()
220
202
def service_name():
221
203
"""The name service group this unit belongs to"""
222
204
return local_unit().split('/')[0]
225
@cmdline.subcommand()
227
208
def remote_service_name(relid=None):
228
209
"""The remote service name for a given relation-id (or the current relation)"""
494
def peer_relation_id():
495
'''Get the peers relation id if a peers relation has been joined, else None.'''
497
section = md.get('peers')
500
relids = relation_ids(key)
513
507
def relation_to_interface(relation_name):
515
509
Given the name of a relation, return the interface that relation uses.
523
517
def relation_to_role_and_interface(relation_name):
525
519
Given the name of a relation, return the role and the name of the interface
526
that relation uses (where role is one of ``provides``, ``requires``, or ``peer``).
520
that relation uses (where role is one of ``provides``, ``requires``, or ``peers``).
528
522
:returns: A tuple containing ``(role, interface)``, or ``(None, None)``.
530
524
_metadata = metadata()
531
for role in ('provides', 'requires', 'peer'):
525
for role in ('provides', 'requires', 'peers'):
532
526
interface = _metadata.get(role, {}).get(relation_name, {}).get('interface')
534
528
return role, interface
541
535
Given a role and interface name, return a list of relation names for the
542
536
current charm that use that interface under that role (where role is one
543
of ``provides``, ``requires``, or ``peer``).
537
of ``provides``, ``requires``, or ``peers``).
545
539
:returns: A list of relation names.
561
555
:returns: A list of relation names.
564
for role in ('provides', 'requires', 'peer'):
558
for role in ('provides', 'requires', 'peers'):
565
559
results.extend(role_and_interface_to_relations(role, interface_name))
642
636
return unit_get('private-address')
640
def storage_get(attribute=None, storage_id=None):
641
"""Get storage attributes"""
642
_args = ['storage-get', '--format=json']
644
_args.extend(('-s', storage_id))
646
_args.append(attribute)
648
return json.loads(subprocess.check_output(_args).decode('UTF-8'))
654
def storage_list(storage_name=None):
655
"""List the storage IDs for the unit"""
656
_args = ['storage-list', '--format=json']
658
_args.append(storage_name)
660
return json.loads(subprocess.check_output(_args).decode('UTF-8'))
665
if e.errno == errno.ENOENT:
666
# storage-list does not exist
645
671
class UnregisteredHookError(Exception):
646
672
"""Raised when an undefined hook is called"""
788
814
def status_get():
789
"""Retrieve the previously set juju workload state
791
If the status-set command is not found then assume this is juju < 1.23 and
815
"""Retrieve the previously set juju workload state and message
817
If the status-get command is not found then assume this is juju < 1.23 and
821
cmd = ['status-get', "--format=json", "--include-data"]
796
raw_status = subprocess.check_output(cmd, universal_newlines=True)
797
status = raw_status.rstrip()
823
raw_status = subprocess.check_output(cmd)
799
824
except OSError as e:
800
825
if e.errno == errno.ENOENT:
826
return ('unknown', "")
830
status = json.loads(raw_status.decode("UTF-8"))
831
return (status["status"], status["message"])
806
834
def translate_exc(from_exc, to_exc):
807
835
def inner_translate_exc1(f):
808
837
def inner_translate_exc2(*args, **kwargs):
810
839
return f(*args, **kwargs)
849
878
subprocess.check_call(cmd)
881
@translate_exc(from_exc=OSError, to_exc=NotImplementedError)
882
def payload_register(ptype, klass, pid):
883
""" is used while a hook is running to let Juju know that a
884
payload has been started."""
885
cmd = ['payload-register']
886
for x in [ptype, klass, pid]:
888
subprocess.check_call(cmd)
891
@translate_exc(from_exc=OSError, to_exc=NotImplementedError)
892
def payload_unregister(klass, pid):
893
""" is used while a hook is running to let Juju know
894
that a payload has been manually stopped. The <class> and <id> provided
895
must match a payload that has been previously registered with juju using
897
cmd = ['payload-unregister']
898
for x in [klass, pid]:
900
subprocess.check_call(cmd)
903
@translate_exc(from_exc=OSError, to_exc=NotImplementedError)
904
def payload_status_set(klass, pid, status):
905
"""is used to update the current status of a registered payload.
906
The <class> and <id> provided must match a payload that has been previously
907
registered with juju using payload-register. The <status> must be one of the
908
follow: starting, started, stopping, stopped"""
909
cmd = ['payload-status-set']
910
for x in [klass, pid, status]:
912
subprocess.check_call(cmd)
915
@translate_exc(from_exc=OSError, to_exc=NotImplementedError)
916
def resource_get(name):
917
"""used to fetch the resource path of the given name.
919
<name> must match a name of defined resource in metadata.yaml
921
returns either a path or False if resource not available
926
cmd = ['resource-get', name]
928
return subprocess.check_output(cmd).decode('UTF-8')
929
except subprocess.CalledProcessError:
853
934
def juju_version():
854
935
"""Full version string (eg. '1.23.3.1-trusty-amd64')"""
913
994
for callback, args, kwargs in reversed(_atexit):
914
995
callback(*args, **kwargs)
999
@translate_exc(from_exc=OSError, to_exc=NotImplementedError)
1000
def network_get_primary_address(binding):
1002
Retrieve the primary network address for a named binding
1004
:param binding: string. The name of a relation of extra-binding
1005
:return: string. The primary IP address for the named binding
1006
:raise: NotImplementedError if run on Juju < 2.0
1008
cmd = ['network-get', '--primary-address', binding]
1009
return subprocess.check_output(cmd).strip()