Commit graph

69 commits

Author SHA1 Message Date
Spencer McElmurry
12bba418d1 (POOLER-81) Add time remaining information (#280)
* (POOLER-81) Add time_remaining information

Before, the only time calculation displayed for a given VM was the
lifetime parameter. Added the remaining parameter which will
display time until the VM is destroyed as a float.

Additionally, start_time and end_time were added to api to return
as UTC based times (e.g. 2018-07-10 11:01:03 -0700).

* Remove abs eval from GET, rework spec tests to check each field.
This allows us to account for "flakiness" of the remaining return.

* Change datetime to RFC3339 for start_time and end_time
2018-07-12 09:40:22 -07:00
Spencer McElmurry
84065dd059 Revert "(POOLER-81) Add time_remaining information" (#279)
* Revert "(POOLER-34) Ship clone request to ready time to metrics (#277)"

This reverts commit a865e6bd2f.

* Revert "(POOLER-81) Add time_remaining information (#276)"

This reverts commit 1910cffaf7.
2018-07-10 09:00:12 -07:00
Spencer McElmurry
1910cffaf7 (POOLER-81) Add time_remaining information (#276)
* (POOLER-81) Add time_remaining information

Before, the only time calculation displayed for a given VM was the
lifetime parameter. Added the time_remaining parameter which will
display time until the VM is destroyed in hours, minutes, seconds.

Additionally, updated the running parameter to display in a similar
fashion as time_remaining.

* Add spec testing for testing time_remaining stat
2018-07-09 16:22:05 -07:00
kirby@puppetlabs.com
4fa54c8008 Move net/ldap require into vmpooler.rb
This commit moves net/ldap require from authenticate_ldap in api/helpers to vmpooler.rb. Without this change net/ldap and rubygems are required again every time authenticate_ldap is run.
2018-06-27 15:21:15 -07:00
kirby@puppetlabs.com
9fa27af8e5 (POOLER-113) Add support for multiple LDAP search bases
This commit updates vmpooler to support setting an array of search bases
in addition to a single base provided as a string. Without this change
it is not possible to specify multiple search bases to use with the LDAP
authentication provider. Additionally, test coverage is added to
the authentication helper method.
2018-06-25 21:37:22 -07:00
kirby@puppetlabs.com
9bb4df7d8e (POOLER-107) Add configuration API endpoint
This commit adds a configuration endpoint to the vmpooler API. Pool
size, and pool template, can be adjusted for pools that are configured
at vmpooler application start time. Pool template changes trigger a pool
refresh, and the new template has delta disks created automatically by
vmpooler.

Additionally, the capability to create template delta disks is added to
the vsphere provider, and this is implemented to ensure that templates
have delta disks created at application start time.

The mechanism used to find template VM objects is simplified to make the flow of logic easier to understand. As an additional benefit, performance of this lookup is improved by using FindByInventoryPath.

A table of contents is added to API.md to ease navigation. Without this change API.md has no table of contents and is difficult to navigate.

Add mutex object for managing pool configuration updates

This commit adds a mutex object for ensuring that pool configuration changes are synchronized across multiple running threads, removing the possibility of two threads attempting to update something at once, without relying on redis data. Without this change this is managed crudely by specifying in redis that a configuration update is taking place. This redis data is left so the REPOPULATE section of _check_pool can still identify when a configuration change is in progress, and prevent a pool from repopulating at that time.

Add wake up event for pool template changes

This commit adds a wake up event to detect pool template changes.
Additionally, GET /config has a template_ready section added to the
output for each pool, which makes clear when a pool is ready to populate
itself.
2018-06-15 10:15:47 -07:00
Samuel Beaulieu
10245321bf (maint) Add the last boot time for each pool
This commit add a redis hash where there is one key per pool, and the
stored value is the last time a VM was booted e.g. the last time
a VM went from 'pending' to 'ready'. This is also displayed in the
API as lastBoot:'2018-03-23 17:43:39 +0000'. The data can then be
used by any external system, in this case our alarming system.
2018-03-28 11:11:49 -07:00
Samuel Beaulieu
96541729fb (POOLER-93) Extend API endpoint to provide just what is needed
The status endpoint provides a lot of statistics. This commit extends it
by supporting a query parameter called 'view' which may contain one or
multiple comma separated names for the top-level statistics returned
in the JSON response. status is always returned.
Optional elements are capacity,queue,clone,boot,pools
Everything is returned when 'view' is not specified, which is
backwards compatible with the current behavior.
2017-10-20 15:53:40 -05:00
Samuel Beaulieu
f10bcadf7e (POOLER-92) Add the alias information in the API status page for each pool
Before this change if a pool had an alias configured, the information would not be
made public in the API. This commit adds the alias key in the pool object for each
pool if configured. The alias key can be abscent, a string or an one or multiple
array of strings. The value of the alias is copied from the configuration and can
represent another name for the pool, or another configured pool.
2017-10-18 12:28:51 -05:00
Glenn Sarti
06100ddea6 (maint) Fix rubocop violations
This commit fixes minor rubocopy violations in eleven source files.  Minor
violations are those that include formatting, single quotes, and recently added
classes.
2017-03-16 15:39:15 -07:00
Glenn Sarti
2c74f4fa3a (POOLER-71) Add dummy authentication provider
Previously it was difficult to do local development as VMPooler requires an LDAP
service for authentication. This commit adds a dummy authentication provider.
The provider has passes authentication if the username and password are
different, and fails if the username and password are the same.  This commit
also updates the documentation in the config YML file.
2017-02-09 16:23:10 -08:00
kirby@puppetlabs.com
506c124578 Add vmpooler__migrating__vm at VM checkout 2016-10-14 12:40:33 -07:00
Rick Bradley
5a1d547830 [QENG-4181] Add inline documentation for /status endpoint
It's useful to be able to see, in the code, what sort of output we
are generating with endpoints like `/status`.
2016-09-10 12:36:25 -05:00
Rick Bradley
30dc060731 [QENG-4181] Add per-pool stats to /status API
Prior to this the only per-pool statistics that could be extracted from the API
were a list of empty pools in the "status" section of the returned results of
the `/status` endpoint.

This adds a new "pools" section to the '/status' results which lists, for each
pool, the following results:

 - The number of ready vms in the pool
 - The number of running vms in the pool
 - The number of pending vms in the pool
 - The maximum size of the pool (as specified in the vmpooler configuration)

Example:

```
{
  "boot": {
  "duration": {
    "average": 163.6,
    "min": 65.49,
    "max": 830.07,
    "total": 247744.71000000002
  },
  "count": {
    "total": 1514
  }
  # ...
  "pools": {
    "pool1": {
      "ready":   5,
      "running": 2,
      "pending": 1,
      "max":     15
    },
    "pool2": {
      "ready":   0,
      "running": 10,
      "pending": 0,
      "max:      10
    }
  }
}
```

This includes spec coverage for this change (we could use more specs on `/status` in general); as well as a couple of general spec improvements.
2016-09-09 17:00:47 -05:00
Rick Sherman
34b93ca6c3 Merge CI.next into Master (#161)
* [QENG-3919] Make vmpooler checkouts be all or nothing (#153)

* (QENG-3919) spike for implementation of all-or-nothing checkout

* Fix two botched variable references

* Aggregate API helper methods

* Add specs for failed multi-vm allocation API endpoints

* (QENG-3919) Add tests for multiple vm requests

* (QENG-3919) Add (failing) specs for POST /vm/pool1+pool2 usages

This exposes the old (bad) behavior on this other code path. Will fix this up next.

* (QENG-3919) Bring query params version in line with JSON post version

Not clear to me why these had to be implemented so differently.

* (QENG-3919) extract common method from both methods of VM allocation

* (QENG-3919) Naming fix, cosmetic cleanups

I mean, I presume all these commits are going to get squashed away on merge anyway.

* (QENG-3919) Update API docs

We consider it a bug that the actual behavior was not this behavior, but the
documentation was also silent on this point.

* (QENG-3919) minor readability tweak in refactored method

* (QENG-3919) Clean up interim comments re: status codes

* (QENG-3919) Drop now-orphaned `checkout_vm` method

We kept this up-to-date while we were upgrading and refactoring, but, turns out,
this method is no longer called anywhere.  💀 🔥

* (QENG-3919) Return 503 status on failed allocation

Making sure we go back to the original functionality, which was:

 - status 200 when vms successfully allocated
 - status 404 when a pool name is unknown
 - status 404 when no pool name is specified
 - status 503 when vm allocation failed

* (QENG-3919) add net-ldap to Gemfile

Maybe we shouldn't foil-ball gems onto servers.

* (QENG-3919) Turns out, spush isn't a redis command

And hence we see once again the weakness of mockist tests.

* (QENG-3919) Pin the net-ldap gem to 0.11 for the jrubies, etc.

* (QENG-3919) Correct an old spelling error in spec descriptions

* (QENG-3919) Further tweak net-ldap version

* (QENG-3919) return_single_vm -> return_vm_to_ready_state

cc @shermdog

* (RE-7014) Add support for statsd
They way we were using graphite was incorrect for the type of data we were sending it.  statsd is the appropriate mechanism for our needs.
statsd and graphite are mutually exclusive and configuring statsd will take precendence over Graphite.  Example of configuration in vmpooler.yaml.example

* (RE-7014) Add tracking of vm gets via statsd
Add the tracking of successful, failed, invalid, and empty pool vm gets.  It is possible we may want to tweak this, but have validated with spec tests and pcaps.

```
vmpooler-tmp-dev.ready.debian-7-x86_64:1|c
vmpooler-tmp-dev.running.debian-7-x86_64:1|c

vmpooler-tmp-dev.checkout.invalid:1|c
vmpooler-tmp-dev.checkout.success.debian-7-x86_64:1|c
vmpooler-tmp-dev.checkout.empty:1|c

vmpooler-tmp-dev.running.debian-7-x86_64:1|c

vmpooler-tmp-dev.clone.debian-7-x86_64:12.10|ms

vmpooler-tmp-dev.ready.debian-7-x86_64:1|c
```

* (RE-7014) statsd nitpicks and additional rspec
Cleaned up some code review nitpicks and added pool_manager_spec for empty pool.

* (RE-7014) update statsd to use gauge for running/ready
Previously was using increment which was incorrect for that particular application.

* Revert "Merge pull request #155 from shermdog/RE-7014-cinext"

This reverts commit cc03a86f6a, reversing
changes made to 5aaab7c5c2.

* (QENG-4070) Consistently return 503 if valid pool is empty

There were several problems with how the pooler checked out vms with
respect to empty pools, invalid pools, and aliases:

- If the vmpooler config did not contain any aliases and the caller
requested a vm from an empty pool or a non-existent one, the vmpooler
would error with:

    NoMethodError - undefined method `[]' for nil:NilClass

If the config contained a non-nil alias section, then:

- If the caller requested a vm from an empty pool and either the vm
didn't have an alias or the aliased pool was empty or non-existent, then
the request for that vm would be silently ignored. The vmpooler would
return 200 if the caller asked for multiple vms and the vmpooler was
able to checkout at least one vm.  Otherwise it would return 404.

- Similarly, if the caller requested a vm from a non-existent pool, then
the request was silently ignored.

This commit adds a `pool_names` Set to the config containing all valid
pool names including aliases. This is used to determine whether a
requested template name is valid or not. This is necessary because redis
does not distinguish between empty and non-existent sets, e.g. the
following returns false in both cases:

    backend.exists('vmpooler__ready__' + key)

If the caller requests a vm (single or multiple), and any vm references
an invalid pool name, we immediately return 404. Otherwise, we know the
request is for valid pool names, since the vmpooler requires a restart
to change pool names and counts.

We then attempt to acquire each vm, trying to match on pool name or
failing back to aliased pool name, as was the previous behavior.

The resulting behavior is:

- If the caller asks for at least one vm from an unknown pool, then
don't try to checkout any vms and respond with 404.
- If the caller asks for a vm, and at least one pool is empty, then
respond with 503, returning checked out vms back to the pool.
- Otherwise return 200 with the list of checked out vms.

This commit also makes `alias` optional again.

This commit also re-enables tests that were merged in from master, but
originally commented out due to the bugs described above..

* (maint) Add json pessimistic pin

json 2.0.x was released on July 1 and is not compatible with ruby < 2.0.
Since we still support that version, add a pessimistic pin, which is
what we were using prior to July 1.

* [QENG-4070] Make json version conditional on RUBY_VERSION

* Drop extraneous mocks from updated test

* Revert "Revert "Merge pull request #155 from shermdog/RE-7014-cinext""

This reverts commit 0fd6fff934.

* Fix some spec errors

These were caused in part by dropping changes from the original PR when we
dropped the v1_spec.rb master test file (in favor of the updated and separated
versions).

* [QENG-4075] Fix bug with template name on allocation failure

We're returning [nil,nil] in this case, meaning that name will not be set. This
means we'll get an error trying to concatenate the stats string. Use the
requested template name here instead.

* [QENG-4075] Refactor statsd methods / classes

Prior to this we could easily run into situations where `statds_prefix` would
be `nil` (and possibly the `statsd` handle itself). There was some significant
complexity and brittleness in how statsd was set up.

Refactored so that:

 - `statsd_prefix` is no longer exposed to any callers of statsd methods
 - there is now a `Vmpooler::DummyStatsd` class which can be returned when we are not actually going to publish stats, but would like to keep the calling interface consistent
 - setup of the statsd handle is via just passing in `config[:statsd]`, if `nil`, this will result in a dummy handle being return
 - defaulting of `server` values was fixed -- this did not actually work in the previous implementation. `config[:statsd][:server]` is now required.
 - tests use a `DummyStatsd` instance instead of an rspec double.
 - calls to `statsd.increment` were taking incorrect arguments (some our fault, some part of the prior implementation), and were not collecting data on which pools were "invalid" or "empty". Fixed this and are now explicitly tracking the invalid/empty pool names.

* [QENG-4075] Drop now-superfluous :statsd config defaulting

* [QENG-4075] Unify graphite and statsd for the pool manager

Prior to this, the `pool_manager.rb` library could take handles for both
graphite and statsd endpoints (which were considered mutually exclusive),
and then would use one. There was a bevy of conditional logic around sending
metrics to the graphite/statsd handles (and actually at least one bug of
omission).

Here we refactor more, building on earlier work:

 - Our graphite class comes into line with the API of our Statsd and DummyStatsd classes
 - In `pool_manager.rb` we now accept a single "metrics" handle, and we drop all the conditional logic around statsd vs. graphite
 - We move the inconsistent error handling out of the calling classes and into our metrics classes, actually logging to `$stderr` when we can't publish metrics
 - We unify the setup code to use `config` to determine whether statsd, graphite, or a dummy metrics handle should be used, and make that happen.
 - Cleaned up some tests. We could probably stand to do a bit more work in this area.

* [QENG-4075] Clean up pool manager, specs

Prior to this, `pool_manager.rb` allowed the `metrics` argument to be optional,
but at this point it will be an instance of `Vmpooler::Statsd`,
'Vmpooler::Graphite', or `Vmpooler::DummyStatsd`, so making this non-optional.

Cleaned up that file's tests, cosmetically, as well as recognizing that the
behavioral difference between graphite and statsd does not depend on the pool
manager.

* [QENG-4075] update example vmpooler.yaml file

This documents the changes to :server being mandatory for all metrics
endpoints, as well as the graphite endpoint supporting an optional :port
configuration value.

* [QENG-4075] Rename usages of statsd -> metrics

Really, let's just support a generic metrics interface.

* (maint) move statsd-ruby require into Vmpooler::Statsd class

We've managed to move mentions of this out of the calling code, so let's
move the require.

* (maint) metrics.log -> metrics.timing

We missed this during the refactoring. Bringing this up to date.

* [QENG-4075] Allow specifying 'graphs:' for dashboard

Prior to this the dashboard front-end would use the configuration settings
for `graphite[:server]`/`graphite[:prefix]` to locate a graphite server
to use for rendering graphs.

Now that we have multiple possible metrics backends, the front-end graph
host for the dashboard could be entirely different from the back-end metrics
server that we publish to (if any).

This decouples those settings:

 - use `graphs[:server]` / `graphs[:prefix]` for the graphite-compatible web front-end to use for dashboard display graphs
 - fall back to `graphite[:server]`/`graphite[:prefix]` if `graphs` is not specified, in order to support legacy `vmpooler.yaml` configurations.

Note that since `statsd` takes precedence over `graphite`, it's possible to specify both `statsd` (for publishing) and `graphite` (for reading). We still prefer `graphs` over `graphite`.

Updated the example `vmpooler.yaml` config file.

* (maint) fix variable reference in new_metrics

This was referencing config directly, when what we want is for a
hash to be passed in (derived from config).

* (maint) Fix typo in updated graph link call

* (maint) default :graphs prefix to 'vmpooler'

* (maint) Fix parse error in vmpooler script

The things you find through manual QA 🧌

* (maint) use strings instead of symbols in config

Nested hash data comes back with string keys, not symbols. Be consistent.

* [QENG-4075] Factor out Vmpooler::DummyStatsd

This makes it visible to lib/vmpooler.rb, as well as putting this dummy
metrics endpoint in its own file for easier discovery.

* (maint) clean up statsd inclusion and require lines

The library is actually required as 'statsd' and not 'ruby-statsd', best I can tell.

* (maint) construct ::Statsd instead of Statsd

Because it's ambiguous in this scope, and, well, it doesn't
actually work in production.

* [QENG-4075] Also track completely invalid requests

When we don't even get a pool name we still want metrics to be recorded.
2016-07-25 10:43:32 -05:00
FOXX
4e2a1fb62c Added IP lookup functionality for /vm/hostname (#154) 2016-06-28 17:29:06 -05:00
Scott Schneider
48a1a8d621 Add new disks via API
Add an additional disk to a running VM via the vmpooler API.

````
$ curl -X POST -H X-AUTH-TOKEN:a9znth9dn01t416hrguu56ze37t790bl --url vmpooler.company.com/api/v1/vm/fq6qlpjlsskycq6/disk/8
````
````json
{
  "ok": true,
  "fq6qlpjlsskycq6": {
    "disk": "+8mb"
  }
}
````

Provisioning and attaching disks can take a moment, but once the task completes it will be reflected in a `GET /vm/<hostname>` query:

````
$ curl --url vmpooler.company.com/api/v1/vm/fq6qlpjlsskycq6
````
````json
{
  "ok": true,
  "fq6qlpjlsskycq6": {
    "template": "debian-7-x86_64",
    "lifetime": 2,
    "running": 0.08,
    "state": "running",
    "disk": [
      "+8mb"
    ],
    "domain": "delivery.puppetlabs.net"
  }
}
2016-01-14 10:46:57 -08:00
Scott Schneider
20fa7d20be Merge pull request #138 from sschneid/qeng_2807
(QENG-2807) Allow pool 'alias' names
2015-11-13 09:24:48 -08:00
Scott Schneider
17b24d69ad Allow pool 'alias' names
The following pool configuration would allow a pool to be aliased in POST
requests as 'centos-6-x86_64', 'centos-6-amd64', or 'centos-6-64':

````yaml
- name: 'centos-6-x86_64'
  alias: [ 'centos-6-amd64', 'centos-6-64' ]
  template: 'templates/centos-6-x86_64'
  folder: 'vmpooler/centos-6-x86_64'
  datastore: 'instance1'
  size: 5
````

The 'alias' configuration can be either a string or an array.

Note that even when requesting an alias, the pool's 'name' is returned in
the JSON response:

````
$ curl -d '{"centos-6-64":"1"}' --url vmpooler/api/v1/vm
````
````json
{
  "ok": true,
  "centos-6-x86_64": {
    "hostname": "cuna2qeahwlzji7"
  },
  "domain": "company.com"
}
````
2015-11-05 11:51:53 -08:00
Scott Schneider
d74c9ff512 Don't require username/password authentication for GET /token/:token route 2015-11-04 13:19:15 -08:00
Scott Schneider
e0356968df (QENG-2995) Display associated VMs in GET /token/:token endpoint 2015-11-04 12:35:35 -08:00
Scott Schneider
906ae89987 Remove duplicate (nested) "ok" responses
As we approach an "official" v1.0.0 of the API I'd like to remove some old
nested "ok" responses.  These were left in as the Beaker vmpooler
hypervisor used them, but I long-ago patched that code and I think it's
time to deprecate these.
2015-08-21 13:58:07 -07:00
Scott Schneider
89ce70dba9 Track token use times
* rename the Redis token 'timestamp' var to 'created'
* update the Redis token 'last' var when token is successfully validataed
* expose the Redis token 'last' var in GET /token route
2015-08-20 19:54:59 -07:00
Scott Schneider
492cfb06a3 List tokens via GET /token 2015-08-20 18:50:51 -07:00
Colin
acb95d34c8 (MAINT) Reduce redis Calls in API
The return values from most redis calls inform the caller of whether a
key or hash value exists. Several exists() calls can be removed in
favor of this approach.

Updated spec tests to account for a removal of exists() and ismember()
calls in API tests.
2015-07-28 14:47:01 -07:00
Scott Schneider
add88c7bba (QENG-1304) vmpooler should require an auth key for VM destruction 2015-07-28 12:03:14 -07:00
Scott Schneider
1689133b19 Require an auth token to use snapshots 2015-07-16 10:59:30 -07:00
Scott Schneider
1c3045fd65 Host snapshot functionality 2015-07-16 10:29:49 -07:00
Scott Schneider
c720f12c05 Move tag-filtering and exporting to API helper methods 2015-06-30 19:45:16 -07:00
Scott Schneider
6523062b62 Allow for only a [configurable] tag set 2015-06-30 12:54:46 -07:00
Scott Schneider
3aa8389749 Discard/skip empty tags 2015-06-30 11:20:13 -07:00
Colin
b6cb20ba9f Merge pull request #108 from sschneid/api_summary_reorg
API summary rework
2015-06-08 11:28:45 -07:00
Scott Schneider
d3f4f6fb77 Rerouting for new /summary routes 2015-06-04 14:55:26 -07:00
Scott Schneider
ce05c94677 Generate summaries from helpers; individual routes
- '/summary*' routes are now generated from helper methods
- many '/summary/...' combinations now possible
  - '/summary/tag'
  - '/summary/tag/beaker_version'
  - '/summary/boot'
  - '/summary/boot/duration'
  - '/summary/clone'
  - '/summary/clone/count?from=2015-06-01'
  - etc.
2015-06-04 14:55:19 -07:00
Scott Schneider
d938a50ee8 Add get_tag_summary and get_task_summary helpers 2015-06-04 10:39:07 -07:00
Scott Schneider
1f62379be8 Only filter regex matches
and a spec test for it.

Previously using the example shown in vmpooler.yaml.example was failing
to tag strings WITHOUT a '/' in them.
2015-06-02 19:12:30 -07:00
Scott Schneider
4bed6edde4 This implements regex-based tag filtering 2015-06-02 10:53:14 -07:00
Scott Schneider
4cfc078684 Create daily tag indexes, report in /summary
- Store daily tag roll-ups in vmpooler__tag__<date>
- GET /summary will display daily tag counts and roll-up
2015-05-07 15:24:08 -07:00
Colin
640b1ef4da Merge pull request #101 from sschneid/token_metadata_in_vm_obj
Store token metadata in vmpooler__vm__ Redis hash
2015-05-06 13:33:01 -07:00
Scott Schneider
64bbd7c973 Display VM state in GET /vm/:hostname route 2015-04-30 19:38:31 -07:00
Scott Schneider
7bddfdef1b Store token metadata in vmpooler__vm__ Redis hash 2015-04-30 19:29:18 -07:00
Scott Schneider
f3979de5ef Check for 'checkout' key if calculating 'running' time 2015-04-30 18:30:13 -07:00
Scott Schneider
13df748cc6 Add basic auth token functionality
...and rspec tests, hooray!
2015-04-28 10:47:57 -07:00
Scott Schneider
e447b754c3 Add basic HTTP authentication and /token routes
- the only initial backend option for auth is LDAP
2015-04-21 11:44:38 -07:00
Colin
ab990e2081 (QENG-2208) Move Sinatra Helpers to own file
This moves the inline Helpers contained in V1.rb to their own file:
helpers.rb. In making this change, any API.settings call was removed
from the helper method itself and passed through from V1.

This also adds tests for hostname shortener and validate date string.
2015-04-15 12:44:12 -07:00
Scott Schneider
798aa3f9ff Ensure 'lifetime' val returned by GET /vm/:hostname is an int 2015-04-09 12:48:56 -07:00
Colin
a5edb9bae2 (maint) Fix bad redis reference
This fixes a redis reference that was missed in a previous refactor.
2015-04-02 11:30:43 -07:00
Colin
1408f35867 (QENG-1906) Refactor initialize to allow config passing
Prior to this commit, several pieces of vmpooler performed configuration
and initialization steps within 'initialize'. This made it difficult to
pass in mock objects for testing purposes.

This commit performs a single configuration and passes the results to
the various pieces of vmpooler.
2015-03-30 14:23:06 -07:00
Scott Schneider
858d4c7541 Use 'checkout' time to calculate 'running' time 2015-03-25 13:41:26 -07:00
Colin
91563c0408 Merge pull request #71 from sschneid/historic_redis_vm_metadata
(QENG-2057) Historic Redis VM metadata
2015-03-25 12:48:56 -07:00