mirror of https://github.com/apache/jclouds.git
First version of ebs2.clj
This commit is contained in:
parent
8d317d67b5
commit
36cc3805b5
|
@ -0,0 +1,290 @@
|
|||
;
|
||||
;
|
||||
; Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
;
|
||||
; ====================================================================
|
||||
; Licensed under the Apache License, Version 2.0 (the "License");
|
||||
; you may not use this file except in compliance with the License.
|
||||
; You may obtain a copy of the License at
|
||||
;
|
||||
; http://www.apache.org/licenses/LICENSE-2.0
|
||||
;
|
||||
; Unless required by applicable law or agreed to in writing, software
|
||||
; distributed under the License is distributed on an "AS IS" BASIS,
|
||||
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
; See the License for the specific language governing permissions and
|
||||
; limitations under the License.
|
||||
; ====================================================================
|
||||
;
|
||||
|
||||
(ns
|
||||
#^{:author "Chas Emerick, cemerick@snowtide.com"
|
||||
:doc "A clojure binding to the jclouds EBS service interface."}
|
||||
org.jclouds.ec2.ebs2
|
||||
(:use [org.jclouds.compute2])
|
||||
(:use (clojure.contrib def core))
|
||||
(:import org.jclouds.aws.domain.Region
|
||||
org.jclouds.compute.domain.NodeMetadata
|
||||
(org.jclouds.ec2.domain Volume Volume$Status Snapshot Snapshot$Status AvailabilityZoneInfo)
|
||||
(org.jclouds.ec2.options DescribeSnapshotsOptions DetachVolumeOptions CreateSnapshotOptions)))
|
||||
(defn snapshot?
|
||||
"Returns true iff the argument is a org.jclouds.ec2.domain.Snapshot."
|
||||
[s]
|
||||
(instance? Snapshot s))
|
||||
|
||||
(defn volume?
|
||||
"Returns true iff the argument is a org.jclouds.ec2.domain.Volume."
|
||||
[v]
|
||||
(instance? Volume v))
|
||||
|
||||
(defn #^org.jclouds.ec2.services.ElasticBlockStoreClient
|
||||
ebs-service
|
||||
""
|
||||
[compute]
|
||||
(-> compute
|
||||
.getContext
|
||||
.getProviderSpecificContext
|
||||
.getApi
|
||||
.getElasticBlockStoreServices))
|
||||
|
||||
(defn get-region
|
||||
"Coerces the first parameter into a Region string; strings, keywords, and
|
||||
NodeMetadata instances are acceptable arguments. An optional second argument
|
||||
is returned if the first cannot be coerced into a region string.
|
||||
Returns nil otherwise."
|
||||
([v] (get-region v nil))
|
||||
([v default-region]
|
||||
(cond
|
||||
(string? v) v
|
||||
(keyword? v) (name v)
|
||||
(instance? NodeMetadata v) (let [zone (location v)]
|
||||
; no easier way to go from zone -> region?
|
||||
(if (> (.indexOf zone "-") -1)
|
||||
(subs zone 0 (-> zone count dec))
|
||||
zone))
|
||||
:else default-region)))
|
||||
|
||||
(defn get-volume-id
|
||||
"Returns a string volume ID taken from the given string, keyword, or Volume argument."
|
||||
[v]
|
||||
(cond
|
||||
(instance? Volume v) (.getId #^Volume v)
|
||||
(keyword? v) (name v)
|
||||
(string? v) v
|
||||
:else (throw (IllegalArgumentException.
|
||||
(str "Can't obtain volume id from argument of type " (class v))))))
|
||||
|
||||
(defn volumes
|
||||
"Returns a set of org.jclouds.ec2.domain.Volume instances corresponding to the
|
||||
volumes in the specified region (defaulting to your account's default region)."
|
||||
|
||||
[compute & [region & volume-ids]]
|
||||
(set
|
||||
(.describeVolumesInRegion (ebs-service compute)
|
||||
(get-region region)
|
||||
(into-array String (map get-volume-id
|
||||
(if (get-region region)
|
||||
volume-ids
|
||||
(when region (cons region volume-ids))))))))
|
||||
(defn- as-string
|
||||
[v]
|
||||
(cond
|
||||
(string? v) v
|
||||
(keyword? v) (name v)
|
||||
:else v))
|
||||
(defn- get-string
|
||||
[map key]
|
||||
(as-string (get map key)))
|
||||
(defn- as-int
|
||||
[v]
|
||||
(cond
|
||||
(number? v) (int v)
|
||||
(string? v) (Integer/parseInt v)
|
||||
:else (throw (IllegalArgumentException.
|
||||
(str "Don't know how to convert object of type " (class v) " to a string")))))
|
||||
|
||||
(defn- snapshot-options
|
||||
[optmap]
|
||||
(let [string-array #(let [v (% optmap)]
|
||||
(into-array String (cond
|
||||
(keyword? v) [(name v)]
|
||||
(string? v) [v]
|
||||
:else (map as-string v))))]
|
||||
(-> (DescribeSnapshotsOptions.)
|
||||
(.ownedBy (string-array :owner))
|
||||
(.snapshotIds (string-array :ids))
|
||||
(.restorableBy (string-array :restorable-by)))))
|
||||
|
||||
(defn snapshots
|
||||
"Returns a set of org.jclouds.aws.ec2.domain.Snapshot instances that match
|
||||
the criteria provided. Options include:
|
||||
|
||||
:region - region string, keyword, or NodeMetadata
|
||||
:owner - AWS account id (or \"amazon\" or \"self\")
|
||||
:restorable-by - AWS account id
|
||||
|
||||
Multiple values for each type of criteria can be provided by passing a seq
|
||||
of the appropriate types as values."
|
||||
[compute & options]
|
||||
(let [options (apply hash-map options)
|
||||
region (:region options)
|
||||
options (snapshot-options (dissoc options :region))]
|
||||
(set
|
||||
(.describeSnapshotsInRegion (ebs-service compute)
|
||||
(get-region region)
|
||||
(into-array DescribeSnapshotsOptions [options])))))
|
||||
|
||||
(defn create-snapshot
|
||||
"Creates a snapshot of a volume in the specified region with an optional description.
|
||||
If provided, the description must be < 255 characters in length. Returns the
|
||||
org.jclouds.aws.ec2.domain.Snapshot object representing the created snapshot."
|
||||
|
||||
([computess #^Volume volume] (create-snapshot computess volume nil))
|
||||
([compute #^Volume volume description] (create-snapshot compute (.getRegion volume) (.getId volume) description))
|
||||
([compute region volume-id description]
|
||||
(.createSnapshotInRegion (ebs-service compute)
|
||||
(get-region region)
|
||||
(as-string volume-id)
|
||||
(into-array CreateSnapshotOptions (when description
|
||||
[(.withDescription (CreateSnapshotOptions.) description)])))))
|
||||
|
||||
(defn delete-snapshot
|
||||
"Deletes a snapshot in the specified region."
|
||||
([compute #^Snapshot snapshot] (delete-snapshot compute (.getRegion snapshot) (.getId snapshot)))
|
||||
([compute region snapshot-id]
|
||||
(.deleteSnapshotInRegion (ebs-service compute)
|
||||
(get-region region)
|
||||
(as-string snapshot-id))))
|
||||
|
||||
(defn get-zone
|
||||
[v]
|
||||
(cond
|
||||
(instance? AvailabilityZoneInfo v) (.getZone v)
|
||||
(instance? NodeMetadata v) (location #^NodeMetadata v)
|
||||
(string? v) v
|
||||
(keyword? v) (name v)
|
||||
:else (throw (IllegalArgumentException.
|
||||
(str "Can't obtain zone from argument of type " (class v))))))
|
||||
|
||||
(defn attach-volume
|
||||
"Attaches a volume to an instance, returning the resulting org.jclouds.aws.ec2.domain.Attachment."
|
||||
([compute #^NodeMetadata node volume device]
|
||||
(attach-volume compute node (.getProviderId node) (get-volume-id volume) device))
|
||||
([compute region instance-id volume-id device]
|
||||
(apply #(.attachVolumeInRegion (ebs-service compute)
|
||||
(get-region region) % %2 %3)
|
||||
(map as-string [volume-id instance-id device]))))
|
||||
|
||||
(defn detach-volume
|
||||
"Detaches a volume from the instance to which it is currently attached.
|
||||
The volume may be specified with a Volume instance, a string, or a keyword.
|
||||
Providing a logical true value for the :force option will cause the volume
|
||||
to be forcibly detached, regardless of whether it is in-use (mounted) or not.
|
||||
|
||||
If the volume is specified as a string or keyword, one of the following options
|
||||
is additionally required:
|
||||
|
||||
:region - the region where the volume is allocated
|
||||
:node - a node in the region where the volume is allocated
|
||||
|
||||
FYI: It appears that issuing a detatch-volume command while the volume in question is mounted
|
||||
will cause the volume to be detatched immediately upon the volume beign unmounted."
|
||||
[compute volume & options]
|
||||
(let [options (apply hash-map options)
|
||||
volume-id (get-volume-id volume)
|
||||
region (get-region (if (instance? Volume volume)
|
||||
(.getRegion volume)
|
||||
(or (:region options) (:node options))))]
|
||||
(when (not region)
|
||||
(throw (IllegalArgumentException.
|
||||
"Must specify volume's region via :region or :node options, or by providing a Volume instance.")))
|
||||
(.detachVolumeInRegion (ebs-service compute)
|
||||
region
|
||||
volume-id
|
||||
(boolean (:force options))
|
||||
(into-array DetachVolumeOptions []))))
|
||||
|
||||
(defn create-volume
|
||||
"Creates a new volume given a set of options:
|
||||
|
||||
- one of :zone (keyword, string, or AvailabilityZoneInfo) or :node (NodeMetadata)
|
||||
- one or both of :snapshot (keyword, string, or Snapshot instance) or :size
|
||||
(string, keyword, or number)
|
||||
- :device (string or keyword) provided *only* when you want to attach the new volume to
|
||||
the :node you specified!
|
||||
|
||||
Returns a vector of [created org.jclouds.ec2.domain.Volume,
|
||||
optional org.jclouds.ec2.domain.Attachment]
|
||||
|
||||
Note that specifying :node instead of :zone will only attach the created volume
|
||||
:device is also provided. Otherwise, the node is only used to obtain the desired
|
||||
availability zone.
|
||||
|
||||
Note also that if :device and :node are specified, and the attach operation fails,
|
||||
you will have \"leaked\" the newly-created volume
|
||||
(volume creation and attachment cannot be done atomically)."
|
||||
|
||||
[compute & options]
|
||||
(when (-> options count odd?)
|
||||
(throw (IllegalArgumentException. "Must provide key-value pairs, e.g. :zone :us-east-1d :size 200")))
|
||||
(let [options (apply hash-map options)
|
||||
snapshot (get-string options :snapshot)
|
||||
snapshot (if (snapshot? snapshot) (.getId snapshot) snapshot)
|
||||
size (-?> (get-string options :size) as-int)
|
||||
#^NodeMetadata node (:node options)
|
||||
zone (or node (get-string options :zone))
|
||||
zone (if zone
|
||||
(get-zone zone)
|
||||
(throw (IllegalArgumentException. "Must supply a :zone or :node option.")))
|
||||
ebs (ebs-service compute)]
|
||||
(when (and (:device options) (not node))
|
||||
(throw (IllegalArgumentException. "Cannot create and attach new volume; no :node specified")))
|
||||
(let [new-volume (cond
|
||||
(and snapshot size) (.createVolumeFromSnapshotInAvailabilityZone ebs zone size snapshot)
|
||||
snapshot (.createVolumeFromSnapshotInAvailabilityZone ebs zone snapshot)
|
||||
size (.createVolumeInAvailabilityZone ebs zone size)
|
||||
:else (throw (IllegalArgumentException. "Must supply :size and/or :snapshot options.")))]
|
||||
[new-volume (when (:device options)
|
||||
(attach-volume compute node new-volume (as-string (:device options))))])))
|
||||
|
||||
(defn delete-volume
|
||||
"Deletes a volume in the specified region."
|
||||
([compute #^Volume volume]
|
||||
(delete-volume (.getRegion volume) (.getId volume)))
|
||||
([compute region volume-id]
|
||||
(.deleteVolumeInRegion (ebs-service compute)
|
||||
(get-region region)
|
||||
(as-string volume-id))))
|
||||
|
||||
(defn status
|
||||
"Returns the status of the given entity; works for Volumes and Snapshots."
|
||||
[k]
|
||||
(.getStatus k))
|
||||
|
||||
(defn status-available?
|
||||
[#^Volume v]
|
||||
(= Volume$Status/AVAILABLE (status v)))
|
||||
|
||||
(defn status-creating?
|
||||
[#^Volume v]
|
||||
(= Volume$Status/CREATING (status v)))
|
||||
|
||||
(defn status-deleting?
|
||||
[#^Volume v]
|
||||
(= Volume$Status/DELETING (status v)))
|
||||
|
||||
(defn status-in-use?
|
||||
[#^Volume v]
|
||||
(= Volume$Status/IN_USE (status v)))
|
||||
|
||||
(defn status-completed?
|
||||
[#^Snapshot s]
|
||||
(= Snapshot$Status/COMPLETED (status s)))
|
||||
|
||||
(defn status-error?
|
||||
[#^Snapshot s]
|
||||
(= Snapshot$Status/ERROR (status s)))
|
||||
|
||||
(defn status-pending?
|
||||
[#^Snapshot s]
|
||||
(= Snapshot$Status/PENDING (status s)))
|
Loading…
Reference in New Issue