Merge lp://staging/~chad.smith/charms/precise/storage/storage-nfs-python-provider into lp://staging/~dpb/charms/precise/storage/trunk

Proposed by Chad Smith
Status: Merged
Merged at revision: 30
Proposed branch: lp://staging/~chad.smith/charms/precise/storage/storage-nfs-python-provider
Merge into: lp://staging/~dpb/charms/precise/storage/trunk
Diff against target: 2318 lines (+1932/-206)
27 files modified
.bzrignore (+2/-0)
Makefile (+19/-0)
charm-helpers.yaml (+5/-0)
config.yaml (+8/-27)
hooks/charmhelpers/cli/README.rst (+57/-0)
hooks/charmhelpers/cli/__init__.py (+147/-0)
hooks/charmhelpers/cli/commands.py (+2/-0)
hooks/charmhelpers/cli/host.py (+15/-0)
hooks/charmhelpers/core/hookenv.py (+395/-0)
hooks/charmhelpers/core/host.py (+291/-0)
hooks/common_util.py (+219/-0)
hooks/hooks (+3/-15)
hooks/install (+0/-2)
hooks/storage-provider.d/nfs/config-changed (+12/-0)
hooks/storage-provider.d/nfs/data-relation-changed (+12/-0)
hooks/storage-provider.d/nfs/data-relation-departed (+5/-0)
hooks/storage-provider.d/nfs/nfs-relation-changed (+23/-0)
hooks/storage-provider.d/nfs/nfs-relation-departed (+7/-0)
hooks/storage-provider.d/nova/common (+0/-120)
hooks/storage-provider.d/nova/config-changed (+0/-14)
hooks/storage-provider.d/nova/data-relation-broken (+0/-7)
hooks/storage-provider.d/nova/data-relation-changed (+0/-7)
hooks/storage-provider.d/nova/start (+0/-6)
hooks/storage-provider.d/nova/stop (+0/-7)
hooks/test_common_util.py (+607/-0)
hooks/testing.py (+101/-0)
metadata.yaml (+2/-1)
To merge this branch: bzr merge lp://staging/~chad.smith/charms/precise/storage/storage-nfs-python-provider
Reviewer Review Type Date Requested Status
David Britton Approve
Fernando Correa Neto (community) Approve
Adam Collard (community) Abstain
Review via email: mp+202379@code.staging.launchpad.net

Description of the change

- Add nfs provider type to the storage charm.
- centralize common functions in common_util.py
- move all nova functions into nova_util.py for ease of testing in the future
- Add a heaping pile of unit tests to the storage charm as well as a common TestHookenv class for intercepting juju commands
- Add a makefile with "test" and "lint" targets
- convert most of the bash to python for easier unit testing
- use charm-helpers.core code to standardize juju interaction with other charms
   Note: this MP is not to review any of the charmhelpers code as that didn't change, it was only included unaltered in this charm

In terms of storage charm interaction:
  1. the storage charm now requires the principal charm to set its requested mountpoint via relation-set mountpoint=/desired/mountpoint
  2. when the device (nfs/nova) is attached, initialized, fsck'd and mounted the storage charm will respond to the principal over the "data" relation that the mount is ready by setting the mountpoint to publish the mounted device path

To post a comment you must log in.
Revision history for this message
Chad Smith (chad.smith) wrote :

Simplest deployment using juju-deployer:

common:
    services:
        postgresql:
            branch: lp:charms/precise/postgresql
            constraints: mem=2048
            options:
                extra-packages: python-apt postgresql-contrib postgresql-9.1-debversion
                max_connections: 500
        storage:
            branch: lp:~fcorrea/charms/precise/storage/consume-principal-charm-mountpoint
            options:
                provider: nova
                key: <your_os_name>
                tenant: <your_os_name>_project
                secret: <your_os_password>
                endpoint: https://keystone.canonistack.canonical.com:443/v2.0/
                region: lcy01

doit:
    inherits: common
    series: precise

juju-deployer -c postgres-storage.cfg doit

Revision history for this message
Chad Smith (chad.smith) wrote :

Typo corrections on the charm paths
     services:
         postgresql:
             branch: lp:~fcorrea/charms/precise/postgresql/delegate-blockstorage-to-storage-subordinate

         ...
         storage:
             branchL lp~chad.smith/charms/precise/storage/storage-nfs-python-provider

85. By Chad Smith

merge dpb's upstream storage charm trunk. resolve conflict

86. By Chad Smith

unit test fix for umount_volume. needed to mock lsof calls

87. By Chad Smith

make sure tearDown removes any provider persistent data for nova and/or nfs left around by unit tests

88. By Chad Smith

sync missed relation_set fix to specify relid in hookenv.relation_set calls

Revision history for this message
Chad Smith (chad.smith) wrote :

Here's the corrected postgres-storage.cfg deployer file (make sure to set all OS_* to your env values)

common:
    services:
        postgresql:
            branch: lp:~fcorrea/charms/precise/postgresql/delegate-blockstorage-to-storage-subordinate
            constraints: mem=2048
            options:
                extra-packages: python-apt postgresql-contrib postgresql-9.1-debversion
                max_connections: 500
        storage:
            branch: lp:~chad.smith/charms/precise/storage/storage-nfs-python-provider
            options:
                provider: nova
                key: OS_USERNAME
                tenant: OS_TENANT_NAME
                secret: OS_PASSWORD
                endpoint: https://keystone.canonistack.canonical.com:443/v2.0/
                region: OS_REGION_NAME

jfdi:
    inherits: common
    series: precise
    relations:
        - ["postgresql:data", "storage:data"]

https://pastebin.canonical.com/103331/

Revision history for this message
David Britton (dpb) wrote :

[0] I would pull out all partition logic from common_util. Creating a whole-disk partition just to turn around and mount it is kind of busy-work. Create the filesystem on the whole disk and mount that.

Revision history for this message
David Britton (dpb) wrote :

[1] Makefile: change test directive to "CHARM_DIR=`pwd` trial hooks"

Revision history for this message
David Britton (dpb) wrote :

[2] Add _trial_tmp to .bzrignore

Revision history for this message
David Britton (dpb) wrote :

[3] Some lint errors:

hooks/__init__.py:1:1: W391 blank line at end of file
hooks/testing.py:66:1: F821 undefined name 'util'
hooks/testing.py:78:19: E702 multiple statements on one line (semicolon)

Revision history for this message
David Britton (dpb) wrote :

[4] Remove the revision file.

89. By Chad Smith

add .bzrignore and Makefile changes to call trial hooks instead cd hooks etc.

Revision history for this message
David Britton (dpb) wrote :

[5] Make sure there is docstring on all methods, please.

90. By Chad Smith

lint fixes

91. By Chad Smith

make a number of functions private, add docstrings to all functions

92. By Chad Smith

unit tests to test _private functions

Revision history for this message
David Britton (dpb) wrote :

[6] Why are we storing PERSIST_MOUNTPOINT. Can't we fetch that from the relation data? Chad seemed to think in IRC that it had to do with the broken/departed scenerio.

Revision history for this message
David Britton (dpb) wrote :

[7] in common_util:mount_volume, you are reading persisted data from the
nfs relation. Instead of doing this, I wonder if you should instead use
the relation-get concept of probing the relation to get that data.

It's something like:

    ids=$(relation-ids --type nfs)
    relation-get --id <id>

I'll leave it to you to decide if this is a direction you want to go.

--
David Britton <email address hidden>

Revision history for this message
David Britton (dpb) wrote :

[8] There seem to be some mount and unmout functions in charmsupport,
should we use those? The answer could be no, I haven't looked closely
at them.

== nfs/data-relation-changed ==

[9] extra import sys

[10] you have a formatted variable "config_changed" that is used, but
a replacement is never run on it. Seems wrong

[11] General: what are all the calls to config-changed for?

[12] nfs: you could symlink stop and nfs-relation-departed, also get rid
of -broken?? I'm not sure why we need stop actually. I'd get rid of it
and start if possible.

== nova/config-changed ==

[13] remove jsonpipe install, and use python json module to read data in
nova_util.py. I get that this was a fast conversion, but that one
cleanup saves a package installation. :)

== nova/data-relation-* ==

[14] not sure we need a call to config_changed here, do we? I think I'm
seeing the problem. We need to install software in an install hook, but
if someone changes configuration that software will not be installed.
I'm thinking that we should symlink install and config-changed in all
these, and stop calling config_changed at the top of all relation hooks.

== nova/nova-util.py ==

[15] add docs

[16] get_volume_id(): your else here is dangerous, I think we should
fail if either the volume_id or name is not specified, or we create it.
Those are the only two options. Also, if the name doesn't return just
one volume, that should be an error.

[17] attach_nova_vlume(): combine first two sentinals to be check for
"available" status, and exit with error if not in that state?

[18] I would change most of the call() to check_call(), to get the
return code checked.

== nova/ ==

[19] same comments about start/stop hooks, and if they are needed. I
wonder if we just need to worry about the relation changed and departed?

--
David Britton <email address hidden>

Revision history for this message
David Britton (dpb) wrote :

The idea is good, but there are some thing I think need to be done before upstream will accept. But I like that it's working for simple cases. I'll do more testing tomorrow and post back with results.

review: Needs Fixing
93. By Chad Smith

drop PERSIST_MOUNTPOINT and use relation-get data instead

94. By Chad Smith

rename variable from charm_data -> persistent_data for clarity

Revision history for this message
Chad Smith (chad.smith) wrote :

Thanks david for all the comments:
[1-6] handled

[7] will look at the PERSIST_DATA again tomorrow on a full landscape deployment to ensure I can access "nfs-relation" mount settings data from within the "data-relation" that is running between landscape-charm and storage.

[8] using charmherlpers.host.mount(): I looked at charmhelpers we will need some significant patch (and should do this in phase 2) to make those methods functional for us in both nfs and nova environments and that may take more time than we have at the moment.

[9-10] fixed

[11 & 14] config-changed calls just ensure the dependencies of the provider are installed, the reason this happens in config-changed instead of install is because the config-change could have been changing provider type from "nova" to "nfs" in the config so we wouldn't be hitting an install hook in that case. There probably needs to be a better mechanism to install dependencies in the event the provider changes in the storage config.
-- Maybe the potential solution is to symlink config-changed and install and use the current provider setting to install the needed dependencies WDYT?

[12 & 19] ditched start/stop and all -broken hooks renamed to -departed (because departed still has the relation data available in juju on which we depend for unmounting the "mountpoint" etc) *broken hooks do not have that persistent data so we wouldn't be able to unmount or detach devices that we don't know about

Will resolve 13 and 15-18 tomorrow. Sorry about the size of this review and thanks for the good notes

95. By Chad Smith

rename (nfs|data)-relation-broken hooks to (nfs|data)-relation-departed so we can reference existing hook relation-data for mountpoint instead of using PERSIST_MOUNTPOINT file. Drop start/stop hooks as they are duplicate function of (data|nova)-relation-changed hooks

Revision history for this message
Fernando Correa Neto (fcorrea) wrote :

All seats already taken but I'll leave some feedback:

[1] in hooks/hooks
+charm_hooks_dir="$CHARM_DIR/hooks"
+export PYTHONPATH=$charm_hooks_dir

That one can be condensed in a single line:

export PYTHONPATH="$CHARM_DIR/hooks"

[2] in hooks/storage-provider.d/nova/config-changed
+cat > `dirname $0`/nova_environment <<EOF
+OS_USERNAME=$(config-get key)
+OS_TENANT_NAME=$(config-get tenant)
+OS_PASSWORD=$(config-get secret)
+OS_AUTH_URL=$(config-get endpoint)
+OS_REGION_NAME=$(config-get region)
+EOF
+

Given all the conversation that we had about separating the storage service from the block storage provider, I think that storing the credentials in a file might not be the best thing to do.
I think that "hiding" it in the config state might make a little harder to find the credentials in this case.
Of course, by saying that I'm neglecting the fact that we were logging all the credentials upon making nova calls.
In that case, I think we should make our best to make it hard to find those creds. WDYT?

[3]
+current_dir = sys.path[0]

I wonder if we could use os.path.dirname(os.path.realpath(__file__)) as it seems to be advertised as the best way of getting the current directory of a file.

[4] in hooks/common_util.py Line 157
def _is_ext4_filesystem(device_path):
    """Return C{True} if a filesystem is of type C{ext4}"""
    command = "file -s %s | egrep -q ext4" % device_path
    return subprocess.call(command, shell=True) == 0

Maybe we could have something more generic as _assert_fstype(device_path, fstype)?

[5] in hooks/common_util.py Line 197
I'm fine leaving the "if provider ==" in there and I don't think that part of the code will grow that fast. Maybe if it's the case people add support for handling SAN volumes or other types of storage as a service technology, but in general, this is something I'd delegate to each provider as they will mount things differently and also to concentrate the logic in smaller functions that are easy to maintain.

The dangerous of having a "common" module is that they can grow quite big as new "common" logic are required while having an API that exposes an expected interface might work better as things are kept separate in each provider.

96. By Chad Smith

add .PHONY targets to makefile

97. By Chad Smith

link install hook to config-changed hooks within nova and nfs providers to ensure dependencies for the provider are installed both at install time and during a provider config change

98. By Chad Smith

fix symlinks for install hooks

99. By Chad Smith

get_volume_id to exit(1) if multiple volumes have labels related to this instance

100. By Chad Smith

fix multiple-volume associated with juju unit. error out in this case

Revision history for this message
Chad Smith (chad.smith) wrote :

Resolved dpb's
15: docs added
16: get_volume_id will exit in error if multiple potential volume candidates are discovered for this unit through nova volume-list

17: attach_nova_volume will only proceed if volume status is "available" and exit in error for other status
18:
[18] leaving subprocess.call as we do check the exit code. it just doesn't traceback for us like check_call would have

101. By Chad Smith

address fcorrea's comments:
[1] hooks/hooks variable consolidation
[3] os.path.dirname(os.path.realpath(__file__)) instead of sus.path[0]
[4] _is_ext4_filesystem -> _assert_fstype(path, fstype)

Revision history for this message
Adam Collard (adam-collard) :
review: Abstain
102. By Chad Smith

- drop charmhelpers.contrib|fetch|payload which we don't use
- add update-charm-helpers make target to refresh charmhelpers
- common_util drop PERSIST_DATA

103. By Chad Smith

use sys.exit(0) instead of return 0

104. By Chad Smith

check subprocess.call output to ensure exit 0 from called script

105. By Chad Smith

mova get_volume_status below the initial creation of the volume

106. By Chad Smith

instead of using a persistent data file to store the nova attached device_path, allow nova's data-relation-changed hook to provide a device_path parameter to mount_volume

Revision history for this message
Chad Smith (chad.smith) wrote :

Thanks for the review fcorrea, I resolved [1]

[2] Given all the conversation that we had about separating the storage service from the block storage provider, I think that storing the credentials in a file might not be the best thing to do.
....

I've resolved [2] and dropping writing any persistent files during the mirgration of nova code to the block-storage-broker charm at lp:~chad.smith/charms/precise/block-storage-broker/trunk
So, I'll leave it in this branch as our next branch against this charm will be to remove all nova provider code.

[3]
+current_dir = sys.path[0] # I wonder if we could use os.path.dirname(os.path.realpath(__file__))

Fixed

[4] in hooks/common_util.py Line 157
def _is_ext4_filesystem(device_path): # Maybe we could have something more generic as _assert_fstype(device_path, fstype)?

Fixed

[5] in hooks/common_util.py Line 197
I'm fine leaving the "if provider ==" in there and I don't think that part of the code will grow that fast.
...

Yeah I can only see about 3-4 providers in the near future, when we cross that threshold where handling provider type is an issue, we can make a quick branch to resolve that.

107. By Chad Smith

nova data-relation-changed calls mount_volume passing the device returned from attach_nova_volume

108. By Chad Smith

drop partitioning actions of nova attached volues. still ensure volume is formatted ext4

109. By Chad Smith

lint

110. By Chad Smith

unit test updates to drop partitioning a nova volume to a single partition at max capacity. we can mount nova volume without the extra sfdisk work

111. By Chad Smith

drop all of nova provider and stub in the blockstoragebroker provider handling for when block-storage-broker charm is accepted

112. By Chad Smith

drop any nova-specific provider references and settings from config.yaml, update unit tests to test blockstoragebroker provider stub in preparation for jseutter's branch

113. By Chad Smith

fix fcorrea review comment, Makefile to handle _trial_temp directory

114. By Chad Smith

test__ changed to test_wb for internal functions

Revision history for this message
Fernando Correa Neto (fcorrea) wrote :

Hey Chad.

Just so we keep record as we talked about this already on IRC.
1) s/test__/test_wb/ for the private funtions
2) fs related utility functions are left here because Jerry's branch depends on it.

I looks good to me. +1

review: Approve
Revision history for this message
David Britton (dpb) wrote :

== common_util.py ==

[20] I think you can remove the guts of the log function, and just have it do

return hookenv.log(message, level). It seems to handle the 'None' case already

[21] RE: TODO: you don't need to check files existing "below" a mount. mount doesn't care, it will happily eclipse them (not remove them). Once the device is unmounted, the files will be visible again.

+1, I'll test and review further on Jerry's Branch. :)

review: Approve
115. By Chad Smith

resolve dpb's comments. simplify hookenv.log wrapper in common_util, drop comment about mount eclipsing existing files

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
The diff is not available at this time. You can reload the page or download it.

Subscribers

People subscribed via source and target branches

to all changes: