Compare commits
17 commits
Author | SHA1 | Date | |
---|---|---|---|
b21f7c319d | |||
95cabef567 | |||
12639f766c | |||
59db51d2ed | |||
e9321d4de6 | |||
2255c784bc | |||
7e02cbbe84 | |||
18c0b6434a | |||
0282694a28 | |||
88e38eab7a | |||
10645196d1 | |||
6af5922217 | |||
0a0392d532 | |||
d25e1a9762 | |||
ad44237905 | |||
d51b56a67b | |||
0af010b358 |
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -8,6 +8,8 @@
|
||||||
/spec/reports/
|
/spec/reports/
|
||||||
/tmp/
|
/tmp/
|
||||||
|
|
||||||
|
/Gemfile.lock
|
||||||
|
|
||||||
# ---> Vim
|
# ---> Vim
|
||||||
# Swap
|
# Swap
|
||||||
[._]*.s[a-v][a-z]
|
[._]*.s[a-v][a-z]
|
||||||
|
|
1
Gemfile
1
Gemfile
|
@ -1,4 +1,5 @@
|
||||||
source "https://rubygems.org"
|
source "https://rubygems.org"
|
||||||
|
ruby '>=2.7'
|
||||||
|
|
||||||
# Specify your gem's dependencies in knot.gemspec
|
# Specify your gem's dependencies in knot.gemspec
|
||||||
gemspec
|
gemspec
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
require_relative 'lib/knot/version'
|
require_relative 'lib/knot/version'
|
||||||
|
|
||||||
Gem::Specification.new do |spec|
|
Gem::Specification.new do |spec|
|
||||||
spec.name = "knot"
|
spec.name = "knot-ruby"
|
||||||
spec.version = Knot::VERSION
|
spec.version = Knot::VERSION
|
||||||
spec.authors = ['Denis Knauf']
|
spec.authors = ['Denis Knauf']
|
||||||
spec.email = ['gems+knot@denkn.at']
|
spec.email = ['gems+knot@denkn.at']
|
||||||
|
@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
||||||
spec.summary = %q{Provides interface to knot-server.}
|
spec.summary = %q{Provides interface to knot-server.}
|
||||||
spec.description = %q{Implements knot-protocol to provide an interface to knot-DNS-server}
|
spec.description = %q{Implements knot-protocol to provide an interface to knot-DNS-server}
|
||||||
spec.homepage = 'https://git.denkn.at/deac/knot'
|
spec.homepage = 'https://git.denkn.at/deac/knot'
|
||||||
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
|
||||||
|
|
||||||
#spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
#spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
||||||
|
|
||||||
|
|
10
lib/knot.rb
10
lib/knot.rb
|
@ -2,3 +2,13 @@ require 'knot/version'
|
||||||
require 'knot/errors'
|
require 'knot/errors'
|
||||||
require 'knot/protocol'
|
require 'knot/protocol'
|
||||||
require 'knot/interface'
|
require 'knot/interface'
|
||||||
|
|
||||||
|
module Knot
|
||||||
|
class <<self
|
||||||
|
def new *as, **os
|
||||||
|
Protocol.new *as, **os
|
||||||
|
end
|
||||||
|
alias connect new
|
||||||
|
alias open new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -11,22 +11,35 @@ class Knot::Zone
|
||||||
@zone, @transaction_opened = zone, 0
|
@zone, @transaction_opened = zone, 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# If no transaction opened, yet, it opens a new transaction.
|
||||||
|
# Counts calling begin.
|
||||||
|
# Knot allowes only one global transaction, so if two clients tries to open a transaction,
|
||||||
|
# the second will fail. But the second client can change items in the same
|
||||||
|
# transaction like the first client.
|
||||||
|
# The first client, which calls commit or abort, wins and the transaction will be closed.
|
||||||
|
# So, the transaction-handling is only safe, if one client connects to knot.
|
||||||
def begin
|
def begin
|
||||||
@transaction_opened += 1
|
@transaction_opened += 1
|
||||||
@protocol.call command: 'zone-begin', zone: @zone if 1 == @transaction_opened
|
@protocol.call command: 'zone-begin', zone: @zone if 1 == @transaction_opened
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Decreases opened transactions.
|
||||||
|
# If opened transactions is zero, it stores items and closes transaction successfully.
|
||||||
def commit
|
def commit
|
||||||
@protocol.call command: 'zone-commit', zone: @zone if 1 == @transaction_opened
|
@protocol.call command: 'zone-commit', zone: @zone if 1 == @transaction_opened
|
||||||
@transaction_opened -= 1 if 0 < @transaction_opened
|
@transaction_opened -= 1 if 0 < @transaction_opened
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Closes transaction without saving immidiatly.
|
||||||
|
# Counter of opened transaction will be reset to 0.
|
||||||
def abort
|
def abort
|
||||||
@protocol.call command: 'zone-abort', zone: @zone
|
@protocol.call command: 'zone-abort', zone: @zone
|
||||||
@transaction_opened = 0
|
@transaction_opened = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def transaction
|
# Opens transaction, calls yielded Proc and closes transaction after return.
|
||||||
|
# If exception was raised, the transaction will be aborted.
|
||||||
|
def transaction # :yield:
|
||||||
self.begin
|
self.begin
|
||||||
yield self
|
yield self
|
||||||
rescue Object
|
rescue Object
|
||||||
|
@ -72,6 +85,7 @@ class Knot::Zone
|
||||||
rescue Knot::Errors::ENONODE, Knot::Errors::ENOENT
|
rescue Knot::Errors::ENONODE, Knot::Errors::ENOENT
|
||||||
end
|
end
|
||||||
alias delete unset
|
alias delete unset
|
||||||
|
alias del unset
|
||||||
|
|
||||||
def get owner = nil, type = nil
|
def get owner = nil, type = nil
|
||||||
@protocol.call command: 'zone-get',
|
@protocol.call command: 'zone-get',
|
||||||
|
@ -113,20 +127,24 @@ class Knot::Conf
|
||||||
self.commit
|
self.commit
|
||||||
end
|
end
|
||||||
|
|
||||||
def parse_item k
|
def parse_item kv
|
||||||
case k
|
case kv
|
||||||
when k
|
when Hash
|
||||||
case k.keys.sort
|
r = {}
|
||||||
when %w[section], %w[id section], %w[item section], %w[id item section] then k
|
kv.each {|k,v| r[k.to_s.to_sym] = v }
|
||||||
else raise ArgumentError, "Invalid Item-format"
|
case r.keys.sort
|
||||||
|
when %i[section], %i[id section], %i[item section], %i[id item section]
|
||||||
|
r
|
||||||
|
else
|
||||||
|
raise ArgumentError, "Invalid Item-format: #{k}"
|
||||||
end
|
end
|
||||||
|
|
||||||
when Array
|
when Array
|
||||||
case k.length
|
case kv.length
|
||||||
when 1 then {section: k[0]}
|
when 1 then {section: kv[0]}
|
||||||
when 2 then {section: k[0], item: k[1]}
|
when 2 then {section: kv[0], item: kv[1]}
|
||||||
when 3 then {section: k[0], id: k[1], item: k[2]}
|
when 3 then {section: kv[0], id: kv[1], item: kv[2]}
|
||||||
else raise ArgumentError, "Invalid Item-format"
|
else raise ArgumentError, "Invalid Item-format: #{kv}"
|
||||||
end
|
end
|
||||||
|
|
||||||
when /\A
|
when /\A
|
||||||
|
@ -134,26 +152,39 @@ class Knot::Conf
|
||||||
(?: \[ (?<id> [a-z0-9_.-]+) \] )?
|
(?: \[ (?<id> [a-z0-9_.-]+) \] )?
|
||||||
(?: \. (?<item>[a-z0-9_-]+) )?
|
(?: \. (?<item>[a-z0-9_-]+) )?
|
||||||
\z/xi
|
\z/xi
|
||||||
|
|
||||||
$~.named_captures.delete_if {|_,v| v.nil? }
|
$~.named_captures.delete_if {|_,v| v.nil? }
|
||||||
else raise ArgumentError, "Invalid Item-format"
|
|
||||||
|
when nil
|
||||||
|
{}
|
||||||
|
|
||||||
|
else
|
||||||
|
raise ArgumentError, "Invalid Item-format: #{kv}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get item = nil
|
||||||
|
@protocol.call **parse_item( item).update( command: 'conf-get')
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sets or adds a new value to item.
|
||||||
|
# knot knows single-value items like `server.rundir` and multi-value items like `server.listen`.
|
||||||
|
# If you set a single-value, it will replace the old value. On a multi-value, it will add it.
|
||||||
def set item, value
|
def set item, value
|
||||||
@protocol.call parse_item( item).update( command: 'conf-set', data: value)
|
@protocol.call **parse_item( item).update( command: 'conf-set', data: value)
|
||||||
end
|
end
|
||||||
alias [] set
|
|
||||||
|
|
||||||
|
# Removes value from item. If you provide a value, this value will be removed.
|
||||||
def unset item, value = nil
|
def unset item, value = nil
|
||||||
@protocol.call parse_item( item).update( command: 'conf-unset', data: value)
|
@protocol.call **parse_item( item).update( command: 'conf-unset', data: value)
|
||||||
end
|
end
|
||||||
alias delete unset
|
alias delete unset
|
||||||
|
|
||||||
def list item = nil
|
def list item = nil
|
||||||
@protocol.call (item ? parse_item( item) : {}).update( command: 'conf-list')
|
@protocol.call **parse_item( item).update( command: 'conf-list')
|
||||||
end
|
end
|
||||||
|
|
||||||
def read item = nil
|
def read item = nil
|
||||||
@protocol.call (item ? parse_item( item) : {}).update( command: 'conf-read')
|
@protocol.call **parse_item( item).update( command: 'conf-read')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
require 'iounpack'
|
require 'iounpack'
|
||||||
|
require 'pathname'
|
||||||
|
require 'socket'
|
||||||
|
require 'logger'
|
||||||
require_relative 'errors'
|
require_relative 'errors'
|
||||||
|
|
||||||
module Knot
|
module Knot
|
||||||
|
@ -51,44 +54,120 @@ module Knot::Protocol::Type
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#class Knot::KnotC
|
||||||
|
# attr_accessor :binary
|
||||||
|
#
|
||||||
|
# def initialize path = nil, binary: nil
|
||||||
|
# @path = path
|
||||||
|
# @binary = binary || 'knotc'
|
||||||
|
# @conf = Knot::Conf.new self
|
||||||
|
# @zones = Hash.new {|h, zone| h[zone] = Knot::Zone.new zone, self }
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# def call command:, flags: nil, section: nil, item: nil, id: nil, zone: nil, owner: nil, ttl: nil, type: nil, data: nil, filter: nil
|
||||||
|
# cs =
|
||||||
|
# case command.to_s
|
||||||
|
# when 'conf-begin', 'conf-commit', 'conf-abort', 'status', 'stop', 'reload'
|
||||||
|
# [@binary, command, ]
|
||||||
|
# else raise ArgumentError, "Unknown Command: #{command}"
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#end
|
||||||
|
|
||||||
|
class Knot::Protocol::Code
|
||||||
|
include Comparable
|
||||||
|
attr_reader :name, :code, :cname, :description
|
||||||
|
|
||||||
|
def initialize name, code, cname, description
|
||||||
|
raise ArgumentError, "Expecting Symbol for #{self.class.name} instead of: #{name.inspect}" unless Symbol === name
|
||||||
|
raise ArgumentError, "Expecting Integer for #{self.class.name} instead of: #{code.inspect}" unless Integer === code
|
||||||
|
@name, @code, @cname, @description = name, code, cname, description
|
||||||
|
freeze
|
||||||
|
end
|
||||||
|
|
||||||
|
def === x
|
||||||
|
case x
|
||||||
|
when self.class then @id == x.id
|
||||||
|
when Symbol then @name == x
|
||||||
|
when String then @name == x.to_sym
|
||||||
|
when Integer then @code == x
|
||||||
|
else nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def <=>( x) @id <=> x.id end
|
||||||
|
def to_s() @name.to_s end
|
||||||
|
def to_sym() @name end
|
||||||
|
def to_i() @code end
|
||||||
|
end
|
||||||
|
|
||||||
|
module Knot::Protocol::Codes
|
||||||
|
include Enumerable
|
||||||
|
def [] k
|
||||||
|
case k
|
||||||
|
when Symbol
|
||||||
|
self::Name[k] or raise Knot::Errors::EINVAL, "Unknown Codes: #{k}"
|
||||||
|
when Integer
|
||||||
|
self::Code[k] or raise Knot::Errors::EINVAL, "Unknown Codes: #{k}"
|
||||||
|
else
|
||||||
|
raise ArgumentError, "Unknown Codes-Type: #{k}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def each &exe
|
||||||
|
block_given? ? self::Codes.each( &exe) : self::Codes.to_enum( :each)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
module Knot::Protocol::Idx
|
module Knot::Protocol::Idx
|
||||||
Idx = [
|
extend Knot::Protocol::Codes
|
||||||
:command, # 10, :CMD, # Control command name.
|
|
||||||
:flags, # 11, :FLAGS, # Control command flags.
|
Codes = [
|
||||||
:error, # 12, :ERROR, # Error message.
|
Knot::Protocol::Code.new( :command, 0x10, :CMD, 'Control command name.'),
|
||||||
:section, # 13, :SECTION, # Configuration section name.
|
Knot::Protocol::Code.new( :flags, 0x11, :FLAGS, 'Control command flags.'),
|
||||||
:item, # 14, :ITEM, # Configuration item name.
|
Knot::Protocol::Code.new( :error, 0x12, :ERROR, 'Error message.'),
|
||||||
:id, # 15, :ID, # Congiguration item identifier.
|
Knot::Protocol::Code.new( :section, 0x13, :SECTION, 'Configuration section name.'),
|
||||||
:zone, # 16, :ZONE, # Zone name.
|
Knot::Protocol::Code.new( :item, 0x14, :ITEM, 'Configuration item name.'),
|
||||||
:owner, # 17, :OWNER, # Zone record owner
|
Knot::Protocol::Code.new( :id, 0x15, :ID, 'Congiguration item identifier.'),
|
||||||
:ttl, # 18, :TTL, # Zone record TTL.
|
Knot::Protocol::Code.new( :zone, 0x16, :ZONE, 'Zone name.'),
|
||||||
:type, # 19, :TYPE, # Zone record type name.
|
Knot::Protocol::Code.new( :owner, 0x17, :OWNER, 'Zone record owner'),
|
||||||
:data, # 1a, :DATA, # Configuration item/zone record data.
|
Knot::Protocol::Code.new( :ttl, 0x18, :TTL, 'Zone record TTL.'),
|
||||||
:filter, # 1b, :FILTER, # An option or a filter for output data processing.
|
Knot::Protocol::Code.new( :type, 0x19, :TYPE, 'Zone record type name.'),
|
||||||
|
Knot::Protocol::Code.new( :data, 0x1a, :DATA, 'Configuration item/zone record data.'),
|
||||||
|
Knot::Protocol::Code.new( :filter, 0x1b, :FILTER, 'An option or a filter for output data processing.'),
|
||||||
]
|
]
|
||||||
Name = {}
|
Name = {}
|
||||||
Code = {}
|
Code = {}
|
||||||
Idx.each_with_index do |v, i|
|
|
||||||
Code[0x10+i] = v
|
Codes.each do |id|
|
||||||
Name[v] = i
|
Code[id.to_i] = id
|
||||||
end
|
Name[id.to_sym] = id
|
||||||
def self.[] k
|
|
||||||
case k
|
|
||||||
when Symbol
|
|
||||||
Name[k] or raise Knot::Errors::EINVAL, "Unknown Idx: #{k}"
|
|
||||||
when Integer
|
|
||||||
Idx[k] or raise Knot::Errors::EINVAL, "Unknown Idx: #{k}"
|
|
||||||
else
|
|
||||||
raise ArgumentError, "Unknown Idx-Type: #{k}"
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module Knot::Protocol::Types
|
||||||
|
extend Knot::Protocol::Codes
|
||||||
|
|
||||||
|
Codes = [
|
||||||
|
Knot::Protocol::Code.new( :end, 0x00, :END, 'Type END.'),
|
||||||
|
Knot::Protocol::Code.new( :data, 0x01, :DATA, 'Type DATA.'),
|
||||||
|
Knot::Protocol::Code.new( :extra, 0x02, :EXTRA, 'Type EXTRA.'),
|
||||||
|
Knot::Protocol::Code.new( :block, 0x03, :BLOCK, 'Type BLOCK.'),
|
||||||
|
]
|
||||||
|
|
||||||
|
Name = {}
|
||||||
|
Code = {}
|
||||||
|
|
||||||
|
Codes.each do |id|
|
||||||
|
Code[id.to_i] = id
|
||||||
|
Name[id.to_sym] = id
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Knot::Protocol
|
class Knot::Protocol
|
||||||
attr_reader :sock, :conf, :zones
|
attr_reader :sock, :conf, :zones, :logger
|
||||||
attr_accessor :debug
|
|
||||||
|
|
||||||
def initialize path_or_sock = nil
|
def initialize path_or_sock = nil, logger: nil
|
||||||
case path_or_sock
|
case path_or_sock
|
||||||
when String, Pathname
|
when String, Pathname
|
||||||
@sock = UNIXSocket.new path_or_sock.to_s
|
@sock = UNIXSocket.new path_or_sock.to_s
|
||||||
|
@ -97,24 +176,24 @@ class Knot::Protocol
|
||||||
when nil
|
when nil
|
||||||
@sock = UNIXSocket.new '/run/knot/knot.sock'
|
@sock = UNIXSocket.new '/run/knot/knot.sock'
|
||||||
end
|
end
|
||||||
@debug = false
|
@logger = logger || Logger.new(STDERR)
|
||||||
@conf = Knot::Conf.new self
|
@conf = Knot::Conf.new self
|
||||||
@zones = Hash.new {|h, zone| h[zone] = Knot::Zone.new zone, self }
|
@zones = Hash.new {|h, zone| h[zone] = Knot::Zone.new zone, self }
|
||||||
end
|
end
|
||||||
|
|
||||||
def snd sock: nil, **data
|
def snd sock: nil, **data
|
||||||
rsock = sock || @sock
|
rsock = sock || @sock
|
||||||
s = ''
|
data = Hash[ *data.map {|k,v| [k.to_sym, v]}.flatten]
|
||||||
sock = StringIO.new s
|
|
||||||
sock.write [1].pack( 'c')
|
|
||||||
data[:flags] ||= ''
|
data[:flags] ||= ''
|
||||||
Idx::Idx.each_with_index do |n, i|
|
ds =
|
||||||
v = data[n]&.to_s
|
Idx.
|
||||||
sock.write [0x10+i, v.size, v].pack( 'c na*') if v
|
select {|n| data[n.to_sym] }.
|
||||||
|
map {|n| v = data[n.to_sym].to_s.b; [n.to_i, v.size, v] }
|
||||||
|
s = [Types[:data].to_i, ds, Types[:block].to_i].flatten.pack( "c #{'c na*'*ds.length} c").b
|
||||||
|
if 0 >= @logger.sev_threshold
|
||||||
|
@logger.debug "send data #{data.inspect}"
|
||||||
|
@logger.debug "send raw #{s.inspect}"
|
||||||
end
|
end
|
||||||
sock.write [3].pack( 'c')
|
|
||||||
sock.flush
|
|
||||||
STDERR.puts( {data: data, _: s}.inspect) if @debug
|
|
||||||
rsock.write s
|
rsock.write s
|
||||||
rsock.flush
|
rsock.flush
|
||||||
end
|
end
|
||||||
|
@ -123,45 +202,48 @@ class Knot::Protocol
|
||||||
attr_reader :str
|
attr_reader :str
|
||||||
|
|
||||||
def initialize sock, str = nil
|
def initialize sock, str = nil
|
||||||
@str, @sock = str || '', sock
|
@str, @sock = str || ''.b, sock
|
||||||
end
|
end
|
||||||
|
|
||||||
def unpack pattern
|
def unpack pattern
|
||||||
IOUnpack.new(pattern).unpack self
|
IOUnpack.new( pattern).unpack self
|
||||||
end
|
end
|
||||||
|
|
||||||
def unpack1 pattern
|
def unpack1 pattern
|
||||||
IOUnpack.new(pattern).unpack1 self
|
IOUnpack.new( pattern).unpack1 self
|
||||||
end
|
end
|
||||||
|
|
||||||
def read n
|
def read n
|
||||||
s = @sock.read n
|
s = @sock.read n
|
||||||
@str.insert -1, s
|
@str.insert -1, s unless s.nil?
|
||||||
s
|
s || ''
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def rcv sock: nil
|
def rcv sock: nil
|
||||||
ret, r = [], nil
|
ret, r = [], nil
|
||||||
sock = sock || @sock
|
sock = sock || @sock
|
||||||
sock = RecordIO.new sock if @debug
|
sock = RecordIO.new sock if 0 >= @logger.sev_threshold
|
||||||
loop do
|
loop do
|
||||||
t = sock.unpack1 'c'
|
t = sock.unpack1 'c'
|
||||||
case t
|
case t
|
||||||
when 0, 3
|
when Knot::Protocol::Types[:end], Knot::Protocol::Types[:block]
|
||||||
return ret
|
return ret
|
||||||
when 1, 2
|
when Knot::Protocol::Types[:data], Knot::Protocol::Types[:extra]
|
||||||
type = t
|
type = t
|
||||||
ret.push( r = {})
|
ret.push( r = {})
|
||||||
else
|
else
|
||||||
raise Knot::Errors::EINVAL, "Missing Type before: #{t}" if ret.empty?
|
raise Knot::Errors::EINVAL, "Missing Type before: #{t}" if ret.empty?
|
||||||
i = Idx::Idx[t - 0x10] or raise Knot::Errors::EINVAL, "Unknown index: #{t-0x10}"
|
i = Idx[t] or raise Knot::Errors::EINVAL, "Unknown index: #{t}"
|
||||||
l = sock.unpack1 'n'
|
l = sock.unpack1 'n'
|
||||||
r[i] = sock.read( l)
|
r[i.to_sym] = sock.read( l)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
ensure
|
ensure
|
||||||
STDERR.puts( {rcvd: ret, read: sock.str}.inspect) if @debug
|
if RecordIO === sock
|
||||||
|
@logger.debug "rcvd raw #{sock.str.inspect}"
|
||||||
|
@logger.debug "rcvd data #{ret.inspect}"
|
||||||
|
end
|
||||||
ret
|
ret
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -179,10 +261,10 @@ class Knot::Protocol
|
||||||
|
|
||||||
def zone( zone) @zones[zone.to_s.to_sym] end
|
def zone( zone) @zones[zone.to_s.to_sym] end
|
||||||
|
|
||||||
def conf_set( **opts) call opts.update( command: 'conf-set') end
|
def conf_set( **opts) call **opts.update( command: 'conf-set') end
|
||||||
def conf_unset( **opts) call opts.update( command: 'conf-set') end
|
def conf_unset( **opts) call **opts.update( command: 'conf-unset') end
|
||||||
|
|
||||||
def zone_set( **opts) call opts.update( command: 'zone-set') end
|
def zone_set( **opts) call **opts.update( command: 'zone-set') end
|
||||||
def zone_unset( **opts) call opts.update( command: 'zone-unset') end
|
def zone_unset( **opts) call **opts.update( command: 'zone-unset') end
|
||||||
def zone_get( **opts) call opts.update( command: 'zone-get') end
|
def zone_get( **opts) call **opts.update( command: 'zone-get') end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
module Knot
|
module Knot
|
||||||
VERSION = "0.1.0"
|
VERSION = "0.3.2"
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue