[PATCH conductor 0/1] Test fix
by Matt Wagner
I'm surprised this test wasn't failing more widely. This method was including 1-9 as integers, rather than strings. If you append an integer to a string, Ruby takes it as an ASCII value. Since they were just single digits, it's all nasty stuff -- and 8 is a backspace. As best as I can tell, that is the reason that this method, called with a length of 101, was sometimes returning strings shorter than 100 characters.
I started to fix this method to just do '1'..'9' instead of 1..9, but Jay pointed out that there is no reason we actually need to generate random strings each time. So rather than trying to fix some complicated code, I just cut it down to do only what's required.
-- Matt
11 years, 12 months
Aeolus + OpenStack status update
by Matt Wagner
Hi all,
After a bit of discussion today in IRC, I went back to look at getting
OpenStack working in Conductor, since it feels like we're close. Today I
had partial success importing and launching an image.
I suspect something is wrong with the local OpenStack server I have been
testing on, where I get exceptions when attempting to launch an
instance, or even list instances. (Of note, if I ssh into the OpenStack
machine and run "nova list", it gives me a 404 -- so the error is before
Deltacloud.)
Today, with Ronelle's help, I was able to use the setup she had used for
testing Deltacloud. With my previous patch[1] to accept '0 GB' for
storage as a HWP and an as-yet-unsent patch adding CredentialDefinitions
for OpenStack, I was able to add a ProviderAccount. Working with
Deltacloud on the command line, I was able to fetch a list of images and
import one of them by ID into Conductor. With that, I build a deployable
and successfully launched it.
I did hit several issues, including one that keeps this from being
overly useful -- there is no way to log into the instance. It appears to
want a keypair injected, but that doesn't happen right now. Ronelle had
observed the same issue with Deltacloud[2]; it appears that the
functionality to pass in a keypair for injection needs to be added to
the openstack gem before it's available up the chain. Thus, the instance
successfully launches, but there is no apparent way to log into it.
Another issue, also on the Deltacloud/openstack gem side[3], is that all
of the IPs were reported as private IPs, even though one is actually a
public IP. This isn't a big deal per se, except that the "pretty view"
in Conductor only shows public IPs, making it appear in running state
with no IP unless you click through to the detail view. This needs to be
fixed in Deltacloud or the openstack gem, but I wonder if Conductor
should also display a private IP if that's all it has -- not just for
this bug, but also for instances where someone uses Conductor to manage
a local-only cloud, like an internal RHEV system.
When terminating an instance, there are two problems, though the
instance is stopped successfully in spite of them. The first is that
Deltacloud doesn't report the state transition properly; it goes from
RUNNING to PENDING instead of STOPPING. Ronelle has already caught and
reported this as a Deltacloud issue[4]. The second issue, and I'm not
quite sure how to deal with this one, is that OpenStack (or at least
HP's offering) seems to stop reporting the instance over the API as soon
as it stops running. Since dbomatic polls only once a minute, this means
that we frequently end up with the stopped instances in 'vanished' state
-- one minute we poll them and it's in 'running' state, and the next
minute, it's been stopped and no longer comes back at all, so we move it
to 'vanished'. I'm not sure what to do here, since saying "vanished on
OpenStack drivers means it's stopped" is (a) not entirely safe, and (b)
introducing provider-specific logic that we're not supposed to need in
Conductor. (IMHO, the "right" solution here, but the one we have zero
control over, would be for OpenStack to report it in 'stopped' state for
some period of time, at least when we query for a specific instance.
This is how most other clouds behave.)
Still, I think this is good news. I was able to import an image and
launch it through Conductor. Most of the issues I encountered were in
Deltacloud (and have JIRAs associated with them), or are in the
openstack gem (and have JIRAs for Marios to see if he can fix upstream).
This is encouraging.
There's still a little cosmetic cleanup related to displaying the
appropriate labels for OpenStack values that I need to add, but I
suspect this is either a quick change to en.yml and/or a problem with
the way I added CredentialDefinitions via the console. (I'd like to
clean this up and send out a patch adding the CredentialDefinitions to
seeds.rb plus the requisite string changes.)
-- Matt
[1]
http://lists.fedorahosted.org/pipermail/aeolus-devel/2012-May/010147.html
(still needs review)
[2] https://issues.apache.org/jira/browse/DTACLOUD-205
[3] https://issues.apache.org/jira/browse/DTACLOUD-206
[4] https://issues.apache.org/jira/browse/DTACLOUD-209
11 years, 12 months
[PATCH conductor] BZ786535: display failures for instances (rev. 3)
by Imre Farkas
From: Imre Farkas <ifarkas(a)redhat.com>
https://bugzilla.redhat.com/show_bug.cgi?id=786535
Resending this patch based on Jozef's suggestion to add autoupdate to the error messages. I also moved the error messages from flash messages to alerts for readabilty reasons.
---
src/app/controllers/deployments_controller.rb | 2 +-
src/app/models/deployment.rb | 2 +-
src/app/util/taskomatic.rb | 8 +++
src/app/views/deployments/_alerts_show.html.haml | 62 ++++++++++++++++-----
src/public/javascripts/backbone/views.js | 34 ++++++++++++
5 files changed, 91 insertions(+), 17 deletions(-)
diff --git a/src/app/controllers/deployments_controller.rb b/src/app/controllers/deployments_controller.rb
index 504a050..4ef653a 100644
--- a/src/app/controllers/deployments_controller.rb
+++ b/src/app/controllers/deployments_controller.rb
@@ -183,7 +183,7 @@ class DeploymentsController < ApplicationController
require_privilege(Privilege::VIEW, @deployment)
init_new_deployment_attrs
save_breadcrumb(deployment_path(@deployment, :viewstate => viewstate_id), @deployment.name)
- @failed_instances = @deployment.failed_instances
+ @failed_instances = @deployment.failed_instances.list(sort_column(Instance), sort_direction)
if filter_view?
@view = 'instances/list'
params[:instances_preset_filter] = "" unless params[:instances_preset_filter]
diff --git a/src/app/models/deployment.rb b/src/app/models/deployment.rb
index 4f80aff..6b0277a 100644
--- a/src/app/models/deployment.rb
+++ b/src/app/models/deployment.rb
@@ -432,7 +432,7 @@ class Deployment < ActiveRecord::Base
end
def failed_instances
- instances.select {|instance| instance.failed?}
+ instances.failed
end
PRESET_FILTERS_OPTIONS = []
diff --git a/src/app/util/taskomatic.rb b/src/app/util/taskomatic.rb
index a7181dc..463f521 100644
--- a/src/app/util/taskomatic.rb
+++ b/src/app/util/taskomatic.rb
@@ -73,6 +73,14 @@ module Taskomatic
rescue Exception => ex
task.state = Task::STATE_FAILED
task.message = ex.message
+ task.instance.update_attributes(:last_error => ex.message)
+
+ # For RHEV-M, since we need to start up the instance after the vm has been created
+ # we also have to handle create_failed state events separately
+ if task.instance.state == Instance::STATE_STOPPED && task.action == InstanceTask::ACTION_START &&
+ task.instance.provider_account.provider.provider_type.deltacloud_driver == 'rhevm'
+ task.instance.update_attributes(:state => Instance::STATE_CREATE_FAILED)
+ end
ensure
task.save! if Task.exists?(task.id)
end
diff --git a/src/app/views/deployments/_alerts_show.html.haml b/src/app/views/deployments/_alerts_show.html.haml
index f850c6c..6bc1519 100644
--- a/src/app/views/deployments/_alerts_show.html.haml
+++ b/src/app/views/deployments/_alerts_show.html.haml
@@ -1,15 +1,47 @@
-- if @failed_instances.count > 0
- %section.content-section.alerts.collapse_entity
- %header
- %h2=t 'alerts_label'
- %span.label.badge.alert.count= @failed_instances.count
- .section-controls
- = link_to t("providers.edit.toggle_alerts"), "#", :class => 'collapse alerts' unless @statistics[:instances_failed].blank?
- .content.collapsible
- %dl.alerts
- %ul
- - @failed_instances.each do |inst|
- %li.alert
- %dt.subject.critical= inst.name
- %dd.type=t 'alerts.instance_failure'
- %dd.desc= "#{inst.last_error.blank? ? inst.state : inst.last_error}"
+#deployment-alerts
+ - if @failed_instances.count > 0
+ %section.content-section.alerts.collapse_entity
+ %header
+ %h2=t 'alerts_label'
+ %span.label.badge.alert.count= @failed_instances.count
+ .section-controls
+ = link_to t("providers.edit.toggle_alerts"), "#", :class => 'collapse alerts' unless @statistics[:instances_failed].blank?
+ .content.collapsible
+ %dl.alerts
+ %ul
+ - @failed_instances.each do |inst|
+ %li.alert
+ %dt.subject.critical= inst.name
+ %dd.type=t 'alerts.instance_failure'
+ %dd.desc= "#{inst.last_error.blank? ? inst.state : inst.last_error}"
+
+%script#deploymentAlertsHeaderTemplate{ :type => 'text/x-jquery-tmpl' }
+ :plain
+ <section class="content-section alerts collapse_entity">
+ <header>
+ <h2>Alerts</h2>
+ <span class="label badge alert count">${failedCount}</span>
+ <div class="section-controls">
+ <a class="collapse alerts" href="#">#{t('providers.edit.toggle_alerts')}</a>
+ </div>
+ </header>
+ <div class="content collapsible">
+ <dl class="alerts">
+ <ul></ul>
+ </dl>
+ </div>
+ </section>
+
+%script#deploymentAlertTemplate{ :type => 'text/x-jquery-tmpl' }
+ :plain
+ <li class="alert">
+ <dt class="subject critical">${name}</dt>
+ <dd class="type">#{t('alerts.instance_failure')}</dd>
+ <dd class="desc">
+ {{if last_error}}
+ ${last_error}
+ {{else}}
+ ${state}
+ {{/if}}
+ </dd>
+ </li>
diff --git a/src/public/javascripts/backbone/views.js b/src/public/javascripts/backbone/views.js
index a28bb1f..8f2b6eb 100644
--- a/src/public/javascripts/backbone/views.js
+++ b/src/public/javascripts/backbone/views.js
@@ -219,6 +219,40 @@ Conductor.Views.DeploymentsShow = Backbone.View.extend({
$instances.empty();
$('#instanceTemplate').tmpl(this.collection.toJSON()).appendTo($instances);
+
+ var $alerts = this.$('#deployment-alerts');
+ var alertsVisible = $alerts.find('.collapsible').length == 0 || $alerts.find('.collapsible').is(":visible")
+ var failedInstances = _.compact(_.map(this.collection.models, function(model) {
+ if(model.get('is_failed')) {
+ return model.toJSON();
+ }
+ else {
+ return null;
+ }
+ }));
+
+ // Render alerts table
+ $alerts.empty();
+ if(failedInstances.length == 0) return;
+
+ $('#deploymentAlertsHeaderTemplate').tmpl({
+ 'failedCount' : failedInstances.length
+ }).appendTo($alerts);
+
+ // Add callback to Toggle link
+ $alerts.find('a.collapse').click(function(e) {
+ e.preventDefault();
+ $(this).parents('.collapse_entity').find('.collapsible').slideToggle(80);
+ });
+
+ // Restore toggle state on alerts table
+ if(!alertsVisible) {
+ $alerts.find('.collapsible').hide();
+ }
+
+ // Render alerts
+ var $alertsList = this.$('dl.alerts > ul');
+ $('#deploymentAlertTemplate').tmpl(failedInstances).appendTo($alertsList);
}
});
--
1.7.6.5
11 years, 12 months
[PATCH conductor 2/4] properly quote strings
by Mo Morsi
---
src/config/locales/de.yml | 12 ++++++------
src/config/locales/en.yml | 6 +++---
src/config/locales/fr.yml | 4 ++--
src/config/locales/it.yml | 4 ++--
src/config/locales/pt-BR.yml | 2 +-
5 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/src/config/locales/de.yml b/src/config/locales/de.yml
index 902480f..2be3fcb 100644
--- a/src/config/locales/de.yml
+++ b/src/config/locales/de.yml
@@ -260,7 +260,7 @@ de:
no_catalog_selected: "Kein Katalog ausgewählt"
new_from_image:
catalog_entry_name: "%{name}"
- description: "Markup- und Image-Referenzen in \%{name}\ wurden erfolgreich überprüft. Sie können das Image nun speichern und eine Deployable erstellen."
+ description: "Markup- und Image-Referenzen in %{name} wurden erfolgreich überprüft. Sie können das Image nun speichern und eine Deployable erstellen."
no_available_catalog: "Kein Katalog"
title: "Deployable erstellen"
properties:
@@ -384,7 +384,7 @@ de:
count_of_instances: "Anzahl der Instanzen: "
operational_status: "Betriebsstatus von"
overview:
- confirmation: "Sind Sie sicher, dass Sie \%{deployment}\ im %{pool} Pool deployen möchten? Dadurch werden %{quota} Ihres gesamten Deployment-Kontingents verbraucht."
+ confirmation: "Sind Sie sicher, dass Sie %{deployment} im %{pool} Pool deployen möchten? Dadurch werden %{quota} Ihres gesamten Deployment-Kontingents verbraucht."
deployable_xml: Deployable-XML
description: Beschreibung
header: "\"%{deployment}\" in %{pool} Pool deployen?"
@@ -538,7 +538,7 @@ de:
os_version: OS-Version
new:
choose_file: "Wählen Sie eine Vorlagendatei: "
- description: "Markup- und Image-Referenzen in \%{name}\ wurden erfolgreich überprüft. Sie können das Image nun speichern und eine Deployable erstellen."
+ description: "Markup- und Image-Referenzen in %{name} wurden erfolgreich überprüft. Sie können das Image nun speichern und eine Deployable erstellen."
edit_file: "Datei vor dem Speichern bearbeiten: "
from_url: "Von URL"
image_url: "Vorlagen-URL: "
@@ -548,7 +548,7 @@ de:
not_on_provider: "Angefordertes Image konnte nicht auf Provider gefunden werden."
overview:
back: Zurück
- description: "Markup- und Image-Referenzen in \%{name}\ wurden erfolgreich überprüft. Sie können das Image nun speichern und eine Deployable erstellen."
+ description: "Markup- und Image-Referenzen in %{name} wurden erfolgreich überprüft. Sie können das Image nun speichern und eine Deployable erstellen."
make_deployable: "Automatisch \"%{name}\" Deployable erstellen."
save_template: "Vorlage speichern"
valid_image: "Gültiges Image"
@@ -977,7 +977,7 @@ de:
provider_images:
flash:
notice:
- deleted: "Provider-Image gelöscht. Falls Sie das vom Provider gespeicherte Image selbst entfernen möchten, rufen Sie die Verwaltungskonsole von %{provider} auf und suchen das \%{target_id}\ Image."
+ deleted: "Provider-Image gelöscht. Falls Sie das vom Provider gespeicherte Image selbst entfernen möchten, rufen Sie die Verwaltungskonsole von %{provider} auf und suchen das %{target_id} Image."
upload_start: "Upload des Provider-Images gestartet"
warning:
delete_failed: "Provider-Image konnte nicht gelöscht werden"
@@ -1293,7 +1293,7 @@ de:
delete_selected: "Auswahl löschen"
unlimited: Unbegrenzt
new:
- neccessary_information: "Geben Sie die notwendigen Informationen über Ihren neuen Benutzer ein und klicken Sie auf \Benutzer speichern\, wenn Sie fertig sind."
+ neccessary_information: "Geben Sie die notwendigen Informationen über Ihren neuen Benutzer ein und klicken Sie auf Benutzer speichern, wenn Sie fertig sind."
new_user: "Neuer Benutzer"
preset_filters:
all_users: "Alle Benutzer"
diff --git a/src/config/locales/en.yml b/src/config/locales/en.yml
index 6bee3f4..129dd9c 100644
--- a/src/config/locales/en.yml
+++ b/src/config/locales/en.yml
@@ -215,7 +215,7 @@ en:
arch: ARCH
deployable_xml: Deployable XML
show:
- name: %{name} Deployment
+ name: "%{name} Deployment"
new:
launch: Launch this deployment
deployable_definition: Deployable definition
@@ -535,8 +535,8 @@ en:
new:
new_hwp: New Hardware Profile
show:
- frontend_hwp: %{name} (Front End)
- backend_hwp: %{name} (Provider)
+ frontend_hwp: "%{name} (Front End)"
+ backend_hwp: "%{name} (Provider)"
matching_hwp: Matching Provider Hardware Profiles
flash:
notice:
diff --git a/src/config/locales/fr.yml b/src/config/locales/fr.yml
index 3f787c5..f24dda7 100644
--- a/src/config/locales/fr.yml
+++ b/src/config/locales/fr.yml
@@ -384,7 +384,7 @@ fr:
count_of_instances: "Décompte des instances : "
operational_status: "Statut opérationnel de"
overview:
- confirmation: "Êtes-vous certain de vouloir déployer \%{deployment}\ sur le pool %{pool} ? Ce faire utilisera %{quota} de votre quota de déploiement total."
+ confirmation: "Êtes-vous certain de vouloir déployer %{deployment} sur le pool %{pool} ? Ce faire utilisera %{quota} de votre quota de déploiement total."
deployable_xml: "XML déployable"
description: Description
header: "Déployer \"%{deployment}\" sur le pool %{pool} ?"
@@ -977,7 +977,7 @@ fr:
provider_images:
flash:
notice:
- deleted: "Image du fournisseur supprimée. Si vous souhaitez supprimer l'image stockée par le fournisseur, veuillez vous rendre sur la console de gestion de %{provider} et recherchez l'image \%{target_id}\."
+ deleted: "Image du fournisseur supprimée. Si vous souhaitez supprimer l'image stockée par le fournisseur, veuillez vous rendre sur la console de gestion de %{provider} et recherchez l'image %{target_id}."
upload_start: "Téléchargement de l'image du fournisseur lancé"
warning:
delete_failed: "Suppression de l'image du fournisseur impossible"
diff --git a/src/config/locales/it.yml b/src/config/locales/it.yml
index 0769cf7..fb3c811 100644
--- a/src/config/locales/it.yml
+++ b/src/config/locales/it.yml
@@ -384,7 +384,7 @@ it:
count_of_instances: "Conteggio delle istanze: "
operational_status: "Stato operativo di"
overview:
- confirmation: "Sei sicuro di voler implementare \%{deployment}\ sul gruppo %{pool}? Così facendo utilizzerete %{quota} del tuo quota totale."
+ confirmation: "Sei sicuro di voler implementare %{deployment} sul gruppo %{pool}? Così facendo utilizzerete %{quota} del tuo quota totale."
deployable_xml: "XML distribuibile"
description: Descrizione
header: "Implementare \"%{deployment}\" sul pool %{pool}?"
@@ -977,7 +977,7 @@ it:
provider_images:
flash:
notice:
- deleted: "Immagine provider cancellata. Se desideri rimuovere l'immagine archiviata dal provider visitare la console di gestione di %{provider} e trovare l'immagine \%{target_id}\."
+ deleted: "Immagine provider cancellata. Se desideri rimuovere l'immagine archiviata dal provider visitare la console di gestione di %{provider} e trovare l'immagine %{target_id}."
upload_start: "Caricamento immagine del provider iniziato"
warning:
delete_failed: "Impossibile cancellare l'immagine del provider"
diff --git a/src/config/locales/pt-BR.yml b/src/config/locales/pt-BR.yml
index a941096..f9f758e 100644
--- a/src/config/locales/pt-BR.yml
+++ b/src/config/locales/pt-BR.yml
@@ -384,7 +384,7 @@ pt-BR:
count_of_instances: "Contagem das Instâncias: "
operational_status: "Status operacional de: "
overview:
- confirmation: "Você tem certeza de que deseja implementar o Pool \%{deployment}\ to the %{pool}? Isto usará a %{quota} de sua cota de implementação geral."
+ confirmation: "Você tem certeza de que deseja implementar o Pool %{deployment} to the %{pool}? Isto usará a %{quota} de sua cota de implementação geral."
deployable_xml: "XML Implementável"
description: Descrição
header: "Implementar \"%{deployment}\" em %{pool} Pool?"
--
1.7.6.5
11 years, 12 months
FWIW: Current OpenStack status
by Matt Wagner
Hi all,
One of the tasks I've been working on is #3178, "Assess the viability of
importing and launching images from a openstack provider." We don't have
many OpenStack implementation tasks in this sprint, but I was kind of
roaring to see something working, so I wanted to sort of take inventory
of where things stood today.
With the patches adding support applied to master a bit ago, I pulled up
a Rails console and saw how far I could get. [1] lists the steps I took.
The first obstacle I hit was some exception in the Deltacloud logs,
raised when I tried to list instances[2]. Marios helped me debug this
and indicated that the problem was that I was using an older version of
the Deltacloud package; it was using the Rackspace driver versus the
OpenStack one. (See the filename in line 20 of the link.)
Until this is updated in the next Deltacloud release, I worked around
this by building a local gem for Deltacloud and restarting with that.
That much worked great.
My next issue was that we do not appear to have anything in place to
collect any credentials for those setting up an OpenStack provider
account. Thus I cheated and used the Rails console for now, talking to
Deltacloud directly. And with that, basic Deltacloud API operations work
-- I can list images and instances, for example.
Complicating things, the credentials we need are semi-variable. If you
are running Keystone, we need to collect your username, password, and
tenant name. If you're not running Keystone, we don't need your tenant
name. (OpenStack also allows authentication via key + secret key, but
Deltacloud does not presently support this.) I'm not sure what the most
elegant solution is here just yet, nor if there's a good way to proceed
without provider-specific logic. As far as Deltacloud is concerned, the
tenant name is just added to the username, so we could do the same,
except it might not be particularly clear to end users.
I attempted to add Credential Definitions and add a provider account,
but that resulted in me receiving some exceptions about hardware
profiles.
I'd like to investigate further, but I think I'm fairly far afield from
my stated objective of investigating the viability of importing images.
It may be that I'm just around the bend from getting it working, but I'm
starting to think that the conclusion here is that, in its present form,
the answer to the task is "Not very viable at the moment." I'd be happy
to carry on, but only if people think it's a worthwhile endeavor before
Factory adds support for building for OpenStack.
-- Matt
[1] https://gist.github.com/2431166
[2] https://gist.github.com/2431171
11 years, 12 months
[PATCH 0/1] Cucumber test fix
by Jason Guiditta
Note that you need the newly pushed (to fedora 16) version of deltacloud
client to see this error. This patch should not cause 0.4 to fail for
those still on that older version (you need to upgrade soon, btw). As
Fedora 15 is closing in on EOL, you can always gem install this, or
grab the rpm from koji for version 0.5[1]. The exact test to run is in
the RM issue, but for your convenience in testing, it is:
cucumber features/provider_account.feature:17
[1] http://koji.fedoraproject.org/koji/packageinfo?packageID=11102
-j
11 years, 12 months
RFC: Rails i18n vs. gettext_i18n_rails
by Matt Wagner
Hi all,
As we look at sharing portions of our UI with the Katelo project, one
thing that has come up is that we use different internationalization
libraries. I wanted to share what I've found and solicit feedback on how
we should proceed.
Right now, we use the built-in i18n gem and Rails functionality. Katello
uses gettext_i18n_rails[1]. There are a few key differences that keep
them from being interchangeable. They have flat .po files and translate
text by running something like #{_("Some string to translate")}, versus
our #{t("some.string.with.dots")} with nested yaml.
I do think that their method has a few advantages:
* They use .po files, which should work seamlessly with Zanata. Our
* language files seem to take some manual effort to import to/export
* form Zanata.
* As flat files, their files are less likely to have duplication. We
* have pools.pools as "Pools", but "breadcrumbs.pools" is also "Pools".
* (As an arbitrary example.) This isn't the end of the world, though.
* We would gain similarity with our sister project, and could see
* benefits such as being able to borrow their Javascript i18n
* implementation (!).
Of course, there are also some disadvantages:
* Changing our libraries would be a lot of work that I really don't look
* forward to.
* gettext_i18n_rails is another dependency we'd have to add. (Though
* it's already packaged, since Katello has been using it for a long
* time.)
Right now, the only place this matters is in a couple copyright-type
strings in the footer, so it seems like an exorbitantly huge amount of
work just for that. But I think there's also benefit in switching to
something consistent. The question to me is whether it's worth the large
switching cost. Anyone want to weigh in?
-- Matt
[1] https://fedorahosted.org/katello/wiki/I18N
11 years, 12 months
[PATCH conductor] Adds 0 as a valid value for storage in Hardware Profile Properties
by Matt Wagner
While we have encouraged the use of "nil" to mean "No storage," some
providers may report 0 GB. Currently, the code rejects this as invalid.
Instead, we should treat this as valid. We still do not allow 0 for
memory or CPU count, since those would not result in a system that
can run -- but a system with no local storage may still be valid.
---
src/app/models/hardware_profile_property.rb | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/src/app/models/hardware_profile_property.rb b/src/app/models/hardware_profile_property.rb
index 4f883eb..2b5fd4e 100644
--- a/src/app/models/hardware_profile_property.rb
+++ b/src/app/models/hardware_profile_property.rb
@@ -63,7 +63,9 @@ class HardwareProfileProperty < ActiveRecord::Base
validates_presence_of :unit
validates_numericality_of :value, :greater_than => 0,
- :if => Proc.new{|p| (p.name == MEMORY or p.name == STORAGE or p.name == CPU) and p.value.present?}
+ :if => Proc.new{|p| (p.name == MEMORY or p.name == CPU) and p.value.present?}
+ validates_numericality_of :value, :greater_than_or_equal_to => 0,
+ :if => Proc.new{|p| p.name == STORAGE and p.value.present?}
validates_numericality_of :range_first, :greater_than => 0,
:if => Proc.new{|p| (p.name == MEMORY or p.name == STORAGE or p.name == CPU) and
--
1.7.7.6
11 years, 12 months