~ibmcharmers/trusty/ibm-xcat

Owner: mbruzek
Status: Needs Fixing
Vote: -1 (+2 needed for approval)

CPP?: No
OIL?: No

Migrating the ibm-xcat from the old review queue please refer to the bug: https://bugs.launchpad.net/charms/+bug/1441622 if you need any additional review information.


Tests

Substrate Status Results Last Updated
gce FAIL http://juju-ci.vapour.ws/job/charm-bundle-test-gce/157/console 7 months ago
aws FAIL Test Results 7 months ago
lxc FAIL Test Results 7 months ago

Voted: -1
kos.tsakalozos wrote 4 months ago
Thank you for your work on this charm. Here are a couple of points that may need your attention:

- There seems to be dead code eg configure_ibm_xcat

- There seem to be assumptions on the networking eg "updateresolvdb = ["resolvconf", "-a", "eth0.inet"]"

- No ports are exposed. Based on the documentation this is not required however
there is an apache server at port 80 that the tests try to ping it, so testing should
fail on any provider other than lxd.

- Following the README I got into thi error:
ubuntu@juju-7bea65-0:~$ tabdump site
Unable to open socket connection to xcatd daemon on localhost:3001.
Verify that the xcatd daemon is running and that your SSL setup is correct.
Connection failure: SSL connect attempt failed with unknown error error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed at /opt/xcat/lib/perl/xCAT/Client.pm line 265.

This is when deployin on lxd.

- There is little documentation on the config variables

- Running a "make all" on the charm shown failing tests

Thank you again for your effort.

Add Comment

Login to comment/vote on this review.


Policy Checklist

Description Unreviewed Pass Fail

General

Must verify that any software installed or utilized is verified as coming from the intended source. kos.tsakalozos
  • Any software installed from the Ubuntu or CentOS default archives satisfies this due to the apt and yum sources including cryptographic signing information.
  • Third party repositories must be listed as a configuration option that can be overridden by the user and not hard coded in the charm itself.
  • Launchpad PPAs are acceptable as the add-apt-repository command retrieves the keys securely.
  • Other third party repositories are acceptable if the signing key is embedded in the charm.
Must provide a means to protect users from known security vulnerabilities in a way consistent with best practices as defined by either operating system policies or upstream documentation. kos.tsakalozos
Basically, this means there must be instructions on how to apply updates if you use software not from distribution channels.
Must have hooks that are idempotent. kos.tsakalozos
Should be built using charm layers. kos.tsakalozos
Should use Juju Resources to deliver required payloads.

Testing and Quality

charm proof must pass without errors or warnings. kos.tsakalozos
Must include passing unit, functional, or integration tests. kos.tsakalozos
Tests must exercise all relations. kos.tsakalozos
Tests must exercise config. kos.tsakalozos
set-config, unset-config, and re-set must be tested as a minimum
Must not use anything infrastructure-provider specific (i.e. querying EC2 metadata service). kos.tsakalozos
Must be self contained unless the charm is a proxy for an existing cloud service, e.g. ec2-elb charm.
Must not use symlinks. kos.tsakalozos
Bundles must only use promulgated charms, they cannot reference charms in personal namespaces. kos.tsakalozos
Must call Juju hook tools (relation-*, unit-*, config-*, etc) without a hard coded path. kos.tsakalozos
Should include a tests.yaml for all integration tests.

Metadata

Must include a full description of what the software does. kos.tsakalozos
Must include a maintainer email address for a team or individual who will be responsive to contact. kos.tsakalozos
Must include a license. Call the file 'copyright' and make sure all files' licenses are specified clearly. kos.tsakalozos
Must be under a Free license. kos.tsakalozos
Must have a well documented and valid README.md. kos.tsakalozos
Must describe the service. kos.tsakalozos
Must describe how it interacts with other services, if applicable. kos.tsakalozos
Must document the interfaces. kos.tsakalozos
Must show how to deploy the charm. kos.tsakalozos
Must define external dependencies, if applicable. kos.tsakalozos
Should link to a recommend production usage bundle and recommended configuration if this differs from the default.
Should reference and link to upstream documentation and best practices. kos.tsakalozos

Security

Must not run any network services using default passwords. kos.tsakalozos
Must verify and validate any external payload kos.tsakalozos
  • Known and understood packaging systems that verify packages like apt, pip, and yum are ok.
  • wget | sh style is not ok.
Should make use of whatever Mandatory Access Control system is provided by the distribution. kos.tsakalozos
Should avoid running services as root. kos.tsakalozos

Source Diff

Inline diff comments 0

No comments yet.

Back to file index

Makefile

 1
--- 
 2
+++ Makefile
 3
@@ -0,0 +1,24 @@
 4
+#!/usr/bin/make
 5
+
 6
+all: lint unit_test
 7
+
 8
+
 9
+.PHONY: clean
10
+clean:
11
+	@rm -rf .tox
12
+
13
+.PHONY: apt_prereqs
14
+apt_prereqs:
15
+	@# Need tox, but don't install the apt version unless we have to (don't want to conflict with pip)
16
+	@which tox >/dev/null || (sudo apt-get install -y python-pip && sudo pip install tox)
17
+
18
+.PHONY: lint
19
+lint: apt_prereqs
20
+	@tox --notest
21
+	@PATH=.tox/py34/bin:.tox/py35/bin flake8 $(wildcard hooks reactive lib unit_tests tests)
22
+	@charm proof
23
+
24
+.PHONY: unit_test
25
+unit_test: apt_prereqs
26
+	@echo Starting tests...
27
+	tox
Back to file index

README.md

 1
--- 
 2
+++ README.md
 3
@@ -0,0 +1,93 @@
 4
+Charm for IBM xCAT v2.1.1
 5
+
 6
+Overview
 7
+--------
 8
+
 9
+xCAT is Extreme Cluster/Cloud Administration Toolkit, xCAT offers complete management for HPC clusters, RenderFarms, Grids, WebFarms, Online Gaming Infrastructure, Clouds, Datacenters, and whatever tomorrow's buzzwords may be. It is agile, extensible, and based on years of system administration best practices and experience. It enables you to:
10
+
11
+- Provision Operating Systems on physical or virtual machines: RHEL, CentOS, Fedora, SLES, Ubuntu, AIX, Windows, VMWare, KVM, PowerVM, PowerKVM, zVM.
12
+- Provision using scripted install, stateless, statellite, iSCSI, or cloning
13
+- Remotely manage systems: lights-out management, remote console, and distributed shell support
14
+- Quickly configure and control management node services: DNS, HTTP, DHCP, TFTP, NFS
15
+
16
+Ensure to run this charm on release that has xCAT support. The minimum release expected is trusty.
17
+
18
+For More information on xCAT visit [here][xcat-info]
19
+
20
+
21
+Usage
22
+-----
23
+
24
+This charm installs the basic IBM xCAT application on either Ubuntu x86 or Power machines which can be used as a KVM machine to manage VM's.
25
+
26
+`Note`: Managing docker container feature of xCAT is not avilable, will be available in the upcoming releases and hence it is not available in this charm also.
27
+
28
+
29
+Deploy
30
+------
31
+
32
+The user can deploy IBM xCAT as shown below:
33
+
34
+	juju deploy ibm-xcat
35
+
36
+The user should browse the Quick Start guide for customization steps in the [link][installation-steps] 
37
+
38
+Post Installation of IBM-xCAT user can create stateless node by running the below action command. Here the term stateless or diskless node is a node where the operating system is installed into memory. 
39
+
40
+The state of the machine is held in memory (RAM) and will not persist on subsequent reboots of the node. The state will return to what has been set in the master image. 
41
+
42
+	juju run-action ibm-xcat/0 create-vm-stateless \
43
+	kvm-host="<IP Address of the KVM host where the VM's will be created>" \
44
+	kvm-root-password="<root password of kvm-host machine>" \
45
+	kvm-bridge="<Name of the bridge interface of kvm>" \
46
+	vm-name="<Name of the VM to be created>" \
47
+	vm-ip="<IP of the VM. It should be one of the unused IP from kvm-bridge network>" \
48
+	vm-cpu="<Number of CPU's for the VM>" \
49
+	vm-mem="<2048>" \
50
+	vm-image="<ISO of the osimage from which stateless compute node will be started the VM>" \
51
+	vm-pkgdir="Operating system's iso image repository for creating stateless image"
52
+
53
+In the above command kvm-host, kvm-root-password, and vm-ip has to be passed while running the action command. And rest of the parameter values could make use of default values if it is not set.
54
+
55
+
56
+Verify xCAT installation
57
+------------------------
58
+
59
+Post deployment, Access to the container is achieved as in the example below. 
60
+
61
+	juju ssh ibm-xcat/0
62
+
63
+Login to the container as a root user then run the below commands to verify xCAT installation.
64
+
65
+- Add xCAT commands into your path 
66
+
67
+		source /etc/profile.d/xcat.sh
68
+
69
+- To display the installed version of xcat
70
+
71
+		lsxcatd -a
72
+
73
+- To view the site table contents
74
+
75
+		tabdump site
76
+
77
+IBM xCAT Information
78
+----------------------------
79
+
80
+(1) General Information
81
+
82
+General Information on IBM xCAT available [here] [xcat-info]
83
+
84
+(2) Contact Information
85
+
86
+For issues with this charm, please contact IBM Juju Support Team <jujusupp@us.ibm.com>
87
+
88
+(3) Known Limitations
89
+
90
+This charm makes use of Juju features that are only available in version `2.0` or greater.
91
+
92
+<!-- Links -->
93
+
94
+[xcat-info]: http://xcat-docs.readthedocs.io/en/stable/index.html#
95
+
96
+[installation-steps]: http://sourceforge.net/p/xcat/wiki/Ubuntu_Quick_Start/
Back to file index

actions.yaml

 1
--- 
 2
+++ actions.yaml
 3
@@ -0,0 +1,42 @@
 4
+"create-vm-stateless":
 5
+  "description": "Creates a xCAT VM with given parameters"
 6
+  "params":
 7
+    "kvm-host":
 8
+      "description": "IP Address of the KVM host where the VM's will be created"
 9
+      "type": "string"
10
+      "default": ""
11
+    "kvm-root-password":
12
+      "description": "root password of kvm-host machine"
13
+      "type": "string"
14
+      "default": ""
15
+    "kvm-bridge":
16
+      "description": "Name of the bridge interface of kvm"
17
+      "type": "string"
18
+      "default": "virbr0"
19
+    "vm-name":
20
+      "description": "Name of the VM to be created"
21
+      "type": "string"
22
+      "default": "vm1"
23
+    "vm-ip":
24
+      "description": "IP of the VM. It should be one of the unused IP from kvm-bridge\
25
+        \ network"
26
+      "type": "string"
27
+      "default": ""
28
+    "vm-cpu":
29
+      "description": "Number of CPU's for the VM"
30
+      "type": "string"
31
+      "default": "2"
32
+    "vm-mem":
33
+      "description": "Amount of memory for the VM in MB"
34
+      "type": "string"
35
+      "default": "2048"
36
+    "vm-image":
37
+      "description": "ISO of the osimage from which stateless compute node will be\
38
+        \ started the VM"
39
+      "type": "string"
40
+      "default": "ubuntu-14.04.4-server-amd64.iso"
41
+    "vm-pkgdir":
42
+      "description": "pkgdir attribute for creating stateless image"
43
+      "type": "string"
44
+      "default": "http://archive.ubuntu.com/ubuntu trusty main,http://archive.ubuntu.com/ubuntu\
45
+        \ trusty-updates main"
Back to file index

actions/create-vm-stateless

 1
--- 
 2
+++ actions/create-vm-stateless
 3
@@ -0,0 +1,75 @@
 4
+#!/usr/bin/python3
 5
+from charmhelpers.core import hookenv
 6
+import subprocess
 7
+import string
 8
+import random
 9
+import os
10
+
11
+
12
+def random_string(size=8, chars=string.ascii_uppercase
13
+                  + string.ascii_lowercase + string.digits):
14
+    return ''.join(random.SystemRandom().choice(chars) for _ in range(size))
15
+
16
+
17
+def copy_ssh_key(key, host, password):
18
+    import paramiko
19
+    ssh = paramiko.SSHClient()
20
+    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
21
+    ssh.connect(host, username='root', password=password)
22
+    stdin, stdout, stderr = ssh.exec_command(
23
+        "cat ~/.ssh/authorized_keys | grep \"" + key + "\";echo $?\n")
24
+
25
+    output = stdout.readlines()[-1].rstrip('\n')
26
+    if output == "0":
27
+        return True
28
+
29
+    stdin, stdout, stderr = ssh.exec_command("echo " + key +
30
+                                             " >> ~/.ssh/authorized_keys;"
31
+                                             "echo $?\n")
32
+    output = stdout.readlines()[0].rstrip('\n')
33
+    if output == "0":
34
+        return True
35
+
36
+    return False
37
+
38
+
39
+def create_vm():
40
+
41
+    kvm_host = hookenv.action_get('kvm-host')
42
+    kvm_root_password = hookenv.action_get('kvm-root-password')
43
+    with open(os.path.expanduser('~') + "/.ssh/id_rsa.pub") as sshkeyfile:
44
+        sshkey = sshkeyfile.readlines()
45
+
46
+    if not copy_ssh_key(sshkey[0].rstrip('\n'), kvm_host, kvm_root_password):
47
+        hookenv.action_fail("Error while copying ssh key")
48
+        return
49
+
50
+    try:
51
+        vm_name = hookenv.action_get('vm-name')
52
+        vm_ip = hookenv.action_get('vm-ip')
53
+        vmrootpassword = random_string()
54
+        vmvncpassword = random_string()
55
+        vm_cpu = hookenv.action_get('vm-cpu')
56
+        vm_mem = hookenv.action_get('vm-mem')
57
+        kvm_bridge = hookenv.action_get('kvm-bridge')
58
+        vm_image = hookenv.action_get('vm-image')
59
+        vm_pkgdir = hookenv.action_get('vm-pkgdir')
60
+        if not os.path.isfile('files/archives/' + vm_image):
61
+            hookenv.action_fail(vm_image + " image does not exist."
62
+                                "please download using curl_url option")
63
+            return
64
+
65
+        args = ['scripts/create_xcat_stateless_vm.sh', vm_name, vm_ip,
66
+                vmrootpassword, vmvncpassword, vm_cpu, kvm_host,
67
+                vm_mem, kvm_bridge, 'files/archives/' + vm_image,
68
+                "\"" + vm_pkgdir + "\""]
69
+
70
+        subprocess.check_call(args)
71
+    except subprocess.CalledProcessError as e:
72
+        print(e.output)
73
+        hookenv.action_fail("Error while creating VM")
74
+
75
+    hookenv.action_set({'vm-root-password': vmrootpassword,
76
+                       'vm-vnc-password': vmvncpassword})
77
+
78
+create_vm()
Back to file index

bin/layer_option

 1
--- 
 2
+++ bin/layer_option
 3
@@ -0,0 +1,24 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+import sys
 7
+sys.path.append('lib')
 8
+
 9
+import argparse
10
+from charms.layer import options
11
+
12
+
13
+parser = argparse.ArgumentParser(description='Access layer options.')
14
+parser.add_argument('section',
15
+                    help='the section, or layer, the option is from')
16
+parser.add_argument('option',
17
+                    help='the option to access')
18
+
19
+args = parser.parse_args()
20
+value = options(args.section).get(args.option, '')
21
+if isinstance(value, bool):
22
+    sys.exit(0 if value else 1)
23
+elif isinstance(value, list):
24
+    for val in value:
25
+        print(val)
26
+else:
27
+    print(value)
Back to file index

config.yaml

  1
--- 
  2
+++ config.yaml
  3
@@ -0,0 +1,122 @@
  4
+options:
  5
+  extra_packages:
  6
+    description: 'Space separated list of extra deb packages to install.
  7
+
  8
+      '
  9
+    type: string
 10
+    default: ''
 11
+  package_status:
 12
+    default: install
 13
+    type: string
 14
+    description: 'The status of service-affecting packages will be set to this value
 15
+      in the dpkg database. Valid values are "install" and "hold".
 16
+
 17
+      '
 18
+  install_sources:
 19
+    description: |
 20
+      List of extra apt sources, per charm-helpers standard
 21
+      format (a yaml list of strings encoded as a string). Each source
 22
+      may be either a line that can be added directly to
 23
+      sources.list(5), or in the form ppa:<user>/<ppa-name> for adding
 24
+      Personal Package Archives, or a distribution component to enable.
 25
+    type: string
 26
+    default: |
 27
+      - deb http://xcat.org/files/xcat/repos/apt/2.11/xcat-core trusty main
 28
+      - deb https://xcat.org/files/xcat/repos/apt/xcat-dep trusty main
 29
+  install_keys:
 30
+    description: |
 31
+      List of signing keys for install_sources package sources, per
 32
+      charmhelpers standard format (a yaml list of strings encoded as
 33
+      a string). The keys should be the full ASCII armoured GPG public
 34
+      keys. While GPG key ids are also supported and looked up on a
 35
+      keyserver, operators should be aware that this mechanism is
 36
+      insecure. null can be used if a standard package signing key is
 37
+      used that will already be installed on the machine, and for PPA
 38
+      sources where the package signing key is securely retrieved from
 39
+      Launchpad.
 40
+    type: string
 41
+    default: "- |\n    -----BEGIN PGP PUBLIC KEY BLOCK-----\n    Version: GnuPG v2.0.9\
 42
+      \ (GNU/Linux)\n    \n    mQGiBFSszWwRBADNkKKBDJT0HuAsga06rauS35lw1XMYdkh3eYWMzafs/r0hQgqx\n\
 43
+      \    +6nNWWiRMCFXKoNBB7TrxnH2f1eV869HsDYBO6IXkhrFurFYfgZL3MEocUI/YQ8R\n    0l/sexExVGUQNdPMHmadguSXgotUwcv1z9tXW/w4xYk4Yy6Cwu26iBlq2wCgj0kT\n\
 44
+      \    UZmu7JsD2Qii7sL0UYoGDOUD/0XEYRxDVDm9kWJbnIYlIrs6HJnzp6uFuhhA2yPF\n    cmk1aBb1hcZMlbA6HxsXDHBZiC6pGSomVsPxCJyuWYHhCOAqkKZdPGsa+HLjfIqN\n\
 45
+      \    uEh/CoK3TSAmPYa+aJA/ofcO2CgUwnAOVNb7r3tSuygV7nIi9vpwFEEmxT+E2ShY\n    PzumBACnuZpdI1bczp6kmQI+Dgn2e8o/bjrJ8ToZkA7ZTkK35bxtxNhvhgUpo5AQ\n\
 46
+      \    hsKvGb0gQ1CZOJFRev9iP3s/Tua6xPt0nAetJZbxK1E3hNcg+FuSomRHjerMg/yY\n    uoGHIwVZgFjdIJ7D7Y8a4xV3ujUdPWP9sw4VeTI1OUgDQtply7QjeENBVCBTZWN1\n\
 47
+      \    cml0eSBLZXkgPHhjYXRAY24uaWJtLmNvbT6IYAQTEQIAIAUCVKzNbAIbAwYLCQgH\n    AwIEFQIIAwQWAgMBAh4BAheAAAoJEGCj6azGVlvJQ2sAn2eEoSFQlwnBabq+fHay\n\
 48
+      \    nyWaDq4DAJ9l1Nk2Kl6Ui2t2rRPduiKeuonIgLkCDQRUrM1sEAgAkj5RDSFRa0Om\n    RrxXtvZHbywbbJ7+I86wdRK1KIq67ZwtuOv0V8r0KnxhQVqRmhP4nv7PbesPyF6e\n\
 49
+      \    5kfZ9A7jVvasyRg5JDyqCGGjRAirZXzNORUGzk0yC5GaoJNZGx3DuHHehlucZlYr\n    UcsOr42mcOwZ+oAxstRdBtWPw4VLmycKWkj1HvndbEtYdm/xdGybPRiZuaO6Ip1h\n\
 50
+      \    hOo+MTwCbxDBtHL5iAdeBl74LhCx1bCDxZYijIZjil24oT1e4bZMsMDwwIsdRK7p\n    6Qsd1h1Bjb5ldlMXI6fKTbHm6MlGR+cbA/2xzRf1Gd8xnlcL60x5qDVEH+wKH5AV\n\
 51
+      \    LA8EPaOI9wADBQf8CRSthdwCmf+Uo7GHlfsCNh4N/72b7/eP09N4w4FdqpmW9GWM\n    xNmOB4n0gsH/xQIBjeavpugHQDna+37U0Ez0FbMG/kU/5aS2lU6+oYvl+q//hRpA\n\
 52
+      \    qgr8jVwVqhzu2qaZvxuTSv22jZhCHZyIKUzTwjlHTR78qyvXvzyUpBt996fPkDNj\n    A+rS/Y19pmZHh+8w6TwIsuqC8K7wUasDzO5T3TGIfT/lqqT1nlT3on6SSK9YXoXE\n\
 53
+      \    GitIGQwZXhDdHcoHcI9RNxdfDIcHoCOjUdZCnKD03hxLF4i3lMh/6J7rBDPMreqh\n    WsVltt8ZiFrN3VXlnBC5ivNY3h3DKXuuLdLDWIhJBBgRAgAJBQJUrM1sAhsMAAoJ\n\
 54
+      \    EGCj6azGVlvJYS8An2DLTHZVjuZ8TicdevZSZTuA3qj0AJ9GkvZDZMpX1UiJGlZ+\n    9ywfCgbjcQ==\n\
 55
+      \    =UMyq\n    -----END PGP PUBLIC KEY BLOCK-----\n    -----BEGIN PGP PUBLIC\
 56
+      \ KEY BLOCK-----\n    Version: GnuPG v1.4.5 (GNU/Linux)\n\n    mQGiBEhk3UsRBADVYqwh28ZnuktVsHK3YFPjdK+NEmEfitsbDsTFAX02hLAJPjM6\n\
 57
+      \    V+o0hJKXoqHW4w9ZfYds+KiBSAxsbnBsObwVjw8VpztdozLBCh+Ld48PyeZ6bLez\n    0iac9Wj9s+0alug+EUZggZwskDKqngsZ5iX6VGrlXn5DuRyUv9fOEgGgOwCgzuPc\n\
 58
+      \    RF7IyyTS1R+biMPtb+8BxAkD/2pI+wYULGVb6CQgNZ10V/BJ3RUh7UiYtlMgfzJ1\n    kedapbXuZ885uAMXB7UPSJoaKVbP+nDOfNa1tANwNKuDgbh+mxvlKna9hbjmtYhQ\n\
 59
+      \    MgOvUK3vFL+4w3QgyH1ec+Wof75+s+oTBFK3bC9rXYi81f3x22r7r5vpPkEKjtUj\n    QFuVA/40vmDI0N3pFcG5BquZ2+S0EXTBsTJywkAI69NzuSTJLe03m90OjEi40Dil\n\
 60
+      \    hefWHP3oYJm0RErCtUCJMDSPm1E5N6IkKZcQLVhlRFq3Wh9fSdnAPc0ffWOiuZFS\n    hTjKcQLhGXedMlTPxdSjlC4fljzbuUlG4Ak9qhbpOBF4iEShT7QkSmFycm9kIEpv\n\
 61
+      \    aG5zb24gPGpiam9obnNvQHVzLmlibS5jb20+iGAEExECACAFAkhk3UsCGwMGCwkI\n    BwMCBBUCCAMEFgIDAQIeAQIXgAAKCRAg5HWo2nNsaKK1AKDHVv0Oh6QrwM4RU8Bd\n\
 62
+      \    J2E4cBIwswCgxUSdizrkbgsQ5lG5tHO9aTbD8W25Ag0ESGTdVxAIANxDZDKjJZUw\n    jzvHtmvxTfNxhHQhpq2K48irIBVtyNVrlFYYcuY3fwX5qUtTJqVGKE50zgsxZrqt\n\
 63
+      \    o2RML2cPmyITHyB7cnYe337dT3aD+/KAnAH7DuhUMEMKtmY7WRsAUMCkDJ4Y3gMC\n    BAP/HerQe3peEz8XIi7+FHawCFj4OdtFvyavGwp055njH71aBoShrEBDf/S9Xx7i\n\
 64
+      \    vT06GEX6a3HkeaAUAH0cM7rgMxzHkCGAN6oliSTrnpTsVexJ68Xi99cz+hEucYil\n    +5bRY5zU08DI1P4z3tdSCJSj7YNgXbpYA1KWouDBDiYMDmOm2hUTWZ2mMNEe5pyv\n\
 65
+      \    HronDf370x8AAwcH/3zR29vhi1AHCzkpx8T3FIagjk6eHMmuMht0Z7rVix+r7iTP\n    XsFEZAxMh2gMs2rXJBLZATjjiqnZ21leU5/8AftIMyqWQM0Ev64t0HrftD7Enj2L\n\
 66
+      \    ZKTvpM//WDhXYqDeBcu/B7nQQS5sDi6bsSWc5at49Fj2VgY8Ly/V06IGcVHYiENI\n    HgBGjGtoLaiLw61J1xNsH/LeaTWwJSalmnARpieOj+lQJi7w6mryLpn2ny/ds38B\n\
 67
+      \    pgnAEvIW6vHK7TzzfuEZBAk3OMGgxyxbIRp4RXTOLu0eFC+XrUH/ubTKoyW40yh/\n    vjjFAUoMxFNU87pQ7EoT1VRvRgmtSXbO/BVOQRKISQQYEQIACQUCSGTdVwIbDAAK\n\
 68
+      \    CRAg5HWo2nNsaBhHAKCoBltVVPyoaOvEqClPNugwg4K+GQCfab/e9e0S133BxaQE\n    PkKeSdN44us=\n\
 69
+      \    =IR/6\n    -----END PGP PUBLIC KEY BLOCK----- \n\n- |\n    -----BEGIN PGP\
 70
+      \ PUBLIC KEY BLOCK-----\n    Version: GnuPG v2.0.9 (GNU/Linux)\n\n    mQGiBFSszWwRBADNkKKBDJT0HuAsga06rauS35lw1XMYdkh3eYWMzafs/r0hQgqx\n\
 71
+      \    +6nNWWiRMCFXKoNBB7TrxnH2f1eV869HsDYBO6IXkhrFurFYfgZL3MEocUI/YQ8R\n    0l/sexExVGUQNdPMHmadguSXgotUwcv1z9tXW/w4xYk4Yy6Cwu26iBlq2wCgj0kT\n\
 72
+      \    UZmu7JsD2Qii7sL0UYoGDOUD/0XEYRxDVDm9kWJbnIYlIrs6HJnzp6uFuhhA2yPF\n    cmk1aBb1hcZMlbA6HxsXDHBZiC6pGSomVsPxCJyuWYHhCOAqkKZdPGsa+HLjfIqN\n\
 73
+      \    uEh/CoK3TSAmPYa+aJA/ofcO2CgUwnAOVNb7r3tSuygV7nIi9vpwFEEmxT+E2ShY\n    PzumBACnuZpdI1bczp6kmQI+Dgn2e8o/bjrJ8ToZkA7ZTkK35bxtxNhvhgUpo5AQ\n\
 74
+      \    hsKvGb0gQ1CZOJFRev9iP3s/Tua6xPt0nAetJZbxK1E3hNcg+FuSomRHjerMg/yY\n    uoGHIwVZgFjdIJ7D7Y8a4xV3ujUdPWP9sw4VeTI1OUgDQtply7QjeENBVCBTZWN1\n\
 75
+      \    cml0eSBLZXkgPHhjYXRAY24uaWJtLmNvbT6IYAQTEQIAIAUCVKzNbAIbAwYLCQgH\n    AwIEFQIIAwQWAgMBAh4BAheAAAoJEGCj6azGVlvJQ2sAn2eEoSFQlwnBabq+fHay\n\
 76
+      \    nyWaDq4DAJ9l1Nk2Kl6Ui2t2rRPduiKeuonIgLkCDQRUrM1sEAgAkj5RDSFRa0Om\n    RrxXtvZHbywbbJ7+I86wdRK1KIq67ZwtuOv0V8r0KnxhQVqRmhP4nv7PbesPyF6e\n\
 77
+      \    5kfZ9A7jVvasyRg5JDyqCGGjRAirZXzNORUGzk0yC5GaoJNZGx3DuHHehlucZlYr\n    UcsOr42mcOwZ+oAxstRdBtWPw4VLmycKWkj1HvndbEtYdm/xdGybPRiZuaO6Ip1h\n\
 78
+      \    hOo+MTwCbxDBtHL5iAdeBl74LhCx1bCDxZYijIZjil24oT1e4bZMsMDwwIsdRK7p\n    6Qsd1h1Bjb5ldlMXI6fKTbHm6MlGR+cbA/2xzRf1Gd8xnlcL60x5qDVEH+wKH5AV\n\
 79
+      \    LA8EPaOI9wADBQf8CRSthdwCmf+Uo7GHlfsCNh4N/72b7/eP09N4w4FdqpmW9GWM\n    xNmOB4n0gsH/xQIBjeavpugHQDna+37U0Ez0FbMG/kU/5aS2lU6+oYvl+q//hRpA\n\
 80
+      \    qgr8jVwVqhzu2qaZvxuTSv22jZhCHZyIKUzTwjlHTR78qyvXvzyUpBt996fPkDNj\n    A+rS/Y19pmZHh+8w6TwIsuqC8K7wUasDzO5T3TGIfT/lqqT1nlT3on6SSK9YXoXE\n\
 81
+      \    GitIGQwZXhDdHcoHcI9RNxdfDIcHoCOjUdZCnKD03hxLF4i3lMh/6J7rBDPMreqh\n    WsVltt8ZiFrN3VXlnBC5ivNY3h3DKXuuLdLDWIhJBBgRAgAJBQJUrM1sAhsMAAoJ\n\
 82
+      \    EGCj6azGVlvJYS8An2DLTHZVjuZ8TicdevZSZTuA3qj0AJ9GkvZDZMpX1UiJGlZ+\n    9ywfCgbjcQ==\n\
 83
+      \    =UMyq\n    -----END PGP PUBLIC KEY BLOCK-----\n    -----BEGIN PGP PUBLIC\
 84
+      \ KEY BLOCK-----\n    Version: GnuPG v1.4.5 (GNU/Linux)\n\n    mQGiBEhk3UsRBADVYqwh28ZnuktVsHK3YFPjdK+NEmEfitsbDsTFAX02hLAJPjM6\n\
 85
+      \    V+o0hJKXoqHW4w9ZfYds+KiBSAxsbnBsObwVjw8VpztdozLBCh+Ld48PyeZ6bLez\n    0iac9Wj9s+0alug+EUZggZwskDKqngsZ5iX6VGrlXn5DuRyUv9fOEgGgOwCgzuPc\n\
 86
+      \    RF7IyyTS1R+biMPtb+8BxAkD/2pI+wYULGVb6CQgNZ10V/BJ3RUh7UiYtlMgfzJ1\n    kedapbXuZ885uAMXB7UPSJoaKVbP+nDOfNa1tANwNKuDgbh+mxvlKna9hbjmtYhQ\n\
 87
+      \    MgOvUK3vFL+4w3QgyH1ec+Wof75+s+oTBFK3bC9rXYi81f3x22r7r5vpPkEKjtUj\n    QFuVA/40vmDI0N3pFcG5BquZ2+S0EXTBsTJywkAI69NzuSTJLe03m90OjEi40Dil\n\
 88
+      \    hefWHP3oYJm0RErCtUCJMDSPm1E5N6IkKZcQLVhlRFq3Wh9fSdnAPc0ffWOiuZFS\n    hTjKcQLhGXedMlTPxdSjlC4fljzbuUlG4Ak9qhbpOBF4iEShT7QkSmFycm9kIEpv\n\
 89
+      \    aG5zb24gPGpiam9obnNvQHVzLmlibS5jb20+iGAEExECACAFAkhk3UsCGwMGCwkI\n    BwMCBBUCCAMEFgIDAQIeAQIXgAAKCRAg5HWo2nNsaKK1AKDHVv0Oh6QrwM4RU8Bd\n\
 90
+      \    J2E4cBIwswCgxUSdizrkbgsQ5lG5tHO9aTbD8W25Ag0ESGTdVxAIANxDZDKjJZUw\n    jzvHtmvxTfNxhHQhpq2K48irIBVtyNVrlFYYcuY3fwX5qUtTJqVGKE50zgsxZrqt\n\
 91
+      \    o2RML2cPmyITHyB7cnYe337dT3aD+/KAnAH7DuhUMEMKtmY7WRsAUMCkDJ4Y3gMC\n    BAP/HerQe3peEz8XIi7+FHawCFj4OdtFvyavGwp055njH71aBoShrEBDf/S9Xx7i\n\
 92
+      \    vT06GEX6a3HkeaAUAH0cM7rgMxzHkCGAN6oliSTrnpTsVexJ68Xi99cz+hEucYil\n    +5bRY5zU08DI1P4z3tdSCJSj7YNgXbpYA1KWouDBDiYMDmOm2hUTWZ2mMNEe5pyv\n\
 93
+      \    HronDf370x8AAwcH/3zR29vhi1AHCzkpx8T3FIagjk6eHMmuMht0Z7rVix+r7iTP\n    XsFEZAxMh2gMs2rXJBLZATjjiqnZ21leU5/8AftIMyqWQM0Ev64t0HrftD7Enj2L\n\
 94
+      \    ZKTvpM//WDhXYqDeBcu/B7nQQS5sDi6bsSWc5at49Fj2VgY8Ly/V06IGcVHYiENI\n    HgBGjGtoLaiLw61J1xNsH/LeaTWwJSalmnARpieOj+lQJi7w6mryLpn2ny/ds38B\n\
 95
+      \    pgnAEvIW6vHK7TzzfuEZBAk3OMGgxyxbIRp4RXTOLu0eFC+XrUH/ubTKoyW40yh/\n    vjjFAUoMxFNU87pQ7EoT1VRvRgmtSXbO/BVOQRKISQQYEQIACQUCSGTdVwIbDAAK\n\
 96
+      \    CRAg5HWo2nNsaBhHAKCoBltVVPyoaOvEqClPNugwg4K+GQCfab/e9e0S133BxaQE\n    PkKeSdN44us=\n\
 97
+      \    =IR/6\n    -----END PGP PUBLIC KEY BLOCK----- \n"
 98
+  curl_url:
 99
+    type: string
100
+    default: ''
101
+    description: |
102
+      Location of the IBM product installation file(s). This should be a URL
103
+      that curl can use to download files. Multiple URLs should be separated
104
+      by a space. NOTE: cryptographic verification is required and must be
105
+      specified as part of the URL query string with the key a valid hash
106
+      algorithms md5, sha256, or sha512, and the the checksum value itself
107
+      (http://<url>?[md5|sha256|sha512]=<checksum>).
108
+      For example:
109
+        'http://example.com/file.tgz?sha256=<sum>'
110
+        'sftp://example.com/file1.tgz?md5=<sum> ftp://example.com/file2.tgz?md5=<sum>'
111
+  curl_opts:
112
+    type: string
113
+    default: ''
114
+    description: |
115
+      The options passed to the 'curl' command when fetching files from
116
+      curl_url. For example:
117
+        '-u <user:password>'
118
+  license_accepted:
119
+    type: boolean
120
+    default: false
121
+    description: |
122
+      Some IBM charms require acceptance of a license before installation
123
+      can proceed. If required, setting this option to True indicates that you
124
+      have read and accepted the IBM terms and conditions found in the license
125
+      file referenced by the charm.
Back to file index

copyright

 1
--- 
 2
+++ copyright
 3
@@ -0,0 +1,13 @@
 4
+Copyright 2015 IBM Corporation
 5
+
 6
+This Charm is licensed under the Apache License, Version 2.0 (the "License");
 7
+you may not use this file except in compliance with the License.
 8
+You may obtain a copy of the License at
 9
+
10
+    http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+Unless required by applicable law or agreed to in writing, software
13
+distributed under the License is distributed on an "AS IS" BASIS,
14
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+See the License for the specific language governing permissions and
16
+limitations under the License.
Back to file index

hooks/config-changed

 1
--- 
 2
+++ hooks/config-changed
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/hook.template

 1
--- 
 2
+++ hooks/hook.template
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/install

 1
--- 
 2
+++ hooks/install
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/leader-elected

 1
--- 
 2
+++ hooks/leader-elected
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/leader-settings-changed

 1
--- 
 2
+++ hooks/leader-settings-changed
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/start

 1
--- 
 2
+++ hooks/start
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/stop

 1
--- 
 2
+++ hooks/stop
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/update-status

 1
--- 
 2
+++ hooks/update-status
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/upgrade-charm

 1
--- 
 2
+++ hooks/upgrade-charm
 3
@@ -0,0 +1,28 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import os
 8
+import sys
 9
+sys.path.append('lib')
10
+
11
+# This is an upgrade-charm context, make sure we install latest deps
12
+if not os.path.exists('wheelhouse/.upgrade'):
13
+    open('wheelhouse/.upgrade', 'w').close()
14
+    if os.path.exists('wheelhouse/.bootstrapped'):
15
+        os.unlink('wheelhouse/.bootstrapped')
16
+else:
17
+    os.unlink('wheelhouse/.upgrade')
18
+
19
+from charms.layer import basic
20
+basic.bootstrap_charm_deps()
21
+basic.init_config_states()
22
+
23
+
24
+# This will load and run the appropriate @hook and other decorated
25
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
26
+# and $CHARM_DIR/hooks/relations.
27
+#
28
+# See https://jujucharms.com/docs/stable/authors-charm-building
29
+# for more information on this pattern.
30
+from charms.reactive import main
31
+main()
Back to file index

icon.svg

 1
--- 
 2
+++ icon.svg
 3
@@ -0,0 +1,30 @@
 4
+<?xml version="1.0" encoding="UTF-8"?>
 5
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
 6
+<!-- Creator: CorelDRAW X6 -->
 7
+<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="1in" height="0.999996in" version="1.1" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd"
 8
+viewBox="0 0 1000 1000"
 9
+ xmlns:xlink="http://www.w3.org/1999/xlink">
10
+ <defs>
11
+    <linearGradient id="id0" gradientUnits="userSpaceOnUse" x1="500.002" y1="999.996" x2="500.002" y2="0">
12
+     <stop offset="0" stop-color="#07425A"/>
13
+     <stop offset="1" stop-color="#0465B2"/>
14
+    </linearGradient>
15
+    <mask id="id1">
16
+      <linearGradient id="id2" gradientUnits="userSpaceOnUse" x1="500.002" y1="58.4805" x2="500.002" y2="307.017">
17
+       <stop offset="0" stop-opacity="1" stop-color="white"/>
18
+       <stop offset="0.141176" stop-opacity="127.996" stop-color="white"/>
19
+       <stop offset="1" stop-opacity="0" stop-color="white"/>
20
+      </linearGradient>
21
+     <rect fill="url(#id2)" width="1000" height="365"/>
22
+    </mask>
23
+ </defs>
24
+ <g id="Layer_x0020_1">
25
+  <metadata id="CorelCorpID_0Corel-Layer"/>
26
+  <g id="_873476816">
27
+   <path id="Background" fill="url(#id0)" d="M0 676l0 -352c0,-283 41,-324 324,-324l352 0c284,0 324,41 324,324l0 352c0,283 -40,324 -324,324l-352 0c-283,0 -324,-41 -324,-324z"/>
28
+   <path fill="#E6E6E6" d="M780 222l-46 0 -32 0 -9 0 -5 14 92 0 0 -14zm-560 0l109 0 0 14 -109 0 0 -14zm109 30l0 14 -31 0 -47 0 -31 0 0 -14 109 0zm-31 29l0 14 -47 0 0 -14 47 0zm0 30l0 14 -47 0 0 -14 47 0zm0 30l0 14 -47 0 0 -14 47 0zm0 29l0 14 -47 0 0 -14 47 0zm0 30l31 0 0 14 -109 0 0 -14 31 0 47 0zm31 30l0 14 -109 0 0 -14 109 0zm16 -208l108 0c17,0 35,4 48,14l-156 0 0 -14zm169 30c2,4 3,8 4,14l-173 0 0 -14 169 0zm4 29c0,5 -1,10 -2,14l-47 0c0,-4 0,-9 0,-14l49 0zm-8 30c-4,6 -8,11 -13,14l-121 0 0 -14 134 0zm-13 30c5,3 9,8 13,14l-134 0 0 -14 121 0zm19 29c1,5 2,9 2,14l-49 0 0 -14 47 0zm2 30c-1,5 -2,10 -4,14l-169 0 0 -14 173 0zm-17 30c-13,10 -31,14 -48,14l0 0 -108 0 0 -14 156 0zm-79 -149l0 14 -46 0 0 -14 46 0zm0 89l0 14 -46 0 0 -14 46 0zm261 -118l-5 14 71 0 31 0 0 -14 -97 0zm-10 29l-5 14 81 0 0 -14 -76 0zm-11 30l-5 14 41 0 4 -11 0 11 47 0 0 -14 -87 0zm-8 14l-5 -14 -87 0 0 14 47 0 0 -11 4 11 41 0zm-11 -30l-5 -14 -76 0 0 14 81 0zm-10 -29l-5 -14 -97 0 0 14 31 0 71 0zm-11 -30l-4 -14 -9 0 -32 0 -46 0 0 14 91 0zm-60 105l0 14 47 0 0 -14 -47 0zm0 29l0 14 47 0 0 -14 -47 0zm0 30l-31 0 0 14 78 0 0 -14 -47 0zm-31 30l0 14 78 0 0 -14 -78 0zm87 -89l5 14 65 0 5 -14 -75 0zm11 29l5 14 43 0 5 -14 -53 0zm10 30l5 14 23 0 5 -14 -33 0zm10 30l5 14 1 0 0 0 2 0 5 -14 -13 0zm53 -89l0 14 47 0 0 -14 -47 0zm0 29l0 14 47 0 0 -14 -47 0zm0 30l0 14 78 0 0 -14 -31 0 -47 0zm0 30l0 14 78 0 0 -14 -78 0z"/>
29
+   <path fill="white" fill-rule="nonzero" d="M277 801l35 -50 -33 -46 20 0 16 22c2,5 5,8 6,11 3,-4 6,-7 8,-11l16 -22 20 0 -33 46 35 50 -20 0 -20 -30 -5 -7 -25 37 -20 0zm201 -46l18 4c-4,14 -11,25 -20,33 -10,8 -22,12 -35,12 -15,0 -26,-3 -35,-9 -9,-6 -16,-15 -21,-26 -5,-11 -7,-23 -7,-35 0,-14 3,-26 8,-37 5,-10 13,-18 23,-23 9,-6 20,-8 32,-8 13,0 24,3 33,10 9,7 16,16 19,29l-17 4c-3,-10 -7,-17 -13,-22 -6,-4 -14,-6 -23,-6 -10,0 -18,2 -25,7 -7,5 -12,12 -15,20 -2,8 -4,17 -4,26 0,11 2,21 5,29 3,9 9,15 15,19 7,4 15,6 23,6 10,0 18,-2 25,-8 7,-6 12,-14 14,-25zm25 46l51 -133 19 0 55 133 -20 0 -16 -40 -56 0 -14 40 -19 0zm38 -54l46 0 -14 -37c-4,-12 -8,-21 -10,-28 -2,8 -4,17 -7,25l-15 40zm121 54l0 -117 -44 0 0 -16 105 0 0 16 -44 0 0 117 -17 0z"/>
30
+   <path fill="#999999" mask="url(#id1)" d="M0 365l0 -41c0,-283 41,-324 324,-324l352 0c284,0 324,41 324,324l0 41c0,-283 -40,-324 -324,-324l-352 0c-283,0 -324,41 -324,324z"/>
31
+  </g>
32
+ </g>
33
+</svg>
Back to file index

layer.yaml

 1
--- 
 2
+++ layer.yaml
 3
@@ -0,0 +1,16 @@
 4
+includes:
 5
+- layer:basic
 6
+- layer:apt
 7
+- layer:ibm-base
 8
+options:
 9
+  basic:
10
+    packages: [build-essential, libssl-dev, libffi-dev, python-dev]
11
+    use_venv: false
12
+    include_system_packages: false
13
+  ibm-base: {}
14
+  ibm-xcat:
15
+    xcat_domain: mydomain.org
16
+  leadership: {}
17
+  apt:
18
+    packages: []
19
+is: ibm-xcat
Back to file index

lib/charms/__init__.py

1
--- 
2
+++ lib/charms/__init__.py
3
@@ -0,0 +1,2 @@
4
+from pkgutil import extend_path
5
+__path__ = extend_path(__path__, __name__)
Back to file index

lib/charms/apt.py

  1
--- 
  2
+++ lib/charms/apt.py
  3
@@ -0,0 +1,182 @@
  4
+# Copyright 2015-2016 Canonical Ltd.
  5
+#
  6
+# This file is part of the Apt layer for Juju.
  7
+#
  8
+# This program is free software: you can redistribute it and/or modify
  9
+# it under the terms of the GNU General Public License version 3, as
 10
+# published by the Free Software Foundation.
 11
+#
 12
+# This program is distributed in the hope that it will be useful, but
 13
+# WITHOUT ANY WARRANTY; without even the implied warranties of
 14
+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
 15
+# PURPOSE.  See the GNU General Public License for more details.
 16
+#
 17
+# You should have received a copy of the GNU General Public License
 18
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 19
+
 20
+'''
 21
+charms.reactive helpers for dealing with deb packages.
 22
+
 23
+Add apt package sources using add_source(). Queue deb packages for
 24
+installation with install(). Configure and work with your software
 25
+once the apt.installed.{packagename} state is set.
 26
+'''
 27
+import itertools
 28
+import subprocess
 29
+
 30
+from charmhelpers import fetch
 31
+from charmhelpers.core import hookenv, unitdata
 32
+from charms import reactive
 33
+
 34
+
 35
+__all__ = ['add_source', 'update', 'queue_install', 'install_queued',
 36
+           'installed', 'purge', 'ensure_package_status']
 37
+
 38
+
 39
+def add_source(source, key=None):
 40
+    '''Add an apt source.
 41
+
 42
+    Sets the apt.needs_update state.
 43
+
 44
+    A source may be either a line that can be added directly to
 45
+    sources.list(5), or in the form ppa:<user>/<ppa-name> for adding
 46
+    Personal Package Archives, or a distribution component to enable.
 47
+
 48
+    The package signing key should be an ASCII armoured GPG key. While
 49
+    GPG key ids are also supported, the retrieval mechanism is insecure.
 50
+    There is no need to specify the package signing key for PPAs or for
 51
+    the main Ubuntu archives.
 52
+    '''
 53
+    # Maybe we should remember which sources have been added already
 54
+    # so we don't waste time re-adding them. Is this time significant?
 55
+    fetch.add_source(source, key)
 56
+    reactive.set_state('apt.needs_update')
 57
+
 58
+
 59
+def queue_install(packages, options=None):
 60
+    """Queue one or more deb packages for install.
 61
+
 62
+    The `apt.installed.{name}` state is set once the package is installed.
 63
+
 64
+    If a package has already been installed it will not be reinstalled.
 65
+
 66
+    If a package has already been queued it will not be requeued, and
 67
+    the install options will not be changed.
 68
+
 69
+    Sets the apt.queued_installs state.
 70
+    """
 71
+    if isinstance(packages, str):
 72
+        packages = [packages]
 73
+    # Filter installed packages.
 74
+    store = unitdata.kv()
 75
+    queued_packages = store.getrange('apt.install_queue.', strip=True)
 76
+    packages = {package: options for package in packages
 77
+                if not (package in queued_packages or
 78
+                        reactive.helpers.is_state('apt.installed.' + package))}
 79
+    if packages:
 80
+        unitdata.kv().update(packages, prefix='apt.install_queue.')
 81
+        reactive.set_state('apt.queued_installs')
 82
+
 83
+
 84
+def installed():
 85
+    '''Return the set of deb packages completed install'''
 86
+    return set(state.split('.', 2)[2] for state in reactive.bus.get_states()
 87
+               if state.startswith('apt.installed.'))
 88
+
 89
+
 90
+def purge(packages):
 91
+    """Purge one or more deb packages from the system"""
 92
+    fetch.apt_purge(packages, fatal=True)
 93
+    store = unitdata.kv()
 94
+    store.unsetrange(packages, prefix='apt.install_queue.')
 95
+    for package in packages:
 96
+        reactive.remove_state('apt.installed.{}'.format(package))
 97
+
 98
+
 99
+def update():
100
+    """Update the apt cache.
101
+
102
+    Removes the apt.needs_update state.
103
+    """
104
+    status_set(None, 'Updating apt cache')
105
+    fetch.apt_update(fatal=True)  # Friends don't let friends set fatal=False
106
+    reactive.remove_state('apt.needs_update')
107
+
108
+
109
+def install_queued():
110
+    '''Installs queued deb packages.
111
+
112
+    Removes the apt.queued_installs state and sets the apt.installed state.
113
+
114
+    On failure, sets the unit's workload state to 'blocked' and returns
115
+    False. Package installs remain queued.
116
+
117
+    On success, sets the apt.installed.{packagename} state for each
118
+    installed package and returns True.
119
+    '''
120
+    store = unitdata.kv()
121
+    queue = sorted((options, package)
122
+                   for package, options in store.getrange('apt.install_queue.',
123
+                                                          strip=True).items())
124
+
125
+    installed = set()
126
+    for options, batch in itertools.groupby(queue, lambda x: x[0]):
127
+        packages = [b[1] for b in batch]
128
+        try:
129
+            status_set(None, 'Installing {}'.format(','.join(packages)))
130
+            fetch.apt_install(packages, options, fatal=True)
131
+            store.unsetrange(packages, prefix='apt.install_queue.')
132
+            installed.update(packages)
133
+        except subprocess.CalledProcessError:
134
+            status_set('blocked',
135
+                       'Unable to install packages {}'
136
+                       .format(','.join(packages)))
137
+            return False  # Without setting reactive state.
138
+
139
+    for package in installed:
140
+        reactive.set_state('apt.installed.{}'.format(package))
141
+
142
+    reactive.remove_state('apt.queued_installs')
143
+    return True
144
+
145
+
146
+def ensure_package_status():
147
+    '''Hold or unhold packages per the package_status configuration option.
148
+
149
+    All packages installed using this module and handlers are affected.
150
+
151
+    An mechanism may be added in the future to override this for a
152
+    subset of installed packages.
153
+    '''
154
+    packages = installed()
155
+    if not packages:
156
+        return
157
+    config = hookenv.config()
158
+    package_status = config['package_status']
159
+    changed = reactive.helpers.data_changed('apt.package_status',
160
+                                            (package_status, sorted(packages)))
161
+    if changed:
162
+        if package_status == 'hold':
163
+            hookenv.log('Holding packages {}'.format(','.join(packages)))
164
+            fetch.apt_hold(packages)
165
+        else:
166
+            hookenv.log('Unholding packages {}'.format(','.join(packages)))
167
+            fetch.apt_unhold(packages)
168
+    reactive.remove_state('apt.needs_hold')
169
+
170
+
171
+def status_set(state, message):
172
+    """Set the unit's workload status.
173
+
174
+    Set state == None to keep the same state and just change the message.
175
+    """
176
+    if state is None:
177
+        state = hookenv.status_get()[0]
178
+        if state == 'unknown':
179
+            state = 'maintenance'  # Guess
180
+    if state in ('error', 'blocked'):
181
+        lvl = hookenv.WARNING
182
+    else:
183
+        lvl = hookenv.INFO
184
+    hookenv.status_set(state, message)
185
+    hookenv.log('{}: {}'.format(state, message), lvl)
Back to file index

lib/charms/layer/__init__.py

 1
--- 
 2
+++ lib/charms/layer/__init__.py
 3
@@ -0,0 +1,21 @@
 4
+import os
 5
+
 6
+
 7
+class LayerOptions(dict):
 8
+    def __init__(self, layer_file, section=None):
 9
+        import yaml  # defer, might not be available until bootstrap
10
+        with open(layer_file) as f:
11
+            layer = yaml.safe_load(f.read())
12
+        opts = layer.get('options', {})
13
+        if section and section in opts:
14
+            super(LayerOptions, self).__init__(opts.get(section))
15
+        else:
16
+            super(LayerOptions, self).__init__(opts)
17
+
18
+
19
+def options(section=None, layer_file=None):
20
+    if not layer_file:
21
+        base_dir = os.environ.get('CHARM_DIR', os.getcwd())
22
+        layer_file = os.path.join(base_dir, 'layer.yaml')
23
+
24
+    return LayerOptions(layer_file, section)
Back to file index

lib/charms/layer/basic.py

  1
--- 
  2
+++ lib/charms/layer/basic.py
  3
@@ -0,0 +1,159 @@
  4
+import os
  5
+import sys
  6
+import shutil
  7
+import platform
  8
+from glob import glob
  9
+from subprocess import check_call
 10
+
 11
+from charms.layer.execd import execd_preinstall
 12
+
 13
+
 14
+def bootstrap_charm_deps():
 15
+    """
 16
+    Set up the base charm dependencies so that the reactive system can run.
 17
+    """
 18
+    # execd must happen first, before any attempt to install packages or
 19
+    # access the network, because sites use this hook to do bespoke
 20
+    # configuration and install secrets so the rest of this bootstrap
 21
+    # and the charm itself can actually succeed. This call does nothing
 22
+    # unless the operator has created and populated $CHARM_DIR/exec.d.
 23
+    execd_preinstall()
 24
+    # ensure that $CHARM_DIR/bin is on the path, for helper scripts
 25
+    os.environ['PATH'] += ':%s' % os.path.join(os.environ['CHARM_DIR'], 'bin')
 26
+    venv = os.path.abspath('../.venv')
 27
+    vbin = os.path.join(venv, 'bin')
 28
+    vpip = os.path.join(vbin, 'pip')
 29
+    vpy = os.path.join(vbin, 'python')
 30
+    if os.path.exists('wheelhouse/.bootstrapped'):
 31
+        from charms import layer
 32
+        cfg = layer.options('basic')
 33
+        if cfg.get('use_venv') and '.venv' not in sys.executable:
 34
+            # activate the venv
 35
+            os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']])
 36
+            reload_interpreter(vpy)
 37
+        return
 38
+    # bootstrap wheelhouse
 39
+    if os.path.exists('wheelhouse'):
 40
+        with open('/root/.pydistutils.cfg', 'w') as fp:
 41
+            # make sure that easy_install also only uses the wheelhouse
 42
+            # (see https://github.com/pypa/pip/issues/410)
 43
+            charm_dir = os.environ['CHARM_DIR']
 44
+            fp.writelines([
 45
+                "[easy_install]\n",
 46
+                "allow_hosts = ''\n",
 47
+                "find_links = file://{}/wheelhouse/\n".format(charm_dir),
 48
+            ])
 49
+        apt_install(['python3-pip', 'python3-setuptools', 'python3-yaml'])
 50
+        from charms import layer
 51
+        cfg = layer.options('basic')
 52
+        # include packages defined in layer.yaml
 53
+        apt_install(cfg.get('packages', []))
 54
+        # if we're using a venv, set it up
 55
+        if cfg.get('use_venv'):
 56
+            if not os.path.exists(venv):
 57
+                distname, version, series = platform.linux_distribution()
 58
+                if series in ('precise', 'trusty'):
 59
+                    apt_install(['python-virtualenv'])
 60
+                else:
 61
+                    apt_install(['virtualenv'])
 62
+                cmd = ['virtualenv', '-ppython3', '--never-download', venv]
 63
+                if cfg.get('include_system_packages'):
 64
+                    cmd.append('--system-site-packages')
 65
+                check_call(cmd)
 66
+            os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']])
 67
+            pip = vpip
 68
+        else:
 69
+            pip = 'pip3'
 70
+            # save a copy of system pip to prevent `pip3 install -U pip`
 71
+            # from changing it
 72
+            if os.path.exists('/usr/bin/pip'):
 73
+                shutil.copy2('/usr/bin/pip', '/usr/bin/pip.save')
 74
+        # need newer pip, to fix spurious Double Requirement error:
 75
+        # https://github.com/pypa/pip/issues/56
 76
+        check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse',
 77
+                    'pip'])
 78
+        # install the rest of the wheelhouse deps
 79
+        check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse'] +
 80
+                   glob('wheelhouse/*'))
 81
+        if not cfg.get('use_venv'):
 82
+            # restore system pip to prevent `pip3 install -U pip`
 83
+            # from changing it
 84
+            if os.path.exists('/usr/bin/pip.save'):
 85
+                shutil.copy2('/usr/bin/pip.save', '/usr/bin/pip')
 86
+                os.remove('/usr/bin/pip.save')
 87
+        os.remove('/root/.pydistutils.cfg')
 88
+        # flag us as having already bootstrapped so we don't do it again
 89
+        open('wheelhouse/.bootstrapped', 'w').close()
 90
+        # Ensure that the newly bootstrapped libs are available.
 91
+        # Note: this only seems to be an issue with namespace packages.
 92
+        # Non-namespace-package libs (e.g., charmhelpers) are available
 93
+        # without having to reload the interpreter. :/
 94
+        reload_interpreter(vpy if cfg.get('use_venv') else sys.argv[0])
 95
+
 96
+
 97
+def reload_interpreter(python):
 98
+    """
 99
+    Reload the python interpreter to ensure that all deps are available.
100
+
101
+    Newly installed modules in namespace packages sometimes seemt to
102
+    not be picked up by Python 3.
103
+    """
104
+    os.execle(python, python, sys.argv[0], os.environ)
105
+
106
+
107
+def apt_install(packages):
108
+    """
109
+    Install apt packages.
110
+
111
+    This ensures a consistent set of options that are often missed but
112
+    should really be set.
113
+    """
114
+    if isinstance(packages, (str, bytes)):
115
+        packages = [packages]
116
+
117
+    env = os.environ.copy()
118
+
119
+    if 'DEBIAN_FRONTEND' not in env:
120
+        env['DEBIAN_FRONTEND'] = 'noninteractive'
121
+
122
+    cmd = ['apt-get',
123
+           '--option=Dpkg::Options::=--force-confold',
124
+           '--assume-yes',
125
+           'install']
126
+    check_call(cmd + packages, env=env)
127
+
128
+
129
+def init_config_states():
130
+    import yaml
131
+    from charmhelpers.core import hookenv
132
+    from charms.reactive import set_state
133
+    from charms.reactive import toggle_state
134
+    config = hookenv.config()
135
+    config_defaults = {}
136
+    config_defs = {}
137
+    config_yaml = os.path.join(hookenv.charm_dir(), 'config.yaml')
138
+    if os.path.exists(config_yaml):
139
+        with open(config_yaml) as fp:
140
+            config_defs = yaml.safe_load(fp).get('options', {})
141
+            config_defaults = {key: value.get('default')
142
+                               for key, value in config_defs.items()}
143
+    for opt in config_defs.keys():
144
+        if config.changed(opt):
145
+            set_state('config.changed')
146
+            set_state('config.changed.{}'.format(opt))
147
+        toggle_state('config.set.{}'.format(opt), config.get(opt))
148
+        toggle_state('config.default.{}'.format(opt),
149
+                     config.get(opt) == config_defaults[opt])
150
+    hookenv.atexit(clear_config_states)
151
+
152
+
153
+def clear_config_states():
154
+    from charmhelpers.core import hookenv, unitdata
155
+    from charms.reactive import remove_state
156
+    config = hookenv.config()
157
+    remove_state('config.changed')
158
+    for opt in config.keys():
159
+        remove_state('config.changed.{}'.format(opt))
160
+        remove_state('config.set.{}'.format(opt))
161
+        remove_state('config.default.{}'.format(opt))
162
+    unitdata.kv().flush()
Back to file index

lib/charms/layer/execd.py

  1
--- 
  2
+++ lib/charms/layer/execd.py
  3
@@ -0,0 +1,138 @@
  4
+# Copyright 2014-2016 Canonical Limited.
  5
+#
  6
+# This file is part of layer-basic, the reactive base layer for Juju.
  7
+#
  8
+# charm-helpers is free software: you can redistribute it and/or modify
  9
+# it under the terms of the GNU Lesser General Public License version 3 as
 10
+# published by the Free Software Foundation.
 11
+#
 12
+# charm-helpers is distributed in the hope that it will be useful,
 13
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
 14
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 15
+# GNU Lesser General Public License for more details.
 16
+#
 17
+# You should have received a copy of the GNU Lesser General Public License
 18
+# along with charm-helpers.  If not, see <http://www.gnu.org/licenses/>.
 19
+
 20
+# This module may only import from the Python standard library.
 21
+import os
 22
+import sys
 23
+import subprocess
 24
+import time
 25
+
 26
+'''
 27
+execd/preinstall
 28
+
 29
+It is often necessary to configure and reconfigure machines
 30
+after provisioning, but before attempting to run the charm.
 31
+Common examples are specialized network configuration, enabling
 32
+of custom hardware, non-standard disk partitioning and filesystems,
 33
+adding secrets and keys required for using a secured network.
 34
+
 35
+The reactive framework's base layer invokes this mechanism as
 36
+early as possible, before any network access is made or dependencies
 37
+unpacked or non-standard modules imported (including the charms.reactive
 38
+framework itself).
 39
+
 40
+Operators needing to use this functionality may branch a charm and
 41
+create an exec.d directory in it. The exec.d directory in turn contains
 42
+one or more subdirectories, each of which contains an executable called
 43
+charm-pre-install and any other required resources. The charm-pre-install
 44
+executables are run, and if successful, state saved so they will not be
 45
+run again.
 46
+
 47
+    $CHARM_DIR/exec.d/mynamespace/charm-pre-install
 48
+
 49
+An alternative to branching a charm is to compose a new charm that contains
 50
+the exec.d directory, using the original charm as a layer,
 51
+
 52
+A charm author could also abuse this mechanism to modify the charm
 53
+environment in unusual ways, but for most purposes it is saner to use
 54
+charmhelpers.core.hookenv.atstart().
 55
+'''
 56
+
 57
+
 58
+def default_execd_dir():
 59
+    return os.path.join(os.environ['CHARM_DIR'], 'exec.d')
 60
+
 61
+
 62
+def execd_module_paths(execd_dir=None):
 63
+    """Generate a list of full paths to modules within execd_dir."""
 64
+    if not execd_dir:
 65
+        execd_dir = default_execd_dir()
 66
+
 67
+    if not os.path.exists(execd_dir):
 68
+        return
 69
+
 70
+    for subpath in os.listdir(execd_dir):
 71
+        module = os.path.join(execd_dir, subpath)
 72
+        if os.path.isdir(module):
 73
+            yield module
 74
+
 75
+
 76
+def execd_submodule_paths(command, execd_dir=None):
 77
+    """Generate a list of full paths to the specified command within exec_dir.
 78
+    """
 79
+    for module_path in execd_module_paths(execd_dir):
 80
+        path = os.path.join(module_path, command)
 81
+        if os.access(path, os.X_OK) and os.path.isfile(path):
 82
+            yield path
 83
+
 84
+
 85
+def execd_sentinel_path(submodule_path):
 86
+    module_path = os.path.dirname(submodule_path)
 87
+    execd_path = os.path.dirname(module_path)
 88
+    module_name = os.path.basename(module_path)
 89
+    submodule_name = os.path.basename(submodule_path)
 90
+    return os.path.join(execd_path,
 91
+                        '.{}_{}.done'.format(module_name, submodule_name))
 92
+
 93
+
 94
+def execd_run(command, execd_dir=None, stop_on_error=True, stderr=None):
 95
+    """Run command for each module within execd_dir which defines it."""
 96
+    if stderr is None:
 97
+        stderr = sys.stdout
 98
+    for submodule_path in execd_submodule_paths(command, execd_dir):
 99
+        # Only run each execd once. We cannot simply run them in the
100
+        # install hook, as potentially storage hooks are run before that.
101
+        # We cannot rely on them being idempotent.
102
+        sentinel = execd_sentinel_path(submodule_path)
103
+        if os.path.exists(sentinel):
104
+            continue
105
+
106
+        try:
107
+            subprocess.check_call([submodule_path], stderr=stderr,
108
+                                  universal_newlines=True)
109
+            with open(sentinel, 'w') as f:
110
+                f.write('{} ran successfully {}\n'.format(submodule_path,
111
+                                                          time.ctime()))
112
+                f.write('Removing this file will cause it to be run again\n')
113
+        except subprocess.CalledProcessError as e:
114
+            # Logs get the details. We can't use juju-log, as the
115
+            # output may be substantial and exceed command line
116
+            # length limits.
117
+            print("ERROR ({}) running {}".format(e.returncode, e.cmd),
118
+                  file=stderr)
119
+            print("STDOUT<<EOM", file=stderr)
120
+            print(e.output, file=stderr)
121
+            print("EOM", file=stderr)
122
+
123
+            # Unit workload status gets a shorter fail message.
124
+            short_path = os.path.relpath(submodule_path)
125
+            block_msg = "Error ({}) running {}".format(e.returncode,
126
+                                                       short_path)
127
+            try:
128
+                subprocess.check_call(['status-set', 'blocked', block_msg],
129
+                                      universal_newlines=True)
130
+                if stop_on_error:
131
+                    sys.exit(0)  # Leave unit in blocked state.
132
+            except Exception:
133
+                pass  # We care about the exec.d/* failure, not status-set.
134
+
135
+            if stop_on_error:
136
+                sys.exit(e.returncode or 1)  # Error state for pre-1.24 Juju
137
+
138
+
139
+def execd_preinstall(execd_dir=None):
140
+    """Run charm-pre-install for each module within execd_dir."""
141
+    execd_run('charm-pre-install', execd_dir=execd_dir)
Back to file index

lib/charms/leadership.py

 1
--- 
 2
+++ lib/charms/leadership.py
 3
@@ -0,0 +1,58 @@
 4
+# Copyright 2015-2016 Canonical Ltd.
 5
+#
 6
+# This file is part of the Leadership Layer for Juju.
 7
+#
 8
+# This program is free software: you can redistribute it and/or modify
 9
+# it under the terms of the GNU General Public License version 3, as
10
+# published by the Free Software Foundation.
11
+#
12
+# This program is distributed in the hope that it will be useful, but
13
+# WITHOUT ANY WARRANTY; without even the implied warranties of
14
+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15
+# PURPOSE.  See the GNU General Public License for more details.
16
+#
17
+# You should have received a copy of the GNU General Public License
18
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
+
20
+from charmhelpers.core import hookenv
21
+from charmhelpers.core import unitdata
22
+
23
+from charms import reactive
24
+from charms.reactive import not_unless
25
+
26
+
27
+__all__ = ['leader_get', 'leader_set']
28
+
29
+
30
+@not_unless('leadership.is_leader')
31
+def leader_set(settings=None, **kw):
32
+    '''Change leadership settings, per charmhelpers.core.hookenv.leader_set.
33
+
34
+    The leadership.set.{key} reactive state will be set while the
35
+    leadership hook environment setting remains set.
36
+
37
+    Changed leadership settings will set the leadership.changed.{key}
38
+    and leadership.changed states. These states will remain set until
39
+    the following hook.
40
+
41
+    These state changes take effect immediately on the leader, and
42
+    in future hooks run on non-leaders. In this way both leaders and
43
+    non-leaders can share handlers, waiting on these states.
44
+    '''
45
+    settings = settings or {}
46
+    settings.update(kw)
47
+    previous = unitdata.kv().getrange('leadership.settings.', strip=True)
48
+
49
+    for key, value in settings.items():
50
+        if value != previous.get(key):
51
+            reactive.set_state('leadership.changed.{}'.format(key))
52
+            reactive.set_state('leadership.changed')
53
+        reactive.helpers.toggle_state('leadership.set.{}'.format(key),
54
+                                      value is not None)
55
+    hookenv.leader_set(settings)
56
+    unitdata.kv().update(settings, prefix='leadership.settings.')
57
+
58
+
59
+def leader_get(attribute=None):
60
+    '''Return leadership settings, per charmhelpers.core.hookenv.leader_get.'''
61
+    return hookenv.leader_get(attribute)
Back to file index

metadata.yaml

 1
--- 
 2
+++ metadata.yaml
 3
@@ -0,0 +1,14 @@
 4
+name: ibm-xcat
 5
+summary: This charm installs base IBM xCAT.
 6
+maintainer: IBM Juju Support Team <jujusupp@us.ibm.com>
 7
+description: "xCAT enables you to easily manage large number of servers for any type\
 8
+  \ of technical computing workload. \nxCAT is known for exceptional scaling, wide\
 9
+  \ variety of supported hardware and operating systems, \nvirtualization platforms,\
10
+  \ and complete .day0. setup capabilities.\n"
11
+tags:
12
+- ibm
13
+- ops
14
+- misc
15
+- virtualization
16
+- scaling
17
+subordinate: false
Back to file index

reactive/apt.py

  1
--- 
  2
+++ reactive/apt.py
  3
@@ -0,0 +1,131 @@
  4
+# Copyright 2015-2016 Canonical Ltd.
  5
+#
  6
+# This file is part of the Apt layer for Juju.
  7
+#
  8
+# This program is free software: you can redistribute it and/or modify
  9
+# it under the terms of the GNU General Public License version 3, as
 10
+# published by the Free Software Foundation.
 11
+#
 12
+# This program is distributed in the hope that it will be useful, but
 13
+# WITHOUT ANY WARRANTY; without even the implied warranties of
 14
+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
 15
+# PURPOSE.  See the GNU General Public License for more details.
 16
+#
 17
+# You should have received a copy of the GNU General Public License
 18
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 19
+
 20
+'''
 21
+charms.reactive helpers for dealing with deb packages.
 22
+
 23
+Add apt package sources using add_source(). Queue deb packages for
 24
+installation with install(). Configure and work with your software
 25
+once the apt.installed.{packagename} state is set.
 26
+'''
 27
+import subprocess
 28
+
 29
+from charmhelpers import fetch
 30
+from charmhelpers.core import hookenv
 31
+from charmhelpers.core.hookenv import WARNING
 32
+from charms import layer
 33
+from charms import reactive
 34
+from charms.reactive import when, when_not
 35
+
 36
+import charms.apt
 37
+
 38
+
 39
+@when('apt.needs_update')
 40
+def update():
 41
+    charms.apt.update()
 42
+
 43
+
 44
+@when('apt.queued_installs')
 45
+@when_not('apt.needs_update')
 46
+def install_queued():
 47
+    charms.apt.install_queued()
 48
+
 49
+
 50
+@when_not('apt.queued_installs')
 51
+def ensure_package_status():
 52
+    charms.apt.ensure_package_status()
 53
+
 54
+
 55
+def filter_installed_packages(packages):
 56
+    # Don't use fetch.filter_installed_packages, as it depends on python-apt
 57
+    # and not available if the basic layer's use_site_packages option is off
 58
+    # TODO: Move this to charm-helpers.fetch
 59
+    cmd = ['dpkg-query', '--show', r'--showformat=${Package}\n']
 60
+    installed = set(subprocess.check_output(cmd,
 61
+                                            universal_newlines=True).split())
 62
+    return set(packages) - installed
 63
+
 64
+
 65
+def clear_removed_package_states():
 66
+    """On hook startup, clear install states for removed packages."""
 67
+    removed = filter_installed_packages(charms.apt.installed())
 68
+    if removed:
 69
+        hookenv.log('{} missing packages ({})'.format(len(removed),
 70
+                                                      ','.join(removed)),
 71
+                    WARNING)
 72
+        for package in removed:
 73
+            reactive.remove_state('apt.installed.{}'.format(package))
 74
+
 75
+
 76
+def configure_sources():
 77
+    """Add user specified package sources from the service configuration.
 78
+
 79
+    See charmhelpers.fetch.configure_sources for details.
 80
+    """
 81
+    config = hookenv.config()
 82
+
 83
+    # We don't have enums, so we need to validate this ourselves.
 84
+    package_status = config.get('package_status')
 85
+    if package_status not in ('hold', 'install'):
 86
+        charms.apt.status_set('blocked',
 87
+                              'Unknown package_status {}'
 88
+                              ''.format(package_status))
 89
+        # Die before further hooks are run. This isn't very nice, but
 90
+        # there is no other way to inform the operator that they have
 91
+        # invalid configuration.
 92
+        raise SystemExit(0)
 93
+
 94
+    sources = config.get('install_sources')
 95
+    keys = config.get('install_keys')
 96
+    if reactive.helpers.data_changed('apt.configure_sources', (sources, keys)):
 97
+        fetch.configure_sources(update=False,
 98
+                                sources_var='install_sources',
 99
+                                keys_var='install_keys')
100
+        reactive.set_state('apt.needs_update')
101
+
102
+    extra_packages = sorted(config.get('extra_packages', '').split())
103
+    if extra_packages:
104
+        charms.apt.queue_install(extra_packages)
105
+
106
+
107
+def queue_layer_packages():
108
+    """Add packages listed in build-time layer options."""
109
+    # Both basic and apt layer. basic layer will have already installed
110
+    # its defined packages, but rescheduling it here gets the apt layer
111
+    # state set and they will pinned as any other apt layer installed
112
+    # package.
113
+    opts = layer.options()
114
+    for section in ['basic', 'apt']:
115
+        if section in opts and 'packages' in opts[section]:
116
+            charms.apt.queue_install(opts[section]['packages'])
117
+
118
+
119
+# Per https://github.com/juju-solutions/charms.reactive/issues/33,
120
+# this module may be imported multiple times so ensure the
121
+# initialization hook is only registered once. I have to piggy back
122
+# onto the namespace of a module imported before reactive discovery
123
+# to do this.
124
+if not hasattr(reactive, '_apt_registered'):
125
+    # We need to register this to run every hook, not just during install
126
+    # and config-changed, to protect against race conditions. If we don't
127
+    # do this, then the config in the hook environment may show updates
128
+    # to running hooks well before the config-changed hook has been invoked
129
+    # and the intialization provided an opertunity to be run.
130
+    hookenv.atstart(hookenv.log, 'Initializing Apt Layer')
131
+    hookenv.atstart(clear_removed_package_states)
132
+    hookenv.atstart(configure_sources)
133
+    hookenv.atstart(queue_layer_packages)
134
+    reactive._apt_registered = True
Back to file index

reactive/ibm-base.sh

  1
--- 
  2
+++ reactive/ibm-base.sh
  3
@@ -0,0 +1,107 @@
  4
+#!/bin/bash
  5
+source charms.reactive.sh
  6
+set -e
  7
+
  8
+
  9
+# Utility function to verify a downloaded resource
 10
+# :param: file name
 11
+# :param: checksum type
 12
+# :param: checksum value
 13
+function verify_curl_resource() {
 14
+  local FILE=$1
 15
+  local TYPE=$2
 16
+  local EXPECTED_SUM=$3
 17
+  local CALCULATED_SUM=""
 18
+  local PROG=""
 19
+
 20
+  if [ ! -r ${FILE} ]; then
 21
+    status-set blocked "ibm-base: could not read ${FILE}"
 22
+    juju-log "Could not verify the downloaded resource. File could not be read: ${FILE}"
 23
+  fi
 24
+
 25
+  # Set our checksum utility based on the requested type
 26
+  case "${TYPE}" in
 27
+    md5)
 28
+      PROG='md5sum'
 29
+      ;;
 30
+    sha256)
 31
+      PROG='sha256sum'
 32
+      ;;
 33
+    sha512)
 34
+      PROG='sha512sum'
 35
+      ;;
 36
+    *)
 37
+      status-set blocked "ibm-base: checksum type must be md5, sha215, or sha512"
 38
+      juju-log "Could not verify the downloaded resource ${FILE}. Unknown checksum type: ${TYPE}"
 39
+      return 1
 40
+  esac
 41
+
 42
+  CALCULATED_SUM=`${PROG} ${FILE} | awk '{print $1}'`
 43
+  if [ "${CALCULATED_SUM}" = "${EXPECTED_SUM}" ]; then
 44
+    juju-log "Checksum verified for ${FILE}."
 45
+    return 0
 46
+  else
 47
+    status-set blocked "ibm-base: checksums did not match"
 48
+    juju-log "Checksum mismatch for ${FILE}. Expected ${EXPECTED_SUM}, got ${CALCULATED_SUM}"
 49
+    return 1
 50
+  fi
 51
+}
 52
+
 53
+
 54
+# Fetch curl resources if a URL is configured
 55
+@when 'config.set.curl_url'
 56
+@when_any 'config.new.curl_url' 'config.changed.curl_url' 'config.new.curl_opts' 'config.changed.curl_opts'
 57
+function fetch_curl_resource() {
 58
+  local ARCHIVE_DIR="${CHARM_DIR}/files/archives"
 59
+  local CURL_URL=$(config-get 'curl_url')
 60
+  local CURL_OPTS=$(config-get 'curl_opts')
 61
+
 62
+  status-set maintenance "ibm-base: fetching resource(s)"
 63
+
 64
+  mkdir -p ${ARCHIVE_DIR}
 65
+  cd ${ARCHIVE_DIR}
 66
+  # Multiple URLs may be separated by a space, so loop.
 67
+  for URL_STRING in ${CURL_URL}
 68
+  do
 69
+    # For each URL_STRING, set the url, checksum type, and checksum value.
 70
+    local URL=${URL_STRING%%\?*}        # string before the first '?'
 71
+    local FILE_NAME=${URL##*\/}         # string after the last '/'
 72
+    local SUM_STRING=${URL_STRING#*\?}  # string after the first '?'
 73
+    local SUM_TYPE=${SUM_STRING%%\=*}   # string before the first '='
 74
+    local SUM_VALUE=${SUM_STRING#*\=}   # string after the first '='
 75
+
 76
+    if [ -z ${FILE_NAME} ]; then
 77
+      FILE_NAME="juju-${RANDOM}"
 78
+    fi
 79
+    curl --silent --show-error ${CURL_OPTS} -o ${FILE_NAME} ${URL}
 80
+
 81
+    # Verify our resource checksum. If this fails, let verify_resource log
 82
+    # the reason and exit successfully. Exiting non-zero would fail the hook,
 83
+    # so return 0 and simply inform the user that verification failed.
 84
+    verify_curl_resource ${FILE_NAME} ${SUM_TYPE} ${SUM_VALUE} || return 0
 85
+  done
 86
+  cd -
 87
+
 88
+  set_state 'ibm-base.curl.resource.fetched'
 89
+  status-set active "ibm-base: curl resource(s) fetched"
 90
+  juju-log 'Curl resource fetched'
 91
+}
 92
+
 93
+
 94
+# Handle license acceptance
 95
+@when 'config.changed.license_accepted'
 96
+function check_license_acceptance() {
 97
+  local LIC_ACCEPTED=$(config-get 'license_accepted')
 98
+
 99
+  # compare lowercase LIC_ACCEPTED (requires bash > 4)
100
+  if [ "${LIC_ACCEPTED,,}" = "true" ]; then
101
+    set_state 'ibm-base.license.accepted'
102
+    juju-log 'License accepted'
103
+  else
104
+    remove_state 'ibm-base.license.accepted'
105
+    juju-log 'License NOT accepted'
106
+  fi
107
+}
108
+
109
+# Main reactive entry point
110
+reactive_handler_main
Back to file index

reactive/ibm_xcat.py

  1
--- 
  2
+++ reactive/ibm_xcat.py
  3
@@ -0,0 +1,160 @@
  4
+from charms.reactive import when, when_not, set_state
  5
+import charms.apt
  6
+import subprocess
  7
+import socket
  8
+import re
  9
+import os
 10
+import random
 11
+import string
 12
+from charmhelpers.core import hookenv
 13
+from charms import layer
 14
+
 15
+
 16
+def random_string(size=8, chars=string.ascii_uppercase +
 17
+                  string.ascii_lowercase + string.digits):
 18
+    return ''.join(random.SystemRandom().choice(chars) for _ in range(size))
 19
+
 20
+
 21
+def check_and_add_hosts_entry(ip, hostname, fqdn, xcat_domain):
 22
+    if hostname != fqdn:
 23
+        return False
 24
+
 25
+    with open("/etc/hosts", "r") as hostsfile:
 26
+        lines = hostsfile.readlines()
 27
+    found = False
 28
+    with open("/etc/hosts", "w") as hostsfile:
 29
+        for line in lines:
 30
+            if re.match(ip+r'.*', line):
 31
+                found = True
 32
+                line = re.sub(ip+r'.*', ip+"\t" + hostname + "." +
 33
+                              xcat_domain + "\t" + hostname, line)
 34
+            hostsfile.write(line+"\n")
 35
+        if not found:
 36
+            hostsfile.write(ip+"\t"+hostname+"."+xcat_domain+"\t"
 37
+                            + hostname + "\n")
 38
+    set_state('ibm-xcat.host.configured')
 39
+    return True
 40
+
 41
+
 42
+def check_and_add_resolvconf_entry(ip, xcat_domain):
 43
+    with open("/etc/resolv.conf", "r") as resolvconf:
 44
+        for line in resolvconf:
 45
+            if re.match(r'nameserver.+'+ip, line):
 46
+                return False
 47
+
 48
+    try:
 49
+        nameserver = ["echo", "nameserver " +
 50
+                      ip+"\nsearch "+xcat_domain]
 51
+        ps = subprocess.Popen(nameserver, stdout=subprocess.PIPE)
 52
+        updateresolvdb = ["resolvconf", "-a", "eth0.inet"]
 53
+        subprocess.check_call(updateresolvdb, stdin=ps.stdout)
 54
+        ps.wait()
 55
+        subprocess.check_call(["resolvconf", "-u"])
 56
+        return True
 57
+    except subprocess.CalledProcessError as e:
 58
+        print(e.output)
 59
+
 60
+
 61
+def copy_ssh_key(key, host, password):
 62
+    import paramiko
 63
+    ssh = paramiko.SSHClient()
 64
+    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
 65
+    ssh.connect(host, username='root', password=password)
 66
+    stdin, stdout, stderr = ssh.exec_command("echo " + key +
 67
+                                             " >> ~/.ssh/authorized_keys;"
 68
+                                             "echo $?\n")
 69
+    output = stdout.readlines()[0].rstrip('\n')
 70
+    if output == "0":
 71
+        return True
 72
+
 73
+    return False
 74
+
 75
+
 76
+@when_not('ibm-xcat.host.configured')
 77
+def configure_xcat_management_node():
 78
+    hookenv.status_set("maintenance", "Configuring xCAT Management Node")
 79
+    hostname = socket.gethostname()
 80
+    fqdn = socket.getfqdn()
 81
+    ip = hookenv.unit_get('private-address')
 82
+    cfg = layer.options('ibm-xcat')
 83
+    xcat_domain = cfg.get('xcat_domain')
 84
+
 85
+    if not check_and_add_hosts_entry(ip, hostname, fqdn, xcat_domain):
 86
+        hookenv.log("Host name already configured with FQDN."
 87
+                    "Ignoring xcat_domain parameter")
 88
+    else:
 89
+        hookenv.log("Host name not configured with FQDN. Configured"
 90
+                    "domain name of the host using xcat_domain parameter")
 91
+
 92
+    if not check_and_add_resolvconf_entry(ip, xcat_domain):
 93
+        hookenv.log("xCAT MN is already added as a nameserver")
 94
+    else:
 95
+        hookenv.log("Added xCAT MN host in the nameserver list")
 96
+
 97
+    set_state('ibm-xcat.host.configured')
 98
+    return
 99
+
100
+
101
+@when('ibm-xcat.host.configured')
102
+@when_not('ibm-xcat.queued')
103
+def queue_ibm_xcat():
104
+    charms.apt.queue_install(['software-properties-common', 'xcat'])
105
+    hookenv.log("xCAT queued for installation")
106
+    set_state('ibm-xcat.queued')
107
+
108
+
109
+@when('apt.installed.software-properties-common', 'apt.installed.xcat')
110
+@when_not('ibm-xcat.started')
111
+def check_ibm_xcat():
112
+    try:
113
+        subprocess.check_call("scripts/check_xcat_installed.sh")
114
+        hookenv.log("xCAT is installed and started")
115
+        hookenv.status_set("active", "xCAT is running")
116
+        set_state('ibm-xcat.started')
117
+    except subprocess.CalledProcessError as e:
118
+        print(e.output)
119
+        hookenv.status_set("error", "Error while installing xCAT")
120
+
121
+
122
+def configure_ibm_xcat():
123
+    cfg = layer.options('ibm-xcat')
124
+    xcat_configure_kvm = cfg.get('xcat_configure_kvm')
125
+    create_sample_vm = cfg.get('create_sample_vm')
126
+
127
+    if not (xcat_configure_kvm or create_sample_vm):
128
+        set_state('ibm-xcat.configured')
129
+        return
130
+
131
+    kvm_host = cfg.get('kvm_host')
132
+    kvm_root_password = cfg.get('kvm_root_password')
133
+    with open(os.path.expanduser('~') + "/.ssh/id_rsa.pub") as sshkeyfile:
134
+        sshkey = sshkeyfile.readlines()
135
+
136
+    if not copy_ssh_key(sshkey[0].rstrip('\n'), kvm_host, kvm_root_password):
137
+        hookenv.status_set("error", "Error while copying ssh key")
138
+        return
139
+
140
+    if not create_sample_vm:
141
+        set_state('ibm-xcat.configured')
142
+        return
143
+
144
+    try:
145
+        sample_vm_name = cfg.get('sample_vm_name')
146
+        sample_vm_ip = cfg.get('sample_vm_ip')
147
+        vmrootpassword = random_string()
148
+        vmvncpassword = random_string()
149
+        sample_vm_cpu = cfg.get('sample_vm_cpu')
150
+        sample_vm_mem = cfg.get('sample_vm_mem')
151
+        kvm_bridge = cfg.get('kvm_bridge')
152
+        sample_vm_image = cfg.get('sample_vm_image')
153
+        args = ['scripts/create_xcat_vm.sh', sample_vm_name,
154
+                sample_vm_ip, vmrootpassword, vmvncpassword,
155
+                sample_vm_cpu, kvm_host, sample_vm_mem, kvm_bridge,
156
+                'files/archive/'+sample_vm_image]
157
+
158
+        subprocess.check_call(args)
159
+    except subprocess.CalledProcessError as e:
160
+        print(e.output)
161
+        hookenv.status_set("error", "Error while installing xCAT")
162
+
163
+    set_state('ibm-xcat.configured')
Back to file index

reactive/leadership.py

 1
--- 
 2
+++ reactive/leadership.py
 3
@@ -0,0 +1,68 @@
 4
+# Copyright 2015-2016 Canonical Ltd.
 5
+#
 6
+# This file is part of the Leadership Layer for Juju.
 7
+#
 8
+# This program is free software: you can redistribute it and/or modify
 9
+# it under the terms of the GNU General Public License version 3, as
10
+# published by the Free Software Foundation.
11
+#
12
+# This program is distributed in the hope that it will be useful, but
13
+# WITHOUT ANY WARRANTY; without even the implied warranties of
14
+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15
+# PURPOSE.  See the GNU General Public License for more details.
16
+#
17
+# You should have received a copy of the GNU General Public License
18
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
+
20
+from charmhelpers.core import hookenv
21
+from charmhelpers.core import unitdata
22
+
23
+from charms import reactive
24
+from charms.leadership import leader_get, leader_set
25
+
26
+
27
+__all__ = ['leader_get', 'leader_set']  # Backwards compatibility
28
+
29
+
30
+def initialize_leadership_state():
31
+    '''Initialize leadership.* states from the hook environment.
32
+
33
+    Invoked by hookenv.atstart() so states are available in
34
+    @hook decorated handlers.
35
+    '''
36
+    is_leader = hookenv.is_leader()
37
+    if is_leader:
38
+        hookenv.log('Initializing Leadership Layer (is leader)')
39
+    else:
40
+        hookenv.log('Initializing Leadership Layer (is follower)')
41
+
42
+    reactive.helpers.toggle_state('leadership.is_leader', is_leader)
43
+
44
+    previous = unitdata.kv().getrange('leadership.settings.', strip=True)
45
+    current = hookenv.leader_get()
46
+
47
+    # Handle deletions.
48
+    for key in set(previous.keys()) - set(current.keys()):
49
+        current[key] = None
50
+
51
+    any_changed = False
52
+    for key, value in current.items():
53
+        reactive.helpers.toggle_state('leadership.changed.{}'.format(key),
54
+                                      value != previous.get(key))
55
+        if value != previous.get(key):
56
+            any_changed = True
57
+        reactive.helpers.toggle_state('leadership.set.{}'.format(key),
58
+                                      value is not None)
59
+    reactive.helpers.toggle_state('leadership.changed', any_changed)
60
+
61
+    unitdata.kv().update(current, prefix='leadership.settings.')
62
+
63
+
64
+# Per https://github.com/juju-solutions/charms.reactive/issues/33,
65
+# this module may be imported multiple times so ensure the
66
+# initialization hook is only registered once. I have to piggy back
67
+# onto the namespace of a module imported before reactive discovery
68
+# to do this.
69
+if not hasattr(reactive, '_leadership_registered'):
70
+    hookenv.atstart(initialize_leadership_state)
71
+    reactive._leadership_registered = True
Back to file index

requirements.txt

1
--- 
2
+++ requirements.txt
3
@@ -0,0 +1,2 @@
4
+flake8
5
+pytest
Back to file index

revision

1
--- 
2
+++ revision
3
@@ -0,0 +1 @@
4
+0
Back to file index

scripts/check_xcat_installed.sh

1
--- 
2
+++ scripts/check_xcat_installed.sh
3
@@ -0,0 +1,4 @@
4
+#!/bin/bash
5
+source /etc/profile.d/xcat.sh
6
+lsxcatd -a
7
+tabdump site
Back to file index

scripts/create_xcat_stateless_vm.sh

 1
--- 
 2
+++ scripts/create_xcat_stateless_vm.sh
 3
@@ -0,0 +1,67 @@
 4
+#!/bin/bash
 5
+
 6
+source /etc/profile.d/xcat.sh
 7
+
 8
+set -e
 9
+
10
+function create_vm() {
11
+
12
+  vm=$1
13
+  ip=$2
14
+  vmrootpassword=$3
15
+  vmvncpassword=$4
16
+  vmcpu=$5
17
+  vmhost=$6
18
+  vmmem=$7
19
+  bridge=$8
20
+  ubuntuimgloc=$9
21
+  pkgdir=${10}
22
+
23
+  makedhcp -n
24
+  service isc-dhcp-server restart
25
+  mkdef $vm groups=vm,all
26
+  chdef $vm ip=$ip
27
+  makehosts $vm
28
+  if [ -f /etc/bind/named.conf.options ] ;
29
+  then
30
+      > /etc/bind/named.conf.options
31
+  fi
32
+  makedns -a
33
+  chdef $vm vmcpus=$vmcpu vmhost=$vmhost vmmemory=$vmmem mgt=kvm vmnics=$bridge vmnicnicmodel=virtio serialport=0 serialspeed=115200 netboot=xnba
34
+  chtab node=$vm vm.vidpassword=$vmvncpassword
35
+  chtab key=system passwd.password=$vmrootpassword passwd.username="root"
36
+  mkvm $vm
37
+  copycds ${ubuntuimgloc} 
38
+  netboot=`lsdef -t osimage | grep netboot-compute | cut -d' ' -f1`
39
+  if [ ! -z $netboot ];
40
+  then
41
+    chdef -t osimage -o $netboot -p pkgdir="${pkgdir//\"}"
42
+    if [ ! -f /tftpboot/xcat/osimage/$netboot/*stateless* ];
43
+    then
44
+      genimage $netboot
45
+      packimage $netboot
46
+    fi
47
+    nodeset $vm osimage=$netboot
48
+    rpower $vm on
49
+    count=0
50
+    while [ $count -le 120 ];
51
+    do
52
+        status=`ssh $vm print 2>/dev/null; echo $?`
53
+        if [ $status -eq 0 ];
54
+        then
55
+            echo "VM is up and running"
56
+            exit 0
57
+        fi
58
+        sleep 5
59
+        echo "Waiting for the VM to come up"
60
+        count=$((count+1))
61
+    done
62
+    action-fail "Problem while bringing the VM up"
63
+  else
64
+    action-fail "netboot compute image not found"
65
+  fi
66
+
67
+}
68
+
69
+echo $@
70
+create_vm "$@"
Back to file index

tests/00-setup

1
--- 
2
+++ tests/00-setup
3
@@ -0,0 +1,5 @@
4
+#!/bin/bash
5
+
6
+sudo add-apt-repository ppa:juju/stable -y
7
+sudo apt-get update
8
+sudo apt-get install amulet python-requests -y
Back to file index

tests/10-deploy

 1
--- 
 2
+++ tests/10-deploy
 3
@@ -0,0 +1,31 @@
 4
+#!/usr/bin/python3
 5
+
 6
+import amulet
 7
+import requests
 8
+import unittest
 9
+
10
+
11
+class TestCharm(unittest.TestCase):
12
+    def setUp(self):
13
+        self.d = amulet.Deployment()
14
+
15
+        self.d.add('ibm-xcat')
16
+        self.d.expose('ibm-xcat')
17
+
18
+        self.d.setup(timeout=900)
19
+        self.d.sentry.wait()
20
+
21
+        self.unit = self.d.sentry['ibm-xcat'][0]
22
+
23
+    def test_service(self):
24
+        # test we can access over http
25
+        page = requests.get('http://{}'.format(self.unit.info['public-address']))
26
+        self.assertEqual(page.status_code, 200)
27
+        # Now you can use self.d.sentry[SERVICE][UNIT] to address each of the units and perform
28
+        # more in-depth steps. Each self.d.sentry[SERVICE][UNIT] has the following methods:
29
+        # - .info - An array of the information of that unit from Juju
30
+        # - .file(PATH) - Get the details of a file on that unit
31
+        # - .file_contents(PATH) - Get plain text output of PATH file from that unit
32
+        # - .directory(PATH) - Get details of directory
33
+        # - .directory_contents(PATH) - List files and folders in PATH on that unit
34
+        # - .relation(relation, service:rel) - Get relation data from return service
Back to file index

tox.ini

 1
--- 
 2
+++ tox.ini
 3
@@ -0,0 +1,12 @@
 4
+[tox]
 5
+skipsdist=True
 6
+envlist = py34, py35
 7
+skip_missing_interpreters = True
 8
+
 9
+[testenv]
10
+commands = py.test -v
11
+deps =
12
+    -r{toxinidir}/requirements.txt
13
+
14
+[flake8]
15
+exclude=docs