[RFC PATCH 0/3]: Remove condor from the conductor
by Chris Lalancette
All,
This is a RFC patch series to remove condor from the conductor. In short,
condor presents problems for our project because it is an external project, it
is written in C++ (when most of our developers are ruby), and it is too complex
for our current needs.
The new way we do scheduling is described pretty well in patch 1, so I
won't delve into it here. This is an RFC series because there are 2 known
problems and it has only been lightly tested.
The first known problem is that since we are doing the deltacloud create
calls inline in the conductor, this can cause the UI itself to timeout. This is
going to be a problem when using the VMware backend, as we know that the
create call there can take a long time. Possible solutions are to use a
different thread or process for the call, but there may be others.
The second known problem is that for reasons I don't really understand,
updating the instance row in the database (using instance.save!) has some
surprising results. One example of this is public_addresses; the
public_addresses field I get from the deltacloud backend looks correct
(ec2-50-7-27-214.compute-1.amazonaws.com, or whatever), but when it is saved
into the database it looks odd (---\n- ec2-50-7-27-214.compute-1.amazonaws.com\n).
Since dbomatic is not doing this manipulation, I can only presume that some
observer is screwing it up, but I can't see how. A second example of this
problem is that the public key that gets created when the instance is launched
disappears from the UI as soon as dbomatic runs.
I have only tested it so far using the EC2 backend. Except for the two
bugs above, things work pretty well there; I can start and stop deployments
from the UI, and the state gets updated along the way. To really put this
into the repository we would need to test on the other backends, particularly
RHEV-M and VMware.
Comments and questions about the patchset and what we are trying to
accomplish here are welcome.
Chris Lalancette
12 years, 6 months
[PATCH configure 2/2] Moved rhevm and vsphere configs into profiles
by Richard Su
New -p/--profile option in aeolus-configure to select which
profile to run. To setup rhevm, run "aeolus-configure -p rhevm",
and to setup vsphere, run "aeolus-configure -p vsphere". Default
profile is selected if one isn't specified.
rhevm and vsphere configuration parameters have moved out of
default_configure and into default_rhevm and default_vsphere
in /etc/aeolus-configure/nodes. To make naming consistent,
vmware parameters have been renamed to vsphere.
---
conf/cleanup | 4 +-
conf/configure | 25 +--------
conf/rhevm | 19 +++++++
conf/vsphere | 18 ++++++
recipes/aeolus/manifests/profiles/rhevm.pp | 73 ++++++++++++++++++++++++++
recipes/aeolus/manifests/profiles/vsphere.pp | 37 +++++++++++++
recipes/aeolus/manifests/rhevm.pp | 45 ----------------
recipes/aeolus/manifests/vmware.pp | 14 -----
recipes/aeolus/templates/rhevm.json | 2 +-
recipes/aeolus/templates/vmware.json | 10 ++--
10 files changed, 156 insertions(+), 91 deletions(-)
create mode 100644 conf/rhevm
create mode 100644 conf/vsphere
create mode 100644 recipes/aeolus/manifests/profiles/rhevm.pp
create mode 100644 recipes/aeolus/manifests/profiles/vsphere.pp
delete mode 100644 recipes/aeolus/manifests/rhevm.pp
delete mode 100644 recipes/aeolus/manifests/vmware.pp
diff --git a/conf/cleanup b/conf/cleanup
index c17134e..5e2fcd7 100644
--- a/conf/cleanup
+++ b/conf/cleanup
@@ -19,6 +19,4 @@ classes:
- aeolus::image-factory::disabled
- aeolus::deltacloud::disabled
# Uncomment this to clean up RHEV details
-#- aeolus::rhevm::disabled
-# Uncomment to remove vmware
-#- aeolus::vmware::disabled
+#- aeolus::profiles::rhevm::disabled
diff --git a/conf/configure b/conf/configure
index 9b570a8..db33897 100644
--- a/conf/configure
+++ b/conf/configure
@@ -12,37 +12,16 @@
#apart from https on the web server for conductor, you should
#consider intermachine communications insecure. Securing
#intermachine service calls is on the roadmap.
+#
---
parameters:
enable_https: true
enable_security: false
package_provider: rpm
-# Uncomment this section and provide reasonable values to
-# enable RHEV values to be populated.
-# rhevm_nfs_server: nfs.server.com
-# rhevm_nfs_export: /export/export_domain
-# rhevm_nfs_mount_point: /mnt/rhevm-nfs
-# rhevm_deltacloud_port: 3002
-# rhevm_deltacloud_username: username@fqdn
-# rhevm_deltacloud_password: password
-# rhevm_deltacloud_powershell_url: https://rhevm.server.com:8543/rhevm-api-powershell
-#
-# Uncomment this section and provide appropriate values to configure vmware
-# vmware_api_endpoint: vsphere.server.com
-# vmware_username: username
-# vmware_password: password
-# vmware_datastore: datastore
-# vmware_network_name: network_name
-# vmware_deltacloud_port: 3002
classes:
- aeolus::conductor
- aeolus::image-factory
- aeolus::iwhd
-- aeolus::profiles::default
- aeolus::deltacloud::core
- aeolus::deltacloud::ec2
-# Uncomment this section to include rhev setup
-#- aeolus::rhevm
-#
-# Uncomment this section to include vmware setup
-#- aeolus::vmware
+- aeolus::profiles::default
diff --git a/conf/rhevm b/conf/rhevm
new file mode 100644
index 0000000..14d1080
--- /dev/null
+++ b/conf/rhevm
@@ -0,0 +1,19 @@
+---
+parameters:
+ enable_https: true
+ enable_security: false
+ package_provider: rpm
+# Uncomment and provide values to match your RHEV environment.
+# Values below are examples.
+# rhevm_nfs_server: nfs.server.com
+# rhevm_nfs_export: /export/export_domain
+# rhevm_nfs_mount_point: /mnt/rhevm-nfs
+# rhevm_deltacloud_username: username@fqdn
+# rhevm_deltacloud_password: password
+# rhevm_deltacloud_provider: https://rhevm.server.com:8443/api
+classes:
+- aeolus::conductor
+- aeolus::image-factory
+- aeolus::iwhd
+- aeolus::deltacloud::core
+- aeolus::profiles::rhevm
diff --git a/conf/vsphere b/conf/vsphere
new file mode 100644
index 0000000..b000bc4
--- /dev/null
+++ b/conf/vsphere
@@ -0,0 +1,18 @@
+---
+parameters:
+ enable_https: true
+ enable_security: false
+ package_provider: rpm
+# Uncomment and provide values to match your vSphere environment.
+# Values below are examples.
+# vsphere_deltacloud_provider: vsphere.server.com
+# vsphere_username: username
+# vsphere_password: password
+# vsphere_datastore: datastore
+# vsphere_network_name: network_name
+classes:
+- aeolus::conductor
+- aeolus::image-factory
+- aeolus::iwhd
+- aeolus::deltacloud::core
+- aeolus::profiles::vsphere
diff --git a/recipes/aeolus/manifests/profiles/rhevm.pp b/recipes/aeolus/manifests/profiles/rhevm.pp
new file mode 100644
index 0000000..17feeb4
--- /dev/null
+++ b/recipes/aeolus/manifests/profiles/rhevm.pp
@@ -0,0 +1,73 @@
+class aeolus::profiles::rhevm {
+ file {"/etc/rhevm.json":
+ content => template("aeolus/rhevm.json"),
+ mode => 755,
+ require => Package['aeolus-conductor-daemons'] }
+
+ file {"/etc/iwhd/conf.js":
+ content => template("aeolus/iwhd-conf.js"),
+ mode => 755,
+ require => Package['aeolus-conductor-daemons'] }
+
+ file {"$rhevm_nfs_mount_point":
+ ensure => 'directory'}
+
+ mount {"$rhevm_nfs_mount_point":
+ ensure => mounted,
+ device => "$rhevm_nfs_server:$rhevm_nfs_export",
+ fstype => "nfs",
+ options => "rw",
+ require => File["$rhevm_nfs_mount_point"]}
+
+ # give iwhd a restart to pick up new configuration files
+ # in the event iwhd had already initialized at /var/lib/iwhd
+ exec { "/sbin/service iwhd restart":
+ require => [Service['iwhd'],
+ Mount["$rhevm_nfs_mount_point"],
+ File["/etc/rhevm.json"],
+ File["/etc/iwhd/conf.js"]]}
+
+ aeolus::create_bucket{"aeolus":}
+
+ aeolus::conductor::site_admin{"admin":
+ email => 'dcuser(a)aeolusproject.org',
+ password => "password",
+ first_name => 'aeolus',
+ last_name => 'user'}
+
+ aeolus::conductor::login{"admin": password => "password",
+ require => Aeolus::Conductor::Site_admin['admin']}
+
+ aeolus::conductor::provider{"rhevm":
+ type_id => "5",
+ url => "http://localhost:3002/api",
+ deltacloud_provider => "$rhevm_deltacloud_provider",
+ require => Aeolus::Conductor::Login["admin"] }
+
+ aeolus::conductor::provider::account{"rhevm":
+ provider => 'rhevm',
+ type => 'rhevm',
+ username => '$rhevm_deltacloud_username',
+ password => '$rhevm_deltacloud_password',
+ require => Aeolus::Conductor::Provider["rhevm"] }
+
+ aeolus::conductor::hwp{"hwp1":
+ memory => "512",
+ cpu => "1",
+ storage => "",
+ architecture => "x86_64",
+ require => Aeolus::Conductor::Login["admin"] }
+
+ aeolus::conductor::logout{"admin":
+ require => [Aeolus::Conductor::Provider['rhevm'],
+ Aeolus::Conductor::Provider::Account['rhevm'],
+ Aeolus::Conductor::Hwp['hwp1']] }
+
+ # TODO: create a realm and mappings
+}
+
+class aeolus::profiles::rhevm::disabled {
+ mount {"$rhevm_nfs_mount_point":
+ ensure => unmounted,
+ device => "$rhevm_nfs_server:$rhevm_nfs_export"}
+}
diff --git a/recipes/aeolus/manifests/profiles/vsphere.pp b/recipes/aeolus/manifests/profiles/vsphere.pp
new file mode 100644
index 0000000..838fd87
--- /dev/null
+++ b/recipes/aeolus/manifests/profiles/vsphere.pp
@@ -0,0 +1,37 @@
+class aeolus::profiles::vsphere {
+ aeolus::create_bucket{"aeolus":}
+
+ aeolus::conductor::site_admin{"admin":
+ email => 'dcuser(a)aeolusproject.org',
+ password => "password",
+ first_name => 'aeolus',
+ last_name => 'user'}
+
+ aeolus::conductor::login{"admin": password => "password",
+ require => Aeolus::Conductor::Site_admin['admin']}
+
+ aeolus::conductor::provider{"vsphere":
+ type_id => "8",
+ url => "http://localhost:3002/api",
+ deltacloud_provider => "$vsphere_deltacloud_provider",
+ require => [Aeolus::Conductor::Login["admin"]] }
+
+ aeolus::conductor::provider::account{"vsphere":
+ provider => 'vsphere',
+ type => 'vsphere',
+ username => '$vsphere_username',
+ password => '$vsphere_password',
+ require => Aeolus::Conductor::Provider["vsphere"] }
+
+ aeolus::conductor::hwp{"hwp1":
+ memory => "512",
+ cpu => "1",
+ storage => "",
+ architecture => "x86_64",
+ require => Aeolus::Conductor::Login["admin"] }
+
+ aeolus::conductor::logout{"admin":
+ require => [Aeolus::Conductor::Provider['vsphere'],
+ Aeolus::Conductor::Provider::Account['vsphere'],
+ Aeolus::Conductor::Hwp['hwp1']] }
+}
diff --git a/recipes/aeolus/manifests/rhevm.pp b/recipes/aeolus/manifests/rhevm.pp
deleted file mode 100644
index cc1d051..0000000
--- a/recipes/aeolus/manifests/rhevm.pp
+++ /dev/null
@@ -1,45 +0,0 @@
-class aeolus::rhevm inherits aeolus {
- file {"/etc/rhevm.json":
- content => template("aeolus/rhevm.json"),
- mode => 755,
- require => Package['aeolus-conductor-daemons'] }
-
- file {"/etc/iwhd/conf.js":
- content => template("aeolus/iwhd-conf.js"),
- mode => 755,
- require => Package['aeolus-conductor-daemons'] }
-
- file {"$rhevm_nfs_mount_point":
- ensure => 'directory'}
-
- mount {"$rhevm_nfs_mount_point":
- ensure => mounted,
- device => "$rhevm_nfs_server:$rhevm_nfs_export",
- fstype => "nfs",
- options => "rw",
- require => File["$rhevm_nfs_mount_point"]}
-
- # give iwhd a restart to pick up new configuration files
- # in the event iwhd had already initialized at /var/lib/iwhd
- exec { "/sbin/service iwhd restart":
- require => [Service['iwhd'],
- Mount["$rhevm_nfs_mount_point"],
- File["/etc/rhevm.json"],
- File["/etc/iwhd/conf.js"]]}
-
- aeolus::provider{"rhevm":
- type => "rhevm",
- port => 3002,
- require => Aeolus::Conductor::Login["admin"] }
-
- # TODO:
- # 1. since we have credentials, create provider account
- # 2. create a realm and mappings
-}
-
-class aeolus::rhevm::disabled {
-
- mount {"$rhevm_nfs_mount_point":
- ensure => unmounted,
- device => "$rhevm_nfs_server:$rhevm_nfs_export"}
-}
diff --git a/recipes/aeolus/manifests/vmware.pp b/recipes/aeolus/manifests/vmware.pp
deleted file mode 100644
index cafb015..0000000
--- a/recipes/aeolus/manifests/vmware.pp
+++ /dev/null
@@ -1,14 +0,0 @@
-class aeolus::vmware inherits aeolus {
- file {"/etc/vmware.json":
- content => template("aeolus/vmware.json"),
- mode => 755,
- require => Package['aeolus-conductor-daemons'] }
-
- aeolus::provider{"vsphere":
- type => "vsphere",
- port => 3002,
- require => [Aeolus::Conductor::Login["admin"]] }
-}
-
-class aeolus::vmware::disabled {
-}
diff --git a/recipes/aeolus/templates/rhevm.json b/recipes/aeolus/templates/rhevm.json
index 45e6d67..6a2bb43 100644
--- a/recipes/aeolus/templates/rhevm.json
+++ b/recipes/aeolus/templates/rhevm.json
@@ -5,7 +5,7 @@
"nfs-dir": "<%= rhevm_nfs_mount_point %>",
"nfs-path": "<%= rhevm_nfs_export %>",
"nfs-host": "<%= rhevm_nfs_server %>",
- "api-url": "<%= rhevm_deltacloud_powershell_url %>",
+ "api-url": "<%= rhevm_deltacloud_provider %>",
"password": "<%= rhevm_deltacloud_password %>",
"cluster": "_any_"
}
diff --git a/recipes/aeolus/templates/vmware.json b/recipes/aeolus/templates/vmware.json
index ec69d5e..6f70d62 100644
--- a/recipes/aeolus/templates/vmware.json
+++ b/recipes/aeolus/templates/vmware.json
@@ -1,10 +1,10 @@
{
"vsphere":
{
- "api-url": "https://<%= vmware_api_endpoint %>/sdk",
- "username": "<%= vmware_username %>",
- "password": "<%= vmware_password %>",
- "datastore": "<%= vmware_datastore %>",
- "network_name": "<%= vmware_network_name %>"
+ "api-url": "https://<%= vsphere_deltacloud_provider %>/sdk",
+ "username": "<%= vsphere_username %>",
+ "password": "<%= vsphere_password %>",
+ "datastore": "<%= vsphere_datastore %>",
+ "network_name": "<%= vsphere_network_name %>"
}
}
--
1.7.6
12 years, 6 months
[PATCH] Add systemd support to sshd RedHat module
by Steven Dake
sshd was added to systemd service files in F16. F16 doesn't currently work
without this patch.
Signed-off-by: Steven Dake <sdake(a)redhat.com>
---
oz/RedHat.py | 32 ++++++++++++++++++++++----------
1 files changed, 22 insertions(+), 10 deletions(-)
diff --git a/oz/RedHat.py b/oz/RedHat.py
index 70062aa..67905e8 100644
--- a/oz/RedHat.py
+++ b/oz/RedHat.py
@@ -41,6 +41,7 @@ class RedHatCDGuest(oz.Guest.CDGuest):
config, iso_allowed, url_allowed)
self.sshprivkey = os.path.join('/etc', 'oz', 'id_rsa-icicle-gen')
self.crond_was_active = False
+ self.sshd_was_active = False
self.sshd_config = \
"""SyslogFacility AUTHPRIV
PasswordAuthentication yes
@@ -193,11 +194,15 @@ Subsystem sftp /usr/libexec/openssh/sftp-server
# reset the service link
self.log.debug("Resetting sshd service")
- startuplink = self._get_service_runlevel_link(g_handle, 'sshd')
- if g_handle.exists(startuplink):
- g_handle.rm(startuplink)
- if g_handle.exists(startuplink + ".icicle"):
- g_handle.mv(startuplink + ".icicle", startuplink)
+ if g_handle.exists('/lib/systemd/system/sshd.service'):
+ if not self.sshd_was_active:
+ g_handle.rm('/etc/systemd/system/multi-user.target.wants/sshd.service')
+ else:
+ startuplink = self._get_service_runlevel_link(g_handle, 'sshd')
+ if g_handle.exists(startuplink):
+ g_handle.rm(startuplink)
+ if g_handle.exists(startuplink + ".icicle"):
+ g_handle.mv(startuplink + ".icicle", startuplink)
def _image_ssh_teardown_step_3(self, g_handle):
"""
@@ -296,13 +301,20 @@ Subsystem sftp /usr/libexec/openssh/sftp-server
"""
# part 2; check and setup sshd
self.log.debug("Step 2: setup sshd")
- if not g_handle.exists('/etc/init.d/sshd') or not g_handle.exists('/usr/sbin/sshd'):
+ if not g_handle.exists('/usr/sbin/sshd'):
raise oz.OzException.OzException("ssh not installed on the image, cannot continue")
- startuplink = self._get_service_runlevel_link(g_handle, 'sshd')
- if g_handle.exists(startuplink):
- g_handle.mv(startuplink, startuplink + ".icicle")
- g_handle.ln_sf('/etc/init.d/sshd', startuplink)
+ if g_handle.exists('/lib/systemd/system/sshd.service'):
+ if g_handle.exists('/etc/systemd/system/multi-user.target.wants/sshd.service'):
+ self.sshd_was_active = True
+ else:
+ g_handle.ln_sf('/lib/systemd/system/sshd.service',
+ '/etc/systemd/system/multi-user.target.wants/sshd.service')
+ else:
+ startuplink = self._get_service_runlevel_link(g_handle, 'sshd')
+ if g_handle.exists(startuplink):
+ g_handle.mv(startuplink, startuplink + ".icicle")
+ g_handle.ln_sf('/etc/init.d/sshd', startuplink)
sshd_config_file = self.icicle_tmp + "/sshd_config"
f = open(sshd_config_file, 'w')
--
1.7.6.1
12 years, 6 months
Snapshot builds (ala EC2 style) for vSphere and/or RHEV-M?
by Justin Clift
Hi Chris,
How much effort do you reckon it would take, to get snapshot builds working for vSphere and/or RHEV-M?
In the same way they're done for EC2 presently that is, so people don't have to have KVM running locally, nor potentially upload large quantities of data to a remote location.
Regards and best wishes,
Justin Clift
--
Aeolus Community Manager
http://www.aeolusproject.org
12 years, 6 months
BZ#723885 Print error on non supported target
by Martyn Taylor
Applies on top of last patch set:
[PATCH aeolus-image 1/2] Brings WarehouseModel objects in from Conductor
[PATCH aeolus-image 2/2] Added Template Model and fields to Image
12 years, 6 months
Aeolus Management Model API
by Steven Dake
As has been previously discussed on this mailing list and irc, there are
gaps between what deltacloud provides and what higher level management
systems (such as Aeolus) have interest. Deltacloud provides a great
low-level interface to various cloud providers objects, but doesn't
enforce any particular policy or organization on these low level objects.
In the Aeolus project we are interested in enforcing organization and
policy. The question becomes do we want to create a coherent set of
APIs that model the Aeolus system. My answer to this is yes, we do, and
to kick that process off, I'll propose a few APIs that we need exposed
from aeolus to support the pacemaker-cloud[1] project.
What pacemaker cloud needs today:
1 notifications that third parties modify a deployable state
1.1 A deployable was detected faulty by a third party
1.2 An assembly was detected faulty by a third party
1.3 A deployable was started by a third party
1.4 A deployable was stopped by a third party
2 third party notification involving deployable state monitoring
2.1 Active monitoring has determined that an assembly has failed
2.2 Active monitoring has determined that a deployable has failed
A proposed API model follows:
Exported from the management system
-----------------------------------
POST api/monitors/add
Add a third party monitor to a list of monitors maintained by the
management system
inputs: internet protocol address, port, and version of a third party
software component implementing the monitoring API
outputs: monitor identifier and success or failure
POST api/monitors/remove
Remove a third party monitor from the list of monitors maintained by the
management system
inputs: internet protocol address, port, version, and identifier of a
third party software component implementing the monitoring API
outputs: monitor identifier and success or failure
GET api/monitors/list
Retrieve a list of third party monitors from the management system
inputs: version
outputs: list of monitors including internet protocol address, port, and
version
POST api/monitors/deployable/id
inputs: version and START DETECTED or FAIL DETECTED
outputs: success or fail
POST api/monitors/deployable/assembly/id
inputs: version and START DETECTED or FAIL DETECTED
outputs: success or fail
Exported by the third party monitor API
---------------------------------------
POST api/deployable/id
inputs: version, deployment ID, and state (START DETECTED, FAIL
DETECTED, STOP DETECTED), deployable metadata
outputs: success or fail
POST api/deployable/assembly/id
inputs: version, assebmly ID, deployable metadata, and state (START
DETECTED, STOP DETECTED, FAIL DETECTED)
outputs: success or fail
Regards
-steve
[1] http://pacemaker-cloud.org
12 years, 6 months
[PATCH aeolus-image 1/2] Brings WarehouseModel objects in from Conductor
by Martyn Taylor
From: Matt Wagner <matt.wagner(a)redhat.com>
---
lib/aeolus_image.rb | 20 ++-
lib/aeolus_image/base_command.rb | 139 -----------------
lib/aeolus_image/build_command.rb | 68 --------
lib/aeolus_image/command/base_command.rb | 139 +++++++++++++++++
lib/aeolus_image/command/build_command.rb | 68 ++++++++
lib/aeolus_image/command/config_parser.rb | 228 ++++++++++++++++++++++++++++
lib/aeolus_image/command/delete_command.rb | 146 ++++++++++++++++++
lib/aeolus_image/command/import_command.rb | 44 ++++++
lib/aeolus_image/command/list_command.rb | 221 +++++++++++++++++++++++++++
lib/aeolus_image/command/push_command.rb | 72 +++++++++
lib/aeolus_image/config_parser.rb | 228 ----------------------------
lib/aeolus_image/delete_command.rb | 146 ------------------
lib/aeolus_image/import_command.rb | 44 ------
lib/aeolus_image/list_command.rb | 221 ---------------------------
lib/aeolus_image/model/image.rb | 27 ++++
lib/aeolus_image/model/image_build.rb | 43 +++++
lib/aeolus_image/model/provider_image.rb | 32 ++++
lib/aeolus_image/model/target_image.rb | 27 ++++
lib/aeolus_image/model/warehouse_client.rb | 165 ++++++++++++++++++++
lib/aeolus_image/model/warehouse_model.rb | 135 ++++++++++++++++
lib/aeolus_image/push_command.rb | 72 ---------
21 files changed, 1360 insertions(+), 925 deletions(-)
delete mode 100644 lib/aeolus_image/base_command.rb
delete mode 100644 lib/aeolus_image/build_command.rb
create mode 100644 lib/aeolus_image/command/base_command.rb
create mode 100644 lib/aeolus_image/command/build_command.rb
create mode 100644 lib/aeolus_image/command/config_parser.rb
create mode 100644 lib/aeolus_image/command/delete_command.rb
create mode 100644 lib/aeolus_image/command/import_command.rb
create mode 100644 lib/aeolus_image/command/list_command.rb
create mode 100644 lib/aeolus_image/command/push_command.rb
delete mode 100644 lib/aeolus_image/config_parser.rb
delete mode 100644 lib/aeolus_image/delete_command.rb
delete mode 100644 lib/aeolus_image/import_command.rb
delete mode 100644 lib/aeolus_image/list_command.rb
create mode 100644 lib/aeolus_image/model/image.rb
create mode 100644 lib/aeolus_image/model/image_build.rb
create mode 100644 lib/aeolus_image/model/provider_image.rb
create mode 100644 lib/aeolus_image/model/target_image.rb
create mode 100644 lib/aeolus_image/model/warehouse_client.rb
create mode 100644 lib/aeolus_image/model/warehouse_model.rb
delete mode 100644 lib/aeolus_image/push_command.rb
diff --git a/lib/aeolus_image.rb b/lib/aeolus_image.rb
index e0f0770..7abd8f8 100644
--- a/lib/aeolus_image.rb
+++ b/lib/aeolus_image.rb
@@ -1,9 +1,15 @@
require 'optparse'
require 'logger'
-require File.join(File.dirname(__FILE__), 'aeolus_image', 'base_command')
-require File.join(File.dirname(__FILE__), 'aeolus_image', 'list_command')
-require File.join(File.dirname(__FILE__), 'aeolus_image', 'build_command')
-require File.join(File.dirname(__FILE__), 'aeolus_image', 'push_command')
-require File.join(File.dirname(__FILE__), 'aeolus_image', 'import_command')
-require File.join(File.dirname(__FILE__), 'aeolus_image', 'delete_command')
-require File.join(File.dirname(__FILE__), 'aeolus_image', 'config_parser')
\ No newline at end of file
+require File.join(File.dirname(__FILE__), 'aeolus_image/command', 'base_command')
+require File.join(File.dirname(__FILE__), 'aeolus_image/command', 'list_command')
+require File.join(File.dirname(__FILE__), 'aeolus_image/command', 'build_command')
+require File.join(File.dirname(__FILE__), 'aeolus_image/command', 'push_command')
+require File.join(File.dirname(__FILE__), 'aeolus_image/command', 'import_command')
+require File.join(File.dirname(__FILE__), 'aeolus_image/command', 'delete_command')
+require File.join(File.dirname(__FILE__), 'aeolus_image/command', 'config_parser')
+require File.join(File.dirname(__FILE__), 'aeolus_image/model', 'warehouse_client') # Not sure we need this
+require File.join(File.dirname(__FILE__), 'aeolus_image/model', 'warehouse_model') # We may be able to factor this out?
+require File.join(File.dirname(__FILE__), 'aeolus_image/model', 'image')
+require File.join(File.dirname(__FILE__), 'aeolus_image/model', 'image_build')
+require File.join(File.dirname(__FILE__), 'aeolus_image/model', 'provider_image')
+require File.join(File.dirname(__FILE__), 'aeolus_image/model', 'target_image')
\ No newline at end of file
diff --git a/lib/aeolus_image/base_command.rb b/lib/aeolus_image/base_command.rb
deleted file mode 100644
index 727153c..0000000
--- a/lib/aeolus_image/base_command.rb
+++ /dev/null
@@ -1,139 +0,0 @@
-require 'yaml'
-require 'rest_client'
-require 'nokogiri'
-
-module Aeolus
- module Image
- #This will house some methods that multiple Command classes need to use.
- class BaseCommand
- attr_accessor :options
-
- def initialize(opts={}, logger=nil)
- logger(logger)
- @options = opts
- @config_location = "~/.aeolus-cli"
- @config = load_config
- end
-
- protected
- def not_implemented
- "This option or combination is not yet implemented"
- end
-
- def logger(logger=nil)
- @logger ||= logger
- unless @logger
- @logger = Logger.new(STDOUT)
- @logger.level = Logger::INFO
- @logger.datetime_format = "%Y-%m-%d %H:%M:%S"
- end
- return @logger
- end
-
- def iwhd
- create_resource(:iwhd)
- end
-
- def conductor
- create_resource(:conductor)
- end
-
- def read_file(path)
- begin
- full_path = File.expand_path(path)
- if is_file?(path)
- File.read(full_path)
- else
- return nil
- end
- rescue
- nil
- end
- end
-
- # TODO: Consider ripping all this file-related stuff into a module or
- # class for better encapsulation and testability
- def is_file?(path)
- full_path = File.expand_path(path)
- if File.exist?(full_path) && !File.directory?(full_path)
- return true
- end
- false
- end
-
- def quit(code)
- exit(code)
- end
-
- def validate_xml_document(schema_path, xml_string)
- schema = Nokogiri::XML::RelaxNG(File.read(schema_path))
- doc = Nokogiri::XML xml_string
- schema.validate(doc)
- end
-
- def is_uuid?(id)
- uuid = Regexp.new('[\w]{8}[-][\w]{4}[-][\w]{4}[-][\w]{4}[-][\w]{12}')
- uuid.match(id).nil? ? false : true
- end
-
- private
- def load_config
- begin
- file_str = read_file(@config_location)
- if is_file?(@config_location) && !file_str.include?(":url")
- lines = File.readlines(File.expand_path((a)config_location)).map do |line|
- "#" + line
- end
- File.open(File.expand_path(@config_location), 'w') do |file|
- file.puts lines
- end
- write_file
- end
- write_file unless is_file?(@config_location)
- YAML::load(File.open(File.expand_path(@config_location)))
- rescue Errno::ENOENT
- #TODO: Create a custom exception to wrap CLI Exceptions
- raise "Unable to locate or write configuration file: \"" + @config_location + "\""
- end
- end
-
- def write_file
- example = File.read(File.expand_path(File.dirname(__FILE__) + "/../../examples/aeolus-cli"))
- File.open(File.expand_path(@config_location), 'a+') do |f|
- f.write(example)
- end
- end
-
- def create_resource(resource_name)
- # Check to see if config has a resource with this name
- if !(a)config.has_key?(resource_name)
- raise "Unable to determine resource: " + resource_name.to_s + " from configuration file. Please check: " + @config_location
- return
- end
-
- #Use command line arguments for username/password
- if @options.has_key?(:username)
- if @options.has_key?(:password)
- RestClient::Resource.new(@config[resource_name][:url], :user => @options[:username], :password => @options[:password])
- else
- #TODO: Create a custom exception to wrap CLI Exceptions
- raise "Password not found for user: " + @options[:username]
- end
-
- #Use config for username/password
- elsif @config[resource_name].has_key?(:username)
- if @config[resource_name].has_key?(:password)
- RestClient::Resource.new(@config[resource_name][:url], :user => @config[resource_name][:username], :password => @config[resource_name][:password])
- else
- #TODO: Create a custom exception to wrap CLI Exceptions
- raise "Password not found for user: " + @config[resource_name][:username]
- end
-
- # Do not use authentication
- else
- RestClient::Resource.new(@config[resource_name][:url])
- end
- end
- end
- end
-end
diff --git a/lib/aeolus_image/build_command.rb b/lib/aeolus_image/build_command.rb
deleted file mode 100644
index 07ced02..0000000
--- a/lib/aeolus_image/build_command.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-require 'imagefactory'
-
-module Aeolus
- module Image
- class BuildCommand < BaseCommand
- attr_accessor :console
- def initialize(opts={}, logger=nil)
- super(opts, logger)
- default = {
- :template_str => '',
- :template => '',
- :target => [],
- :image => '',
- :build => ''
- }
- @options = default.merge(@options)
- @console = ImageFactoryConsole.new()
- @console.start
- end
-
- def run
- if combo_implemented?
- @options[:template_str] = read_file(@options[:template])
- if @options[:template_str].nil?
- puts "Cannot find specified file"
- quit(1)
- end
-
- # Validate XML against TDL Schema
- errors = validate_xml_document(File.dirname(__FILE__) + "/../../examples/tdl.rng", @options[:template_str])
- if errors.length > 0
- puts "ERROR: The given Template does not conform to the TDL Schema, see below for specific details:"
- errors.each do |error|
- puts "- " + error.message
- end
- quit(1)
- end
-
- #This is a temporary hack in case the agent doesn't show up on bus immediately
- sleep(5)
- @console.build(@options[:template_str], @options[:target], @options[:image], @options[:build]).each do |adaptor|
- puts ""
- puts "Target Image: #{adaptor.image_id}"
- puts "Image: #{adaptor.image}"
- puts "Build: #{adaptor.build}"
- puts "Status: #{adaptor.status}"
- puts "Percent Complete: #{adaptor.percent_complete}"
- end
- quit(0)
- end
- end
-
- def combo_implemented?
- if @options[:template].empty? || @options[:target].empty?
- puts "This combination of parameters is not currently supported"
- quit(1)
- end
- true
- end
-
- def quit(code)
- @console.shutdown
- super
- end
-
- end
- end
-end
diff --git a/lib/aeolus_image/command/base_command.rb b/lib/aeolus_image/command/base_command.rb
new file mode 100644
index 0000000..727153c
--- /dev/null
+++ b/lib/aeolus_image/command/base_command.rb
@@ -0,0 +1,139 @@
+require 'yaml'
+require 'rest_client'
+require 'nokogiri'
+
+module Aeolus
+ module Image
+ #This will house some methods that multiple Command classes need to use.
+ class BaseCommand
+ attr_accessor :options
+
+ def initialize(opts={}, logger=nil)
+ logger(logger)
+ @options = opts
+ @config_location = "~/.aeolus-cli"
+ @config = load_config
+ end
+
+ protected
+ def not_implemented
+ "This option or combination is not yet implemented"
+ end
+
+ def logger(logger=nil)
+ @logger ||= logger
+ unless @logger
+ @logger = Logger.new(STDOUT)
+ @logger.level = Logger::INFO
+ @logger.datetime_format = "%Y-%m-%d %H:%M:%S"
+ end
+ return @logger
+ end
+
+ def iwhd
+ create_resource(:iwhd)
+ end
+
+ def conductor
+ create_resource(:conductor)
+ end
+
+ def read_file(path)
+ begin
+ full_path = File.expand_path(path)
+ if is_file?(path)
+ File.read(full_path)
+ else
+ return nil
+ end
+ rescue
+ nil
+ end
+ end
+
+ # TODO: Consider ripping all this file-related stuff into a module or
+ # class for better encapsulation and testability
+ def is_file?(path)
+ full_path = File.expand_path(path)
+ if File.exist?(full_path) && !File.directory?(full_path)
+ return true
+ end
+ false
+ end
+
+ def quit(code)
+ exit(code)
+ end
+
+ def validate_xml_document(schema_path, xml_string)
+ schema = Nokogiri::XML::RelaxNG(File.read(schema_path))
+ doc = Nokogiri::XML xml_string
+ schema.validate(doc)
+ end
+
+ def is_uuid?(id)
+ uuid = Regexp.new('[\w]{8}[-][\w]{4}[-][\w]{4}[-][\w]{4}[-][\w]{12}')
+ uuid.match(id).nil? ? false : true
+ end
+
+ private
+ def load_config
+ begin
+ file_str = read_file(@config_location)
+ if is_file?(@config_location) && !file_str.include?(":url")
+ lines = File.readlines(File.expand_path((a)config_location)).map do |line|
+ "#" + line
+ end
+ File.open(File.expand_path(@config_location), 'w') do |file|
+ file.puts lines
+ end
+ write_file
+ end
+ write_file unless is_file?(@config_location)
+ YAML::load(File.open(File.expand_path(@config_location)))
+ rescue Errno::ENOENT
+ #TODO: Create a custom exception to wrap CLI Exceptions
+ raise "Unable to locate or write configuration file: \"" + @config_location + "\""
+ end
+ end
+
+ def write_file
+ example = File.read(File.expand_path(File.dirname(__FILE__) + "/../../examples/aeolus-cli"))
+ File.open(File.expand_path(@config_location), 'a+') do |f|
+ f.write(example)
+ end
+ end
+
+ def create_resource(resource_name)
+ # Check to see if config has a resource with this name
+ if !(a)config.has_key?(resource_name)
+ raise "Unable to determine resource: " + resource_name.to_s + " from configuration file. Please check: " + @config_location
+ return
+ end
+
+ #Use command line arguments for username/password
+ if @options.has_key?(:username)
+ if @options.has_key?(:password)
+ RestClient::Resource.new(@config[resource_name][:url], :user => @options[:username], :password => @options[:password])
+ else
+ #TODO: Create a custom exception to wrap CLI Exceptions
+ raise "Password not found for user: " + @options[:username]
+ end
+
+ #Use config for username/password
+ elsif @config[resource_name].has_key?(:username)
+ if @config[resource_name].has_key?(:password)
+ RestClient::Resource.new(@config[resource_name][:url], :user => @config[resource_name][:username], :password => @config[resource_name][:password])
+ else
+ #TODO: Create a custom exception to wrap CLI Exceptions
+ raise "Password not found for user: " + @config[resource_name][:username]
+ end
+
+ # Do not use authentication
+ else
+ RestClient::Resource.new(@config[resource_name][:url])
+ end
+ end
+ end
+ end
+end
diff --git a/lib/aeolus_image/command/build_command.rb b/lib/aeolus_image/command/build_command.rb
new file mode 100644
index 0000000..07ced02
--- /dev/null
+++ b/lib/aeolus_image/command/build_command.rb
@@ -0,0 +1,68 @@
+require 'imagefactory'
+
+module Aeolus
+ module Image
+ class BuildCommand < BaseCommand
+ attr_accessor :console
+ def initialize(opts={}, logger=nil)
+ super(opts, logger)
+ default = {
+ :template_str => '',
+ :template => '',
+ :target => [],
+ :image => '',
+ :build => ''
+ }
+ @options = default.merge(@options)
+ @console = ImageFactoryConsole.new()
+ @console.start
+ end
+
+ def run
+ if combo_implemented?
+ @options[:template_str] = read_file(@options[:template])
+ if @options[:template_str].nil?
+ puts "Cannot find specified file"
+ quit(1)
+ end
+
+ # Validate XML against TDL Schema
+ errors = validate_xml_document(File.dirname(__FILE__) + "/../../examples/tdl.rng", @options[:template_str])
+ if errors.length > 0
+ puts "ERROR: The given Template does not conform to the TDL Schema, see below for specific details:"
+ errors.each do |error|
+ puts "- " + error.message
+ end
+ quit(1)
+ end
+
+ #This is a temporary hack in case the agent doesn't show up on bus immediately
+ sleep(5)
+ @console.build(@options[:template_str], @options[:target], @options[:image], @options[:build]).each do |adaptor|
+ puts ""
+ puts "Target Image: #{adaptor.image_id}"
+ puts "Image: #{adaptor.image}"
+ puts "Build: #{adaptor.build}"
+ puts "Status: #{adaptor.status}"
+ puts "Percent Complete: #{adaptor.percent_complete}"
+ end
+ quit(0)
+ end
+ end
+
+ def combo_implemented?
+ if @options[:template].empty? || @options[:target].empty?
+ puts "This combination of parameters is not currently supported"
+ quit(1)
+ end
+ true
+ end
+
+ def quit(code)
+ @console.shutdown
+ super
+ end
+
+ end
+ end
+end
diff --git a/lib/aeolus_image/command/config_parser.rb b/lib/aeolus_image/command/config_parser.rb
new file mode 100644
index 0000000..c992234
--- /dev/null
+++ b/lib/aeolus_image/command/config_parser.rb
@@ -0,0 +1,228 @@
+require 'optparse'
+require 'logger'
+
+module Aeolus
+ module Image
+ class ConfigParser
+ COMMANDS = %w(list build push import delete)
+ attr_accessor :options, :command, :args
+
+ def initialize(argv)
+ @args = argv
+ # Default options
+ @options = {}
+ parse
+ end
+
+ def process
+ # Check for command, then call appropriate Optionparser and initiate
+ # call to that class.
+ @command = @args.shift
+ # Eventually get the config file from user dir if it exists.
+ # File.expand_path("~")
+ if COMMANDS.include?(@command)
+ self.send((a)command.to_sym)
+ else
+ @args << "-h"
+ puts "Valid command required: \n\n"
+ parse
+ end
+ end
+
+ private
+ def parse
+ @optparse ||= OptionParser.new do|opts|
+ opts.banner = "Usage: aeolus-image [#{COMMANDS.join('|')}] [general options] [command options]"
+
+ opts.separator ""
+ opts.separator "General options:"
+ opts.on('-u', '--user USERNAME', 'Conductor username') do |user|
+ @options[:user] = user
+ end
+ opts.on('-w', '--password PASSWORD', 'Conductor password') do |pw|
+ @options[:password] = pw
+ end
+ opts.on('-d', '--id ID', 'id for a given object') do |id|
+ @options[:id] = id
+ end
+ opts.on('-r', '--description NAME', 'description (e.g. "<image><name>MyImage</name></image>" or "/home/user/myImage.xml")') do |description|
+ @options[:description] = description
+ end
+ opts.on('-r', '--provider NAME1,NAME2', Array,'name of specific provider (ie ec2-us-east1)') do |name|
+ @options[:provider] = name
+ end
+ opts.on('-I', '--image ID', 'ID of the base image, can be used in build and push commands, see examples') do |id|
+ @options[:image] = id
+ end
+ opts.on('-T', '--target TARGET1,TARGET2', Array, 'provider type (ec2, rackspace, rhevm, etc)') do |name|
+ @options[:target] = name
+ end
+ opts.on('-d', '--daemon', 'run as a background process') do
+ @options[:subcommand] = :images
+ end
+ opts.on( '-h', '--help', 'Get usage information for this tool') do
+ puts opts
+ exit(0)
+ end
+
+ opts.separator ""
+ opts.separator "List options:"
+ opts.on('-i', '--images', 'Retrieve a list of images') do
+ @options[:subcommand] = :images
+ end
+ opts.on('-b', '--builds ID', 'Retrieve the builds of an image') do |id|
+ @options[:subcommand] = :builds
+ @options[:id] = id
+ end
+ opts.on('-t', '--targetimages ID', 'Retrieve the target images from a build') do |id|
+ @options[:subcommand] = :targetimages
+ @options[:id] = id
+ end
+ opts.on('-P', '--providerimages ID', 'Retrieve the provider images from a target image') do |id|
+ @options[:subcommand] = :providerimages
+ @options[:id] = id
+ end
+ opts.on('-g', '--targets', 'Retrieve the values available for the --target parameter') do
+ @options[:subcommand] = :targets
+ end
+ opts.on('-p', '--providers', 'Retrieve the values available for the --provider parameter') do
+ @options[:subcommand] = :providers
+ end
+ opts.on('-a', '--accounts', 'Retrieve the values available for the --account parameter') do
+ @options[:subcommand] = :accounts
+ end
+
+ opts.separator ""
+ opts.separator "Build options:"
+ opts.on('-e', '--template FILE', 'path to file that contains template xml') do |file|
+ @options[:template] = file
+ end
+
+ opts.separator ""
+ opts.separator "Push options:"
+ opts.on('-B', '--build ID', 'push all target images for a build, to same providers as previously') do |id|
+ @options[:build] = id
+ end
+ opts.on('-A', '--account NAME', 'name of specific provider account to use for push') do |name|
+ @options[:account] = name
+ end
+
+ opts.separator ""
+ opts.separator "Delete options:"
+ opts.on('-I', '--image ID', 'delete build image and associated objects') do |id|
+ @options[:subcommand] = :image
+ @options[:image] = id
+ end
+ opts.on('-B', '--build ID', 'delete build and associated objects') do |id|
+ @options[:subcommand] = :build
+ @options[:build] = id
+ end
+ opts.on('-m', '--targetimage ID', 'delete target image and its provider images') do |id|
+ @options[:subcommand] = :target_image
+ @options[:targetimage] = id
+ end
+ opts.on('-D', '--providerimage ID', 'delete provider image') do |id|
+ @options[:subcommand] = :provider_image
+ @options[:providerimage] = id
+ end
+ opts.on('-W', '--iwhd', 'Delete everything stored in IWHD') do
+ @options[:subcommand] = :iwhd
+ end
+
+ opts.separator ""
+ opts.separator "List Examples:"
+ opts.separator "aeolus-image list --images # list available images"
+ opts.separator "aeolus-image list --builds $image_id # list the builds of an image"
+ opts.separator "aeolus-image list --targetimages $build_id # list the target images from a build"
+ opts.separator "aeolus-image list --providerimages $target_id # list the provider images from a target image"
+ opts.separator "aeolus-image list --providerimages all # list all provider images"
+ opts.separator "aeolus-image list --targets # list the values available for the --target parameter"
+ opts.separator "aeolus-image list --providers # list the values available for the --provider parameter"
+ opts.separator "aeolus-image list --accounts # list the values available for the --account parameter"
+
+ opts.separator ""
+ opts.separator "Build examples:"
+ opts.separator "aeolus-image build --target ec2 --template my.tmpl # build a new image for ec2 from the template"
+ opts.separator "aeolus-image build --image $image_id # (NOT IMPLEMENTED) rebuild the image template and targets from latest build"
+ opts.separator %q{aeolus-image build --target ec2,rackspace \ # rebuild the image with a new template and set of targets
+ --image $image_i \
+ --template my.tmpl}
+
+ opts.separator ""
+ opts.separator "Push examples:"
+ opts.separator "aeolus-image push --provider ec2-us-east-1 --id $image_id # initial push of an image build via image id to the specified provider"
+ opts.separator "aeolus-image push --build $build_id # push an image build via build id to the specified provider"
+ opts.separator "aeolus-image push --account $provider_account --build $build_id # (NOT IMPLEMENTED) ditto, using a specific provider account"
+ opts.separator "aeolus-image push --image $image_id # (NOT IMPLEMENTED) push all the target images for the latest build"
+
+ opts.separator ""
+ opts.separator "Import examples:"
+ opts.separator "aeolus-image import --provider ec2-us-east-1 --target ec2 --id $ami_id # import an AMI from the specified provider"
+ opts.separator "aeolus-image import --provider ec2-us-east-1 --target ec2 --id $ami_id --description '<image><name>My Image</name></image>' # import an AMI from the specified provider"
+ opts.separator "aeolus-image import --provider ec2-us-east-1 --target ec2 --id $ami_id --description <path_to_xml_file> # import an AMI from the specified provider"
+
+ opts.separator ""
+ opts.separator "Delete examples: (DELETE CURRENTLY NOT IMPLEMENTED) "
+ opts.separator "aeolus-image delete --build $build_id # deletes a build, updating latest/parent references as appropriate"
+ opts.separator "aeolus-image delete --targetimage $target_image # deletes a target image and its provider images"
+ opts.separator "aeolus-image delete --providerimage $provider_image # deletes a provider image"
+ end
+
+ begin
+ @optparse.parse!(@args)
+ rescue OptionParser::InvalidOption
+ puts "Warning: Invalid option"
+ exit(1)
+ rescue OptionParser::MissingArgument => e
+ puts "Warning, #{e.message}"
+ exit(1)
+ end
+ end
+
+ # TODO: Remove all this boilerplate and replace with some metaprogramming,
+ # perhaps method_missing
+ def list
+ # TODO: Instantiate and call object matching command type, for example:
+ # l = ListCommand.new(@options)
+ # Each Command will call it's own internal method depending on the contents of the hash.
+ # For the list example above, that object would call a method 'images' based on the item
+ # @options[:subcommand] being :images, so internally that class may do something like:
+ # self.send(@options[:subcommand])
+ if @options[:subcommand].nil?
+ # TODO: Pull out Print Usage into seporate method, and print
+ puts "Could not find subcommand for list, run `./aeolus-image --help` for usage instructions"
+ exit(1)
+ else
+ list_command = ListCommand.new(@options)
+ list_command.send(@options[:subcommand])
+ end
+ end
+
+ def build
+ b = BuildCommand.new(@options)
+ b.run
+ end
+
+ def push
+ b = PushCommand.new(@options)
+ b.run
+ end
+
+ def import
+ import_command = ImportCommand.new(@options)
+ import_command.import_image
+ end
+
+ def delete
+ if @options[:subcommand].nil?
+ # TODO: Pull out Print Usage into seporate method, and print
+ puts "Could not find subcommand for delete, run `./aeolus-image --help` for usage instructions"
+ exit(1)
+ else
+ delete_command = DeleteCommand.new(@options)
+ delete_command.send(@options[:subcommand])
+ end
+ end
+ end
+ end
+end
diff --git a/lib/aeolus_image/command/delete_command.rb b/lib/aeolus_image/command/delete_command.rb
new file mode 100644
index 0000000..cd30362
--- /dev/null
+++ b/lib/aeolus_image/command/delete_command.rb
@@ -0,0 +1,146 @@
+module Aeolus
+ module Image
+ class DeleteCommand < BaseCommand
+ def initialize(opts={}, logger=nil)
+ super(opts, logger)
+ end
+
+ def provider_image
+ check_id(@options[:providerimage], "Provider Image")
+ begin
+ delete_provider_image(@options[:providerimage])
+ puts "Provider Image: '" + provider_image + "' was succesfully deleted"
+ exit(0)
+ rescue RestClient::ResourceNotFound
+ puts "Error: Could not find Target Image with ID: '" + provider_image + "'"
+ exit(1)
+ rescue => e
+ puts "Error: Could not delete Target Image with ID: '" + provider_image + "'"
+ puts e.inspect
+ exit(1)
+ end
+ end
+
+ def target_image
+ check_id(@options[:targetimage], "Target Image")
+ begin
+ delete_target_image(@options[:targetimage])
+ puts "Target Image: '" + @options[:targetimage] + "' was succesfully deleted"
+ exit(0)
+ rescue RestClient::ResourceNotFound
+ puts "Error: Could not find Target Image with ID: '" + @options[:targetimage] + "'"
+ exit(1)
+ rescue => e
+ puts "Error: Could not delete Target Image with ID: '" + @options[:targetimage] + "'"
+ puts e.inspect
+ exit(1)
+ end
+ end
+
+ def build
+ check_id(@options[:build], "Build")
+ begin
+ delete_build(@options[:build])
+ puts "Build: '" + @options[:build] + "' was succesfully deleted"
+ exit(0)
+ rescue RestClient::ResourceNotFound
+ puts "Error: Could not find Build with ID: '" + @options[:build] + "'"
+ exit(1)
+ rescue => e
+ puts "Error: Could not delete Build with ID: '" + @options[:build] + "'"
+ puts e.inspect
+ exit(1)
+ end
+ end
+
+ def image
+ check_id(@options[:image], "Image")
+ begin
+ delete_image(@options[:image])
+ puts "Image: '" + @options[:image] + "' was succesfully deleted"
+ exit(0)
+ rescue RestClient::ResourceNotFound
+ puts "Error: Could not find Image with ID: '" + @options[:image] + "'"
+ exit(1)
+ rescue => e
+ puts "Error: Could not delete Image with ID: '" + @options[:image] + "'"
+ puts e.inspect
+ exit(1)
+ end
+ end
+
+ # Deletes all objects. Iterates through each object to make sure any stray objects are deleted.
+ def iwhd
+ list_command = ListCommand.new
+ list_command.list_providerimages(nil, true).each do |provider_image|
+ iwhd['/provider_images/' + provider_image].delete
+ end
+
+# list_command.list_targetimages(nil, true).each do |target_image|
+# iwhd['/target_images/' + target_image].delete
+# end
+#
+# list_command.list_builds(nil, true).each do |build|
+# iwhd['/builds/' + build].delete
+# end
+#
+# list_command.list_images.each do |image|
+# iwhd['/images/' + image].delete
+# end
+# puts "IWHD was successfully cleared"
+ exit(0)
+ end
+
+ private
+ def delete_provider_image(provider_image)
+ iwhd['/provider_images/' + provider_image].delete
+ end
+
+ def delete_target_image(target_image)
+ # Delete Provider Images
+ list_command = ListCommand.new
+ list_command.list_providerimages(target_image).each do |provider_image|
+ begin
+ delete_provider_image(provider_image)
+ rescue
+ end
+ end
+ # Delete Target Image
+ iwhd['/target_images/' + target_image].delete
+ end
+
+ def delete_build(build)
+ # Delete Target Images
+ list_command = ListCommand.new
+ list_command.list_targetimages(build).each do |target_image|
+ begin
+ delete_target_image(target_image)
+ rescue
+ end
+ end
+ # Delete Build
+ iwhd['/builds/' + build].delete
+ end
+
+ def delete_image(image)
+ # Delete Builds
+ list_command = ListCommand.new
+ list_command.list_builds(image).each do |build|
+ begin
+ delete_build(build)
+ rescue
+ end
+ end
+ # Delete Image
+ iwhd['/images/' + image].delete
+ end
+
+ def check_id(uuid, type)
+ if !(is_uuid?(uuid))
+ puts "Error: '" + uuid + "' is not a valid " + type + " ID"
+ exit(1)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/aeolus_image/command/import_command.rb b/lib/aeolus_image/command/import_command.rb
new file mode 100644
index 0000000..158f740
--- /dev/null
+++ b/lib/aeolus_image/command/import_command.rb
@@ -0,0 +1,44 @@
+module Aeolus
+ module Image
+ class ImportCommand < BaseCommand
+ def initialize(opts={}, logger=nil)
+ super(opts, logger)
+ default = {
+ :image => '',
+ :build => '',
+ :id => '',
+ :description => '<image><name>' + @options[:id] + '</name></image>',
+ :target => '',
+ :provider => ''
+ }
+ @options = default.merge(@options)
+ @console = ImageFactoryConsole.new()
+ @console.start
+ end
+
+ def import_image
+ description = read_file(@options[:description])
+ if !description.nil?
+ @options[:description] = description
+ end
+ # TODO: Validate Description XML
+
+ #This is a temporary hack in case the agent doesn't show up on bus
+ #immediately
+ sleep(5)
+ import_map = @console.import_image(@options[:image], @options[:build], @options[:id], @options[:description], @options[:target].first, @options[:provider].first)
+ puts ""
+ puts "Target Image: " + import_map['target_image']
+ puts "Image: " + import_map['image']
+ puts "Build: " + import_map['build']
+ puts "Provider Image: " + import_map['provider_image']
+ quit(0)
+ end
+
+ def quit(code)
+ @console.shutdown
+ super
+ end
+ end
+ end
+end
diff --git a/lib/aeolus_image/command/list_command.rb b/lib/aeolus_image/command/list_command.rb
new file mode 100644
index 0000000..cccd382
--- /dev/null
+++ b/lib/aeolus_image/command/list_command.rb
@@ -0,0 +1,221 @@
+module Aeolus
+ module Image
+ class ListCommand < BaseCommand
+ def initialize(opts={}, logger=nil)
+ super(opts, logger)
+ end
+
+ def images
+ check_bucket_exists("images")
+ images = [["IMAGE ID", "LASTEST PUSHED BUILD", "NAME", "TARGET", "OS", "OS VERSION", "ARCH", "DESCRIPTION"]]
+ doc = Nokogiri::XML iwhd['/target_images'].get
+ # Check for any invalid data in iwhd
+ invalid_images = []
+ doc.xpath("/objects/object/key").each do |targetimage|
+ begin
+ build = iwhd["/target_images/" + targetimage.text + "/build"].get
+ image = iwhd["/builds/" + build + "/image"].get
+
+ if template_info = get_template_info(image, targetimage.text)
+ images << [image] + [lastest_pushed(image)] + template_info
+ else
+ images << [image] + [lastest_pushed(image)] +[get_image_name(image), iwhd["/target_images/" + targetimage + "/target"].get, "", "", "", ""]
+ end
+ rescue
+ invalid_images << targetimage.text
+ end
+ end
+ format_print(images)
+
+ unless invalid_images.empty?
+ puts "\nN.B. following images were not listed, aeolus-image encountered some invalid data in iwhd:"
+ puts invalid_images.join "\n"
+ end
+ quit(0)
+ end
+
+ def list_images
+ images = []
+ if check_bucket_exists("images").nil?
+ return images
+ end
+
+ doc = Nokogiri::XML iwhd['/images'].get
+ doc.xpath("/objects/object/key").each do |image|
+ images << image.text
+ end
+ images
+ end
+
+ def builds
+ builds = [["Build ID"]]
+ list_builds(@options[:id]).each do |b|
+ builds << [b]
+ end
+ format_print(builds)
+ quit(0)
+ end
+
+ def list_builds(image, all=false)
+ builds = []
+ if check_bucket_exists("builds").nil?
+ return builds
+ end
+
+ doc = Nokogiri::XML iwhd['/builds'].get
+ doc.xpath("/objects/object/key").each do |build|
+ if all || (iwhd['/builds/' + build.text + "/image"].get == image)
+ builds << build.text
+ end
+ end
+ builds
+ end
+
+ def targetimages
+ targetimages = [["Target Image Id"]]
+ list_targetimages(@options[:id]).each do |ti|
+ targetimages << [ti]
+ end
+ format_print(targetimages)
+ quit(0)
+ end
+
+ def list_targetimages(build, all=false)
+ targetimages = []
+ if check_bucket_exists("target_images").nil?
+ return targetimages
+ end
+
+ doc = Nokogiri::XML iwhd['/target_images'].get
+ doc.xpath("/objects/object/key").each do |target_image|
+ begin
+ if all || (iwhd['/target_images/' + target_image.text + "/build"].get == build)
+ targetimages << target_image.text
+ end
+ rescue RestClient::ResourceNotFound
+ end
+ end
+ targetimages
+ end
+
+ def providerimages
+ providerimages = [["PROVIDER IMAGE", "PROVIDER", "TARGET IMAGE", "TARGET IDENTIFIER", "IMAGE"]]
+ list_providerimages(@options[:id], (@options[:id] == "all")).each do |pi|
+ providerimages << pi
+ end
+ format_print(providerimages)
+ quit(0)
+ end
+
+ def list_providerimages(targetimage, all=false)
+ providerimages = []
+ if check_bucket_exists("provider_images").nil?
+ return providerimages
+ end
+
+ doc = Nokogiri::XML iwhd['/provider_images'].get
+ doc.xpath("/objects/object/key").each do |provider_image|
+ begin
+ thistargetimage = iwhd["/provider_images/" + provider_image.text + "/target_image"].get
+ if all || (thistargetimage == targetimage)
+ build = iwhd["/target_images/" + thistargetimage + "/build"].get
+ image = iwhd["/builds/" + build + "/image"].get
+ providerimages << [provider_image.text] + [iwhd["/provider_images/" + provider_image.text + "/provider"].get] + [thistargetimage] + [iwhd["/provider_images/" + provider_image.text + "/target_identifier"].get] + [image]
+ end
+ rescue RestClient::ResourceNotFound
+ end
+ end
+ providerimages
+ end
+
+ def targets
+ targets = [["NAME", "TARGET CODE"]]
+ targets << ["Mock", "mock"]
+ targets << ["Amazon EC2", "ec2"]
+ targets << ["RHEV-M", "rhevm"]
+ targets << ["VMware vSphere", "vsphere"]
+ targets << ["Condor Cloud", "condor_cloud"]
+ format_print(targets)
+ quit(0)
+ end
+
+ def providers
+ print_values = [["NAME", "TYPE", "URL"]]
+
+ doc = Nokogiri::XML conductor['/providers'].get
+ doc.xpath("/providers/provider").each do |provider|
+ print_values << [provider.xpath("name").text, provider.xpath("provider_type").text, provider.xpath("url").text]
+ end
+
+ format_print(print_values)
+ quit(0)
+ end
+
+ def accounts
+ print_values = [["NAME", "PROVIDER", "PROVIDER TYPE"]]
+ doc = Nokogiri::XML conductor['/provider_accounts/'].get
+ doc.xpath("/provider_accounts/provider_account").each do |account|
+ print_values << [account.xpath("name").text, account.xpath("provider").text, account.xpath("provider_type").text]
+ end
+
+ format_print(print_values)
+ quit(0)
+ end
+
+ private
+ # Takes a 2D array of strings and neatly prints them to STDOUT
+ def format_print(print_values)
+ widths = Array.new(print_values[0].size, 0)
+ print_values.each do |print_value|
+ widths = widths.zip(print_value).map! {|width, value| value.length > width ? value.length : width }
+ end
+
+ print_values.each do |print_value|
+ widths.zip(print_value) do |width, value|
+ printf("%-#{width + 5}s", value)
+ end
+ puts ""
+ end
+ end
+
+ def get_template_info(image, targetimage)
+ begin
+ template = Nokogiri::XML iwhd["/templates/" + iwhd["/target_images/" + targetimage + "/template"].get].get
+ [template.xpath("/template/name").text,
+ iwhd["/target_images/" + targetimage + "/target"].get,
+ template.xpath("/template/os/name").text,
+ template.xpath("/template/os/version").text,
+ template.xpath("/template/os/arch").text,
+ template.xpath("/template/description").text]
+ rescue
+ end
+ end
+
+ def get_image_name(image)
+ begin
+ template_xml = Nokogiri::XML iwhd["images/" + image].get
+ template_xml.xpath("/image/name").text
+ rescue
+ ""
+ end
+ end
+
+ def lastest_pushed(image)
+ begin
+ build = iwhd["/images/" + image + "/latest_build"].get
+ build.nil? ? "" : build
+ rescue
+ ""
+ end
+ end
+
+ def check_bucket_exists(bucket)
+ begin
+ iwhd["/" + bucket].get
+ rescue
+ nil
+ end
+ end
+ end
+ end
+end
diff --git a/lib/aeolus_image/command/push_command.rb b/lib/aeolus_image/command/push_command.rb
new file mode 100644
index 0000000..47baec6
--- /dev/null
+++ b/lib/aeolus_image/command/push_command.rb
@@ -0,0 +1,72 @@
+require 'rest_client'
+
+module Aeolus
+ module Image
+ class PushCommand < BaseCommand
+ attr_accessor :console
+ def initialize(opts={}, logger=nil)
+ super(opts, logger)
+ default = {
+ :provider => [],
+ :id => '',
+ :build => ''
+ }
+ @options = default.merge(@options)
+ @console = ImageFactoryConsole.new()
+ @console.start
+ end
+ def run
+ begin
+ if combo_implemented?
+ if !@options[:id].empty? && pushed?(@options[:id])
+ puts "ERROR: This image has already been pushed, to push to another provider please push via build-id rather than image-id"
+ puts "e.g. aeolus-image push --provider <provider> --build <build-id>"
+ quit(1)
+ end
+
+ sleep(5)
+ @console.push(@options[:provider], get_creds, @options[:id], @options[:build]).each do |adaptor|
+ puts ""
+ puts "Provider Image: #{adaptor.image_id}"
+ puts "Image: #{adaptor.image}"
+ puts "Build: #{adaptor.build}"
+ puts "Status: #{adaptor.status}"
+ puts "Percent Complete: #{adaptor.percent_complete}"
+ end
+ quit(0)
+ end
+ rescue
+ puts "An Error occured whilst trying to push this build, please check aeolus-image --help for details on how to use this command"
+ quit(1)
+ end
+ end
+
+ def get_creds
+ conductor['provider_accounts'].get
+ end
+
+ def combo_implemented?
+ if @options[:provider].empty? || (@options[:build].empty? && @options[:id].empty?)
+ puts "This combination of parameters is not currently supported"
+ quit(1)
+ end
+ true
+ end
+
+ private
+ def quit(code)
+ @console.shutdown
+ exit(code)
+ end
+
+ def pushed?(image)
+ begin
+ uuid = Regexp.new('[\w]{8}[-][\w]{4}[-][\w]{4}[-][\w]{4}[-][\w]{12}')
+ uuid.match(iwhd["/images/" + image + "/latest_unpushed"].get).nil? ? true : false
+ rescue
+ true
+ end
+ end
+ end
+ end
+end
diff --git a/lib/aeolus_image/config_parser.rb b/lib/aeolus_image/config_parser.rb
deleted file mode 100644
index c992234..0000000
--- a/lib/aeolus_image/config_parser.rb
+++ /dev/null
@@ -1,228 +0,0 @@
-require 'optparse'
-require 'logger'
-
-module Aeolus
- module Image
- class ConfigParser
- COMMANDS = %w(list build push import delete)
- attr_accessor :options, :command, :args
-
- def initialize(argv)
- @args = argv
- # Default options
- @options = {}
- parse
- end
-
- def process
- # Check for command, then call appropriate Optionparser and initiate
- # call to that class.
- @command = @args.shift
- # Eventually get the config file from user dir if it exists.
- # File.expand_path("~")
- if COMMANDS.include?(@command)
- self.send((a)command.to_sym)
- else
- @args << "-h"
- puts "Valid command required: \n\n"
- parse
- end
- end
-
- private
- def parse
- @optparse ||= OptionParser.new do|opts|
- opts.banner = "Usage: aeolus-image [#{COMMANDS.join('|')}] [general options] [command options]"
-
- opts.separator ""
- opts.separator "General options:"
- opts.on('-u', '--user USERNAME', 'Conductor username') do |user|
- @options[:user] = user
- end
- opts.on('-w', '--password PASSWORD', 'Conductor password') do |pw|
- @options[:password] = pw
- end
- opts.on('-d', '--id ID', 'id for a given object') do |id|
- @options[:id] = id
- end
- opts.on('-r', '--description NAME', 'description (e.g. "<image><name>MyImage</name></image>" or "/home/user/myImage.xml")') do |description|
- @options[:description] = description
- end
- opts.on('-r', '--provider NAME1,NAME2', Array,'name of specific provider (ie ec2-us-east1)') do |name|
- @options[:provider] = name
- end
- opts.on('-I', '--image ID', 'ID of the base image, can be used in build and push commands, see examples') do |id|
- @options[:image] = id
- end
- opts.on('-T', '--target TARGET1,TARGET2', Array, 'provider type (ec2, rackspace, rhevm, etc)') do |name|
- @options[:target] = name
- end
- opts.on('-d', '--daemon', 'run as a background process') do
- @options[:subcommand] = :images
- end
- opts.on( '-h', '--help', 'Get usage information for this tool') do
- puts opts
- exit(0)
- end
-
- opts.separator ""
- opts.separator "List options:"
- opts.on('-i', '--images', 'Retrieve a list of images') do
- @options[:subcommand] = :images
- end
- opts.on('-b', '--builds ID', 'Retrieve the builds of an image') do |id|
- @options[:subcommand] = :builds
- @options[:id] = id
- end
- opts.on('-t', '--targetimages ID', 'Retrieve the target images from a build') do |id|
- @options[:subcommand] = :targetimages
- @options[:id] = id
- end
- opts.on('-P', '--providerimages ID', 'Retrieve the provider images from a target image') do |id|
- @options[:subcommand] = :providerimages
- @options[:id] = id
- end
- opts.on('-g', '--targets', 'Retrieve the values available for the --target parameter') do
- @options[:subcommand] = :targets
- end
- opts.on('-p', '--providers', 'Retrieve the values available for the --provider parameter') do
- @options[:subcommand] = :providers
- end
- opts.on('-a', '--accounts', 'Retrieve the values available for the --account parameter') do
- @options[:subcommand] = :accounts
- end
-
- opts.separator ""
- opts.separator "Build options:"
- opts.on('-e', '--template FILE', 'path to file that contains template xml') do |file|
- @options[:template] = file
- end
-
- opts.separator ""
- opts.separator "Push options:"
- opts.on('-B', '--build ID', 'push all target images for a build, to same providers as previously') do |id|
- @options[:build] = id
- end
- opts.on('-A', '--account NAME', 'name of specific provider account to use for push') do |name|
- @options[:account] = name
- end
-
- opts.separator ""
- opts.separator "Delete options:"
- opts.on('-I', '--image ID', 'delete build image and associated objects') do |id|
- @options[:subcommand] = :image
- @options[:image] = id
- end
- opts.on('-B', '--build ID', 'delete build and associated objects') do |id|
- @options[:subcommand] = :build
- @options[:build] = id
- end
- opts.on('-m', '--targetimage ID', 'delete target image and its provider images') do |id|
- @options[:subcommand] = :target_image
- @options[:targetimage] = id
- end
- opts.on('-D', '--providerimage ID', 'delete provider image') do |id|
- @options[:subcommand] = :provider_image
- @options[:providerimage] = id
- end
- opts.on('-W', '--iwhd', 'Delete everything stored in IWHD') do
- @options[:subcommand] = :iwhd
- end
-
- opts.separator ""
- opts.separator "List Examples:"
- opts.separator "aeolus-image list --images # list available images"
- opts.separator "aeolus-image list --builds $image_id # list the builds of an image"
- opts.separator "aeolus-image list --targetimages $build_id # list the target images from a build"
- opts.separator "aeolus-image list --providerimages $target_id # list the provider images from a target image"
- opts.separator "aeolus-image list --providerimages all # list all provider images"
- opts.separator "aeolus-image list --targets # list the values available for the --target parameter"
- opts.separator "aeolus-image list --providers # list the values available for the --provider parameter"
- opts.separator "aeolus-image list --accounts # list the values available for the --account parameter"
-
- opts.separator ""
- opts.separator "Build examples:"
- opts.separator "aeolus-image build --target ec2 --template my.tmpl # build a new image for ec2 from the template"
- opts.separator "aeolus-image build --image $image_id # (NOT IMPLEMENTED) rebuild the image template and targets from latest build"
- opts.separator %q{aeolus-image build --target ec2,rackspace \ # rebuild the image with a new template and set of targets
- --image $image_i \
- --template my.tmpl}
-
- opts.separator ""
- opts.separator "Push examples:"
- opts.separator "aeolus-image push --provider ec2-us-east-1 --id $image_id # initial push of an image build via image id to the specified provider"
- opts.separator "aeolus-image push --build $build_id # push an image build via build id to the specified provider"
- opts.separator "aeolus-image push --account $provider_account --build $build_id # (NOT IMPLEMENTED) ditto, using a specific provider account"
- opts.separator "aeolus-image push --image $image_id # (NOT IMPLEMENTED) push all the target images for the latest build"
-
- opts.separator ""
- opts.separator "Import examples:"
- opts.separator "aeolus-image import --provider ec2-us-east-1 --target ec2 --id $ami_id # import an AMI from the specified provider"
- opts.separator "aeolus-image import --provider ec2-us-east-1 --target ec2 --id $ami_id --description '<image><name>My Image</name></image>' # import an AMI from the specified provider"
- opts.separator "aeolus-image import --provider ec2-us-east-1 --target ec2 --id $ami_id --description <path_to_xml_file> # import an AMI from the specified provider"
-
- opts.separator ""
- opts.separator "Delete examples: (DELETE CURRENTLY NOT IMPLEMENTED) "
- opts.separator "aeolus-image delete --build $build_id # deletes a build, updating latest/parent references as appropriate"
- opts.separator "aeolus-image delete --targetimage $target_image # deletes a target image and its provider images"
- opts.separator "aeolus-image delete --providerimage $provider_image # deletes a provider image"
- end
-
- begin
- @optparse.parse!(@args)
- rescue OptionParser::InvalidOption
- puts "Warning: Invalid option"
- exit(1)
- rescue OptionParser::MissingArgument => e
- puts "Warning, #{e.message}"
- exit(1)
- end
- end
-
- # TODO: Remove all this boilerplate and replace with some metaprogramming,
- # perhaps method_missing
- def list
- # TODO: Instantiate and call object matching command type, for example:
- # l = ListCommand.new(@options)
- # Each Command will call it's own internal method depending on the contents of the hash.
- # For the list example above, that object would call a method 'images' based on the item
- # @options[:subcommand] being :images, so internally that class may do something like:
- # self.send(@options[:subcommand])
- if @options[:subcommand].nil?
- # TODO: Pull out Print Usage into seporate method, and print
- puts "Could not find subcommand for list, run `./aeolus-image --help` for usage instructions"
- exit(1)
- else
- list_command = ListCommand.new(@options)
- list_command.send(@options[:subcommand])
- end
- end
-
- def build
- b = BuildCommand.new(@options)
- b.run
- end
-
- def push
- b = PushCommand.new(@options)
- b.run
- end
-
- def import
- import_command = ImportCommand.new(@options)
- import_command.import_image
- end
-
- def delete
- if @options[:subcommand].nil?
- # TODO: Pull out Print Usage into seporate method, and print
- puts "Could not find subcommand for delete, run `./aeolus-image --help` for usage instructions"
- exit(1)
- else
- delete_command = DeleteCommand.new(@options)
- delete_command.send(@options[:subcommand])
- end
- end
- end
- end
-end
diff --git a/lib/aeolus_image/delete_command.rb b/lib/aeolus_image/delete_command.rb
deleted file mode 100644
index cd30362..0000000
--- a/lib/aeolus_image/delete_command.rb
+++ /dev/null
@@ -1,146 +0,0 @@
-module Aeolus
- module Image
- class DeleteCommand < BaseCommand
- def initialize(opts={}, logger=nil)
- super(opts, logger)
- end
-
- def provider_image
- check_id(@options[:providerimage], "Provider Image")
- begin
- delete_provider_image(@options[:providerimage])
- puts "Provider Image: '" + provider_image + "' was succesfully deleted"
- exit(0)
- rescue RestClient::ResourceNotFound
- puts "Error: Could not find Target Image with ID: '" + provider_image + "'"
- exit(1)
- rescue => e
- puts "Error: Could not delete Target Image with ID: '" + provider_image + "'"
- puts e.inspect
- exit(1)
- end
- end
-
- def target_image
- check_id(@options[:targetimage], "Target Image")
- begin
- delete_target_image(@options[:targetimage])
- puts "Target Image: '" + @options[:targetimage] + "' was succesfully deleted"
- exit(0)
- rescue RestClient::ResourceNotFound
- puts "Error: Could not find Target Image with ID: '" + @options[:targetimage] + "'"
- exit(1)
- rescue => e
- puts "Error: Could not delete Target Image with ID: '" + @options[:targetimage] + "'"
- puts e.inspect
- exit(1)
- end
- end
-
- def build
- check_id(@options[:build], "Build")
- begin
- delete_build(@options[:build])
- puts "Build: '" + @options[:build] + "' was succesfully deleted"
- exit(0)
- rescue RestClient::ResourceNotFound
- puts "Error: Could not find Build with ID: '" + @options[:build] + "'"
- exit(1)
- rescue => e
- puts "Error: Could not delete Build with ID: '" + @options[:build] + "'"
- puts e.inspect
- exit(1)
- end
- end
-
- def image
- check_id(@options[:image], "Image")
- begin
- delete_image(@options[:image])
- puts "Image: '" + @options[:image] + "' was succesfully deleted"
- exit(0)
- rescue RestClient::ResourceNotFound
- puts "Error: Could not find Image with ID: '" + @options[:image] + "'"
- exit(1)
- rescue => e
- puts "Error: Could not delete Image with ID: '" + @options[:image] + "'"
- puts e.inspect
- exit(1)
- end
- end
-
- # Deletes all objects. Iterates through each object to make sure any stray objects are deleted.
- def iwhd
- list_command = ListCommand.new
- list_command.list_providerimages(nil, true).each do |provider_image|
- iwhd['/provider_images/' + provider_image].delete
- end
-
-# list_command.list_targetimages(nil, true).each do |target_image|
-# iwhd['/target_images/' + target_image].delete
-# end
-#
-# list_command.list_builds(nil, true).each do |build|
-# iwhd['/builds/' + build].delete
-# end
-#
-# list_command.list_images.each do |image|
-# iwhd['/images/' + image].delete
-# end
-# puts "IWHD was successfully cleared"
- exit(0)
- end
-
- private
- def delete_provider_image(provider_image)
- iwhd['/provider_images/' + provider_image].delete
- end
-
- def delete_target_image(target_image)
- # Delete Provider Images
- list_command = ListCommand.new
- list_command.list_providerimages(target_image).each do |provider_image|
- begin
- delete_provider_image(provider_image)
- rescue
- end
- end
- # Delete Target Image
- iwhd['/target_images/' + target_image].delete
- end
-
- def delete_build(build)
- # Delete Target Images
- list_command = ListCommand.new
- list_command.list_targetimages(build).each do |target_image|
- begin
- delete_target_image(target_image)
- rescue
- end
- end
- # Delete Build
- iwhd['/builds/' + build].delete
- end
-
- def delete_image(image)
- # Delete Builds
- list_command = ListCommand.new
- list_command.list_builds(image).each do |build|
- begin
- delete_build(build)
- rescue
- end
- end
- # Delete Image
- iwhd['/images/' + image].delete
- end
-
- def check_id(uuid, type)
- if !(is_uuid?(uuid))
- puts "Error: '" + uuid + "' is not a valid " + type + " ID"
- exit(1)
- end
- end
- end
- end
-end
diff --git a/lib/aeolus_image/import_command.rb b/lib/aeolus_image/import_command.rb
deleted file mode 100644
index 158f740..0000000
--- a/lib/aeolus_image/import_command.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-module Aeolus
- module Image
- class ImportCommand < BaseCommand
- def initialize(opts={}, logger=nil)
- super(opts, logger)
- default = {
- :image => '',
- :build => '',
- :id => '',
- :description => '<image><name>' + @options[:id] + '</name></image>',
- :target => '',
- :provider => ''
- }
- @options = default.merge(@options)
- @console = ImageFactoryConsole.new()
- @console.start
- end
-
- def import_image
- description = read_file(@options[:description])
- if !description.nil?
- @options[:description] = description
- end
- # TODO: Validate Description XML
-
- #This is a temporary hack in case the agent doesn't show up on bus
- #immediately
- sleep(5)
- import_map = @console.import_image(@options[:image], @options[:build], @options[:id], @options[:description], @options[:target].first, @options[:provider].first)
- puts ""
- puts "Target Image: " + import_map['target_image']
- puts "Image: " + import_map['image']
- puts "Build: " + import_map['build']
- puts "Provider Image: " + import_map['provider_image']
- quit(0)
- end
-
- def quit(code)
- @console.shutdown
- super
- end
- end
- end
-end
diff --git a/lib/aeolus_image/list_command.rb b/lib/aeolus_image/list_command.rb
deleted file mode 100644
index cccd382..0000000
--- a/lib/aeolus_image/list_command.rb
+++ /dev/null
@@ -1,221 +0,0 @@
-module Aeolus
- module Image
- class ListCommand < BaseCommand
- def initialize(opts={}, logger=nil)
- super(opts, logger)
- end
-
- def images
- check_bucket_exists("images")
- images = [["IMAGE ID", "LASTEST PUSHED BUILD", "NAME", "TARGET", "OS", "OS VERSION", "ARCH", "DESCRIPTION"]]
- doc = Nokogiri::XML iwhd['/target_images'].get
- # Check for any invalid data in iwhd
- invalid_images = []
- doc.xpath("/objects/object/key").each do |targetimage|
- begin
- build = iwhd["/target_images/" + targetimage.text + "/build"].get
- image = iwhd["/builds/" + build + "/image"].get
-
- if template_info = get_template_info(image, targetimage.text)
- images << [image] + [lastest_pushed(image)] + template_info
- else
- images << [image] + [lastest_pushed(image)] +[get_image_name(image), iwhd["/target_images/" + targetimage + "/target"].get, "", "", "", ""]
- end
- rescue
- invalid_images << targetimage.text
- end
- end
- format_print(images)
-
- unless invalid_images.empty?
- puts "\nN.B. following images were not listed, aeolus-image encountered some invalid data in iwhd:"
- puts invalid_images.join "\n"
- end
- quit(0)
- end
-
- def list_images
- images = []
- if check_bucket_exists("images").nil?
- return images
- end
-
- doc = Nokogiri::XML iwhd['/images'].get
- doc.xpath("/objects/object/key").each do |image|
- images << image.text
- end
- images
- end
-
- def builds
- builds = [["Build ID"]]
- list_builds(@options[:id]).each do |b|
- builds << [b]
- end
- format_print(builds)
- quit(0)
- end
-
- def list_builds(image, all=false)
- builds = []
- if check_bucket_exists("builds").nil?
- return builds
- end
-
- doc = Nokogiri::XML iwhd['/builds'].get
- doc.xpath("/objects/object/key").each do |build|
- if all || (iwhd['/builds/' + build.text + "/image"].get == image)
- builds << build.text
- end
- end
- builds
- end
-
- def targetimages
- targetimages = [["Target Image Id"]]
- list_targetimages(@options[:id]).each do |ti|
- targetimages << [ti]
- end
- format_print(targetimages)
- quit(0)
- end
-
- def list_targetimages(build, all=false)
- targetimages = []
- if check_bucket_exists("target_images").nil?
- return targetimages
- end
-
- doc = Nokogiri::XML iwhd['/target_images'].get
- doc.xpath("/objects/object/key").each do |target_image|
- begin
- if all || (iwhd['/target_images/' + target_image.text + "/build"].get == build)
- targetimages << target_image.text
- end
- rescue RestClient::ResourceNotFound
- end
- end
- targetimages
- end
-
- def providerimages
- providerimages = [["PROVIDER IMAGE", "PROVIDER", "TARGET IMAGE", "TARGET IDENTIFIER", "IMAGE"]]
- list_providerimages(@options[:id], (@options[:id] == "all")).each do |pi|
- providerimages << pi
- end
- format_print(providerimages)
- quit(0)
- end
-
- def list_providerimages(targetimage, all=false)
- providerimages = []
- if check_bucket_exists("provider_images").nil?
- return providerimages
- end
-
- doc = Nokogiri::XML iwhd['/provider_images'].get
- doc.xpath("/objects/object/key").each do |provider_image|
- begin
- thistargetimage = iwhd["/provider_images/" + provider_image.text + "/target_image"].get
- if all || (thistargetimage == targetimage)
- build = iwhd["/target_images/" + thistargetimage + "/build"].get
- image = iwhd["/builds/" + build + "/image"].get
- providerimages << [provider_image.text] + [iwhd["/provider_images/" + provider_image.text + "/provider"].get] + [thistargetimage] + [iwhd["/provider_images/" + provider_image.text + "/target_identifier"].get] + [image]
- end
- rescue RestClient::ResourceNotFound
- end
- end
- providerimages
- end
-
- def targets
- targets = [["NAME", "TARGET CODE"]]
- targets << ["Mock", "mock"]
- targets << ["Amazon EC2", "ec2"]
- targets << ["RHEV-M", "rhevm"]
- targets << ["VMware vSphere", "vsphere"]
- targets << ["Condor Cloud", "condor_cloud"]
- format_print(targets)
- quit(0)
- end
-
- def providers
- print_values = [["NAME", "TYPE", "URL"]]
-
- doc = Nokogiri::XML conductor['/providers'].get
- doc.xpath("/providers/provider").each do |provider|
- print_values << [provider.xpath("name").text, provider.xpath("provider_type").text, provider.xpath("url").text]
- end
-
- format_print(print_values)
- quit(0)
- end
-
- def accounts
- print_values = [["NAME", "PROVIDER", "PROVIDER TYPE"]]
- doc = Nokogiri::XML conductor['/provider_accounts/'].get
- doc.xpath("/provider_accounts/provider_account").each do |account|
- print_values << [account.xpath("name").text, account.xpath("provider").text, account.xpath("provider_type").text]
- end
-
- format_print(print_values)
- quit(0)
- end
-
- private
- # Takes a 2D array of strings and neatly prints them to STDOUT
- def format_print(print_values)
- widths = Array.new(print_values[0].size, 0)
- print_values.each do |print_value|
- widths = widths.zip(print_value).map! {|width, value| value.length > width ? value.length : width }
- end
-
- print_values.each do |print_value|
- widths.zip(print_value) do |width, value|
- printf("%-#{width + 5}s", value)
- end
- puts ""
- end
- end
-
- def get_template_info(image, targetimage)
- begin
- template = Nokogiri::XML iwhd["/templates/" + iwhd["/target_images/" + targetimage + "/template"].get].get
- [template.xpath("/template/name").text,
- iwhd["/target_images/" + targetimage + "/target"].get,
- template.xpath("/template/os/name").text,
- template.xpath("/template/os/version").text,
- template.xpath("/template/os/arch").text,
- template.xpath("/template/description").text]
- rescue
- end
- end
-
- def get_image_name(image)
- begin
- template_xml = Nokogiri::XML iwhd["images/" + image].get
- template_xml.xpath("/image/name").text
- rescue
- ""
- end
- end
-
- def lastest_pushed(image)
- begin
- build = iwhd["/images/" + image + "/latest_build"].get
- build.nil? ? "" : build
- rescue
- ""
- end
- end
-
- def check_bucket_exists(bucket)
- begin
- iwhd["/" + bucket].get
- rescue
- nil
- end
- end
- end
- end
-end
diff --git a/lib/aeolus_image/model/image.rb b/lib/aeolus_image/model/image.rb
new file mode 100644
index 0000000..73c1b24
--- /dev/null
+++ b/lib/aeolus_image/model/image.rb
@@ -0,0 +1,27 @@
+module Aeolus
+ module Image
+ class Image < WarehouseModel
+ @bucket_name = 'images'
+
+ def initialize(attrs)
+ attrs.each do |k,v|
+ if k.to_sym == :latest_build
+ sym = :attr_writer
+ else
+ sym = :attr_accessor
+ end
+ self.class.send(sym, k.to_sym) unless respond_to?(:"#{k}=")
+ send(:"#{k}=", v)
+ end
+ end
+
+ def latest_build
+ ImageBuild.find(@latest_build) if @latest_build
+ end
+
+ def image_builds
+ ImageBuild.find_all_by_image_uuid(self.uuid)
+ end
+ end
+ end
+end
diff --git a/lib/aeolus_image/model/image_build.rb b/lib/aeolus_image/model/image_build.rb
new file mode 100644
index 0000000..f9ced0b
--- /dev/null
+++ b/lib/aeolus_image/model/image_build.rb
@@ -0,0 +1,43 @@
+module Aeolus
+ module Image
+ class ImageBuild < WarehouseModel
+ @bucket_name = 'builds'
+
+ def initialize(attrs)
+ attrs.each do |k,v|
+ if k.to_sym == :image
+ sym = :attr_writer
+ else
+ sym = :attr_accessor
+ end
+ self.class.send(sym, k.to_sym) unless respond_to?(:"#{k}=")
+ send(:"#{k}=", v)
+ end
+ end
+
+ def self.find_all_by_image_uuid(uuid)
+ self.set_warehouse_and_bucket if self.bucket.nil?
+ self.bucket.objects.map do |wh_object|
+ if wh_object.attr('image')[1] == uuid
+ ImageBuild.new(wh_object.attrs(wh_object.attr_list))
+ end
+ end.compact
+ end
+
+ def image
+ Image.find(@image) if @image
+ end
+
+ def target_images
+ TargetImage.all.select {|ti| ti.build and (ti.build.uuid == self.uuid)}
+ end
+
+ def provider_images
+ targets = target_images
+ ProviderImage.all.select do |pi|
+ targets.include?(pi.target_image)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/aeolus_image/model/provider_image.rb b/lib/aeolus_image/model/provider_image.rb
new file mode 100644
index 0000000..44f576d
--- /dev/null
+++ b/lib/aeolus_image/model/provider_image.rb
@@ -0,0 +1,32 @@
+module Aeolus
+ module Image
+ class ProviderImage < WarehouseModel
+ @bucket_name = 'provider_images'
+
+ def initialize(attrs)
+ attrs.each do |k,v|
+ if [:provider, :target_image].include?(k.to_sym)
+ sym = :attr_writer
+ else
+ sym = :attr_accessor
+ end
+ self.class.send(sym, k.to_sym) unless respond_to?(:"#{k}=")
+ send(:"#{k}=", v)
+ end
+ end
+
+ def target_image
+ TargetImage.find(@target_image) if @target_image
+ end
+
+ def provider_name
+ @provider
+ end
+
+ def provider
+ Provider.find_by_name(@provider)
+ end
+
+ end
+ end
+end
diff --git a/lib/aeolus_image/model/target_image.rb b/lib/aeolus_image/model/target_image.rb
new file mode 100644
index 0000000..1167fd4
--- /dev/null
+++ b/lib/aeolus_image/model/target_image.rb
@@ -0,0 +1,27 @@
+module Aeolus
+ module Image
+ class TargetImage < WarehouseModel
+ @bucket_name = 'target_images'
+
+ def initialize(attrs)
+ attrs.each do |k,v|
+ if k.to_sym == :build
+ sym = :attr_writer
+ else
+ sym = :attr_accessor
+ end
+ self.class.send(sym, k.to_sym) unless respond_to?(:"#{k}=")
+ send(:"#{k}=", v)
+ end
+ end
+
+ def build
+ ImageBuild.find(@build) if @build
+ end
+
+ def provider_images
+ ProviderImage.all.select{|pi| pi.target_image and (pi.target_image.uuid == self.uuid)}
+ end
+ end
+ end
+end
diff --git a/lib/aeolus_image/model/warehouse_client.rb b/lib/aeolus_image/model/warehouse_client.rb
new file mode 100644
index 0000000..fcad944
--- /dev/null
+++ b/lib/aeolus_image/model/warehouse_client.rb
@@ -0,0 +1,165 @@
+require 'rubygems'
+
+require 'rest-client'
+require 'nokogiri'
+
+#TODO: perform iwhd version-dependent URI mapping
+module Aeolus
+ module Image
+ module Warehouse
+
+ class BucketObject
+ attr_reader :key
+
+ def initialize(connection, key, bucket)
+ @connection = connection
+ @key = key
+ @bucket = bucket
+ @path = "/#{@bucket.name}/#{(a)key}"
+ end
+
+ def self.create(connection, key, bucket, body, attrs = {})
+ obj = new(connection, key, bucket)
+ obj.set_body(body)
+ obj.set_attrs(attrs)
+ obj
+ end
+
+ def body
+ @connection.do_request @path, :plain => true
+ end
+
+ def set_body(body)
+ @connection.do_request @path, :content => body, :method => :put
+ end
+
+ def attr_list
+ result = @connection.do_request @path, :content => 'op=parts', :method => :post
+ return result.xpath('/object/object_attr/@name').to_a.map {|item| item.value}
+ end
+
+ def attrs(list)
+ attrs = {}
+ list.each do |att|
+ next if att.match('-')
+ attrs[att] = (@connection.do_request("#{@path}/#{att}", :plain => true) rescue nil)
+ end
+ attrs
+ end
+
+ def attr(name)
+ attrs([name]).first
+ end
+
+ def set_attrs(hash)
+ hash.each do |name, content|
+ set_attr(name, content)
+ end
+ end
+
+ def set_attr(name, content)
+ path = "#{@path}/#{name}"
+ @connection.do_request path, :content => content, :method => :put
+ end
+
+ def delete!
+ @connection.do_request @path, :method => :delete
+ true
+ end
+
+ end
+
+ class Bucket
+ attr_accessor :name
+
+ def initialize(name, connection)
+ @name = name
+ @connection = connection
+ end
+
+ def to_s
+ "Bucket: #{@name}"
+ end
+
+ def object_names
+ result = @connection.do_request "/#{@name}"
+ result.xpath('/objects/object').map do |obj|
+ obj.at_xpath('./key/text()').to_s
+ end
+ end
+
+ def objects
+ object_names.map do |name|
+ object(name)
+ end
+ end
+
+ def object(key)
+ BucketObject.new @connection, key, self
+ end
+
+ def create_object(key, body, attrs)
+ BucketObject.create(@connection, key, self, body, attrs)
+ end
+
+ def include?(key)
+ object_names.include?(key)
+ end
+ end
+
+ class Connection
+ attr_accessor :uri
+
+ def initialize(uri)
+ @uri = uri
+ end
+
+ def do_request(path = '', opts={})
+ opts[:method] ||= :get
+ opts[:content] ||= ''
+ opts[:plain] ||= false
+ opts[:headers] ||= {}
+
+ result = RestClient::Request.execute :method => opts[:method], :url => @uri + path, :payload => opts[:content], :headers => opts[:headers]
+
+ return Nokogiri::XML result unless opts[:plain]
+ return result
+ end
+
+ end
+
+ class Client
+
+ def initialize(uri)
+ @connection = Connection.new(uri)
+ end
+
+ def create_bucket(bucket)
+ @connection.do_request "/#{bucket}", :method => :put rescue RestClient::InternalServerError
+ Bucket.new(bucket, @connection)
+ end
+
+ def bucket(bucket)
+ Bucket.new bucket, @connection
+ end
+
+ def buckets
+ @connection.do_request.xpath('/api/link[@rel="bucket"]').map do |obj|
+ obj.at_xpath('./@href').to_s.gsub(/.*\//, '')
+ end
+ end
+
+ def get_iwhd_version
+ result = @connection.do_request.at_xpath('/api[@service="image_warehouse"]/@version')
+ raise "Response does not contain <api> tag or version information" if result == nil
+ return result.value
+ end
+
+ def query(bucket_name, query_string)
+ @connection.do_request "/#{bucket_name}/_query", {:method => :post, :content => query_string}
+ end
+ end
+
+ end
+ end
+end
diff --git a/lib/aeolus_image/model/warehouse_model.rb b/lib/aeolus_image/model/warehouse_model.rb
new file mode 100644
index 0000000..bc502d6
--- /dev/null
+++ b/lib/aeolus_image/model/warehouse_model.rb
@@ -0,0 +1,135 @@
+module Aeolus
+ module Image
+ # require 'warehouse_client'
+ include Warehouse
+ class BucketObjectNotFound < Exception;end
+ class BucketNotFound < Exception;end
+
+ class WarehouseModel
+
+ def ==(other_obj)
+ # If the objects have different instance variables defined, they're definitely not ==
+ return false unless instance_variables.sort == other_obj.instance_variables.sort
+ # Otherwise, ensure that they're all the same
+ instance_variables.each do |iv|
+ return false unless other_obj.instance_variable_get(iv) == instance_variable_get(iv)
+ end
+ # They have the same instance variables and values, so they're equal
+ true
+ end
+
+ class << self
+ attr_accessor :warehouse, :bucket, :bucket_name
+
+ def set_warehouse_and_bucket
+ begin
+ @@config ||= load_config
+ self.warehouse = Warehouse::Client.new(@@config[:iwhd][:url])
+ self.bucket = self.warehouse.bucket(@bucket_name)
+ rescue
+ raise BucketNotFound
+ end
+ end
+
+ def bucket_objects
+ self.set_warehouse_and_bucket if self.bucket.nil?
+
+ begin
+ self.bucket.objects
+ rescue RestClient::ResourceNotFound
+ []
+ end
+ end
+
+ def first
+ obj = bucket_objects.first
+ obj ? self.new(obj.attrs(obj.attr_list)) : nil
+ end
+
+ def last
+ obj = bucket_objects.last
+ obj ? self.new(obj.attrs(obj.attr_list)) : nil
+ end
+
+ def all
+ bucket_objects.map do |wh_object|
+ self.new(wh_object.attrs(wh_object.attr_list))
+ end
+ end
+
+ def find(uuid)
+ self.set_warehouse_and_bucket if self.bucket.nil?
+ begin
+ if self.bucket.include?(uuid)
+ self.new(self.bucket.object(uuid).attrs(self.bucket.object(uuid).attr_list))
+ else
+ nil
+ end
+ rescue RestClient::ResourceNotFound
+ nil
+ end
+ end
+
+ def where(query_string)
+ self.set_warehouse_and_bucket if self.bucket.nil?
+ self.warehouse.query(@bucket_name, query_string)
+ end
+
+ protected
+ # Copy over entirely too much code to load the config file
+ def load_config
+ # TODO - Is this always the case? We should probably have /etc/aeolus-cli or something too?
+ # Or allow Rails to override this
+ @config_location ||= "~/.aeolus-cli"
+ begin
+ file_str = read_file(@config_location)
+ if is_file?(@config_location) && !file_str.include?(":url")
+ lines = File.readlines(File.expand_path((a)config_location)).map do |line|
+ "#" + line
+ end
+ File.open(File.expand_path(@config_location), 'w') do |file|
+ file.puts lines
+ end
+ write_file
+ end
+ write_file unless is_file?(@config_location)
+ YAML::load(File.open(File.expand_path(@config_location)))
+ rescue Errno::ENOENT
+ #TODO: Create a custom exception to wrap CLI Exceptions
+ raise "Unable to locate or write configuration file: \"" + @config_location + "\""
+ end
+ end
+
+ def write_file
+ example = File.read(File.expand_path(File.dirname(__FILE__) + "/../../examples/aeolus-cli"))
+ File.open(File.expand_path(@config_location), 'a+') do |f|
+ f.write(example)
+ end
+ end
+
+ def read_file(path)
+ begin
+ full_path = File.expand_path(path)
+ if is_file?(path)
+ File.read(full_path)
+ else
+ return nil
+ end
+ rescue
+ nil
+ end
+ end
+
+ def is_file?(path)
+ full_path = File.expand_path(path)
+ if File.exist?(full_path) && !File.directory?(full_path)
+ return true
+ end
+ false
+ end
+
+ end
+
+ end
+ end
+end
diff --git a/lib/aeolus_image/push_command.rb b/lib/aeolus_image/push_command.rb
deleted file mode 100644
index 47baec6..0000000
--- a/lib/aeolus_image/push_command.rb
+++ /dev/null
@@ -1,72 +0,0 @@
-require 'rest_client'
-
-module Aeolus
- module Image
- class PushCommand < BaseCommand
- attr_accessor :console
- def initialize(opts={}, logger=nil)
- super(opts, logger)
- default = {
- :provider => [],
- :id => '',
- :build => ''
- }
- @options = default.merge(@options)
- @console = ImageFactoryConsole.new()
- @console.start
- end
- def run
- begin
- if combo_implemented?
- if !@options[:id].empty? && pushed?(@options[:id])
- puts "ERROR: This image has already been pushed, to push to another provider please push via build-id rather than image-id"
- puts "e.g. aeolus-image push --provider <provider> --build <build-id>"
- quit(1)
- end
-
- sleep(5)
- @console.push(@options[:provider], get_creds, @options[:id], @options[:build]).each do |adaptor|
- puts ""
- puts "Provider Image: #{adaptor.image_id}"
- puts "Image: #{adaptor.image}"
- puts "Build: #{adaptor.build}"
- puts "Status: #{adaptor.status}"
- puts "Percent Complete: #{adaptor.percent_complete}"
- end
- quit(0)
- end
- rescue
- puts "An Error occured whilst trying to push this build, please check aeolus-image --help for details on how to use this command"
- quit(1)
- end
- end
-
- def get_creds
- conductor['provider_accounts'].get
- end
-
- def combo_implemented?
- if @options[:provider].empty? || (@options[:build].empty? && @options[:id].empty?)
- puts "This combination of parameters is not currently supported"
- quit(1)
- end
- true
- end
-
- private
- def quit(code)
- @console.shutdown
- exit(code)
- end
-
- def pushed?(image)
- begin
- uuid = Regexp.new('[\w]{8}[-][\w]{4}[-][\w]{4}[-][\w]{4}[-][\w]{12}')
- uuid.match(iwhd["/images/" + image + "/latest_unpushed"].get).nil? ? true : false
- rescue
- true
- end
- end
- end
- end
-end
--
1.7.6
12 years, 6 months
[PATCH aeolus-image 1/2] Updated Rake and Spec to Rails3
by Martyn Taylor
From: Martyn Taylor <mtaylor(a)redhat.com>
---
.rspec | 2 ++
Rakefile | 16 +++++++---------
spec/spec.opts | 3 ---
spec/spec_helper.rb | 4 ++--
4 files changed, 11 insertions(+), 14 deletions(-)
create mode 100644 .rspec
delete mode 100644 spec/spec.opts
diff --git a/.rspec b/.rspec
new file mode 100644
index 0000000..0d9a0dd
--- /dev/null
+++ b/.rspec
@@ -0,0 +1,2 @@
+--colour
+--format nested
diff --git a/Rakefile b/Rakefile
index 0ef1a90..95705ee 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,10 +1,10 @@
require 'rubygems'
require 'rake'
require 'rake/clean'
-require 'rake/gempackagetask'
-require 'rake/rdoctask'
+require 'rubygems/package_task'
+require 'rdoc/task'
require 'rake/testtask'
-require 'spec/rake/spectask'
+require 'rspec/core/rake_task'
require 'rake/rpmtask'
RPMBUILD_DIR = "#{File.expand_path('~')}/rpmbuild"
@@ -13,7 +13,6 @@ RPM_SPEC = "rubygem-aeolus-image.spec"
spec = Gem::Specification.new do |s|
s.name = 'aeolus-image'
s.version = '0.1.0'
- s.has_rdoc = true
s.summary= 'cli for aeolus cloud suite'
s.description = 'Commandline interface for working with the aeolus cloud management suite'
s.author = 'Jason Guiditta, Martyn Taylor'
@@ -31,7 +30,7 @@ spec = Gem::Specification.new do |s|
s.add_development_dependency('rspec', '~>1.3.0')
end
-Rake::GemPackageTask.new(spec) do |p|
+Gem::PackageTask.new(spec) do |p|
p.gem_spec = spec
p.need_tar = true
p.need_zip = true
@@ -50,13 +49,12 @@ Rake::TestTask.new do |t|
t.test_files = FileList['test/**/*.rb']
end
-Spec::Rake::SpecTask.new do |t|
- t.spec_files = FileList['spec/**/*.rb']
- t.libs << Dir["lib"]
+RSpec::Core::RakeTask.new do |t|
+ t.pattern = FileList['spec/**/*.rb']
end
Rake::RpmTask.new(RPM_SPEC) do |rpm|
rpm.need_tar = true
rpm.package_files.include("lib/*")
rpm.topdir = "#{RPMBUILD_DIR}"
-end
+end
\ No newline at end of file
diff --git a/spec/spec.opts b/spec/spec.opts
deleted file mode 100644
index 4154052..0000000
--- a/spec/spec.opts
+++ /dev/null
@@ -1,3 +0,0 @@
---color
---format specdoc
---diff
\ No newline at end of file
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 013fbd3..135f090 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,4 +1,4 @@
-$: << File.expand_path(File.join(File.dirname(__FILE__), "../lib"))
+$: << File.expand_path(File.join(File.dirname(__FILE__), "../lib/aeolus_image"))
$: << File.expand_path(File.join(File.dirname(__FILE__), "."))
require 'rubygems'
require 'config_parser'
@@ -31,7 +31,7 @@ module Helpers
end
end
-Spec::Runner.configure do |config|
+RSpec.configure do |config|
config.include Helpers
config.before(:all) do
Aeolus::Image::BaseCommand.class_eval do
--
1.7.6
12 years, 6 months