~ibmcharmers/trusty/ibm-dsm-enterprise

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

CPP?: No
OIL?: No

IBM® Data Server Manager is an integrated database management tools platform for DB2® for Linux, UNIX, and Windows databases. It is simple to set up, easy to use, and enterprise ready with the ability to manage hundreds of databases. This charm takes care of installing and configuring IBM DSM Enterprise Edition.


Tests

Substrate Status Results Last Updated
aws RETRY 19 days ago
lxc RETRY 19 days ago
gce RETRY 19 days ago

Voted: -1
petevg wrote 3 months ago
Hello,

Thank you for your work on this charm. I unfortunately cannot approve it due to the following issues:

* The package_ibm_dsm_enterprise routine writes the password to the juju-debug log. It is not a best practice to write sensitive data like passwords to a log file.

* The tests are not marked as executable, which means that bundletester and other tools will not pick them up and execute them.

* There is one small linter error.

I also had a few (non blocking) notes:

* It looks like the check_platform_architecture routine in ibm-dsm-enterprise is unused. You could remove it, along with the platform import. (Removing this will also fix your linter error)

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. petevg
  • 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. petevg
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. petevg
Should be built using charm layers. petevg
Should use Juju Resources to deliver required payloads. petevg

Testing and Quality

charm proof must pass without errors or warnings. kos.tsakalozos
Must include passing unit, functional, or integration tests. petevg
Tests must exercise all relations.
Tests must exercise config.
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). petevg
Must be self contained unless the charm is a proxy for an existing cloud service, e.g. ec2-elb charm.
Must not use symlinks. petevg
Bundles must only use promulgated charms, they cannot reference charms in personal namespaces. petevg
Must call Juju hook tools (relation-*, unit-*, config-*, etc) without a hard coded path. petevg
Should include a tests.yaml for all integration tests. kos.tsakalozos

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. petevg
Must describe the service. petevg
Must describe how it interacts with other services, if applicable. petevg
Must document the interfaces. petevg
Must show how to deploy the charm. petevg
Must define external dependencies, if applicable. petevg
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. petevg

Security

Must not run any network services using default passwords. petevg
Must verify and validate any external payload petevg
  • 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. petevg
Should avoid running services as root. petevg

All changes | Changes since last revision

Source Diff

Files changed 98

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,126 @@
  4
+## Charm for IBM DSM Enterprise
  5
+
  6
+### Overview
  7
+
  8
+IBM DSM Enterprise
  9
+
 10
+IBM Data Server Manager is an integrated database management tools platform for DB2 for Linux, UNIX, and Windows databases.
 11
+It is simple to set up, easy to use, and enterprise ready with the ability to manage hundreds of databases.
 12
+With Data Server Manager, a DBA or a team of DBAs can monitor performance, track configuration changes,
 13
+ administer databases, and proactively or reactively optimize the database environment in one integrated end-to-end tool.
 14
+IBM DSM supports two types of editions, Base and Enterprise Edition. 
 15
+This charm installs IBM DSM and starts DSM Server. In case of Enterprise edition, additionally it configures Repository DB. 
 16
+
 17
+More information on IBM DSM available at [IBM Knowledge Center](http://www.ibm.com/support/knowledgecenter/en/SS5Q8A_2.1.0/com.ibm.datatools.dsweb.ots.over.doc/topics/overview.html)
 18
+
 19
+## Prerequisites
 20
+
 21
+This charm makes use of resources, a feature only available in Juju 2.0. During deploy or upgrade, you will need to specify the installable package(s) required by this charm. 
 22
+
 23
+From [Product Package Site](https://www.ibm.com/services/forms/preLogin.do?source=swg-rddsm), download the product package ibm-datasrvmgr-<platform> and the License Activation Kit (LAK).
 24
+Alternatively, from [Passport Advantage Site](http://www-01.ibm.com/software/passportadvantage/), 
 25
+download the package: ibm-dsm-ee-and-lak-<platform> or ibm-dsm-ee-<platform> for Enterprise Edition or ibm-dsm-base-<platform> for Base Edition.
 26
+
 27
+Before Installing the product, check the prerequisites from [Prerequisites Page](http://www-969.ibm.com/software/reports/compatibility/clarity-reports/report/html/softwareReqsForProduct?deliverableId=7FAAAD904C1E11E5BC9A648287B82B03&osPlatforms=Linux#!)
 28
+
 29
+### Packages for Ubuntu on AMD64 (x86_64):
 30
+
 31
+IBM DSM Server Enterprise(`ibm-dsm-ee-linux-x86_64.tgz`) and 
 32
+IBM DSM Server License Activation Kit (LAK) (`ibm-dsm-lak-only-linux.tgz`)
 33
+
 34
+or both IBM DSM Server Enterprise and IBM DSM Server License Activation Kit (`ibm-dsm-ee-and-lak-linux-x86_64.tgz`)
 35
+
 36
+### Packages for Ubuntu on Power (ppc64le):
 37
+
 38
+For Ubuntu on Power, Both Base Package and Fixpack 2.1 or later version required to install.
 39
+
 40
+IBM DSM Server Enterprise (`ibm-dsm-ee-plinuxle.tgz`) and 
 41
+IBM DSM Server License Activation Kit (LAK) (`ibm-dsm-lak-only-plinuxle.tgz`)
 42
+
 43
+or both IBM DSM Server Enterprise and IBM DSM Server License Activation Kit (`ibm-dsm-ee-and-lak-plinuxle.tgz`)
 44
+
 45
+## Usage
 46
+IBM DSM is of two types or editions. Base and Enterprise Editions. This charm takes care of installing Enterprise edition.
 47
+
 48
+To use this charm, you must agree to the Terms of Use. You can view the full license for `IBM Data Server Manager Enterprise Edition` products by visiting the [IBM-DSM-License-Info](http://www-03.ibm.com/software/sla/sladb.nsf/searchlis/?searchview&searchorder=4&searchmax=0&query=%28IBM+Data+server+manager%29) page for both Base and Enterprise Edition.
 49
+Choose the license that applies to the version and edition that you are using.
 50
+
 51
+### Deploy
 52
+
 53
+1. Update file `config.yaml` for DSM Admin password. 
 54
+   If no option is given, IBM-DSM Enterprise charm will be blocked untill admin password is not provided. This password is required to log on to DSM GUI. 
 55
+   
 56
+    User can provide the dsm admin password through command-line as follow:
 57
+
 58
+        juju config ibm-dsm-enterprise dsm_admin_password="<password>"
 59
+
 60
+2. Run the following commands to deploy this charm:
 61
+
 62
+	On x86_64 and ppc64le:
 63
+	
 64
+	    juju deploy ibm-dsm-enterprise --resource ibm_dsm_installer=</path/to/ibm_dsm_installer.tgz> --resource ibm_dsm_license=</path/to/ibm_dsm_license.tgz> 
 65
+		
 66
+    **Note**: This charm requires acceptance of Terms of Use. When deploying from the Charm Store, these terms will be presented to you for your consideration.  
 67
+         To accept the terms:
 68
+    
 69
+        juju agree ibmcharmers/ibm-dsm-enterprise/1
 70
+
 71
+
 72
+3. To Expose the IBM DSM Service Enterprise to public, run the following command:
 73
+
 74
+		juju expose ibm-dsm-enterprise
 75
+
 76
+
 77
+### Upgrade
 78
+
 79
+Once deployed, user can upgrade the existing installation by installing fixpacks:
 80
+
 81
+If user wants to upgrade existing installation of Installtion Manger, run the following command:
 82
+
 83
+	juju attach ibm-dsm-enterprise ibm_dsm_fixpack=</path/to/ibm_dsm.fixpack.tgz>
 84
+	juju attach ibm-dsm-enterprise ibm_dsm_fixpack_license=</path/to/ibm_dsm.fixpack.license.tgz>
 85
+
 86
+### Verification
 87
+
 88
+After installing IBM DSM Server, The Data server Manager should be Up and Running and it can be accessed through GUI. To get connected to IBM-DSM charm GUI:
 89
+Open following URL in your favorite Browser:
 90
+
 91
+    * Web console HTTP URL
 92
+	    http:<IP Address>:11080   (login: admin)
 93
+	* Web console HTTPS URL
 94
+	    https:<IP Address>:11081   (login: admin)
 95
+
 96
+
 97
+## Relating with IBM DB2 charm
 98
+
 99
+Install IBM DB2 Charm 
100
+
101
+IBM DSM charm is related to IBM DB2 Charm to configure the repository DB in Enterprise Edition only.
102
+To add a relation, run the following command:
103
+
104
+	juju add-relation ibm-dsm-enterprise ibm-db2
105
+	
106
+The above relation uses `ibm-db2` interface. When IBM DSM charm is related to IBM DB2, user can use the IBM DSM GUI to monitor the Databases.
107
+
108
+### Verification
109
+
110
+After establishing relation with IBM DB2 charms, user can access GUI and will have a Repository Profile database created with name as `<IP Address of Repo DB2>-dsment`
111
+
112
+To remove the relation, run the following command:
113
+
114
+	juju remove-relation ibm-dsm-enterprise ibm-db2
115
+
116
+## IBM Data Server Manager Information
117
+
118
+(1) General Information 
119
+
120
+Details about IBM DSM software information is [here](http://www.ibm.com/support/knowledgecenter/en/SS5Q8A/product_welcome.html)
121
+
122
+Information on procuring DSM product is available at the [Passport Advantage site](http://www-01.ibm.com/software/passportadvantage/)
123
+
124
+Information about licensing of IBM DSM on [here](http://www-03.ibm.com/software/sla/sladb.nsf/searchlis/?searchview&searchorder=4&searchmax=0&query=%28IBM+Data+server+manager%29)
125
+
126
+
127
+## Contact Information
128
+
129
+For issues with this charm, please contact IBM Juju Support team <jujusupp@us.ibm.com>
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,6 @@
4
+"options":
5
+  "dsm_admin_password":
6
+    "type": "string"
7
+    "default": ""
8
+    "description": |
9
+      Base or Enetrprise Edition
Back to file index

copyright

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

deps/layer/layer-apt/.gitignore

1
--- 
2
+++ deps/layer/layer-apt/.gitignore
3
@@ -0,0 +1,2 @@
4
+*~
5
+*.pyc
Back to file index

deps/layer/layer-apt/README.md

  1
--- 
  2
+++ deps/layer/layer-apt/README.md
  3
@@ -0,0 +1,195 @@
  4
+# Apt layer
  5
+
  6
+The Apt layer for Juju enables layered charms to more easily deal with
  7
+deb packages and apt sources in a simple and efficient manner. It
  8
+provides consistent configuration for operators, allowing them to
  9
+easily specify custom apt sources and additional debs required for
 10
+their particular installations.
 11
+
 12
+## Configuration
 13
+
 14
+The charm may provide defaults for these service configuration
 15
+(config.yaml) options, and the operator may override them as required.
 16
+
 17
+* `extra_packages`
 18
+
 19
+  A space separated list of additional deb packages to install on
 20
+  each unit.
 21
+
 22
+* `package_status`
 23
+
 24
+  'install' or 'hold'. When set to hold, packages installed using
 25
+  the Apt layer API will be pinned, so that they will not be
 26
+  automatically upgraded when package updates are performed. 'hold'
 27
+  is particularly useful for allowing a service such as Landscape
 28
+  to automatically apply security updates to most of the system,
 29
+  whilst holding back any potentially service affecting updates.
 30
+
 31
+* `install_sources`
 32
+
 33
+  A list of apt sources containing the packages that need to be installed.
 34
+  Each source may be either a line that can be added directly to
 35
+  sources.list(5), or in the form ppa:<user>/<ppa-name> for adding
 36
+  Personal Package Archives, or a distribution component to enable.
 37
+  The list is a yaml list, encoded as a string. The nicest way of
 38
+  declaring this in a yaml file looks like the following (in particular,
 39
+  the | character indicates that the value is a multiline string):
 40
+
 41
+  ```yaml
 42
+  install_sources: |
 43
+      - ppa:stub/cassandra
 44
+      - deb http://www.apache.org/dist/cassandra/debian 21x main
 45
+  ```
 46
+
 47
+* `install_keys`
 48
+
 49
+  A list of GPG signing keys to accept. There needs to be one entry
 50
+  per entry in install_sources. null may be used if no keep is
 51
+  needed, which is the case for PPAs and for the standard Ubuntu
 52
+  archives. Keys should be full ASCII armoured GPG public keys.
 53
+  GPG key ids are also accepted, but in most environments this
 54
+  mechanism is not secure. The install_keys list, like
 55
+  install_sources, must also be a yaml formatted list encoded as
 56
+  a string:
 57
+
 58
+  ```yaml
 59
+  install_keys: |
 60
+      - null
 61
+      - |
 62
+          -----BEGIN PGP PUBLIC KEY BLOCK-----
 63
+          Version: GnuPG v1
 64
+
 65
+          mQINBFQJvgUBEAC0KcYCTj0hd15p4fiXBsbob0sKgsvN5Lm7N9jzJWlGshJ0peMi
 66
+          kH8YhDXw5Lh+mPEHksL7t1L8CIr1a+ntns/Opt65ZPO38ENVkOqEVAn9Z5sIoZsb
 67
+          AUeLlJzSeRLTKhcOugK7UcsQD2FHnMBJz50bxis9X7pjmnc/tWpjAGJfaWdjDIo=
 68
+          =yiQ4
 69
+          -----END PGP PUBLIC KEY BLOCK-----
 70
+  ```
 71
+
 72
+## Usage
 73
+
 74
+Queue packages for installation, and have handlers waiting for
 75
+these packages to finish being installed:
 76
+
 77
+```python
 78
+import charms.apt
 79
+
 80
+@hook('install')
 81
+def install():
 82
+    charms.apt.queue_install(['git'])
 83
+
 84
+@when_not('apt.installed.gnupg')
 85
+def install_gnupg():
 86
+    charms.apt.queue_install(['gnupg'])
 87
+
 88
+@when('apt.installed.git')
 89
+@when('apt.installed.gnupg')
 90
+def grabit():
 91
+    clone_repo()
 92
+    validate_repo()
 93
+```
 94
+
 95
+### API
 96
+
 97
+Several methods are exposed in the charms.apt Python package.
 98
+
 99
+* `add_source(source, key=None)`
100
+
101
+  Add an apt source.
102
+
103
+  A source may be either a line that can be added directly to
104
+  sources.list(5), or in the form ppa:<user>/<ppa-name> for adding
105
+  Personal Package Archives, or a distribution component to enable.
106
+
107
+  The package signing key should be an ASCII armoured GPG key. While
108
+  GPG key ids are also supported, the retrieval mechanism is insecure.
109
+  There is no need to specify the package signing key for PPAs or for
110
+  the main Ubuntu archives.
111
+
112
+  It is preferable if charms do not call this directly to hard
113
+  coded apt sources, but instead have these sources listed
114
+  as defaults in the install_sources config option. This allows
115
+  operators to mirror your packages to internal archives and
116
+  deploy your charm in environments without network access.
117
+
118
+  Sets the `apt.needs_update` reactive state.
119
+
120
+* `queue_install(packages, options=None)`
121
+
122
+  Queue one or more deb packages for install. The actual package
123
+  installation will be performed later by a handler in the
124
+  apt layer. The `apt.installed.{name}` state will be set once
125
+  the package installed (one state for each package).
126
+
127
+  If a package has already been installed it will not be reinstalled.
128
+
129
+  If a package has already been queued it will not be requeued, and
130
+  the install options will not be changed.
131
+
132
+* `installed()`
133
+
134
+  Returns the set of deb packages installed by this layer.
135
+
136
+* `purge(packages)`
137
+
138
+  Purge one or more deb packages from the system
139
+
140
+
141
+### Extras
142
+
143
+These methods are called automatically by the reactive framework as
144
+reactive state demands. However, you can also invoke them directly
145
+if you want the operation done right now.
146
+
147
+* `update()`
148
+
149
+  Update the apt cache. Removes the `apt.needs_update` state.
150
+
151
+
152
+* `install_queued()`
153
+
154
+  Installs deb packages queued for installation. On success, removes
155
+  the `apt.queued_installs` state, sets the `apt.installed.{packagename}`
156
+  state for each installed package, and returns True. On failure,
157
+  sets the unit workload status to blocked and returns False.
158
+  The package installs remain queued.
159
+
160
+
161
+### Layer Options
162
+
163
+Packages can be specified at charm-build time in `layer.yaml`. List the
164
+packages in the 'basic' or 'apt' sections.
165
+
166
+```yaml
167
+includes:
168
+  - layer:basic
169
+  - layer:apt
170
+options:
171
+  basic:
172
+    packages:
173
+      - python3-psycopg2
174
+  apt:
175
+    packages:
176
+      - git
177
+      - bzr
178
+```
179
+
180
+Packages required to import your Python reactive handlers should go
181
+under 'basic'. These get installed by the base layer very early during
182
+charm bootstrap, and only packages available in the main Ubuntu archive
183
+can go here. Other packages should go under 'apt'. These will be
184
+installed later, after custom apt sources such as PPAs have been added
185
+from the `install_sources` configuration option.
186
+
187
+
188
+## Support
189
+
190
+This layer is maintained on Launchpad by
191
+Stuart Bishop (stuart.bishop@canonical.com).
192
+
193
+Code is available using git at git+ssh://git.launchpad.net/layer-apt.
194
+
195
+Bug reports can be made at https://bugs.launchpad.net/layer-apt.
196
+
197
+Queries and comments can be made on the Juju mailing list, Juju IRC
198
+channels, or at https://answers.launchpad.net/layer-apt.
Back to file index

deps/layer/layer-apt/config.yaml

 1
--- 
 2
+++ deps/layer/layer-apt/config.yaml
 3
@@ -0,0 +1,34 @@
 4
+options:
 5
+  extra_packages:
 6
+    description: >
 7
+        Space separated list of extra deb packages to install.
 8
+    type: string
 9
+    default: ""
10
+  package_status:
11
+    default: "install"
12
+    type: string
13
+    description: >
14
+        The status of service-affecting packages will be set to this
15
+        value in the dpkg database. Valid values are "install" and "hold".
16
+  install_sources:
17
+    description: >
18
+        List of extra apt sources, per charm-helpers standard
19
+        format (a yaml list of strings encoded as a string). Each source
20
+        may be either a line that can be added directly to
21
+        sources.list(5), or in the form ppa:<user>/<ppa-name> for adding
22
+        Personal Package Archives, or a distribution component to enable.
23
+    type: string
24
+    default: ""
25
+  install_keys:
26
+    description: >
27
+        List of signing keys for install_sources package sources, per
28
+        charmhelpers standard format (a yaml list of strings encoded as
29
+        a string). The keys should be the full ASCII armoured GPG public
30
+        keys. While GPG key ids are also supported and looked up on a
31
+        keyserver, operators should be aware that this mechanism is
32
+        insecure. null can be used if a standard package signing key is
33
+        used that will already be installed on the machine, and for PPA
34
+        sources where the package signing key is securely retrieved from
35
+        Launchpad.
36
+    type: string
37
+    default: ""
Back to file index

deps/layer/layer-apt/copyright

 1
--- 
 2
+++ deps/layer/layer-apt/copyright
 3
@@ -0,0 +1,15 @@
 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/>.
Back to file index

deps/layer/layer-apt/layer.yaml

1
--- 
2
+++ deps/layer/layer-apt/layer.yaml
3
@@ -0,0 +1,5 @@
4
+defines:
5
+  packages:
6
+    type: array
7
+    default: []
8
+    description: Additional packages to be installed at time of bootstrap
Back to file index

deps/layer/layer-apt/lib/charms/__init__.py

1
--- 
2
+++ deps/layer/layer-apt/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

deps/layer/layer-apt/lib/charms/apt.py

  1
--- 
  2
+++ deps/layer/layer-apt/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.get('package_status') or ''
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

deps/layer/layer-apt/reactive/apt.py

  1
--- 
  2
+++ deps/layer/layer-apt/reactive/apt.py
  3
@@ -0,0 +1,132 @@
  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') or ''
 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') or ''
 95
+    keys = config.get('install_keys') or ''
 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
+    # Clumsy 'config.get() or' per Bug #1641362
103
+    extra_packages = sorted((config.get('extra_packages') or '').split())
104
+    if extra_packages:
105
+        charms.apt.queue_install(extra_packages)
106
+
107
+
108
+def queue_layer_packages():
109
+    """Add packages listed in build-time layer options."""
110
+    # Both basic and apt layer. basic layer will have already installed
111
+    # its defined packages, but rescheduling it here gets the apt layer
112
+    # state set and they will pinned as any other apt layer installed
113
+    # package.
114
+    opts = layer.options()
115
+    for section in ['basic', 'apt']:
116
+        if section in opts and 'packages' in opts[section]:
117
+            charms.apt.queue_install(opts[section]['packages'])
118
+
119
+
120
+# Per https://github.com/juju-solutions/charms.reactive/issues/33,
121
+# this module may be imported multiple times so ensure the
122
+# initialization hook is only registered once. I have to piggy back
123
+# onto the namespace of a module imported before reactive discovery
124
+# to do this.
125
+if not hasattr(reactive, '_apt_registered'):
126
+    # We need to register this to run every hook, not just during install
127
+    # and config-changed, to protect against race conditions. If we don't
128
+    # do this, then the config in the hook environment may show updates
129
+    # to running hooks well before the config-changed hook has been invoked
130
+    # and the intialization provided an opertunity to be run.
131
+    hookenv.atstart(hookenv.log, 'Initializing Apt Layer')
132
+    hookenv.atstart(clear_removed_package_states)
133
+    hookenv.atstart(configure_sources)
134
+    hookenv.atstart(queue_layer_packages)
135
+    reactive._apt_registered = True
Back to file index

deps/layer/layer-basic/.gitignore

1
--- 
2
+++ deps/layer/layer-basic/.gitignore
3
@@ -0,0 +1,5 @@
4
+*.pyc
5
+*~
6
+.ropeproject
7
+.settings
8
+.tox
Back to file index

deps/layer/layer-basic/Makefile

 1
--- 
 2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/README.md

  1
--- 
  2
+++ deps/layer/layer-basic/README.md
  3
@@ -0,0 +1,221 @@
  4
+# Overview
  5
+
  6
+This is the base layer for all charms [built using layers][building].  It
  7
+provides all of the standard Juju hooks and runs the
  8
+[charms.reactive.main][charms.reactive] loop for them.  It also bootstraps the
  9
+[charm-helpers][] and [charms.reactive][] libraries and all of their
 10
+dependencies for use by the charm.
 11
+
 12
+# Usage
 13
+
 14
+To create a charm layer using this base layer, you need only include it in
 15
+a `layer.yaml` file:
 16
+
 17
+```yaml
 18
+includes: ['layer:basic']
 19
+```
 20
+
 21
+This will fetch this layer from [interfaces.juju.solutions][] and incorporate
 22
+it into your charm layer.  You can then add handlers under the `reactive/`
 23
+directory.  Note that **any** file under `reactive/` will be expected to
 24
+contain handlers, whether as Python decorated functions or [executables][non-python]
 25
+using the [external handler protocol][].
 26
+
 27
+### Charm Dependencies
 28
+
 29
+Each layer can include a `wheelhouse.txt` file with Python requirement lines.
 30
+For example, this layer's `wheelhouse.txt` includes:
 31
+
 32
+```
 33
+pip>=7.0.0,<8.0.0
 34
+charmhelpers>=0.4.0,<1.0.0
 35
+charms.reactive>=0.1.0,<2.0.0
 36
+```
 37
+
 38
+All of these dependencies from each layer will be fetched (and updated) at build
 39
+time and will be automatically installed by this base layer before any reactive
 40
+handlers are run.
 41
+
 42
+Note that the `wheelhouse.txt` file is intended for **charm** dependencies only.
 43
+That is, for libraries that the charm code itself needs to do its job of deploying
 44
+and configuring the payload.  If the payload itself has Python dependencies, those
 45
+should be handled separately, by the charm.
 46
+
 47
+See [PyPI][pypi charms.X] for packages under the `charms.` namespace which might
 48
+be useful for your charm.
 49
+
 50
+### Layer Namespace
 51
+
 52
+Each layer has a reserved section in the `charms.layer.` Python package namespace,
 53
+which it can populate by including a `lib/charms/layer/<layer-name>.py` file or
 54
+by placing files under `lib/charms/layer/<layer-name>/`.  (If the layer name
 55
+includes hyphens, replace them with underscores.)  These can be helpers that the
 56
+layer uses internally, or it can expose classes or functions to be used by other
 57
+layers to interact with that layer.
 58
+
 59
+For example, a layer named `foo` could include a `lib/charms/layer/foo.py` file
 60
+with some helper functions that other layers could access using:
 61
+
 62
+```python
 63
+from charms.layer.foo import my_helper
 64
+```
 65
+
 66
+### Layer Options
 67
+
 68
+Any layer can define options in its `layer.yaml`.  Those options can then be set
 69
+by other layers to change the behavior of your layer.  The options are defined
 70
+using [jsonschema][], which is the same way that [action paramters][] are defined.
 71
+
 72
+For example, the `foo` layer could include the following option definitons:
 73
+
 74
+```yaml
 75
+includes: ['layer:basic']
 76
+defines:  # define some options for this layer (the layer "foo")
 77
+  enable-bar:  # define an "enable-bar" option for this layer
 78
+    description: If true, enable support for "bar".
 79
+    type: boolean
 80
+    default: false
 81
+```
 82
+
 83
+A layer using `foo` could then set it:
 84
+
 85
+```yaml
 86
+includes: ['layer:foo']
 87
+options:
 88
+  foo:  # setting options for the "foo" layer
 89
+    enable-bar: true  # set the "enable-bar" option to true
 90
+```
 91
+
 92
+The `foo` layer can then use the `charms.layer.options` helper to load the values
 93
+for the options that it defined.  For example:
 94
+
 95
+```python
 96
+from charms import layer
 97
+
 98
+@when('state')
 99
+def do_thing():
100
+  layer_opts = layer.options('foo')  # load all of the options for the "foo" layer
101
+  if layer_opts['enable-bar']:  # check the value of the "enable-bar" option
102
+      hookenv.log("Bar is enabled")
103
+```
104
+
105
+You can also access layer options in other handlers, such as Bash, using
106
+the command-line interface:
107
+
108
+```bash
109
+. charms.reactive.sh
110
+
111
+@when 'state'
112
+function do_thing() {
113
+    if layer_option foo enable-bar; then
114
+        juju-log "Bar is enabled"
115
+        juju-log "bar-value is: $(layer_option foo bar-value)"
116
+    fi
117
+}
118
+
119
+reactive_handler_main
120
+```
121
+
122
+Note that options of type `boolean` will set the exit code, while other types
123
+will be printed out.
124
+
125
+# Hooks
126
+
127
+This layer provides hooks that other layers can react to using the decorators
128
+of the [charms.reactive][] library:
129
+
130
+  * `config-changed`
131
+  * `install`
132
+  * `leader-elected`
133
+  * `leader-settings-changed`
134
+  * `start`
135
+  * `stop`
136
+  * `upgrade-charm`
137
+  * `update-status`
138
+
139
+Other hooks are not implemented at this time. A new layer can implement storage
140
+or relation hooks in their own layer by putting them in the `hooks` directory.
141
+
142
+**Note:** Because `update-status` is invoked every 5 minutes, you should take
143
+care to ensure that your reactive handlers only invoke expensive operations
144
+when absolutely necessary.  It is recommended that you use helpers like
145
+[`@only_once`][], [`@when_file_changed`][], and [`data_changed`][] to ensure
146
+that handlers run only when necessary.
147
+
148
+# Layer Configuration
149
+
150
+This layer supports the following options, which can be set in `layer.yaml`:
151
+
152
+  * **packages**  A list of system packages to be installed before the reactive
153
+    handlers are invoked.
154
+
155
+  * **use_venv**  If set to true, the charm dependencies from the various
156
+    layers' `wheelhouse.txt` files will be installed in a Python virtualenv
157
+    located at `$CHARM_DIR/../.venv`.  This keeps charm dependencies from
158
+    conflicting with payload dependencies, but you must take care to preserve
159
+    the environment and interpreter if using `execl` or `subprocess`.
160
+
161
+  * **include_system_packages**  If set to true and using a venv, include
162
+    the `--system-site-packages` options to make system Python libraries
163
+    visible within the venv.
164
+
165
+An example `layer.yaml` using these options might be:
166
+
167
+```yaml
168
+includes: ['layer:basic']
169
+options:
170
+  basic:
171
+    packages: ['git']
172
+    use_venv: true
173
+    include_system_packages: true
174
+```
175
+
176
+
177
+# Reactive States
178
+
179
+This layer will set the following states:
180
+
181
+  * **`config.changed`**  Any config option has changed from its previous value.
182
+    This state is cleared automatically at the end of each hook invocation.
183
+
184
+  * **`config.changed.<option>`** A specific config option has changed.
185
+    **`<option>`** will be replaced by the config option name from `config.yaml`.
186
+    This state is cleared automatically at the end of each hook invocation.
187
+
188
+  * **`config.set.<option>`** A specific config option has a True or non-empty
189
+    value set.  **`<option>`** will be replaced by the config option name from
190
+    `config.yaml`.  This state is cleared automatically at the end of each hook
191
+    invocation.
192
+
193
+  * **`config.default.<option>`** A specific config option is set to its
194
+    default value.  **`<option>`** will be replaced by the config option name
195
+    from `config.yaml`.  This state is cleared automatically at the end of
196
+    each hook invocation.
197
+
198
+An example using the config states would be:
199
+
200
+```python
201
+@when('config.changed.my-opt')
202
+def my_opt_changed():
203
+    update_config()
204
+    restart_service()
205
+```
206
+
207
+
208
+# Actions
209
+
210
+This layer currently does not define any actions.
211
+
212
+
213
+[building]: https://jujucharms.com/docs/devel/authors-charm-building
214
+[charm-helpers]: https://pythonhosted.org/charmhelpers/
215
+[charms.reactive]: https://pythonhosted.org/charms.reactive/
216
+[interfaces.juju.solutions]: http://interfaces.juju.solutions/
217
+[non-python]: https://pythonhosted.org/charms.reactive/#non-python-reactive-handlers
218
+[external handler protocol]: https://pythonhosted.org/charms.reactive/charms.reactive.bus.html#charms.reactive.bus.ExternalHandler
219
+[jsonschema]: http://json-schema.org/
220
+[action paramters]: https://jujucharms.com/docs/stable/authors-charm-actions
221
+[pypi charms.X]: https://pypi.python.org/pypi?%3Aaction=search&term=charms.&submit=search
222
+[`@only_once`]: https://pythonhosted.org/charms.reactive/charms.reactive.decorators.html#charms.reactive.decorators.only_once
223
+[`@when_file_changed`]: https://pythonhosted.org/charms.reactive/charms.reactive.decorators.html#charms.reactive.decorators.when_file_changed
224
+[`data_changed`]: https://pythonhosted.org/charms.reactive/charms.reactive.helpers.html#charms.reactive.helpers.data_changed
Back to file index

deps/layer/layer-basic/bin/layer_option

 1
--- 
 2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/copyright

 1
--- 
 2
+++ deps/layer/layer-basic/copyright
 3
@@ -0,0 +1,9 @@
 4
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0
 5
+
 6
+Files: *
 7
+Copyright: 2015, Canonical Ltd.
 8
+License: GPL-3
 9
+
10
+License: GPL-3
11
+ On Debian GNU/Linux system you can find the complete text of the
12
+ GPL-3 license in '/usr/share/common-licenses/GPL-3'
Back to file index

deps/layer/layer-basic/hooks/config-changed

 1
--- 
 2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/hooks/hook.template

 1
--- 
 2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/hooks/install

 1
--- 
 2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/hooks/leader-elected

 1
--- 
 2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/hooks/leader-settings-changed

 1
--- 
 2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/hooks/start

 1
--- 
 2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/hooks/stop

 1
--- 
 2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/hooks/update-status

 1
--- 
 2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/hooks/upgrade-charm

 1
--- 
 2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/layer.yaml

 1
--- 
 2
+++ deps/layer/layer-basic/layer.yaml
 3
@@ -0,0 +1,18 @@
 4
+defines:
 5
+  packages:
 6
+    type: array
 7
+    default: []
 8
+    description: Additional packages to be installed at time of bootstrap
 9
+  use_venv:
10
+    type: boolean
11
+    default: false
12
+    description: >
13
+      Install charm dependencies (wheelhouse) into a Python virtual environment
14
+      to help avoid conflicts with other charms or libraries on the machine.
15
+  include_system_packages:
16
+    type: boolean
17
+    default: false
18
+    description: >
19
+      If using a virtual environment, allow the venv to see Python packages
20
+      installed at the system level.  This reduces isolation, but is necessary
21
+      to use Python packages installed via apt-get.
Back to file index

deps/layer/layer-basic/lib/charms/layer/__init__.py

 1
--- 
 2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/lib/charms/layer/basic.py

  1
--- 
  2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/lib/charms/layer/execd.py

  1
--- 
  2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/metadata.yaml

1
--- 
2
+++ deps/layer/layer-basic/metadata.yaml
3
@@ -0,0 +1 @@
4
+{}
Back to file index

deps/layer/layer-basic/requirements.txt

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

deps/layer/layer-basic/tox.ini

 1
--- 
 2
+++ deps/layer/layer-basic/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
Back to file index

deps/layer/layer-basic/wheelhouse.txt

1
--- 
2
+++ deps/layer/layer-basic/wheelhouse.txt
3
@@ -0,0 +1,3 @@
4
+pip>=7.0.0,<8.2.0
5
+charmhelpers>=0.4.0,<1.0.0
6
+charms.reactive>=0.1.0,<2.0.0
Back to file index

deps/layer/layer-leadership/.gitignore

1
--- 
2
+++ deps/layer/layer-leadership/.gitignore
3
@@ -0,0 +1,2 @@
4
+*~
5
+*.pyc
Back to file index

deps/layer/layer-leadership/README.md

 1
--- 
 2
+++ deps/layer/layer-leadership/README.md
 3
@@ -0,0 +1,88 @@
 4
+# Leadership Layer for Juju Charms
 5
+
 6
+The Leadership layer is for charm-tools and 'charm build', making it
 7
+easier for layered charms to deal with Juju leadership.
 8
+
 9
+This layer will initialize charms.reactive states, allowing you to
10
+write handlers that will be activated by these states. It allows you
11
+to completely avoid writing leader-elected and leader-settings-changed
12
+hooks. As a simple example, these two handlers are all that is required
13
+to make the leader unit generate a password if it is not already set,
14
+and have the shared password stored in a file on all units:
15
+
16
+```python
17
+import charms.leadership
18
+from charmhelpers.core.host import pwgen
19
+
20
+
21
+@when('leadership.is_leader')
22
+@when_not('leadership.set.admin_password')
23
+def generate_secret():
24
+    charms.leadership.leader_set(admin_password=pwgen())
25
+
26
+
27
+@when('leadership.changed.admin_password')
28
+def store_secret():
29
+    write_file('/etc/foopass', leader_get('admin_password'))
30
+```
31
+
32
+
33
+## States
34
+
35
+The following states are set appropriately on startup, before any @hook
36
+decorated methods are invoked:
37
+
38
+* `leadership.is_leader`
39
+
40
+  This state is set when the unit is the leader. The unit will remain
41
+  the leader for the remainder of the hook, but may not be leader in
42
+  future hooks.
43
+
44
+* `leadership.set.{varname}`
45
+
46
+  This state is set for each leadership setting (ie. the
47
+  `leadership.set.foo` state will be set if the leader has set
48
+  the foo leadership setting to any value). It will remain
49
+  set for the remainder of the hook, unless the unit is the leader
50
+  and calls `reactive.leadership.leader_set()` and resets the value
51
+  to None.
52
+
53
+* `leadership.changed.{varname}`
54
+
55
+  This state is set for each leadership setting that has changed
56
+  since the last hook. It will remain set for the remainder of the
57
+  hook. It will not be set in the next hook, unless the leader has
58
+  changed the leadership setting yet again.
59
+
60
+* `leadership.changed`
61
+
62
+  One or more leadership settings has changed since the last hook.
63
+  This state will remain set for the remainder of the hook. It will
64
+  not be set in the next hook, unless the leader has made further
65
+  changes.
66
+
67
+
68
+## Methods
69
+
70
+The `charms.leadership` module exposes the `leader_set()` and
71
+`leader_get()` methods, which match the methods found in the
72
+`charmhelpers.core.hookenv` module. `reactive.leadership.leader_set()`
73
+should be used instead of the charmhelpers function to ensure that
74
+the reactive state is updated when the leadership settings are. If you
75
+do not do this, then you risk handlers waiting on these states to not
76
+be run on the leader (because when the leader changes settings, it 
77
+triggers leader-settings-changed hooks on the follower units but
78
+no hooks on itself).
79
+
80
+
81
+## Support
82
+
83
+This layer is maintained on Launchpad by
84
+Stuart Bishop (stuart.bishop@canonical.com).
85
+
86
+Code is available using git at git+ssh://git.launchpad.net/layer-leadership.
87
+
88
+Bug reports can be made at https://bugs.launchpad.net/layer-leadership.
89
+
90
+Queries and comments can be made on the Juju mailing list, Juju IRC
91
+channels, or at https://answers.launchpad.net/layer-leadership.
Back to file index

deps/layer/layer-leadership/copyright

 1
--- 
 2
+++ deps/layer/layer-leadership/copyright
 3
@@ -0,0 +1,15 @@
 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/>.
Back to file index

deps/layer/layer-leadership/layer.yaml

 1
--- 
 2
+++ deps/layer/layer-leadership/layer.yaml
 3
@@ -0,0 +1,17 @@
 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
+includes:
20
+    - layer:basic
Back to file index

deps/layer/layer-leadership/lib/charms/__init__.py

1
--- 
2
+++ deps/layer/layer-leadership/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

deps/layer/layer-leadership/lib/charms/leadership.py

 1
--- 
 2
+++ deps/layer/layer-leadership/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

deps/layer/layer-leadership/reactive/leadership.py

 1
--- 
 2
+++ deps/layer/layer-leadership/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

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/ibm-dsm-db2-relation-broken

 1
--- 
 2
+++ hooks/ibm-dsm-db2-relation-broken
 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/ibm-dsm-db2-relation-changed

 1
--- 
 2
+++ hooks/ibm-dsm-db2-relation-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/ibm-dsm-db2-relation-departed

 1
--- 
 2
+++ hooks/ibm-dsm-db2-relation-departed
 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/ibm-dsm-db2-relation-joined

 1
--- 
 2
+++ hooks/ibm-dsm-db2-relation-joined
 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/relations/db2/README.md

 1
--- 
 2
+++ hooks/relations/db2/README.md
 3
@@ -0,0 +1,66 @@
 4
+# Overview
 5
+
 6
+This interface layer handles the communication between IBM DB2 and Consumer
 7
+charms. The provider end of this interface provides the IBM DB2 service. The
 8
+consumer part requires the existence of a provider to function.
 9
+
10
+## Usage
11
+
12
+### Provides
13
+
14
+This interface layer will set the following states, as appropriate:
15
+
16
+- `{relation_name}.connected`: The relation is established, but IBM DB2 may not
17
+yet have provided any connection or service information.
18
+
19
+- `{relation_name}.sshconfigured`: A consumer ssh key has been added to the
20
+IBM DB2 `authorized_keys` file.
21
+
22
+- `{relation_name}.ready`: IBM DB2 has established relation with the consumer
23
+charm and the SSH key has been exchanged. DB2 has provided its connection
24
+string information, and is ready to accept requests from the consumer.
25
+
26
+- `{relation_name}.departed`: The relation has been removed. Any cleanup
27
+related to the consumer charm should happen now on the IBM DB2 charm since the
28
+consumer is going away.
29
+
30
+
31
+### Requires
32
+
33
+Consumers like WAS, Mobile First Server, etc require this interface to connect
34
+to IBM DB2. This interface layer will set the following states, as appropriate:
35
+
36
+- `{relation_name}.connected`: The consumer charm has been related to IBM DB2.
37
+At this point, the consumer charm sends the SSH key to IBM DB2 charm.
38
+
39
+    For example, the consumer charm can send the ssh key to IBM DB2 charm as follows:
40
+
41
+    ```
42
+    @when 'db.connected'
43
+    @when_not 'db.sshconfigured'
44
+    function configure_sshkeys_dbs() {
45
+        SSH_PATH=/root/.ssh
46
+        if [ ! -f  $SSH_PATH/id_rsa.pub ]; then
47
+                juju-log "Setting up SSH keys."
48
+                ssh-keygen -t rsa -f $SSH_PATH/id_rsa -N ''
49
+        fi
50
+        key="`cat $SSH_PATH/id_rsa.pub`"
51
+        relation_call --state=db.connected set_ssh_keys "$key" || true
52
+    }
53
+    ```
54
+
55
+-  `{relation_name}.ready`: IBM DB2 is ready to send connection information.
56
+The consumer charm can access the IBM DB2 details from the interface using the
57
+following methods:
58
+
59
+   - get_db2_hostname() - host name of IBM DB2
60
+   - get_db2_port() - port number of IBM DB2
61
+   - get_db2_path() - the install location of IBM DB2
62
+   - get_dbusername() - User name created for the consumer service to connect to DB2
63
+   - get_dbuserpw() - Password for connecting to DB2
64
+   - get_db2_instance_name() - Instance name created for the consumer service
65
+
66
+
67
+- `{relation_name}.departed`: The relation has been removed. Any cleanup
68
+related to IBM DB2 should happen now on the consumer charm since IBM DB2 is
69
+going away.
Back to file index

hooks/relations/db2/interface.yaml

1
--- 
2
+++ hooks/relations/db2/interface.yaml
3
@@ -0,0 +1,4 @@
4
+name: db2
5
+summary: Facilitates communication between IBM DB2 and database consumers
6
+version: 1
7
+maintainer: "IBM Juju Support Team <jujusupp@us.ibm.com>"
Back to file index

hooks/relations/db2/provides.py

 1
--- 
 2
+++ hooks/relations/db2/provides.py
 3
@@ -0,0 +1,88 @@
 4
+from charms.reactive import hook
 5
+from charms.reactive import RelationBase
 6
+from charms.reactive import scopes
 7
+
 8
+
 9
+class db2Provides(RelationBase):
10
+    # Every unit connecting will get the same information
11
+    scope = scopes.SERVICE
12
+    # convenient way to provide accessor methods
13
+    auto_accessors = ['db2_path', 'db2_port']
14
+
15
+    @hook('{provides:db2}-relation-joined')
16
+    def joined(self):
17
+        conversation = self.conversation()
18
+        conversation.remove_state('{relation_name}.departed')
19
+        conversation.set_state('{relation_name}.connected')
20
+
21
+    @hook('{provides:db2}-relation-changed')
22
+    def changed(self):
23
+        conversation = self.conversation()
24
+        conversation.remove_state('{relation_name}.departed')
25
+        conversation.set_state('{relation_name}.connected')
26
+        if str(conversation.get_remote('ssh_key')) != "None":
27
+            conversation.set_state('{relation_name}.ready')
28
+
29
+    @hook('{provides:db2}-relation-departed')
30
+    def departed(self):
31
+        conversation = self.conversation()
32
+        conversation.remove_state('{relation_name}.connected')
33
+        conversation.remove_state('{relation_name}.ready')
34
+        conversation.set_state('{relation_name}.departed')
35
+
36
+    def dismiss(self, service):
37
+        conversation = self.conversation(service)
38
+        conversation.remove_state('{relation_name}.departed')
39
+
40
+    def reset_states(self, service):
41
+        conversation = self.conversation(service)
42
+        conversation.remove_state('{relation_name}.connected')
43
+        conversation.remove_state('{relation_name}.departed')
44
+        conversation.remove_state('{relation_name}.ready')
45
+
46
+    def set_db_details(self, service, db2_path, db2_port, hostname,
47
+                       dbusername, dbuserpw, db2_instance_name, dbs_created):
48
+        conversation = self.conversation(service)
49
+        conversation.set_remote(data={
50
+            'db2_ready': True,
51
+            'db2_path': db2_path,
52
+            'db2_port': db2_port,
53
+            'hostname': hostname,
54
+            'dbusername': dbusername,
55
+            'dbuserpw': dbuserpw,
56
+            'db2_instance_name': db2_instance_name,
57
+            'dbs_created': dbs_created,
58
+        })
59
+
60
+    def set_db_password(self, service, dbuserpw):
61
+        conversation = self.conversation(service)
62
+        conversation.set_local('dbuserpw', dbuserpw)
63
+
64
+    def get_db_password(self, service):
65
+        conversation = self.conversation(service)
66
+        return conversation.get_local('dbuserpw')
67
+
68
+    def set_sshconfigured(self, service):
69
+        conversation = self.conversation(service)
70
+        conversation.set_state('{relation_name}.sshconfigured')
71
+
72
+    def dismiss_sshconfigured(self, service):
73
+        conversation = self.conversation(service)
74
+        conversation.remove_state('{relation_name}.sshconfigured')
75
+
76
+    def get_sshkeys(self, service):
77
+        conversation = self.conversation(service)
78
+        return conversation.get_remote('ssh_key')
79
+
80
+    def get_dbnames(self, service):
81
+        conversation = self.conversation(service)
82
+        return conversation.get_remote('db_names')
83
+
84
+    def services(self):
85
+        """
86
+        Return a list of services requesting databases.
87
+        """
88
+        service = []
89
+        for conversation in self.conversations():
90
+            service.append(conversation.scope)
91
+        return service
Back to file index

hooks/relations/db2/requires.py

 1
--- 
 2
+++ hooks/relations/db2/requires.py
 3
@@ -0,0 +1,65 @@
 4
+# Licensed under the Apache License, Version 2.0 (the "License");
 5
+# you may not use this file except in compliance with the License.
 6
+# You may obtain a copy of the License at
 7
+#
 8
+#     http://www.apache.org/licenses/LICENSE-2.0
 9
+#
10
+# Unless required by applicable law or agreed to in writing, software
11
+# distributed under the License is distributed on an "AS IS" BASIS,
12
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+# See the License for the specific language governing permissions and
14
+# limitations under the License.
15
+
16
+from charms.reactive import hook
17
+from charms.reactive import RelationBase
18
+from charms.reactive import scopes
19
+
20
+
21
+class db2Requires(RelationBase):
22
+    scope = scopes.GLOBAL
23
+    auto_accessors = ['ssh_key']
24
+
25
+    @hook('{requires:db2}-relation-joined')
26
+    def joined(self):
27
+        self.remove_state('{relation_name}.departed')
28
+        self.set_state('{relation_name}.connected')
29
+
30
+    @hook('{requires:db2}-relation-changed')
31
+    def changed(self):
32
+        self.remove_state('{relation_name}.departed')
33
+        self.set_state('{relation_name}.connected')
34
+        if str(self.get_remote('db2_port')) != "None":
35
+            self.set_state('{relation_name}.ready')
36
+
37
+    @hook('{requires:db2}-relation-departed')
38
+    def departed(self):
39
+        self.remove_state('{relation_name}.connected')
40
+        self.remove_state('{relation_name}.ready')
41
+        self.set_state('{relation_name}.departed')
42
+
43
+    def get_db2_port(self):
44
+        return self.get_remote('db2_port')
45
+
46
+    def get_db2_path(self):
47
+        return self.get_remote('db2_path')
48
+
49
+    def get_db2_hostname(self):
50
+        return self.get_remote('hostname')
51
+
52
+    def get_dbusername(self):
53
+        return self.get_remote('dbusername')
54
+
55
+    def get_dbuserpw(self):
56
+        return self.get_remote('dbuserpw')
57
+
58
+    def get_db2_instance_name(self):
59
+        return self.get_remote('db2_instance_name')
60
+
61
+    def get_db2_dbnames(self):
62
+        return self.get_remote('dbs_created')
63
+
64
+    def set_ssh_keys(self, ssh_key):
65
+        self.set_remote('ssh_key', ssh_key)
66
+
67
+    def set_dbs(self, db_names):
68
+        self.set_remote('db_names', db_names)
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,36 @@
 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 X8 -->
 7
+<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="1in" height="0.999996in" version="1.1" style="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
+  <style type="text/css">
12
+   <![CDATA[
13
+    .fil1 {fill:#999999}
14
+    .fil2 {fill:white;fill-rule:nonzero}
15
+    .fil0 {fill:url(#id2)}
16
+   ]]>
17
+  </style>
18
+    <mask id="id0">
19
+  <linearGradient id="id1" gradientUnits="userSpaceOnUse" x1="500.002" y1="0.00393702" x2="500.002" y2="295.537">
20
+   <stop offset="0" style="stop-opacity:1; stop-color:white"/>
21
+   <stop offset="0.141176" style="stop-opacity:-80.3176; stop-color:white"/>
22
+   <stop offset="1" style="stop-opacity:0; stop-color:white"/>
23
+  </linearGradient>
24
+     <rect style="fill:url(#id1)" width="1000" height="365"/>
25
+    </mask>
26
+  <linearGradient id="id2" gradientUnits="userSpaceOnUse" x1="500.002" y1="999.996" x2="500.002" y2="0">
27
+   <stop offset="0" style="stop-opacity:1; stop-color:#B8471D"/>
28
+   <stop offset="1" style="stop-opacity:1; stop-color:#FEB914"/>
29
+  </linearGradient>
30
+ </defs>
31
+ <g id="Layer_x0020_1">
32
+  <metadata id="CorelCorpID_0Corel-Layer"/>
33
+  <g id="_1094168032">
34
+   <path id="Background" class="fil0" 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"/>
35
+   <path class="fil1" style="mask:url(#id0)" 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"/>
36
+   <path class="fil2" d="M213 505l17 0 0 -277 -17 0 0 277zm83 -262l93 0c37,0 71,13 71,54 0,34 -24,57 -71,57l-93 0 0 -111zm-17 262l110 0c58,0 97,-27 97,-78 0,-55 -55,-69 -62,-67l-1 -1c32,-4 54,-29 54,-59 0,-36 -22,-72 -88,-72l-110 0 0 277zm17 -137l93 0c36,0 80,12 80,59 0,42 -30,63 -80,63l-93 0 0 -122zm227 137l17 0 0 -256 1 0 106 256 17 0 106 -256 1 0 0 256 17 0 0 -277 -25 0 -107 258 -1 0 -108 -258 -24 0 0 277zm-270 120l23 0c33,0 50,16 50,50 0,37 -14,51 -50,52l-23 0 0 -102zm-48 143l79 0c56,0 91,-33 91,-93 0,-57 -34,-92 -91,-92l-79 0 0 185zm193 -62c0,47 38,66 80,66 41,0 81,-15 81,-62 0,-33 -27,-46 -54,-53 -27,-8 -53,-10 -53,-24 0,-13 13,-16 23,-16 15,0 31,5 30,22l48 0c0,-41 -38,-59 -74,-59 -35,0 -75,15 -75,56 0,34 28,45 54,53 26,7 53,10 53,27 0,14 -15,19 -30,19 -20,0 -34,-7 -35,-29l-48 0zm192 62l45 0 0 -140 0 0 39 140 37 0 39 -140 0 0 0 140 45 0 0 -185 -71 0 -31 123 -1 0 -32 -123 -70 0 0 185z"/>
37
+  </g>
38
+ </g>
39
+</svg>
Back to file index

layer.yaml

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

metadata.yaml

 1
--- 
 2
+++ metadata.yaml
 3
@@ -0,0 +1,35 @@
 4
+"name": "ibm-dsm-enterprise"
 5
+"summary": "IBM Dataserver manager Enterprise"
 6
+"maintainer": "IBM Juju Support Team <jujusupp@us.ibm.com>"
 7
+"description": "IBM Data Server Manager is an integrated database management tools\
 8
+  \ platform for DB2 for Linux\nIt is simple to set up, easy to use, and enterprise\
 9
+  \ ready\nwith the ability to manage hundreds of databases. \nThis Charm installs\
10
+  \ and configures IBM DSM Enterprise Edition only.\n"
11
+"tags":
12
+- "misc"
13
+- "ibm"
14
+- "dsm"
15
+- "enterprise"
16
+"requires":
17
+  "ibm-dsm-db2":
18
+    "interface": "db2"
19
+"resources":
20
+  "ibm_dsm_installer":
21
+    "type": "file"
22
+    "filename": "ibm_dsm.installer.tgz"
23
+    "description": "DSM installer archive."
24
+  "ibm_dsm_license":
25
+    "type": "file"
26
+    "filename": "ibm_dsm.license.tgz"
27
+    "description": "DSM installer license archive."
28
+  "ibm_dsm_fixpack":
29
+    "type": "file"
30
+    "filename": "ibm_dsm.fixpack.tgz"
31
+    "description": "DSM installer fixpack archive."
32
+  "ibm_dsm_fixpack_license":
33
+    "type": "file"
34
+    "filename": "ibm_dsm.fixpack_license.tgz"
35
+    "description": "DSM license fixpack archive."
36
+"subordinate": !!bool "false"
37
+"terms":
38
+- "ibmcharmers/ibm-dsm-enterprise/1"
Back to file index

reactive/ibm_dsm.py

  1
--- 
  2
+++ reactive/ibm_dsm.py
  3
@@ -0,0 +1,694 @@
  4
+import platform
  5
+import tarfile
  6
+import os
  7
+import subprocess
  8
+import time
  9
+import shutil
 10
+from charms.reactive import hook
 11
+from charms.reactive import when
 12
+from charms.reactive import when_not
 13
+from charms.reactive import when_all
 14
+from charms.reactive import when_none
 15
+from charms.reactive import when_any
 16
+from charms.reactive import set_state
 17
+from charms.reactive import remove_state
 18
+from charms.reactive import is_state
 19
+from charms.reactive.relations import relation_call
 20
+from charmhelpers.core import hookenv
 21
+from charmhelpers.core import templating
 22
+
 23
+ARCHITECTURE = os.uname()
 24
+CHARM_DIR = os.getcwd()
 25
+
 26
+
 27
+def get_admin_password():
 28
+    if is_state('ibm-dsm-adminpassword.set'):
 29
+        # get the admin password from file
 30
+        if os.path.exists(CHARM_DIR + "/file_admin_password.txt"):
 31
+            file_admin_password = CHARM_DIR + "/file_admin_password.txt"
 32
+            file_admin_password_obj = open(file_admin_password, "r")
 33
+            dsm_admin_password = file_admin_password_obj.readline()
 34
+            hookenv.log("get_admin_password: admin password is :" +
 35
+                        dsm_admin_password)
 36
+            file_admin_password_obj.close()
 37
+            if dsm_admin_password != "":
 38
+                return dsm_admin_password
 39
+
 40
+
 41
+def check_platform_architecture():
 42
+    return platform.processor()
 43
+
 44
+
 45
+@when_any('ibm-dsm.base', 'ibm-dsm.enterprise')
 46
+@when_none('ibm-dsm.installed', 'ibm-dsm-db2.connected')
 47
+def install_ibm_dsm():
 48
+    CHARM_DIR = os.getcwd()
 49
+    DSM_HOME = (CHARM_DIR+"/../resources/DSM/ibm-datasrvrmgr/")
 50
+    hookenv.log("install_ibm_dsm: DSM_HOME dir is :"+DSM_HOME)
 51
+
 52
+    if os.path.exists(CHARM_DIR+"/../resources/DSM"):
 53
+        hookenv.log("install_ibm_dsm: DSM dir exist already.")
 54
+    else:
 55
+        hookenv.log("install_ibm_dsm: creating DSM dir")
 56
+        os.makedirs(CHARM_DIR+"/../resources/DSM")
 57
+
 58
+    if (os.path.exists(DSM_HOME) and
 59
+       (os.path.exists(DSM_HOME+"/setup.conf"))):
 60
+        hookenv.log("install_ibm_dsm:"
 61
+                    "Product Packages extracted already.")
 62
+    else:
 63
+        hookenv.log("install_ibm_dsm: Extracting Product package.")
 64
+        try:
 65
+            tar = tarfile.open(CHARM_DIR+"/../resources/"
 66
+                               "ibm_dsm_installer/"
 67
+                               "ibm_dsm.installer.tgz", 'r')
 68
+            tar.extractall(CHARM_DIR+"/../resources/DSM/")
 69
+            tar.close()
 70
+            hookenv.log("install_ibm_dsm:"
 71
+                        "Extraction completed for product package")
 72
+        except subprocess.CalledProcessError as e:
 73
+            hookenv.log(e.output)
 74
+            hookenv.log("install_ibm_dsm: Unable to extract packages")
 75
+            hookenv.status_set("blocked",
 76
+                               "IBM DSM: Package is corrupt")
 77
+            shutil.rmtree(CHARM_DIR+"/../resources/DSM")
 78
+            return 0
 79
+
 80
+        if os.path.exists(CHARM_DIR+"/../resources/ibm_dsm_license"
 81
+                          "/ibm_dsm.license.tgz"):
 82
+            hookenv.log("install_ibm_dsm:"
 83
+                        " Now Extracting tar package for license")
 84
+            tar_license = tarfile.open(CHARM_DIR+"/../resources/"
 85
+                                       "ibm_dsm_installer/"
 86
+                                       "ibm_dsm.installer.tgz", 'r')
 87
+            tar_license.extractall(DSM_HOME)
 88
+            tar_license.close()
 89
+            hookenv.log("install_ibm_dsm:"
 90
+                        " Extraction completed for license")
 91
+        else:
 92
+            hookenv.log("License file not found")
 93
+            hookenv.status_set('maintenance',
 94
+                               "IBM DSM: License file not found")
 95
+            return 0
 96
+
 97
+        hookenv.log("install_ibm_dsm: Setting up setup.conf file")
 98
+        if os.path.exists(DSM_HOME+"/setup_backup.conf"):
 99
+            os.remove(DSM_HOME+"/setup.conf")
100
+        else:
101
+            os.rename(DSM_HOME+"/setup.conf",
102
+                      DSM_HOME+"/setup_backup.conf")
103
+            # Config directory has some file that needs to
104
+            # have write permission when we are upgrading
105
+            # the server from base to enterprise edition
106
+            if os.path.exists(DSM_HOME+"/Config/"):
107
+                try:
108
+                    subprocess.check_call(['sudo', 'chmod',
109
+                                           '-R', '751',
110
+                                           DSM_HOME+"/Config/"])
111
+                except OSError:
112
+                    hookenv.log('Can not find the Config directory')
113
+                    hookenv.status_set('maintenance',
114
+                                       "IBM DSM: Config"
115
+                                       " directory not found")
116
+                    return 0
117
+
118
+            hookenv.log("install_ibm_dsm: Setting up DSM type"
119
+                        " -> Base or Enterprise")
120
+            set_state('ibm-dsm.extracted')
121
+            hookenv.log("install_ibm_dsm:: Setup_test_done is created")
122
+
123
+
124
+# Apply fixpack
125
+@when_any('ibm-dsm.base', 'ibm-dsm.enterprise')
126
+@when('ibm-dsm.installed')
127
+@when_not('ibm-dsm.updated')
128
+def install_ibm_dsm_fixpack():
129
+
130
+        CHARM_DIR = os.getcwd()
131
+        DSM_HOME = (CHARM_DIR+"/../resources/DSM/ibm-datasrvrmgr/")
132
+        hookenv.log("install_ibm_dsm_fixpack:"
133
+                    " fetching ibm_dsm_fixpack and"
134
+                    " ibm_dsm_fixpack_license resource")
135
+
136
+        hookenv.status_set('active', 'IBM DSM: fetching ibm_dsm_fixpack')
137
+        dsm_fixpack = hookenv.resource_get('ibm_dsm_fixpack')
138
+        hookenv.status_set('active', 'IBM DSM: fetched ibm_dsm_fixpack')
139
+
140
+        hookenv.status_set('active', 'IBM DSM: fetching'
141
+                           ' ibm_dsm_fixpack_license')
142
+        dsm_fixpack_license = hookenv.resource_get('ibm_dsm_fixpack_license')
143
+        hookenv.status_set('active', 'IBM DSM: fetched'
144
+                           ' ibm_dsm_fixpack_license')
145
+
146
+        if ((dsm_fixpack is False) or (dsm_fixpack_license is False)):
147
+            hookenv.log("install_ibm_dsm_fixpack: no ibm_dsm_fixpack"
148
+                        " or ibm_dsm_fixpack_license to install")
149
+            hookenv.status_set('active', 'IBM DSM: IBM DSM is ready')
150
+        else:
151
+            if is_state('ibm-dsm-db2.connected'):
152
+                hookenv.log("install_ibm_dsm_fixpack: relation exists"
153
+                            " with DB2. Remove the relation"
154
+                            " before applying fixpack")
155
+                hookenv.status_set('active', 'IBM DSM: Relation exists'
156
+                                   ' with DB2. Remove the relation'
157
+                                   ' before applying fixpack')
158
+                return 0
159
+
160
+            command1 = ["file", dsm_fixpack]
161
+            p1 = subprocess.Popen(command1, stdout=subprocess.PIPE,
162
+                                  stderr=subprocess.PIPE, shell=False)
163
+            output1, err = p1.communicate()
164
+            ibm_dsm_fixpack_msg = str(output1)
165
+
166
+            command2 = ["file", dsm_fixpack_license]
167
+            p2 = subprocess.Popen(command2, stdout=subprocess.PIPE,
168
+                                  stderr=subprocess.PIPE, shell=False)
169
+            output2, err = p2.communicate()
170
+            ibm_dsm_fixpack_license_msg = str(output2)
171
+
172
+            if ("empty" in ibm_dsm_fixpack_msg or
173
+                    "empty" in ibm_dsm_fixpack_license_msg):
174
+                hookenv.log("install_ibm_dsm_fixpack:"
175
+                            " empty packages found for"
176
+                            " ibm_dsm_fixpack or ibm_dsm_fixpack_license")
177
+                hookenv.status_set('active', 'IBM DSM: DSM is ready')
178
+                return 0
179
+            else:
180
+                hookenv.log("install_ibm_dsm_fixpack:"
181
+                            " Stop DSM Server before"
182
+                            " applying fixpack")
183
+
184
+                retVal = stop_dsm_server()
185
+                if retVal is True:
186
+                    if os.path.exists(DSM_HOME+"/setup.conf"):
187
+                        hookenv.log("install_ibm_dsm_fixpack:"
188
+                                    " take a backup of setup.conf"
189
+                                    " file before applying fixpack")
190
+                        shutil.copy(DSM_HOME+"/setup.conf",
191
+                                    CHARM_DIR+"/../resources/DSM/setup.conf")
192
+                        flag_setup_copy = True
193
+                    else:
194
+                        hookenv.log("install_ibm_dsm_fixpack:"
195
+                                    "setup.conf file does not exist")
196
+
197
+                if (os.path.exists(CHARM_DIR+"/../resources/DSM") and
198
+                        os.path.exists(DSM_HOME)):
199
+                        hookenv.log("install_ibm_dsm_fixpack:"
200
+                                    " Product Base Package exists")
201
+                else:
202
+                        hookenv.log("install_ibm_dsm_fixpack:"
203
+                                    " Product Base Package does not exist."
204
+                                    " Get the product base packages")
205
+                        return 0
206
+
207
+                hookenv.log("install_ibm_dsm_fixpack:"
208
+                            " Started Applying DSM fixpack package.")
209
+                try:
210
+                    tar = tarfile.open(CHARM_DIR+"/../resources/"
211
+                                       "ibm_dsm_fixpack/ibm_dsm.fixpack.tgz",
212
+                                       'r')
213
+                    tar.extractall(CHARM_DIR+"/../resources/DSM/")
214
+                    tar.close()
215
+                    hookenv.log("install_ibm_dsm_fixpack:"
216
+                                " Extraction is completed for fixpack package")
217
+                    hookenv.status_set('active', "IBM DSM:"
218
+                                       "Updated successfully")
219
+                except subprocess.CalledProcessError as e:
220
+                    hookenv.log(e.output)
221
+                    hookenv.log("install_ibm_dsm_fixpack:"
222
+                                " Unable to extract fixpack packages")
223
+                    hookenv.status_set("blocked", "IBM-DSM: fixpack"
224
+                                       " package is corrupt")
225
+                    return 0
226
+                if os.path.exists(CHARM_DIR+"/../resources/"
227
+                                  "ibm_dsm_fixpack_license/"
228
+                                  "ibm_dsm.fixpack_license.tgz"):
229
+                    hookenv.log("install_ibm_dsm_fixpack:"
230
+                                " Now Extracting the tar "
231
+                                "package of the license")
232
+                    tar_license = tarfile.open(CHARM_DIR+"/../resources/"
233
+                                               "ibm_dsm_fixpack_license/"
234
+                                               "ibm_dsm.fixpack_license.tgz",
235
+                                               'r')
236
+                    tar_license.extractall(DSM_HOME)
237
+                    tar_license.close()
238
+                    hookenv.log("install_ibm_dsm_fixpack:"
239
+                                " Extraction is completed"
240
+                                " for license fixpack")
241
+                else:
242
+                    hookenv.log("install_ibm_dsm_fixpack:"
243
+                                " License file not found")
244
+                    hookenv.status_set('maintenance',
245
+                                       "IBM DSM: License file not found")
246
+                    return 0
247
+                if flag_setup_copy is True:
248
+                    hookenv.log("install_ibm_dsm_fixpack:"
249
+                                " roll back setup.conf file"
250
+                                " after applying fixpack if exists")
251
+                    if os.path.exists(CHARM_DIR +
252
+                                      "/../resources/DSM/setup.conf"):
253
+                        shutil.copy(CHARM_DIR + "/../resources/DSM/setup.conf",
254
+                                    DSM_HOME + "/setup.conf")
255
+                        os.remove(CHARM_DIR + "/../resources/DSM/setup.conf")
256
+                if os.path.exists(DSM_HOME + "/setup.conf"):
257
+                    hookenv.log("install_ibm_dsm_fixpack: Installing Fixpack")
258
+                    try:
259
+                        subprocess.check_call([DSM_HOME +
260
+                                               '/setup.sh', '-silent'])
261
+                        set_state('ibm-dsm.updated')
262
+                        hookenv.status_set('maintenance',
263
+                                           "IBM DSM: Fixpack"
264
+                                           " applied and started DSM Server")
265
+                    except subprocess.CalledProcessError as e:
266
+                        hookenv.log(e.output)
267
+                        hookenv.log("install_ibm_dsm_fixpack:"
268
+                                    " Error while installing "
269
+                                    "DSM Server: Error " + e.output)
270
+                        hookenv.status_set('maintenance',
271
+                                           "IBM DSM: Error"
272
+                                           " while installing DSM Server")
273
+                    except OSError:
274
+                        hookenv.log('Can not find the setup.sh file')
275
+                        hookenv.status_set('maintenance', "IBM DSM:"
276
+                                           " Installable file not found")
277
+
278
+
279
+# Install IBM-DSM Base edition
280
+@when('ibm-dsm.extracted')
281
+@when_not('ibm-dsm.installed')
282
+def configure_ibm_dsm():
283
+
284
+    CHARM_DIR = os.getcwd()
285
+    DSM_HOME = (CHARM_DIR+"/../resources/DSM/ibm-datasrvrmgr/")
286
+    hookenv.log("configure_ibm_dsm: calling get_admin_password")
287
+    dsm_admin_password = get_admin_password()
288
+    if dsm_admin_password == "":
289
+        remove_state('ibm-dsm-adminpassword.set')
290
+        return 0
291
+    hookenv.log("configure_ibm_dsm: admin password is " + dsm_admin_password)
292
+    templating.render(
293
+        source='setup_test.conf',
294
+        target=CHARM_DIR+'/../resources/DSM/ibm-datasrvrmgr/setup.conf',
295
+        context={
296
+            'product_license_accepted': "y",
297
+            'https_port': "11081",
298
+            'port': "11080",
299
+            'status_port': "11082",
300
+            'admin_user': "admin",
301
+            'admin_password': dsm_admin_password,
302
+        },
303
+    )
304
+    hookenv.log("configure_ibm_dsm: configure_ibm_dsm:"
305
+                " The resource value in second state:" + str(CHARM_DIR))
306
+    try:
307
+        subprocess.check_call([DSM_HOME + '/setup.sh', '-silent'])
308
+    except subprocess.CalledProcessError as e:
309
+        hookenv.log(e.output)
310
+        hookenv.log("configure_ibm_dsm: Error while installing DSM Server")
311
+        hookenv.status_set('maintenance', "IBM DSM: Error while"
312
+                           " installing DSM Server")
313
+    except OSError:
314
+        hookenv.log('configure_ibm_dsm: Can not find the setup.sh file')
315
+        hookenv.status_set('maintenance', "IBM DSM:"
316
+                           " Installable file not found")
317
+
318
+    retVal = status_dsm_server()
319
+    if retVal is True:
320
+        set_state('ibm-dsm.installed')
321
+        if is_state('ibm-dsm.base'):
322
+            hookenv.log('configure_ibm_dsm: Base Installation is completed')
323
+        if is_state('ibm-dsm.enterprise'):
324
+            hookenv.log('configure_ibm_dsm: Enterprise Installation'
325
+                        ' completed. To configure RepoDB,'
326
+                        ' add relation to IBM-DB2')
327
+
328
+
329
+# Stop the server. This is required when applying fixpack
330
+# and when relation detached from db2 repo server
331
+def stop_dsm_server():
332
+    CHARM_DIR = os.getcwd()
333
+    DSM_HOME = (CHARM_DIR+"/../resources/DSM/ibm-datasrvrmgr/")
334
+    command = ([DSM_HOME + '/bin/status.sh'])
335
+    p = subprocess.Popen(command, stdout=subprocess.PIPE,
336
+                         stderr=subprocess.PIPE, shell=True)
337
+    output, err = p.communicate()
338
+    dsm_server_status = str(output)
339
+    hookenv.log("stop_dsm_server: dsm_server_status: " +
340
+                str(dsm_server_status))
341
+    # if INACTIVE
342
+    if "INACTIVE" in dsm_server_status:
343
+        hookenv.log("stop_dsm_server: Server is not running")
344
+        return True
345
+    else:
346
+        if ([CHARM_DIR + '/../resources/DSM/ibm-datasrvrmgr/bin/stop.sh']):
347
+            try:
348
+                return True if subprocess.check_call(
349
+                    [DSM_HOME + '/bin/stop.sh']) == 0 else False
350
+            except subprocess.CalledProcessError as e:
351
+                hookenv.log(e.output)
352
+                hookenv.log("stop_dsm_server: Error while stopping DSM Server")
353
+                hookenv.status_set('maintenance', "IBM DSM: "
354
+                                   "Error while stopping DSM Server")
355
+            except OSError:
356
+                hookenv.log('stop_dsm_server: Can not find the stop.sh file')
357
+                hookenv.status_set('maintenance', "IBM DSM: "
358
+                                   "Stop.sh file not found")
359
+        else:
360
+            hookenv.log("stop_dsm_server: stop.sh not found")
361
+            return True
362
+
363
+
364
+# Check the status of the server "ACTIVE", "INACTIVE"
365
+def status_dsm_server():
366
+    CHARM_DIR = os.getcwd()
367
+    DSM_HOME = (CHARM_DIR+"/../resources/DSM/ibm-datasrvrmgr/")
368
+    command = ([DSM_HOME + '/bin/status.sh'])
369
+    p = subprocess.Popen(command, stdout=subprocess.PIPE,
370
+                         stderr=subprocess.PIPE, shell=True)
371
+    output, err = p.communicate()
372
+    dsm_server_status = str(output)
373
+    hookenv.log("status_dsm_server: dsm_server_status: " +
374
+                str(dsm_server_status))
375
+    if "INACTIVE" in dsm_server_status:
376
+        hookenv.log("status_dsm_server: Sever is not running")
377
+        return False
378
+    else:
379
+        hookenv.log("status_dsm_server: Sever is Up and Running")
380
+        return True
381
+
382
+
383
+# Configure the sshkey to send to db2 server
384
+def configure_sshkeys_dbs(self):
385
+    SSH_PATH = '/root/.ssh'
386
+    key = ""
387
+    hookenv.log("Starting of the configure_ssh_dbs" +
388
+                str(SSH_PATH)+" and "+str(os.path.exists(SSH_PATH)))
389
+    if not os.path.exists(SSH_PATH + '/id_rsa.pub'):
390
+        try:
391
+            subprocess.check_call(['ssh-keygen', '-t',
392
+                                   'rsa', '-f',
393
+                                   SSH_PATH + '/id_rsa', '-N', ''])
394
+            hookenv.log("configure_sshkeys_dbs:"
395
+                        " It is after subprocess.check_call")
396
+            fileopen = open(SSH_PATH + '/id_rsa.pub', 'r')
397
+            key = fileopen.read()
398
+            hookenv.log("The key value is:" + str(key))
399
+            set_state('ibm-dsm.sshconfigured')
400
+        except subprocess.CalledProcessError as e:
401
+            hookenv.log(e.output)
402
+            hookenv.log("configure_sshkeys_dbs: "
403
+                        "SSH keygen generate error")
404
+            hookenv.status_set('maintenance', "IBM DSM:"
405
+                               " SSH keygen generate error")
406
+            hookenv.log("subprocess error")
407
+        except OSError:
408
+            hookenv.status_set('maintenance', "IBM DSM:"
409
+                               " SSH keygen generate failed")
410
+            hookenv.log("The OSError")
411
+        hookenv.log("configure_sshkeys_dbs:"
412
+                    " The key generated value is:" + str(key))
413
+        hookenv.log("configure_sshkeys_dbs:"
414
+                    " ssh configured successfully")
415
+
416
+
417
+# Call configure_sshkeys_dbs()
418
+@when('ibm-dsm-db2.connected', 'ibm-dsm.enterprise')
419
+@when_not('ibm-dsm.sshconfigured')
420
+def configure_first_db(self):
421
+    configure_sshkeys_dbs(self)
422
+
423
+
424
+# Get the ssh key value if it is already there
425
+@when_all('ibm-dsm.enterprise', 'ibm-dsm.sshconfigured',
426
+          'ibm-dsm-db2.connected')
427
+@when_not('ibm-dsm-db2.ready')
428
+def get_sshkey(self):
429
+    SSH_PATH = '/root/.ssh'
430
+    key = ""
431
+    if os.path.exists(SSH_PATH + '/id_rsa.pub'):
432
+        try:
433
+            fileopen = open(SSH_PATH + '/id_rsa.pub', 'r')
434
+            key = fileopen.read()
435
+            hookenv.log("get_sshkey: The key value is:" + str(key))
436
+            relation_call('set_ssh_keys', 'ibm-dsm-db2',
437
+                          'ibm-dsm-db2.connected', str(key))
438
+            dbnames = "dsment"
439
+            relation_call('set_dbs', 'ibm-dsm-db2',
440
+                          'ibm-dsm-db2.connected', dbnames)
441
+        except subprocess.CalledProcessError as e:
442
+            hookenv.log(e.output)
443
+            hookenv.status_set('maintenance',
444
+                               "IBM DSM: SSH keygen file unable to open")
445
+            hookenv.log("get_sshkey: subprocess error")
446
+        except OSError:
447
+            hookenv.status_set('maintenance',
448
+                               "IBM DSM: SSH keygen file can not be opened")
449
+            hookenv.log("get_sshkey: The OSError")
450
+        hookenv.log("get_sshkey: The key value got:" + str(key))
451
+
452
+
453
+# Setup the Repo Server
454
+@when_all('ibm-dsm.enterprise', 'ibm-dsm-db2.ready',
455
+          'ibm-dsm.sshconfigured', 'ibm-dsm.installed')
456
+@when_not('ibm-dsm.repoDBconfigured')
457
+def install_ibm_enterprise_dsm(self):
458
+    hookenv.log("Configuring Repo DB")
459
+    CHARM_DIR = os.getcwd()
460
+    DSM_HOME = (CHARM_DIR+"/../resources/DSM/ibm-datasrvrmgr/")
461
+    retVal = stop_dsm_server()
462
+    if retVal is False:
463
+        hookenv.log("install_ibm_enterprise_dsm:"
464
+                    " Unable to stop ibm-dsm server.")
465
+        hookenv.status_set('maintenance', "IBM DSM: "
466
+                           "Unable to stop ibm-dsm server."
467
+                           " Stop the server before configuring the repoDB")
468
+        return 0
469
+    repositoryDB_port = relation_call('get_db2_port',
470
+                                      'ibm-dsm-db2',
471
+                                      'ibm-dsm-db2.connected')
472
+    repositoryDB_user = relation_call('get_dbusername',
473
+                                      'ibm-dsm-db2',
474
+                                      'ibm-dsm-db2.connected')
475
+    repositoryDB_pwd = relation_call('get_dbuserpw',
476
+                                     'ibm-dsm-db2',
477
+                                     'ibm-dsm-db2.connected')
478
+    repositoryDB_host = relation_call('get_db2_hostname',
479
+                                      'ibm-dsm-db2',
480
+                                      'ibm-dsm-db2.connected')
481
+    db2_instance_name = relation_call('get_db2_instance_name',
482
+                                      'ibm-dsm-db2',
483
+                                      'ibm-dsm-db2.connected')
484
+    hookenv.log("install_ibm_enterprise_dsm:"
485
+                " Port:" + str(repositoryDB_port) +
486
+                " User:" + str(repositoryDB_user) +
487
+                " Pwd:" + str(repositoryDB_pwd) +
488
+                " Host:" + str(repositoryDB_host) +
489
+                " and vlaue of Instance_name:" + db2_instance_name)
490
+    if (os.path.isdir(DSM_HOME + "/java") and
491
+            os.path.exists(DSM_HOME + "/dsutil/bin/crypt.sh")):
492
+        hookenv.log("install_ibm_enterprise_dsm: dsm_admin_password")
493
+        dsm_admin_password = get_admin_password()
494
+        if dsm_admin_password == "":
495
+            remove_state('ibm-dsm-adminpassword.set')
496
+            return 0
497
+        hookenv.log("nstall_ibm_enterprise_dsm: "
498
+                    "dsm_admin_password is " + dsm_admin_password)
499
+        try:
500
+            admin_password = subprocess.check_output(
501
+                                [DSM_HOME + '/dsutil/bin/crypt.sh',
502
+                                 dsm_admin_password])
503
+            db2_password = subprocess.check_output(
504
+                                [DSM_HOME + '/dsutil/bin/crypt.sh',
505
+                                 repositoryDB_pwd])
506
+            hookenv.log("===========================================")
507
+            hookenv.log(admin_password.decode("utf-8"))
508
+            hookenv.log(db2_password.decode("utf-8"))
509
+            admin_password = admin_password.decode("utf-8")
510
+            db2_password = db2_password.decode("utf-8")
511
+            hookenv.log("install_ibm_enterprise_dsm: "
512
+                        "admin_password after crypting:" + admin_password)
513
+            hookenv.log("install_ibm_enterprise_dsm: "
514
+                        "db2_password after crypting:" + db2_password)
515
+            hookenv.log("===========================================")
516
+        except subprocess.CalledProcessError as e:
517
+            hookenv.log(e.output)
518
+            hookenv.log("install_ibm_enterprise_dsm: "
519
+                        "Error while encrypting the password")
520
+            hookenv.status_set('maintenance',
521
+                               "IBM DSM: Error while encrypting the password")
522
+        except OSError:
523
+            hookenv.log('install_ibm_enterprise_dsm: '
524
+                        'Can not find the crypt.sh file')
525
+            hookenv.status_set('maintenance',
526
+                               "IBM DSM: Encyptable file not found")
527
+        if repositoryDB_port != "":
528
+            try:
529
+                templating.render(
530
+                 source='setup_test.conf',
531
+                 target=DSM_HOME + '/setup.conf',
532
+                 context={
533
+                     'product_license_accepted': "y",
534
+                     'https_port': "11081",
535
+                     'port': "11080",
536
+                     'status_port': "11082",
537
+                     'admin_user': "admin",
538
+                     'admin_password': admin_password,
539
+                     'repositoryDB_host': str(repositoryDB_host),
540
+                     'repositoryDB_port': str(repositoryDB_port),
541
+                     'repositoryDB_databaseName': "dsment",
542
+                     'repositoryDB_user': str(repositoryDB_user),
543
+                     'repositoryDB_password': db2_password,
544
+                 },
545
+                )
546
+                hookenv.log("install_ibm_enterprise_dsm: Setup.conf "
547
+                            "file for enterprise edition successfully"
548
+                            " generated:" + (str(DSM_HOME)+"/setup.conf"))
549
+                if os.path.exists(DSM_HOME + "/setup.conf"):
550
+                    try:
551
+                        subprocess.check_call([DSM_HOME + '/setup.sh',
552
+                                              '-silent'])
553
+                    except subprocess.CalledProcessError as e:
554
+                        hookenv.log(e.output)
555
+                        hookenv.log("install_ibm_enterprise_dsm:"
556
+                                    " Error while starting DSM Server")
557
+                        hookenv.status_set('maintenance',
558
+                                           "IBM DSM: Error while"
559
+                                           " starting DSM Server")
560
+                    # Set the state if server is UP and Running
561
+                    status_server = status_dsm_server()
562
+                    if status_server is True:
563
+                        set_state('ibm-dsm.repoDBconfigured')
564
+                        hookenv.log("install_ibm_enterprise_dsm: "
565
+                                    "set state ibm-dsm.repoDBconfigured")
566
+                        hookenv.status_set('maintenance', "IBM DSM: repoDB"
567
+                                           " configured. DSM Server"
568
+                                           " is Up and Running")
569
+                        time.sleep(20)
570
+                    if os.path.exists(DSM_HOME +
571
+                                      "/dsutil/bin/upddbprofile.sh"):
572
+                        hookenv.log("install_ibm_enterprise_dsm: "
573
+                                    "upddbprofile file is" +
574
+                                    DSM_HOME + "/dsutil/bin/upddbprofile.sh")
575
+                        hookenv.log("install_ibm_enterprise_dsm: "
576
+                                    "run upddbprofile.sh to "
577
+                                    "create a Repo DB Profile")
578
+                        hookenv.log("install_ibm_enterprise_dsm: upddbprofile"
579
+                                    " Port:" + str(repositoryDB_port) +
580
+                                    " User:" + str(repositoryDB_user) +
581
+                                    " Pwd:" + str(repositoryDB_pwd) +
582
+                                    " Host:" + str(repositoryDB_host) +
583
+                                    " and vlaue of Instance_name:" +
584
+                                    db2_instance_name)
585
+                        try:
586
+                            subprocess.check_output([DSM_HOME +
587
+                                                     '/dsutil/'
588
+                                                     'bin/upddbprofile.sh',
589
+                                                     '-host',
590
+                                                     repositoryDB_host,
591
+                                                     '-port',
592
+                                                     repositoryDB_port,
593
+                                                     '-user',
594
+                                                     repositoryDB_user,
595
+                                                     '-password',
596
+                                                     repositoryDB_pwd,
597
+                                                     '-databaseName',
598
+                                                     'dsment',
599
+                                                     '-dbProfile',
600
+                                                     repositoryDB_host +
601
+                                                     '-dsment',
602
+                                                     '-dataServerType',
603
+                                                     'DB2LUW'])
604
+                        except subprocess.CalledProcessError as e:
605
+                            hookenv.log(e.output)
606
+                            hookenv.status_set('maintenance',
607
+                                               "IBM DSM: Error while"
608
+                                               " updating the DB Profile")
609
+                        except OSError:
610
+                            hookenv.log('install_ibm_enterprise_dsm:'
611
+                                        ' Can not find the crypt.sh file')
612
+                            hookenv.status_set('maintenance', "IBM DSM:"
613
+                                               " upddbprofile.sh not found")
614
+            except subprocess.CalledProcessError as e:
615
+                hookenv.log(e.output)
616
+                hookenv.log('install_ibm_enterprise_dsm:'
617
+                            ' Error while installing DSM Serverr')
618
+                hookenv.status_set('maintenance',
619
+                                   "IBM DSM: Error "
620
+                                   "while installing DSM Server")
621
+                return 1
622
+            except OSError:
623
+                hookenv.log('install_ibm_enterprise_dsm:'
624
+                            ' Can not find the setup.sh file')
625
+                hookenv.status_set('maintenance',
626
+                                   "IBM DSM: Installable file not found")
627
+                return 0
628
+            hookenv.log('install_ibm_enterprise_dsm:'
629
+                        ' Enterprise edition Installation '
630
+                        'completed for DB2 at '
631
+                        'repositoryDB_host:', repositoryDB_host)
632
+
633
+
634
+# Relation departed from DB2 Repo server
635
+@when('ibm-dsm-db2.departed', 'ibm-dsm.enterprise')
636
+def departed_from_db2(self):
637
+        retVal = stop_dsm_server()
638
+        if retVal is True:
639
+            remove_state('ibm-dsm.repoDBconfigured')
640
+            hookenv.log("departed_from_db2: departed from DB2")
641
+            hookenv.status_set("maintenance",
642
+                               "IBM-DSM: Relation broken "
643
+                               "from DB2. ibm dsm server not running")
644
+            remove_state('ibm-dsm-db2.departed')
645
+            remove_state('ibm-dsm.installed')
646
+
647
+
648
+# Upgrade the fixpack
649
+@hook('upgrade-charm')
650
+def check_fixpack():
651
+    if not is_state('ibm-dsm.updated'):
652
+        hookenv.log("check_fixpack: no fixpack has been installed;"
653
+                    "nothing to upgrade.")
654
+        return 0
655
+    else:
656
+        hookenv.log("check_fixpack: scanning for new fixpacks to install")
657
+        CHARM_DIR = os.getcwd()
658
+        path = CHARM_DIR + "/../resources/ibm_dsm_fixpack"
659
+        hookenv.log("check_fixpack: path is " + path)
660
+        CUR_FIXPACK = path + "/ibm_dsm.fixpack.tgz"
661
+        hookenv.log("check_fixpack: CUR_FIXPACK is " + CUR_FIXPACK)
662
+        if (os.path.exists(path + "/ibm_dsm.fixpack.tgz")):
663
+            hookenv.log("check_fixpack: Checking "
664
+                        "checksum values of CUR_Fixpacks")
665
+            command = ["md5sum", CUR_FIXPACK]
666
+            p = subprocess.Popen(command, stdout=subprocess.PIPE,
667
+                                 stderr=subprocess.PIPE, shell=False)
668
+            output, err = p.communicate()
669
+            value = output.split()
670
+            CUR_FP_MD5 = str(value[0])
671
+            hookenv.log("check_fixpack: CUR_FP_MD5 is " + CUR_FP_MD5)
672
+
673
+            # Calling resource-get here will fetch the fixpack resource.
674
+            NEW_FIXPACK = hookenv.resource_get('ibm_dsm_fixpack')
675
+            if (NEW_FIXPACK is False):
676
+                hookenv.log("check_fixpack: no fixpack to install")
677
+                return 0
678
+            else:
679
+                hookenv.log("check_fixpack: Checking checksum of NEW_FIXPACK")
680
+                command1 = ["md5sum", NEW_FIXPACK]
681
+                p1 = subprocess.Popen(command1, stdout=subprocess.PIPE,
682
+                                      stderr=subprocess.PIPE, shell=False)
683
+                output1, err = p1.communicate()
684
+                value1 = output1.split()
685
+                NEW_FP_MD5 = str(value1[0])
686
+                hookenv.log("check_fixpack: NEW_FP_MD5 is " + NEW_FP_MD5)
687
+
688
+            # If sums don't match, we have a new fp. Configure states so
689
+            # we re-run install-ibm-dsm-fixpack().
690
+            if (CUR_FP_MD5 != NEW_FP_MD5):
691
+                hookenv.log("check_fixpack: new fixpack detected " +
692
+                            CUR_FP_MD5+" versus " + NEW_FP_MD5 + "")
693
+                remove_state('ibm-dsm.updated')
694
+            else:
695
+                hookenv.log("check_fixpack: no new fixpacks to install")
696
+        else:
697
+            hookenv.log("check_fixpack: current fixpack does not exist")
Back to file index

reactive/ibm_dsm_enterprise.py

  1
--- 
  2
+++ reactive/ibm_dsm_enterprise.py
  3
@@ -0,0 +1,116 @@
  4
+import platform
  5
+import os
  6
+import subprocess
  7
+from charms.reactive import when_not
  8
+from charms.reactive import set_state
  9
+from charms.reactive import is_state
 10
+from charmhelpers.core import hookenv
 11
+
 12
+ARCHITECTURE = os.uname()
 13
+CHARM_DIR = os.getcwd()
 14
+file_admin_password = CHARM_DIR + "/file_admin_password.txt"
 15
+
 16
+
 17
+def check_platform_architecture():
 18
+    return platform.processor()
 19
+
 20
+
 21
+@when_not('ibm-dsm.enterprise')
 22
+def packge_ibm_dsm_enterprise():
 23
+
 24
+    # save user password
 25
+    if not is_state('ibm-dsm-adminpassword.set'):
 26
+        config = hookenv.config()
 27
+        cfg_dsm_admin_password = config.get('dsm_admin_password')
 28
+        if cfg_dsm_admin_password == "":
 29
+            hookenv.status_set("blocked",
 30
+                               "IBM DSM: Provide dsm_admin_password")
 31
+            return 0
 32
+        try:
 33
+            file_admin_password_obj = open(file_admin_password, "w")
 34
+            file_admin_password_obj.write(cfg_dsm_admin_password)
 35
+            file_admin_password_obj.close()
 36
+            set_state('ibm-dsm-adminpassword.set')
 37
+            hookenv.log("packge_ibm_dsm_enterprise: user "
 38
+                        "admin password :" + cfg_dsm_admin_password)
 39
+        except subprocess.CalledProcessError as e:
 40
+            hookenv.log(e.output)
 41
+            hookenv.log("packge_ibm_dsm_enterprise: user "
 42
+                        "admin password unable to set")
 43
+            return 0
 44
+
 45
+    if not (("ppc64le" in ARCHITECTURE) or ("x86_64" in ARCHITECTURE)):
 46
+        hookenv.log("packge_ibm_dsm_enterprise: "
 47
+                    "supported only on x86_64 or ppc64le")
 48
+        hookenv.status_set('blocked',
 49
+                           'IBM DSM Enterprise: unsupported architecture')
 50
+        return 1
 51
+
 52
+    if "x86_64" in ARCHITECTURE or "ppc64le" in ARCHITECTURE:
 53
+        if "x86_64" in ARCHITECTURE:
 54
+            hookenv.log("packge_ibm_dsm_enterprise: Architecture is x86_64")
 55
+        else:
 56
+            hookenv.log("packge_ibm_dsm_enterprise: Architecture is ppc64le")
 57
+
 58
+    CHARM_DIR = os.getcwd()
 59
+    DSM_HOME = (CHARM_DIR+"/../resources/DSM/ibm-datasrvrmgr/")
 60
+    hookenv.log("packge_ibm_dsm_enterprise: DSM_HOME dir is :"+DSM_HOME)
 61
+    if not (os.path.exists(DSM_HOME) or
 62
+            (os.path.exists(DSM_HOME+"setup.conf"))):
 63
+        hookenv.log("packge_ibm_dsm_enterprise: "
 64
+                    "fetching ibm_dsm_installer resource")
 65
+        hookenv.status_set('active',
 66
+                           'IBM DSM Enterprise: '
 67
+                           'fetching ibm_dsm_installer resource')
 68
+        dsm_installer = hookenv.resource_get('ibm_dsm_installer')
 69
+
 70
+        hookenv.log("packge_ibm_dsm_enterprise: "
 71
+                    "fetching ibm_dsm_license resource")
 72
+        hookenv.status_set('active',
 73
+                           'IBM DSM Enterprise: '
 74
+                           'fetching ibm_dsm_license resource')
 75
+        dsm_license = hookenv.resource_get('ibm_dsm_license')
 76
+
 77
+        # If we don't have a package,report blocked status;we can't proceed.
 78
+        if (dsm_installer is False):
 79
+            hookenv.log("packge_ibm_dsm_enterprise: missing"
 80
+                        "required ibm_dsm resources")
 81
+            hookenv.status_set("blocked",
 82
+                               "IBM DSM Enterprise: Required product "
 83
+                               "packages are missing")
 84
+            return 0
 85
+
 86
+        # If we don't have a package for license,
 87
+        # report blocked status;we can't proceed.
 88
+        if (dsm_license is False):
 89
+            hookenv.log("packge_ibm_dsm_enterprise:"
 90
+                        "missing required ibm_dsm license resources")
 91
+            hookenv.status_set("blocked",
 92
+                               "IBM DSM Enterprise: required license"
 93
+                               "package is missing")
 94
+            return 0
 95
+
 96
+        command1 = ["file", dsm_installer]
 97
+        p1 = subprocess.Popen(command1, stdout=subprocess.PIPE,
 98
+                              stderr=subprocess.PIPE, shell=False)
 99
+        output1, err = p1.communicate()
100
+        dsm_installer_msg = str(output1)
101
+
102
+        command2 = ["file", dsm_license]
103
+        p2 = subprocess.Popen(command2, stdout=subprocess.PIPE,
104
+                              stderr=subprocess.PIPE, shell=False)
105
+        output2, err = p2.communicate()
106
+        dsm_license_msg = str(output2)
107
+
108
+        if (("empty" in dsm_installer_msg) or
109
+           ("empty" in dsm_license_msg)):
110
+            hookenv.log("packge_ibm_dsm_enterprise: missing required ibm_dsm"
111
+                        "resources,empty packages are found")
112
+            hookenv.status_set("blocked",
113
+                               "IBM DSM Enterprise: Required"
114
+                               "packages are empty")
115
+            return 0
116
+        else:
117
+            set_state('ibm-dsm.enterprise')
118
+            hookenv.log("packge_ibm_dsm_enterprise: "
119
+                        "set state to ibm-dsm-enterprise")
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

templates/setup_test.conf

 1
--- 
 2
+++ templates/setup_test.conf
 3
@@ -0,0 +1,13 @@
 4
+product.license.accepted={{product_license_accepted}}
 5
+port={{port}}
 6
+https.port={{https_port}}
 7
+status.port={{status_port}}
 8
+admin.user={{admin_user}}
 9
+admin.password={{admin_password}}
10
+repositoryDB.dataServerType=DB2LUW
11
+repositoryDB.host={{repositoryDB_host}}
12
+repositoryDB.port={{repositoryDB_port}}
13
+repositoryDB.databaseName={{repositoryDB_databaseName}}
14
+repositoryDB.user={{repositoryDB_user}}
15
+repositoryDB.password={{repositoryDB_password}}
16
+
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/01-deploy.py

 1
--- 
 2
+++ tests/01-deploy.py
 3
@@ -0,0 +1,58 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+import amulet
 7
+import re
 8
+import requests
 9
+import unittest
10
+
11
+seconds_to_wait = 20000
12
+
13
+
14
+class TestDeploy(unittest.TestCase):
15
+    """
16
+    Deployment test for the IBM DMS charm.
17
+    """
18
+    def setUp(self):
19
+        self.d = amulet.Deployment(series='trusty')
20
+        self.d.add('ibm-dsm-enterprise',
21
+                   'cs:~ibmcharmers/trusty/ibm-dsm-enterprise')
22
+        self.d.add('ibm-db2', 'cs:~ibmcharmers/trusty/ibm-db2')
23
+        self.d.relate('ibm-dsm-base-enterprise:ibm-dsm-db2', 'ibm-db2:db')
24
+        self.d.setup(seconds_to_wait)
25
+        self.d.sentry.wait(seconds_to_wait)
26
+
27
+    def test_deploy_with_placeholder_resource(self):
28
+        # The status message when using placeholder resources will include the
29
+        # string "ibm_was_nd_installer resource". If we see that, the test is
30
+        # successful.
31
+        sentry_re1 = re.compile('ibm_dsm_installer resource')
32
+        sentry_re2 = re.compile('ibm_dsm_license resource')
33
+        self.d.sentry.wait_for_messages({"ibm-dsm-enterprise": sentry_re1})
34
+        self.d.sentry.wait_for_messages({"ibm-dsm-enterprise": sentry_re2})
35
+
36
+    def test_ibm_dsm_deployed(self):
37
+        self.assertTrue(self.d.deployed)
38
+        unit = self.d.sentry['ibm-dsm-enterprise'][0]
39
+        state_ibm_dsm_enterprise = unit.info['agent-state']
40
+        print('IBM DSM is %s' % state_ibm_dsm_enterprise)
41
+        url = 'http://%s:11080/ibm/console' % unit.info['public-address']
42
+        httpsurl = 'https://%s:11081/ibm/console' % unit.info['public-address']
43
+        response = requests.get(url, verify=False)
44
+        response = requests.get(httpsurl, verify=False)
45
+        # Raise an exception if the url was not a valid web page.
46
+        response.raise_for_status()
47
+
48
+    def test_ibm_dsm_db2(self):
49
+        dsm_unit = self.d.sentry['ibm-db2'][0]
50
+        state_db2 = dsm_unit.info['agent-state']
51
+        self.d.relate('ibm-dsm-enterprise:ibm-db2', 'ibm-dsm-db2:db2')
52
+        print('DB2 Server is %s' % state_db2)
53
+        url = 'http://%s:11080' % dsm_unit.info['public-address']
54
+        https_url = 'http://%s:11081' % dsm_unit.info['public-address']
55
+        response = requests.get(url, verify=False)
56
+        response = requests.get(https_url, verify=False)
57
+        response.raise_for_status()
58
+
59
+
60
+if __name__ == '__main__':
61
+    unittest.main()
Back to file index

tests/tests.yaml

1
--- 
2
+++ tests/tests.yaml
3
@@ -0,0 +1,5 @@
4
+packages:
5
+  - amulet
6
+  - python3
7
+  - unzip
8
+  - pwgen
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