wakame-vdc-dcmgr 10.12.0 → 11.06.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/LICENSE +164 -201
  2. data/Rakefile +6 -11
  3. data/bin/collector +10 -24
  4. data/config/dcmgr.conf.example +18 -6
  5. data/config/initializers/isono.rb +7 -23
  6. data/config/initializers/sequel.rb +11 -2
  7. data/lib/dcmgr.rb +70 -11
  8. data/lib/dcmgr/cli/base.rb +74 -0
  9. data/lib/dcmgr/cli/errors.rb +59 -0
  10. data/lib/dcmgr/cli/group.rb +101 -0
  11. data/lib/dcmgr/cli/host.rb +101 -0
  12. data/lib/dcmgr/cli/image.rb +108 -0
  13. data/lib/dcmgr/cli/keypair.rb +72 -0
  14. data/lib/dcmgr/cli/network.rb +198 -0
  15. data/lib/dcmgr/cli/quota.rb +28 -0
  16. data/lib/dcmgr/cli/spec.rb +82 -0
  17. data/lib/dcmgr/cli/storage.rb +88 -0
  18. data/lib/dcmgr/cli/tag.rb +81 -0
  19. data/lib/dcmgr/cli/vlan.rb +53 -0
  20. data/lib/dcmgr/drivers/hypervisor.rb +33 -0
  21. data/lib/dcmgr/drivers/iijgio_storage.rb +37 -0
  22. data/lib/dcmgr/drivers/kvm.rb +118 -0
  23. data/lib/dcmgr/drivers/lxc.rb +167 -0
  24. data/lib/dcmgr/drivers/s3_storage.rb +39 -0
  25. data/lib/dcmgr/drivers/snapshot_storage.rb +51 -0
  26. data/lib/dcmgr/endpoints/core_api.rb +188 -324
  27. data/lib/dcmgr/endpoints/core_api_mock.rb +52 -3
  28. data/lib/dcmgr/endpoints/errors.rb +73 -32
  29. data/lib/dcmgr/endpoints/metadata.rb +163 -16
  30. data/lib/dcmgr/helpers/cli_helper.rb +1 -1
  31. data/lib/dcmgr/helpers/nic_helper.rb +35 -0
  32. data/lib/dcmgr/logger.rb +5 -1
  33. data/lib/dcmgr/messaging_client.rb +117 -0
  34. data/lib/dcmgr/models/account.rb +27 -3
  35. data/lib/dcmgr/models/base_new.rb +21 -7
  36. data/lib/dcmgr/models/host_pool.rb +27 -7
  37. data/lib/dcmgr/models/image.rb +31 -3
  38. data/lib/dcmgr/models/instance.rb +72 -23
  39. data/lib/dcmgr/models/instance_nic.rb +12 -2
  40. data/lib/dcmgr/models/instance_spec.rb +16 -0
  41. data/lib/dcmgr/models/ip_lease.rb +37 -1
  42. data/lib/dcmgr/models/netfilter_group.rb +7 -7
  43. data/lib/dcmgr/models/network.rb +42 -3
  44. data/lib/dcmgr/models/quota.rb +25 -0
  45. data/lib/dcmgr/models/request_log.rb +26 -11
  46. data/lib/dcmgr/models/ssh_key_pair.rb +14 -1
  47. data/lib/dcmgr/models/storage_pool.rb +19 -72
  48. data/lib/dcmgr/models/tag.rb +5 -0
  49. data/lib/dcmgr/models/vlan_lease.rb +8 -0
  50. data/lib/dcmgr/models/volume.rb +26 -8
  51. data/lib/dcmgr/models/volume_snapshot.rb +37 -0
  52. data/lib/dcmgr/node_modules/hva_collector.rb +56 -36
  53. data/lib/dcmgr/node_modules/instance_ha.rb +1 -1
  54. data/lib/dcmgr/node_modules/instance_monitor.rb +70 -0
  55. data/lib/dcmgr/node_modules/service_netfilter.rb +914 -0
  56. data/lib/dcmgr/node_modules/sta_collector.rb +7 -30
  57. data/lib/dcmgr/rack/request_logger.rb +60 -0
  58. data/lib/dcmgr/rack/run_initializer.rb +42 -0
  59. data/lib/dcmgr/rpc/hva_handler.rb +388 -0
  60. data/lib/dcmgr/rubygems.rb +7 -0
  61. data/lib/dcmgr/storage_service.rb +98 -0
  62. data/lib/dcmgr/tags.rb +2 -2
  63. data/lib/dcmgr/version.rb +8 -0
  64. data/lib/ext/time.rb +8 -0
  65. data/lib/sinatra/respond_to.rb +3 -0
  66. data/lib/sinatra/sequel_transaction.rb +20 -5
  67. data/web/api/config.ru +9 -13
  68. data/web/metadata/config.ru +10 -13
  69. metadata +162 -120
  70. data/lib/dcmgr/models/physical_host.rb +0 -67
  71. data/lib/dcmgr/web/base.rb +0 -21
@@ -3,12 +3,24 @@
3
3
  #------------------------
4
4
 
5
5
  # Database connection string
6
- database_url 'mysql://localhost/dcmgr?user=root'
6
+ database_url 'mysql://localhost/wakame_dcmgr?user=root'
7
7
  # AMQP broker to be connected.
8
- amqp_server_uri 'amqp://192.168.2.25/'
8
+ amqp_server_uri 'amqp://localhost/'
9
9
 
10
- # UUID for shared host pool.
11
- default_shared_host_pool 'tag-xxxxxxxx'
10
+ # UUID for shared host pool or group.
11
+ default_shared_host_pool 'tag-shhost'
12
12
 
13
- # UUID for shared network pool.
14
- default_shared_network_pool 'tag-xxxxxxxx'
13
+ # UUID for shared network pool or group.
14
+ default_shared_network_pool 'tag-shnet'
15
+
16
+ # UUID for shared storage pool or group.
17
+ default_shared_storage_pool 'tag-shstor'
18
+
19
+ # system wide limit size in MB for creating new volume.
20
+ # (not affect at cloning from snapshot)
21
+ create_volume_max_size 3000
22
+ create_volume_min_size 10
23
+
24
+ # Default account quota limit
25
+ account_instance_total_weight 100.0
26
+ account_volume_total_size 100000 # 100GB
@@ -5,34 +5,18 @@ require 'eventmachine'
5
5
 
6
6
  Signal.trap('EXIT') { EventMachine.stop }
7
7
 
8
- if defined?(PhusionPassenger)
9
- if PhusionPassenger::VERSION_STRING =~ /^3\.0\./
10
- blk = proc { |forked|
11
- if EventMachine.reactor_running?
12
- EventMachine.stop
13
- Dcmgr.class_eval {
14
- @messaging_client = nil
15
- }
16
- end
17
- Thread.new { EventMachine.epoll; EventMachine.run; }
18
- }
19
- else
20
- blk = proc {
21
- if EventMachine.reactor_running?
22
- EventMachine.stop
23
- Dcmgr.class_eval {
24
- @messaging_client = nil
25
- }
26
- end
27
- Thread.new { EventMachine.epoll; EventMachine.run; }
8
+ def restart_reactor_and_messaging_client
9
+ if EventMachine.reactor_running?
10
+ EventMachine.stop
11
+ Dcmgr.class_eval {
12
+ @messaging_client = nil
28
13
  }
29
14
  end
30
- PhusionPassenger.on_event(:starting_worker_process, &blk)
31
- else
32
- EventMachine.stop if EventMachine.reactor_running?
33
15
  Thread.new { EventMachine.epoll; EventMachine.run; }
34
16
  end
35
17
 
18
+ restart_reactor_and_messaging_client
19
+
36
20
  Dcmgr.class_eval {
37
21
  def self.messaging
38
22
  @messaging_client ||= Isono::MessagingClient.start(conf.amqp_server_uri) do
@@ -1,15 +1,21 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  require 'sequel'
4
- db = Sequel.connect(Dcmgr.conf.database_url)
4
+ if Sequel::DATABASES.first.nil?
5
+ db = Sequel.connect(Dcmgr.conf.database_url)
6
+ else
7
+ db = Sequel::DATABASES.first
8
+ end
9
+
5
10
  #require 'logger'
6
- #db.loggers << Logger.new(STDOUT)
11
+ #db.loggers << Logger.new(STDERR)
7
12
  if db.is_a?(Sequel::MySQL::Database)
8
13
  Sequel::MySQL.default_charset = 'utf8'
9
14
  Sequel::MySQL.default_collate = 'utf8_general_ci'
10
15
  Sequel::MySQL.default_engine = 'InnoDB'
11
16
 
12
17
  db << "SET AUTOCOMMIT=0"
18
+ Dcmgr::Models::BaseNew.default_row_lock_mode = nil
13
19
  end
14
20
 
15
21
  # Disable TEXT to Sequel::SQL::Blob translation.
@@ -19,3 +25,6 @@ end
19
25
  [249, 250, 251, 252].each { |v|
20
26
  Sequel::MySQL::MYSQL_TYPES.delete(v)
21
27
  }
28
+
29
+ # Set timezone to UTC
30
+ Sequel.default_timezone = :utc
data/lib/dcmgr.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
+ require 'dcmgr/version'
4
+ require 'ext/time'
5
+
3
6
  module Dcmgr
4
- VERSION='10.12.0'
5
7
 
6
8
  class << self
7
9
  def conf
@@ -28,8 +30,16 @@ module Dcmgr
28
30
  self
29
31
  end
30
32
 
31
- def run_initializers()
33
+ def run_initializers(*files)
32
34
  raise "Complete the configuration prior to run_initializers()." if @conf.nil?
35
+
36
+ @files ||= []
37
+ if files.length == 0
38
+ @files << "*"
39
+ else
40
+ @files = files
41
+ end
42
+
33
43
  initializer_hooks.each { |n|
34
44
  n.call
35
45
  }
@@ -42,29 +52,34 @@ module Dcmgr
42
52
  end
43
53
  @initializer_hooks
44
54
  end
45
-
46
55
  end
47
56
 
48
57
  initializer_hooks {
49
58
  Dcmgr.class_eval {
50
- DCMGR_ROOT = ENV['DCMGR_ROOT'] || File.expand_path('../../', __FILE__)
59
+ unless defined?(DCMGR_ROOT)
60
+ DCMGR_ROOT = ENV['DCMGR_ROOT'] || File.expand_path('../../', __FILE__)
61
+ end
51
62
  }
52
63
  }
53
64
 
54
65
  # Add conf/initializers/*.rb loader
55
66
  initializer_hooks {
56
67
  initializers_root = File.expand_path('config/initializers', DCMGR_ROOT)
57
-
58
- if File.directory?(initializers_root)
59
- Dir.glob("#{initializers_root}/*.rb") { |f|
60
- ::Kernel.load(f)
61
- }
62
- end
68
+
69
+ @files.each { |file|
70
+ if File.directory?(initializers_root)
71
+ Dir.glob("#{initializers_root}/#{file}.rb") { |f|
72
+ ::Kernel.load(f)
73
+ }
74
+ end
75
+ }
63
76
  }
64
77
 
65
78
  autoload :Logger, 'dcmgr/logger'
66
79
 
67
80
  module Models
81
+ class InvalidUUIDError < StandardError; end
82
+ class UUIDPrefixDuplication < StandardError; end
68
83
  autoload :Base, 'dcmgr/models/base'
69
84
 
70
85
  CREATE_TABLE_CLASSES=[:Account,:Tag,:TagMapping,:FrontendSystem,
@@ -74,7 +89,7 @@ module Dcmgr
74
89
  :InstanceNetfilterGroup,
75
90
  :InstanceSpec, :InstanceNic, :Network, :IpLease,
76
91
  :SshKeyPair, :History, :HostnameLease, :MacLease,
77
- :VlanLease,
92
+ :VlanLease, :Quota
78
93
  ].freeze
79
94
  autoload :BaseNew, 'dcmgr/models/base_new'
80
95
  autoload :Account, 'dcmgr/models/account'
@@ -101,6 +116,7 @@ module Dcmgr
101
116
  autoload :HostnameLease, 'dcmgr/models/hostname_lease'
102
117
  autoload :MacLease, 'dcmgr/models/mac_lease'
103
118
  autoload :VlanLease, 'dcmgr/models/vlan_lease'
119
+ autoload :Quota, 'dcmgr/models/quota'
104
120
  end
105
121
 
106
122
  module Endpoints
@@ -112,6 +128,8 @@ module Dcmgr
112
128
  autoload :StaCollector, 'dcmgr/node_modules/sta_collector'
113
129
  autoload :HvaCollector, 'dcmgr/node_modules/hva_collector'
114
130
  autoload :InstanceHA, 'dcmgr/node_modules/instance_ha'
131
+ autoload :ServiceNetfilter, 'dcmgr/node_modules/service_netfilter'
132
+ autoload :InstanceMonitor, 'dcmgr/node_modules/instance_monitor'
115
133
  end
116
134
 
117
135
  module Stm
@@ -122,7 +140,48 @@ module Dcmgr
122
140
 
123
141
  module Helpers
124
142
  autoload :CliHelper, 'dcmgr/helpers/cli_helper'
143
+ autoload :NicHelper, 'dcmgr/helpers/nic_helper'
125
144
  end
126
145
 
127
146
  autoload :Tags, 'dcmgr/tags'
147
+
148
+ module Cli
149
+ require 'dcmgr/cli/errors'
150
+
151
+ autoload :Base, 'dcmgr/cli/base'
152
+ autoload :Network, 'dcmgr/cli/network'
153
+ autoload :Host, 'dcmgr/cli/host'
154
+ autoload :Storage, 'dcmgr/cli/storage'
155
+ autoload :AccountCli, 'dcmgr/cli/account'
156
+ autoload :UsersCli, 'dcmgr/cli/user'
157
+ autoload :Vlan, 'dcmgr/cli/vlan'
158
+ autoload :Image, 'dcmgr/cli/image'
159
+ autoload :KeyPair, 'dcmgr/cli/keypair'
160
+ autoload :Group, 'dcmgr/cli/group'
161
+ autoload :Spec, 'dcmgr/cli/spec'
162
+ autoload :Tag, 'dcmgr/cli/tag'
163
+ autoload :Quota, 'dcmgr/cli/quota'
164
+ end
165
+
166
+ module Rpc
167
+ autoload :HvaHandler, 'dcmgr/rpc/hva_handler'
168
+ autoload :KvmHelper, 'dcmgr/rpc/hva_handler'
169
+ end
170
+
171
+ # namespace for custom Rack HTTP middleware.
172
+ module Rack
173
+ autoload :RequestLogger, 'dcmgr/rack/request_logger'
174
+ autoload :RunInitializer, 'dcmgr/rack/run_initializer'
175
+ end
176
+
177
+ module Drivers
178
+ autoload :SnapshotStorage, 'dcmgr/drivers/snapshot_storage'
179
+ autoload :S3Storage, 'dcmgr/drivers/s3_storage'
180
+ autoload :IIJGIOStorage, 'dcmgr/drivers/iijgio_storage'
181
+ autoload :Hypervisor, 'dcmgr/drivers/hypervisor'
182
+ autoload :Kvm , 'dcmgr/drivers/kvm'
183
+ autoload :Lxc , 'dcmgr/drivers/lxc'
184
+ end
185
+
186
+ autoload :StorageService, 'dcmgr/storage_service'
128
187
  end
@@ -0,0 +1,74 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'thor'
4
+
5
+ module Dcmgr::Cli
6
+ class Base < Thor
7
+ protected
8
+ def self.basename
9
+ "#{super()} #{namespace}"
10
+ end
11
+
12
+ no_tasks {
13
+ public
14
+ # add before/after task hook.
15
+ def invoke_task(task, *args)
16
+ Dcmgr::Models::BaseNew.db.transaction do
17
+ before_task
18
+ super(task, *args)
19
+ after_task
20
+ end
21
+ end
22
+
23
+ protected
24
+ def before_task
25
+ end
26
+
27
+ def after_task
28
+ end
29
+ }
30
+
31
+ def add(model,options)
32
+ raise ArgumentError unless options.is_a? Hash
33
+ #TODO: Make this check a little tighter by checking that the model is either from the wakame backend or frontend
34
+ #UnknownModelError.raise(model) unless model < Dcmgr::Models::BaseNew
35
+ UnknownModelError.raise(model) unless model < Sequel::Model
36
+
37
+ fields = options.dup
38
+
39
+ if fields.has_key?("uuid") || fields.has_key?(:uuid)
40
+ fields[:uuid] = model.trim_uuid(fields[:uuid]) if model.check_uuid_format(fields[:uuid])
41
+ Error.raise("UUID syntax invalid: #{fields[:uuid]}",100) unless model.check_trimmed_uuid_format(fields[:uuid])
42
+ end
43
+
44
+ #Create database fields
45
+ new_record = model.create(fields)
46
+
47
+ #Return uuid if there is one
48
+ new_record.canonical_uuid #if model.respond_to? "canonical_uuid"
49
+ end
50
+
51
+ def del(model,uuid)
52
+ #UnknownModelError.raise(model) unless model < Dcmgr::Models::BaseNew
53
+ UnknownModelError.raise(model) unless model < Sequel::Model
54
+ to_delete = model[uuid] || UnknownUUIDError.raise(uuid)
55
+ to_delete.destroy
56
+ end
57
+
58
+ def modify(model,uuid,fields)
59
+ #UnknownModelError.raise(model) unless model < Dcmgr::Models::BaseNew
60
+ UnknownModelError.raise(model) unless model < Sequel::Model
61
+ raise ArgumentError unless fields.is_a? Hash
62
+ to_modify = model[uuid] || UnknownUUIDError.raise(uuid)
63
+
64
+ #Use a copy of the fields hash so this method can work with frozen hashes
65
+ fields_nonil = fields.merge({})
66
+ #Don't update empty fields
67
+ fields_nonil.delete_if {|key,value| value.nil?}
68
+
69
+ to_modify.set(fields_nonil)
70
+ to_modify.updated_at = Time.now.utc.iso8601 if to_modify.with_timestamps?
71
+ to_modify.save_changes
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,59 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Dcmgr::Cli
4
+ class Error < StandardError
5
+ attr_reader :exit_code
6
+ def initialize(msg, exit_code=1)
7
+ super(msg)
8
+ @exit_code = exit_code
9
+ end
10
+
11
+ def self.raise(msg, exit_code)
12
+ Kernel.raise(if msg.is_a?(self)
13
+ msg
14
+ else
15
+ self.new(msg, exit_code)
16
+ end)
17
+ end
18
+ end
19
+
20
+ class UnknownUUIDError < Error
21
+ def initialize(uuid,exit_code=100)
22
+ super("Unknown UUID: '#{uuid}'.")
23
+ end
24
+
25
+ def self.raise(uuid,exit_code=100)
26
+ super
27
+ end
28
+ end
29
+
30
+ class UnsupportedArchError < Error
31
+ def initialize(arch,exit_code=100)
32
+ super("Unsupported arch type: '#{arch}'.")
33
+ end
34
+
35
+ def self.raise(arch,exit_code=100)
36
+ super
37
+ end
38
+ end
39
+
40
+ class UnknownModelError < Error
41
+ def initialize(model,exit_code=100)
42
+ super("Not a sequel model: '#{model}'.")
43
+ end
44
+
45
+ def self.raise(model,exit_code=100)
46
+ super
47
+ end
48
+ end
49
+
50
+ class UnsupportedHypervisorError < Error
51
+ def initialize(arch,exit_code=100)
52
+ super("Unsupported hypervisor type: '#{arch}'.")
53
+ end
54
+
55
+ def self.raise(arch,exit_code=100)
56
+ super
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,101 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Dcmgr::Cli
4
+ class Group < Base
5
+ namespace :group
6
+ M = Dcmgr::Models
7
+
8
+ desc "add [options]", "Add a new security group"
9
+ method_option :uuid, :type => :string, :aliases => "-u", :desc => "The UUID for the new security group."
10
+ method_option :account_id, :type => :string, :aliases => "-a", :desc => "The UUID of the account this security group belongs to.", :required => true
11
+ method_option :name, :type => :string, :aliases => "-n", :desc => "The name for this security group.", :required => true
12
+ method_option :description, :type => :string, :aliases => "-d", :desc => "The description for this new security group."
13
+ def add
14
+ UnknownUUIDError.raise(options[:account_id]) if M::Account[options[:account_id]].nil?
15
+
16
+ puts super(M::NetfilterGroup,options)
17
+ end
18
+
19
+ desc "del UUID", "Delete a security group"
20
+ def del(uuid)
21
+ super(M::NetfilterGroup,uuid)
22
+ end
23
+
24
+ desc "show [UUID]", "Show security group(s)"
25
+ def show(uuid=nil)
26
+ if uuid
27
+ group = M::NetfilterGroup[uuid] || UnknownUUIDError.raise(uuid)
28
+ puts ERB.new(<<__END, nil, '-').result(binding)
29
+ Group UUID:
30
+ <%= group.canonical_uuid %>
31
+ Account id:
32
+ <%= group.account_id %>
33
+ <%- if group.description -%>
34
+ Description:
35
+ <%= group.description %>
36
+ <%- end -%>
37
+ <%- unless group.netfilter_rules.empty? -%>
38
+ Rules:
39
+ <%- group.netfilter_rules.each { |rule| -%>
40
+ <%= rule.permission %>
41
+ <%- } -%>
42
+ <%- end -%>
43
+ __END
44
+ else
45
+ puts ERB.new(<<__END, nil, '-').result(binding)
46
+ <%- M::NetfilterGroup.all { |row| -%>
47
+ <%= row.canonical_uuid %>\t<%= row.account_id %>\t<%= row.name %>
48
+ <%- } -%>
49
+ __END
50
+ end
51
+ end
52
+
53
+ desc "modify UUID [options]", "Modify an existing security group"
54
+ method_option :account_id, :type => :string, :aliases => "-a", :desc => "The UUID of the account this security group belongs to."
55
+ method_option :name, :type => :string, :aliases => "-n", :desc => "The name for this security group."
56
+ method_option :description, :type => :string, :aliases => "-d", :desc => "The description for this new security group."
57
+ def modify(uuid)
58
+ UnknownUUIDError.raise(options[:account_id]) if options[:account_id] && M::Account[options[:account_id]].nil?
59
+ super(M::NetfilterGroup,uuid,options)
60
+ end
61
+
62
+ desc "apply UUID [options]", "Apply a security group to an instance"
63
+ method_option :instance, :type => :string, :aliases => "-i", :required => :true, :desc => "The instance to apply the group to"
64
+ def apply(uuid)
65
+ group = M::NetfilterGroup[uuid] || UnknownUUIDError.raise(uuid)
66
+ instance = M::Instance[options[:instance]] || UnknownUUIDError.raise(options[:instance])
67
+ Error.raise("Group #{uuid} is already applied to instance #{options[:instance]}.",100) if group.instances.member?(instance)
68
+ group.add_instance(instance)
69
+ end
70
+
71
+ desc "remove UUID [options]", "Remove a security group from an instance"
72
+ method_option :instance, :type => :string, :aliases => "-i", :required => :true, :desc => "The instance to remove the group from"
73
+ def remove(uuid)
74
+ group = M::NetfilterGroup[uuid] || UnknownUUIDError.raise(uuid)
75
+ instance = M::Instance[options[:instance]] || UnknownUUIDError.raise(options[:instance])
76
+ Error.raise("Group #{uuid} is not applied to instance #{options[:instance]}.",100) unless group.instances.member?(instance)
77
+ group.remove_instance(instance)
78
+ end
79
+
80
+ desc "addrule UUID [options]", "Add a rule to a security group"
81
+ method_option :rule, :type => :string, :aliases => "-r", :desc => "The new rule to be added"
82
+ def addrule(g_uuid)
83
+ UnknownUUIDError.raise(g_uuid) if M::NetfilterGroup[g_uuid].nil?
84
+
85
+ #TODO: check rule syntax
86
+ new_rule = M::NetfilterRule.new(:permission => options[:rule])
87
+ new_rule.netfilter_group = M::NetfilterGroup[g_uuid]
88
+ new_rule.save
89
+ end
90
+
91
+ desc "delrule UUID [options]", "Delete a rule from a security group"
92
+ method_option :rule, :type => :string, :aliases => "-r", :desc => "The rule to be deleted."
93
+ def delrule(g_uuid)
94
+ UnknownUUIDError.raise(g_uuid) if M::NetfilterGroup[g_uuid].nil?
95
+ rule = M::NetfilterRule.find(:netfilter_group_id => M::NetfilterGroup[g_uuid].id,:permission => options[:rule])
96
+ Error.raise("Group '#{g_uuid}' does not contain rule '#{options[:rule]}'.",100) if rule.nil?
97
+
98
+ rule.destroy
99
+ end
100
+ end
101
+ end