https://www.aeolusproject.org/redmine/issues/3273
REST api to turn the rufus scheduler on (:new) and off (:destroy) and to check its status (:index).
A singleton class JobScheduler encapsulates the rufus scheduler so that only a single scheduler instance runs at any time.
Only users who have the ROLE view and modify permissions may control the scheduler. --- aeolus-conductor.spec.in | 2 + src/app/controllers/job_scheduler_controller.rb | 45 ++++++++++ src/app/views/job_scheduler/destroy.xml.haml | 2 + src/app/views/job_scheduler/index.xml.haml | 2 + src/app/views/job_scheduler/new.xml.haml | 2 + src/config/routes.rb | 2 + src/lib/job_scheduler.rb | 93 ++++++++++++++++++++ .../controllers/job_scheduler_controller_spec.rb | 84 ++++++++++++++++++ 8 files changed, 232 insertions(+), 0 deletions(-) create mode 100644 src/app/controllers/job_scheduler_controller.rb create mode 100644 src/app/views/job_scheduler/destroy.xml.haml create mode 100644 src/app/views/job_scheduler/index.xml.haml create mode 100644 src/app/views/job_scheduler/new.xml.haml create mode 100644 src/lib/job_scheduler.rb create mode 100644 src/spec/controllers/job_scheduler_controller_spec.rb
diff --git a/aeolus-conductor.spec.in b/aeolus-conductor.spec.in index a40d517..6cca32f 100644 --- a/aeolus-conductor.spec.in +++ b/aeolus-conductor.spec.in @@ -47,6 +47,7 @@ Requires: rubygem(oauth) Requires: rubygem(rake) Requires: rubygem(delayed_job) >= 2.1.4 Requires: rubygem(paranoia) +Requires: rubygem(rufus-scheduler) Requires: postgresql Requires: postgresql-server Requires: system-logos @@ -189,6 +190,7 @@ haml="app/views/hardware_profiles app/views/realm_mappings \ app/views/deployables app/views/catalogs \ app/views/provider_types app/views/api/images \ app/views/config_servers app/views/provider_realms \ + app/views/job_scheduler \ app/views/logs \ app/views/api/hooks \ app/views/api \ diff --git a/src/app/controllers/job_scheduler_controller.rb b/src/app/controllers/job_scheduler_controller.rb new file mode 100644 index 0000000..b1a2701 --- /dev/null +++ b/src/app/controllers/job_scheduler_controller.rb @@ -0,0 +1,45 @@ +# +# Copyright 2012 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'job_scheduler' + +class JobSchedulerController < ApplicationController + before_filter :require_user_api + respond_to :xml + layout :false + + # GET /jobscheduler + # GET /jobscheduler.xml + def index + require_privilege(Privilege::PERM_VIEW) + respond_with(JobScheduler.activated?) + end + + # GET /jobscheduler/new + # GET /jobscheduler/new.xml + def new + require_privilege(Privilege::PERM_SET) + respond_with(JobScheduler.start) + end + + # DELETE /jobscheduler/1 + # DELETE /jobscheduler/1.xml + def destroy + require_privilege(Privilege::PERM_SET) + respond_with(JobScheduler.stop) + end + +end diff --git a/src/app/views/job_scheduler/destroy.xml.haml b/src/app/views/job_scheduler/destroy.xml.haml new file mode 100644 index 0000000..18e796d --- /dev/null +++ b/src/app/views/job_scheduler/destroy.xml.haml @@ -0,0 +1,2 @@ +!!! XML +%activated=JobScheduler.activated? \ No newline at end of file diff --git a/src/app/views/job_scheduler/index.xml.haml b/src/app/views/job_scheduler/index.xml.haml new file mode 100644 index 0000000..b5c75d6 --- /dev/null +++ b/src/app/views/job_scheduler/index.xml.haml @@ -0,0 +1,2 @@ +!!! XML +%activated=JobScheduler.activated? diff --git a/src/app/views/job_scheduler/new.xml.haml b/src/app/views/job_scheduler/new.xml.haml new file mode 100644 index 0000000..18e796d --- /dev/null +++ b/src/app/views/job_scheduler/new.xml.haml @@ -0,0 +1,2 @@ +!!! XML +%activated=JobScheduler.activated? \ No newline at end of file diff --git a/src/config/routes.rb b/src/config/routes.rb index 265f752..518b013 100644 --- a/src/config/routes.rb +++ b/src/config/routes.rb @@ -238,6 +238,8 @@ Conductor::Application.routes.draw do resources :provider_images end
+ resources :job_scheduler, :only => [:index, :new, :destroy] + get 'api', :controller => 'api/entrypoint', :action => 'index' namespace :api do resources :images do diff --git a/src/lib/job_scheduler.rb b/src/lib/job_scheduler.rb new file mode 100644 index 0000000..f30ff2d --- /dev/null +++ b/src/lib/job_scheduler.rb @@ -0,0 +1,93 @@ +# +# Copyright 2012 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'curb' + +class JobScheduler + + @@scheduler = nil + + def self.initialize + self.start + end + + def self.start + if @@scheduler.nil? + @@scheduler = Rufus::Scheduler::PlainScheduler.start_new + schedule_tasks(@@scheduler) + end + end + + def self.stop + if @@scheduler + @@scheduler.stop + @@scheduler = nil + end + end + + def self.activated? + if @@scheduler + true + else + false + end + end + + def self.status_via_http(login, password, hostname) + curl = self.curl_request(login, password) + curl.url = job_scheduler_url(hostname, '') + curl.http_get + curl.body_str.to_s + end + + def self.start_via_http(login, password, hostname) + curl = self.curl_request(login, password) + curl.url = job_scheduler_url(hostname, 'new') + curl.http_get + curl.body_str.to_s + end + + def self.stop_via_http(login, password, hostname) + curl = self.curl_request(login, password) + curl.url = job_scheduler_url(hostname, '1') + curl.http_delete + curl.body_str.to_s + end + + private + + def self.schedule_tasks(scheduler) + scheduler.every("1m") do + puts "job scheduler is running" + end + end + + def self.job_scheduler_url(hostname, suffix) + # TODO: is there a better way to generate this url? + url = "https://#%7Bhostname%7D/conductor/#%7BJobSchedulerController.controller_path..." + end + + def self.curl_request(login, password) + curl = Curl::Easy.new + curl.username = login + curl.password = password + curl.headers['HTTP_ACCEPT'] = 'application/xml' + curl.headers['CONTENT_TYPE'] = 'application/xml' + curl.http_auth_types = :basic + return curl + end + +end diff --git a/src/spec/controllers/job_scheduler_controller_spec.rb b/src/spec/controllers/job_scheduler_controller_spec.rb new file mode 100644 index 0000000..75726ec --- /dev/null +++ b/src/spec/controllers/job_scheduler_controller_spec.rb @@ -0,0 +1,84 @@ +# +# Copyright 2012 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'spec_helper' + +describe JobSchedulerController do + render_views + + context "when authenticated as admin" do + + before(:each) do + @admin_permission = FactoryGirl.create :admin_permission + @admin = @admin_permission.user + mock_warden(@admin) + end + + describe "scheduler controls" do + + it "scheduler status from 'GET index'" do + send_and_accept_xml + get :index + response.should be_success + response.body.strip.should == "<activated>false</activated>" + end + + it "turn on scheduler by 'GET new'" do + send_and_accept_xml + get :new + response.should be_success + response.body.strip.should == "<activated>true</activated>" + end + + it "turn off scheduler by 'POST destroy'" do + send_and_accept_xml + post :destroy, :id => 1 + response.should be_success + response.body.strip.should == "<activated>false</activated>" + end + + it "turn scheduler on and then off" do + send_and_accept_xml + get :index + response.should be_success + response.body.strip.should == "<activated>false</activated>" + get :new + response.should be_success + response.body.strip.should == "<activated>true</activated>" + post :destroy, :id => 1 + response.should be_success + response.body.strip.should == "<activated>false</activated>" + end + end + end + + context "when not authenticated as admin" do + + before(:each) do + @non_admin_permission = FactoryGirl.create :pool_user_permission + @user = @non_admin_permission.user + mock_warden(@user) + end + + it "requests should be denied" do + send_and_accept_xml + get :index + response.should_not be_success + end + + end + +end