~openstack-charmers-next/charms/vivid/hacluster/trunk

« back to all changes in this revision

Viewing changes to hooks/charmhelpers/core/hookenv.py

  • Committer: Liam Young
  • Date: 2016-03-30 09:07:46 UTC
  • mfrom: (63.1.4 trunk)
  • Revision ID: liam.young@canonical.com-20160330090746-rwwe91yjqi9j9ry3
[gnuoy, r=james-page] Add pause/resume actions

Show diffs side-by-side

added added

removed removed

Lines of Context:
34
34
import tempfile
35
35
from subprocess import CalledProcessError
36
36
 
37
 
try:
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,
43
 
    # so mock it out
44
 
    if str(e) == 'No module named cli':
45
 
        class cmdline(object):
46
 
            @classmethod
47
 
            def subcommand(cls, *args, **kwargs):
48
 
                def _wrap(func):
49
 
                    return func
50
 
                return _wrap
51
 
    else:
52
 
        raise
53
 
 
54
37
import six
55
38
if not six.PY3:
56
39
    from UserDict import UserDict
91
74
        res = func(*args, **kwargs)
92
75
        cache[key] = res
93
76
        return res
 
77
    wrapper._wrapped = func
94
78
    return wrapper
95
79
 
96
80
 
190
174
    return os.environ.get('JUJU_RELATION', None)
191
175
 
192
176
 
193
 
@cmdline.subcommand()
194
177
@cached
195
178
def relation_id(relation_name=None, service_or_unit=None):
196
179
    """The relation ID for the current or a specified relation"""
216
199
    return os.environ.get('JUJU_REMOTE_UNIT', None)
217
200
 
218
201
 
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]
223
205
 
224
206
 
225
 
@cmdline.subcommand()
226
207
@cached
227
208
def remote_service_name(relid=None):
228
209
    """The remote service name for a given relation-id (or the current relation)"""
510
491
 
511
492
 
512
493
@cached
 
494
def peer_relation_id():
 
495
    '''Get the peers relation id if a peers relation has been joined, else None.'''
 
496
    md = metadata()
 
497
    section = md.get('peers')
 
498
    if section:
 
499
        for key in section:
 
500
            relids = relation_ids(key)
 
501
            if relids:
 
502
                return relids[0]
 
503
    return None
 
504
 
 
505
 
 
506
@cached
513
507
def relation_to_interface(relation_name):
514
508
    """
515
509
    Given the name of a relation, return the interface that relation uses.
523
517
def relation_to_role_and_interface(relation_name):
524
518
    """
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``).
527
521
 
528
522
    :returns: A tuple containing ``(role, interface)``, or ``(None, None)``.
529
523
    """
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')
533
527
        if interface:
534
528
            return role, interface
540
534
    """
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``).
544
538
 
545
539
    :returns: A list of relation names.
546
540
    """
561
555
    :returns: A list of relation names.
562
556
    """
563
557
    results = []
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))
566
560
    return results
567
561
 
642
636
    return unit_get('private-address')
643
637
 
644
638
 
 
639
@cached
 
640
def storage_get(attribute=None, storage_id=None):
 
641
    """Get storage attributes"""
 
642
    _args = ['storage-get', '--format=json']
 
643
    if storage_id:
 
644
        _args.extend(('-s', storage_id))
 
645
    if attribute:
 
646
        _args.append(attribute)
 
647
    try:
 
648
        return json.loads(subprocess.check_output(_args).decode('UTF-8'))
 
649
    except ValueError:
 
650
        return None
 
651
 
 
652
 
 
653
@cached
 
654
def storage_list(storage_name=None):
 
655
    """List the storage IDs for the unit"""
 
656
    _args = ['storage-list', '--format=json']
 
657
    if storage_name:
 
658
        _args.append(storage_name)
 
659
    try:
 
660
        return json.loads(subprocess.check_output(_args).decode('UTF-8'))
 
661
    except ValueError:
 
662
        return None
 
663
    except OSError as e:
 
664
        import errno
 
665
        if e.errno == errno.ENOENT:
 
666
            # storage-list does not exist
 
667
            return []
 
668
        raise
 
669
 
 
670
 
645
671
class UnregisteredHookError(Exception):
646
672
    """Raised when an undefined hook is called"""
647
673
    pass
786
812
 
787
813
 
788
814
def status_get():
789
 
    """Retrieve the previously set juju workload state
790
 
 
791
 
    If the status-set command is not found then assume this is juju < 1.23 and
792
 
    return 'unknown'
 
815
    """Retrieve the previously set juju workload state and message
 
816
 
 
817
    If the status-get command is not found then assume this is juju < 1.23 and
 
818
    return 'unknown', ""
 
819
 
793
820
    """
794
 
    cmd = ['status-get']
 
821
    cmd = ['status-get', "--format=json", "--include-data"]
795
822
    try:
796
 
        raw_status = subprocess.check_output(cmd, universal_newlines=True)
797
 
        status = raw_status.rstrip()
798
 
        return status
 
823
        raw_status = subprocess.check_output(cmd)
799
824
    except OSError as e:
800
825
        if e.errno == errno.ENOENT:
801
 
            return 'unknown'
 
826
            return ('unknown', "")
802
827
        else:
803
828
            raise
 
829
    else:
 
830
        status = json.loads(raw_status.decode("UTF-8"))
 
831
        return (status["status"], status["message"])
804
832
 
805
833
 
806
834
def translate_exc(from_exc, to_exc):
807
835
    def inner_translate_exc1(f):
 
836
        @wraps(f)
808
837
        def inner_translate_exc2(*args, **kwargs):
809
838
            try:
810
839
                return f(*args, **kwargs)
849
878
    subprocess.check_call(cmd)
850
879
 
851
880
 
 
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]:
 
887
        cmd.append(x)
 
888
    subprocess.check_call(cmd)
 
889
 
 
890
 
 
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
 
896
    payload-register."""
 
897
    cmd = ['payload-unregister']
 
898
    for x in [klass, pid]:
 
899
        cmd.append(x)
 
900
    subprocess.check_call(cmd)
 
901
 
 
902
 
 
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]:
 
911
        cmd.append(x)
 
912
    subprocess.check_call(cmd)
 
913
 
 
914
 
 
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.
 
918
 
 
919
    <name> must match a name of defined resource in metadata.yaml
 
920
 
 
921
    returns either a path or False if resource not available
 
922
    """
 
923
    if not name:
 
924
        return False
 
925
 
 
926
    cmd = ['resource-get', name]
 
927
    try:
 
928
        return subprocess.check_output(cmd).decode('UTF-8')
 
929
    except subprocess.CalledProcessError:
 
930
        return False
 
931
 
 
932
 
852
933
@cached
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)
915
996
    del _atexit[:]
 
997
 
 
998
 
 
999
@translate_exc(from_exc=OSError, to_exc=NotImplementedError)
 
1000
def network_get_primary_address(binding):
 
1001
    '''
 
1002
    Retrieve the primary network address for a named binding
 
1003
 
 
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
 
1007
    '''
 
1008
    cmd = ['network-get', '--primary-address', binding]
 
1009
    return subprocess.check_output(cmd).strip()