diff --git a/app/controllers/admin/jobs_controller.rb b/app/controllers/admin/jobs_controller.rb
new file mode 100644
index 0000000..16b14a7
--- /dev/null
+++ b/app/controllers/admin/jobs_controller.rb
@@ -0,0 +1,20 @@
+module Admin
+ class JobsController < ApplicationController
+ before_action :authenticate_user!
+ before_action :admin_only!
+
+ # GET /jobs
+ def index
+ @pending = Job.pending
+ @completed = Job.completed.order('id desc')
+ end
+
+ # DELETE /jobs/1
+ def destroy
+ @job = Job.find(params[:id])
+ @job.destroy
+ redirect_to admin_jobs_url, notice: "#{@job.id} was successfully destroyed."
+ end
+
+ end
+end
diff --git a/app/models/domain.rb b/app/models/domain.rb
index 0fd518f..ae700f9 100644
--- a/app/models/domain.rb
+++ b/app/models/domain.rb
@@ -1,124 +1,125 @@
class Domain < ActiveRecord::Base
class NotAChild < StandardError; end
self.inheritance_column = :nx
# List all supported domain types.
def self.domain_types
[
'NATIVE',
'MASTER',
'SLAVE',
]
end
# List domain types that can be created.
def self.allowed_domain_types
domain_types - WebDNS.settings[:prohibit_domain_types]
end
belongs_to :group
+ has_many :jobs
has_many :records
# BUG in bump_serial_trigger
has_one :soa, -> { unscope(where: :type) }, class_name: SOA
validates :group_id, presence: true
validates :name, uniqueness: true, presence: true
validates :type, presence: true, inclusion: { in: domain_types }
validates :master, presence: true, ipv4: true, if: :slave?
after_create :generate_soa
after_create :generate_ns
attr_writer :serial_strategy
# Get the zone's serial strategy.
#
# Returns one of the supported serial strategies.
def serial_strategy
@serial_strategy ||= WebDNS.settings[:serial_strategy]
end
# Returns true if this a reverse zone.
def reverse?
name.end_with?('.in-addr.arpa') || name.end_with?('.ip6.arpa')
end
# Returns true if this a ENUM zone.
def enum?
name.end_with?('.e164.arpa')
end
# Returns true if this is a slave zone.
def slave?
type == 'SLAVE'
end
# Compute subnet for reverse records
def subnet
return if not reverse?
if name.end_with?('.in-addr.arpa')
subnet_v4
elsif name.end_with?('.ip6.arpa')
subnet_v6
end
end
def self.replace_ds(parent, child, records)
parent = find_by_name!(parent)
fail NotAChild if not child.end_with?(parent.name)
existing = parent.records.where(name: child, type: 'DS')
recs = records.map { |rec| DS.new(domain: parent, name: child, content: rec) }
ActiveRecord::Base.transaction do
existing.destroy_all
recs.map(&:save!)
end
end
private
def subnet_v4
# get ip octets (remove .in-addr.arpa)
octets = name.split('.')[0...-2].reverse
return if octets.any? { |_| false }
mask = 8 * octets.size
octets += [0, 0, 0, 0]
ip = IPAddr.new octets[0, 4].join('.')
[ip, mask].join('/')
end
def subnet_v6
nibbles = name.split('.')[0...-2].reverse
return if nibbles.any? { |_| false }
mask = 4 * nibbles.size
nibbles += [0] * 32
ip = IPAddr.new nibbles[0, 32].in_groups_of(4).map(&:join).join(':')
[ip, mask].join('/')
end
# Hooks
def generate_soa
soa_record = SOA.new(domain: self)
soa_record.save!
end
def generate_ns
return if slave?
return if WebDNS.settings[:default_ns].empty?
WebDNS.settings[:default_ns].each { |ns|
Record.find_or_create_by!(domain: self, type: 'NS', name: '', content: ns)
}
end
end
diff --git a/app/models/job.rb b/app/models/job.rb
new file mode 100644
index 0000000..d439ba8
--- /dev/null
+++ b/app/models/job.rb
@@ -0,0 +1,70 @@
+class Job < ActiveRecord::Base
+ belongs_to :domain
+
+ scope :pending, -> { where(status: 0) }
+ scope :completed, -> { where(status: [1, 2]) }
+
+ def failed?
+ status == 2
+ end
+
+ class << self
+ def add_domain(domain)
+ ActiveRecord::Base.transaction do
+ jobs_for_domain(domain, :add_domain)
+
+ trigger_event(domain, :installed)
+ end
+ end
+
+ def remove_domain(domain)
+ ActiveRecord::Base.transaction do
+ jobs_for_domain(domain, :remove_domain)
+
+ trigger_event(domain, :cleaned_up)
+ end
+ end
+
+ def convert_to_dnssec(domain)
+ ActiveRecord::Base.transaction do
+ jobs_for_domain(domain,
+ :opendnssec_add,
+ :bind_convert_to_dnssec,
+ :wait_for_ready_to_push_ds,
+ :publish_ds,
+ :wait_for_active)
+
+ trigger_event(domain, :converted)
+ end
+ end
+
+ def convert_to_plain(domain)
+ ActiveRecord::Base.transaction do
+ jobs_for_domain(domain,
+ :remove_domain,
+ :add_domain,
+ :opendnssec_remove)
+
+ trigger_event(domain, :converted)
+ end
+ end
+
+ private
+
+ def trigger_event(domain, event)
+ job_for_domain(domain, :trigger_event, event: event)
+ end
+
+ def jobs_for_domain(domain, *job_names)
+ job_names.each { |job_name| job_for_domain(domain, job_name) }
+ end
+
+ def job_for_domain(domain, job_name, args = {})
+ args = { zone: domain.name }.merge!(args)
+
+ create!(domain: domain, job_type: job_name, args: args.to_json)
+ end
+
+ end
+
+end
diff --git a/app/views/admin/jobs/index.html.erb b/app/views/admin/jobs/index.html.erb
new file mode 100644
index 0000000..e2e7be6
--- /dev/null
+++ b/app/views/admin/jobs/index.html.erb
@@ -0,0 +1,56 @@
+
Pending Jobs
+
+
+
+ Id |
+ Domain |
+ Type |
+ Args |
+ Retries |
+ Controls |
+
+
+
+
+ <% @pending.each do |job| %>
+
+ <%= job.id %> |
+ <%= job.domain.try(:name) %> |
+ <%= job.job_type %> |
+ <%= job.args %> |
+ <%= job.retries %> |
+ <%= link_to_destroy admin_job_path(job), method: :delete, data: { confirm: 'Are you sure?' } %> |
+
+ <% end %>
+
+
+
+Completed Jobs
+
+
+
+ Id |
+ Created At |
+ Domain |
+ Type |
+ Args |
+ Retries |
+ Controls |
+
+
+
+
+ <% @completed.each do |job| %>
+
+ <%= job.id %> |
+ <%= job.created_at %> |
+ <%= job.domain.try(:name) %> |
+ <%= job.job_type %> |
+ <%= job.args %> |
+ <%= job.retries %> |
+ <%= link_to_destroy admin_job_path(job), method: :delete, data: { confirm: 'Are you sure?' } %> |
+
+ <% end %>
+
+
+
diff --git a/app/views/shared/_nav.html.erb b/app/views/shared/_nav.html.erb
index 6dd36cb..b8e879e 100644
--- a/app/views/shared/_nav.html.erb
+++ b/app/views/shared/_nav.html.erb
@@ -1,38 +1,46 @@
diff --git a/config/routes.rb b/config/routes.rb
index 828eef4..badf31b 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,42 +1,43 @@
Rails.application.routes.draw do
# Override devise user removal
devise_scope :users do
delete :users, to: redirect('/')
end
devise_for :users
root to: redirect('/domains')
resources :groups, only: [:show] do
get :search_member,
to: 'groups#search_member', on: :member
post :members,
to: 'groups#create_member', as: :create_member, on: :member
delete 'member/:user_id',
to: 'groups#destroy_member', as: :destroy_member, on: :member
end
resources :domains do
resources :records, except: [:index, :show] do
# Reuse records#update instead of introducing new controller actions
#
# rubocop:disable Style/AlignHash
put :disable, to: 'records#update', on: :member,
defaults: { record: { disabled: true } }
put :enable, to: 'records#update', on: :member,
defaults: { record: { disabled: false } }
# rubocop:enable Style/AlignHash
end
end
# Admin
namespace :admin do
root to: redirect('/admin/groups')
resources :groups, except: [:show]
+ resources :jobs, only: [:index, :destroy]
end
# Private
put 'private/replace_ds', to: 'private#replace_ds'
end
diff --git a/db/migrate/20151207194729_create_jobs.rb b/db/migrate/20151207194729_create_jobs.rb
new file mode 100644
index 0000000..48d34a5
--- /dev/null
+++ b/db/migrate/20151207194729_create_jobs.rb
@@ -0,0 +1,13 @@
+class CreateJobs < ActiveRecord::Migration
+ def change
+ create_table :jobs do |t|
+ t.string :job_type, null: false
+ t.references :domain, index: true
+ t.string :args, null: false
+ t.integer :status, default: 0, null: false
+ t.integer :retries, default: 0, null: false
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/structure.sql b/db/structure.sql
index 3370498..205fa60 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -1,258 +1,281 @@
-- MySQL dump 10.15 Distrib 10.0.20-MariaDB, for debian-linux-gnu (x86_64)
--
-- Host: localhost Database: webns
-- ------------------------------------------------------
-- Server version 10.0.20-MariaDB-3
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table `comments`
--
DROP TABLE IF EXISTS `comments`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `comments` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`domain_id` int(11) NOT NULL,
`name` varchar(255) NOT NULL,
`type` varchar(10) NOT NULL,
`modified_at` int(11) NOT NULL,
`account` varchar(40) NOT NULL,
`comment` mediumtext NOT NULL,
PRIMARY KEY (`id`),
KEY `comments_domain_id_idx` (`domain_id`),
KEY `comments_name_type_idx` (`name`,`type`),
KEY `comments_order_idx` (`domain_id`,`modified_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `cryptokeys`
--
DROP TABLE IF EXISTS `cryptokeys`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `cryptokeys` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`domain_id` int(11) NOT NULL,
`flags` int(11) NOT NULL,
`active` tinyint(1) DEFAULT NULL,
`content` text,
PRIMARY KEY (`id`),
KEY `domainidindex` (`domain_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `domainmetadata`
--
DROP TABLE IF EXISTS `domainmetadata`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `domainmetadata` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`domain_id` int(11) NOT NULL,
`kind` varchar(32) DEFAULT NULL,
`content` text,
PRIMARY KEY (`id`),
KEY `domainmetadata_idx` (`domain_id`,`kind`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `domains`
--
DROP TABLE IF EXISTS `domains`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `domains` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`master` varchar(128) DEFAULT NULL,
`last_check` int(11) DEFAULT NULL,
`type` varchar(6) NOT NULL,
`notified_serial` int(11) DEFAULT NULL,
`account` varchar(40) DEFAULT NULL,
`group_id` int(11) DEFAULT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name_index` (`name`),
KEY `index_domains_on_group_id` (`group_id`)
) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `groups`
--
DROP TABLE IF EXISTS `groups`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `groups` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`disabled` tinyint(1) DEFAULT '0',
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `index_groups_on_name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
+-- Table structure for table `jobs`
+--
+
+DROP TABLE IF EXISTS `jobs`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `jobs` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `job_type` varchar(255) NOT NULL,
+ `domain_id` int(11) DEFAULT NULL,
+ `args` varchar(255) NOT NULL,
+ `status` int(11) NOT NULL DEFAULT '0',
+ `retries` int(11) NOT NULL DEFAULT '0',
+ `created_at` datetime DEFAULT NULL,
+ `updated_at` datetime DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `index_jobs_on_domain_id` (`domain_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
-- Table structure for table `memberships`
--
DROP TABLE IF EXISTS `memberships`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `memberships` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`group_id` int(11) DEFAULT NULL,
`user_id` int(11) DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_memberships_on_group_id` (`group_id`),
KEY `index_memberships_on_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `records`
--
DROP TABLE IF EXISTS `records`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `records` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`domain_id` int(11) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`type` varchar(10) DEFAULT NULL,
`content` mediumtext,
`ttl` int(11) DEFAULT NULL,
`prio` int(11) DEFAULT NULL,
`change_date` int(11) DEFAULT NULL,
`disabled` tinyint(1) DEFAULT '0',
`ordername` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
`auth` tinyint(1) DEFAULT '1',
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `nametype_index` (`name`,`type`),
KEY `domain_id` (`domain_id`),
KEY `recordorder` (`domain_id`,`ordername`),
CONSTRAINT `records_ibfk_1` FOREIGN KEY (`domain_id`) REFERENCES `domains` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `schema_migrations`
--
DROP TABLE IF EXISTS `schema_migrations`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `schema_migrations` (
`version` varchar(255) NOT NULL,
UNIQUE KEY `unique_schema_migrations` (`version`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `supermasters`
--
DROP TABLE IF EXISTS `supermasters`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `supermasters` (
`ip` varchar(64) NOT NULL,
`nameserver` varchar(255) NOT NULL,
`account` varchar(40) NOT NULL,
PRIMARY KEY (`ip`,`nameserver`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `tsigkeys`
--
DROP TABLE IF EXISTS `tsigkeys`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `tsigkeys` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`algorithm` varchar(50) DEFAULT NULL,
`secret` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `namealgoindex` (`name`,`algorithm`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `users`
--
DROP TABLE IF EXISTS `users`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`email` varchar(255) NOT NULL DEFAULT '',
`encrypted_password` varchar(255) NOT NULL DEFAULT '',
`reset_password_token` varchar(255) DEFAULT NULL,
`reset_password_sent_at` datetime DEFAULT NULL,
`remember_created_at` datetime DEFAULT NULL,
`sign_in_count` int(11) NOT NULL DEFAULT '0',
`current_sign_in_at` datetime DEFAULT NULL,
`last_sign_in_at` datetime DEFAULT NULL,
`current_sign_in_ip` varchar(255) DEFAULT NULL,
`last_sign_in_ip` varchar(255) DEFAULT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `index_users_on_email` (`email`),
UNIQUE KEY `index_users_on_reset_password_token` (`reset_password_token`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2015-11-08 12:57:51
INSERT INTO schema_migrations (version) VALUES ('20151028123326');
INSERT INTO schema_migrations (version) VALUES ('20151028123327');
INSERT INTO schema_migrations (version) VALUES ('20151031184819');
INSERT INTO schema_migrations (version) VALUES ('20151107182656');
INSERT INTO schema_migrations (version) VALUES ('20151108093333');
INSERT INTO schema_migrations (version) VALUES ('20151108105701');
+INSERT INTO schema_migrations (version) VALUES ('20151207194729');
+