mirror of
https://github.com/vagrant-libvirt/vagrant-libvirt.git
synced 2025-02-25 18:55:27 -06:00
Add support for clock setup (#1047)
This adds support for setting clock offset and timers. See https://libvirt.org/formatdomain.html#elementsTime for more info.
This commit is contained in:
parent
92964823d0
commit
d0787c803d
22
README.md
22
README.md
@ -49,6 +49,7 @@ can help a lot :-)
|
|||||||
* [Watchdog device](#watchdog-device)
|
* [Watchdog device](#watchdog-device)
|
||||||
* [Smartcard device](#smartcard-device)
|
* [Smartcard device](#smartcard-device)
|
||||||
* [Hypervisor Features](#hypervisor-features)
|
* [Hypervisor Features](#hypervisor-features)
|
||||||
|
* [Clock](#clock)
|
||||||
* [CPU features](#cpu-features)
|
* [CPU features](#cpu-features)
|
||||||
* [Memory Backing](#memory-backing)
|
* [Memory Backing](#memory-backing)
|
||||||
* [No box and PXE boot](#no-box-and-pxe-boot)
|
* [No box and PXE boot](#no-box-and-pxe-boot)
|
||||||
@ -1185,6 +1186,27 @@ Vagrant.configure("2") do |config|
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Clock
|
||||||
|
|
||||||
|
Clock offset can be specified via `libvirt.clock_offset`. (Default is utc)
|
||||||
|
|
||||||
|
Additionally timers can be specified via `libvirt.clock_timer`.
|
||||||
|
Available options for timers are: name, track, tickpolicy, frequency, mode, present
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Vagrant.configure("2") do |config|
|
||||||
|
config.vm.provider :libvirt do |libvirt|
|
||||||
|
# Set clock offset to localtime
|
||||||
|
libvirt.clock_offset = 'localtime'
|
||||||
|
# Timers ...
|
||||||
|
libvirt.clock_timer :name => 'rtc', :tickpolicy => 'catchup'
|
||||||
|
libvirt.clock_timer :name => 'pit', :tickpolicy => 'delay'
|
||||||
|
libvirt.clock_timer :name => 'hpet', :present => 'no'
|
||||||
|
libvirt.clock_timer :name => 'hypervclock', :present => 'yes'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
## CPU features
|
## CPU features
|
||||||
|
|
||||||
You can specify CPU feature policies via `libvirt.cpu_feature`. Available
|
You can specify CPU feature policies via `libvirt.cpu_feature`. Available
|
||||||
|
@ -42,6 +42,8 @@ module VagrantPlugins
|
|||||||
@nodeset = config.nodeset
|
@nodeset = config.nodeset
|
||||||
@features = config.features
|
@features = config.features
|
||||||
@features_hyperv = config.features_hyperv
|
@features_hyperv = config.features_hyperv
|
||||||
|
@clock_offset = config.clock_offset
|
||||||
|
@clock_timers = config.clock_timers
|
||||||
@shares = config.shares
|
@shares = config.shares
|
||||||
@cpu_mode = config.cpu_mode
|
@cpu_mode = config.cpu_mode
|
||||||
@cpu_model = config.cpu_model
|
@cpu_model = config.cpu_model
|
||||||
@ -226,6 +228,10 @@ module VagrantPlugins
|
|||||||
@features_hyperv.each do |feature|
|
@features_hyperv.each do |feature|
|
||||||
env[:ui].info(" -- Feature (HyperV): name=#{feature[:name]}, state=#{feature[:state]}")
|
env[:ui].info(" -- Feature (HyperV): name=#{feature[:name]}, state=#{feature[:state]}")
|
||||||
end
|
end
|
||||||
|
env[:ui].info(" -- Clock offset: #{@clock_offset}")
|
||||||
|
@clock_timers.each do |timer|
|
||||||
|
env[:ui].info(" -- Clock timer: #{timer.map { |k,v| "#{k}=#{v}"}.join(', ')}")
|
||||||
|
end
|
||||||
env[:ui].info(" -- Memory: #{@memory_size / 1024}M")
|
env[:ui].info(" -- Memory: #{@memory_size / 1024}M")
|
||||||
unless @nodeset.nil?
|
unless @nodeset.nil?
|
||||||
env[:ui].info(" -- Nodeset: #{@nodeset}")
|
env[:ui].info(" -- Nodeset: #{@nodeset}")
|
||||||
|
@ -37,6 +37,9 @@ module VagrantPlugins
|
|||||||
xml_descr = REXML::Document.new descr
|
xml_descr = REXML::Document.new descr
|
||||||
descr_changed = false
|
descr_changed = false
|
||||||
|
|
||||||
|
# For outputting XML for comparison
|
||||||
|
formatter = REXML::Formatters::Pretty.new
|
||||||
|
|
||||||
# additional disk bus
|
# additional disk bus
|
||||||
config.disks.each do |disk|
|
config.disks.each do |disk|
|
||||||
device = disk[:device]
|
device = disk[:device]
|
||||||
@ -152,6 +155,34 @@ module VagrantPlugins
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Clock
|
||||||
|
clock = REXML::XPath.first(xml_descr, '/domain/clock')
|
||||||
|
if clock.attributes['offset'] != config.clock_offset
|
||||||
|
@logger.debug "clock offset changed"
|
||||||
|
descr_changed = true
|
||||||
|
clock.attributes['offset'] = config.clock_offset
|
||||||
|
end
|
||||||
|
|
||||||
|
# clock timers - because timers can be added/removed, just rebuild and then compare
|
||||||
|
if !config.clock_timers.empty? || clock.has_elements?
|
||||||
|
oldclock = ''
|
||||||
|
formatter.write(REXML::XPath.first(xml_descr, '/domain/clock'), oldclock)
|
||||||
|
clock.delete_element('//timer')
|
||||||
|
config.clock_timers.each do |clock_timer|
|
||||||
|
timer = REXML::Element.new('timer', clock)
|
||||||
|
clock_timer.each do |attr, value|
|
||||||
|
timer.attributes[attr.to_s] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
newclock = ''
|
||||||
|
formatter.write(clock, newclock)
|
||||||
|
unless newclock.eql? oldclock
|
||||||
|
@logger.debug "clock timers config changed"
|
||||||
|
descr_changed = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Graphics
|
# Graphics
|
||||||
graphics = REXML::XPath.first(xml_descr, '/domain/devices/graphics')
|
graphics = REXML::XPath.first(xml_descr, '/domain/devices/graphics')
|
||||||
if config.graphics_type != 'none'
|
if config.graphics_type != 'none'
|
||||||
|
@ -84,6 +84,8 @@ module VagrantPlugins
|
|||||||
attr_accessor :shares
|
attr_accessor :shares
|
||||||
attr_accessor :features
|
attr_accessor :features
|
||||||
attr_accessor :features_hyperv
|
attr_accessor :features_hyperv
|
||||||
|
attr_accessor :clock_offset
|
||||||
|
attr_accessor :clock_timers
|
||||||
attr_accessor :numa_nodes
|
attr_accessor :numa_nodes
|
||||||
attr_accessor :loader
|
attr_accessor :loader
|
||||||
attr_accessor :nvram
|
attr_accessor :nvram
|
||||||
@ -222,6 +224,8 @@ module VagrantPlugins
|
|||||||
@shares = UNSET_VALUE
|
@shares = UNSET_VALUE
|
||||||
@features = UNSET_VALUE
|
@features = UNSET_VALUE
|
||||||
@features_hyperv = UNSET_VALUE
|
@features_hyperv = UNSET_VALUE
|
||||||
|
@clock_offset = UNSET_VALUE
|
||||||
|
@clock_timers = []
|
||||||
@numa_nodes = UNSET_VALUE
|
@numa_nodes = UNSET_VALUE
|
||||||
@loader = UNSET_VALUE
|
@loader = UNSET_VALUE
|
||||||
@nvram = UNSET_VALUE
|
@nvram = UNSET_VALUE
|
||||||
@ -392,6 +396,25 @@ module VagrantPlugins
|
|||||||
state: options[:state])
|
state: options[:state])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def clock_timer(options = {})
|
||||||
|
if options[:name].nil?
|
||||||
|
raise 'Clock timer name must be specified'
|
||||||
|
end
|
||||||
|
|
||||||
|
options.each do |key, value|
|
||||||
|
case key
|
||||||
|
when :name, :track, :tickpolicy, :frequency, :mode, :present
|
||||||
|
if value.nil?
|
||||||
|
raise "Value of timer option #{key} is nil"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise "Unknown clock timer option: #{key}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@clock_timers.push(options.dup)
|
||||||
|
end
|
||||||
|
|
||||||
def cputopology(options = {})
|
def cputopology(options = {})
|
||||||
if options[:sockets].nil? || options[:cores].nil? || options[:threads].nil?
|
if options[:sockets].nil? || options[:cores].nil? || options[:threads].nil?
|
||||||
raise 'CPU topology must have all of sockets, cores and threads specified'
|
raise 'CPU topology must have all of sockets, cores and threads specified'
|
||||||
@ -762,6 +785,8 @@ module VagrantPlugins
|
|||||||
@shares = nil if @shares == UNSET_VALUE
|
@shares = nil if @shares == UNSET_VALUE
|
||||||
@features = ['acpi','apic','pae'] if @features == UNSET_VALUE
|
@features = ['acpi','apic','pae'] if @features == UNSET_VALUE
|
||||||
@features_hyperv = [] if @features_hyperv == UNSET_VALUE
|
@features_hyperv = [] if @features_hyperv == UNSET_VALUE
|
||||||
|
@clock_offset = 'utc' if @clock_offset == UNSET_VALUE
|
||||||
|
@clock_timers = [] if @clock_timers == UNSET_VALUE
|
||||||
@numa_nodes = @numa_nodes == UNSET_VALUE ? nil : _generate_numa
|
@numa_nodes = @numa_nodes == UNSET_VALUE ? nil : _generate_numa
|
||||||
@loader = nil if @loader == UNSET_VALUE
|
@loader = nil if @loader == UNSET_VALUE
|
||||||
@nvram = nil if @nvram == UNSET_VALUE
|
@nvram = nil if @nvram == UNSET_VALUE
|
||||||
@ -903,6 +928,10 @@ module VagrantPlugins
|
|||||||
c += other.cdroms
|
c += other.cdroms
|
||||||
result.cdroms = c
|
result.cdroms = c
|
||||||
|
|
||||||
|
c = clock_timers.dup
|
||||||
|
c += other.clock_timers
|
||||||
|
result.clock_timers = c
|
||||||
|
|
||||||
c = qemu_env != UNSET_VALUE ? qemu_env.dup : {}
|
c = qemu_env != UNSET_VALUE ? qemu_env.dup : {}
|
||||||
c.merge!(other.qemu_env) if other.qemu_env != UNSET_VALUE
|
c.merge!(other.qemu_env) if other.qemu_env != UNSET_VALUE
|
||||||
result.qemu_env = c
|
result.qemu_env = c
|
||||||
|
@ -104,7 +104,11 @@
|
|||||||
</hyperv>
|
</hyperv>
|
||||||
<% end %>
|
<% end %>
|
||||||
</features>
|
</features>
|
||||||
<clock offset='utc'/>
|
<clock offset='<%= @clock_offset %>'>
|
||||||
|
<% @clock_timers.each do |clock_timer| %>
|
||||||
|
<timer<% clock_timer.each do |attr, value| %> <%= attr %>='<%= value %>'<% end %>/>
|
||||||
|
<% end %>
|
||||||
|
</clock>
|
||||||
<devices>
|
<devices>
|
||||||
<% if @emulator_path %>
|
<% if @emulator_path %>
|
||||||
<emulator><%= @emulator_path %></emulator>
|
<emulator><%= @emulator_path %></emulator>
|
||||||
|
@ -27,7 +27,9 @@ describe VagrantPlugins::ProviderLibvirt::Action::StartDomain do
|
|||||||
|
|
||||||
allow(connection).to receive(:servers).and_return(servers)
|
allow(connection).to receive(:servers).and_return(servers)
|
||||||
allow(servers).to receive(:get).and_return(domain)
|
allow(servers).to receive(:get).and_return(domain)
|
||||||
|
|
||||||
expect(logger).to receive(:info)
|
expect(logger).to receive(:info)
|
||||||
|
expect(ui).to_not receive(:error)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'default config' do
|
context 'default config' do
|
||||||
@ -164,5 +166,66 @@ describe VagrantPlugins::ProviderLibvirt::Action::StartDomain do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'clock_timers' do
|
||||||
|
let(:test_file) { 'clock_timer_rtc.xml' }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(libvirt_domain).to receive(:xml_desc).and_return(domain_xml)
|
||||||
|
|
||||||
|
allow(libvirt_domain).to receive(:max_memory).and_return(512*1024)
|
||||||
|
allow(libvirt_domain).to receive(:num_vcpus).and_return(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'timers unchanged' do
|
||||||
|
let(:vagrantfile_providerconfig) do
|
||||||
|
<<-EOF
|
||||||
|
libvirt.clock_timer(:name => "rtc")
|
||||||
|
EOF
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should not modify the domain' do
|
||||||
|
expect(logger).to_not receive(:debug).with('clock timers config changed')
|
||||||
|
expect(servers).to_not receive(:create)
|
||||||
|
expect(libvirt_domain).to receive(:autostart=)
|
||||||
|
expect(domain).to receive(:start)
|
||||||
|
|
||||||
|
expect(subject.call(env)).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'timers added' do
|
||||||
|
let(:vagrantfile_providerconfig) do
|
||||||
|
<<-EOF
|
||||||
|
libvirt.clock_timer(:name => "rtc")
|
||||||
|
libvirt.clock_timer(:name => "tsc")
|
||||||
|
EOF
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should modify the domain' do
|
||||||
|
expect(libvirt_domain).to receive(:undefine)
|
||||||
|
expect(logger).to receive(:debug).with('clock timers config changed')
|
||||||
|
expect(servers).to receive(:create).with(xml: match(/<clock offset='utc'>\s*<timer name='rtc'\/>\s*<timer name='tsc'\/>\s*<\/clock>/))
|
||||||
|
expect(libvirt_domain).to receive(:autostart=)
|
||||||
|
expect(domain).to receive(:start)
|
||||||
|
|
||||||
|
expect(subject.call(env)).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'timers removed' do
|
||||||
|
let(:updated_test_file) { 'default.xml' }
|
||||||
|
|
||||||
|
it 'should modify the domain' do
|
||||||
|
expect(libvirt_domain).to receive(:undefine)
|
||||||
|
expect(logger).to receive(:debug).with('clock timers config changed')
|
||||||
|
expect(servers).to receive(:create).with(xml: match(/<clock offset='utc'>\s*<\/clock>/))
|
||||||
|
expect(libvirt_domain).to receive(:autostart=)
|
||||||
|
expect(domain).to receive(:start)
|
||||||
|
|
||||||
|
expect(subject.call(env)).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
50
spec/unit/action/start_domain_spec/clock_timer_rtc.xml
Normal file
50
spec/unit/action/start_domain_spec/clock_timer_rtc.xml
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<domain xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0' type=''>
|
||||||
|
<name/>
|
||||||
|
<title/>
|
||||||
|
<description/>
|
||||||
|
<uuid/>
|
||||||
|
<memory/>
|
||||||
|
<vcpu>1</vcpu>
|
||||||
|
|
||||||
|
|
||||||
|
<cpu mode='host-model'>
|
||||||
|
<model fallback='allow'/>
|
||||||
|
</cpu>
|
||||||
|
|
||||||
|
|
||||||
|
<os>
|
||||||
|
<type>hvm</type>
|
||||||
|
<kernel/>
|
||||||
|
<initrd/>
|
||||||
|
<cmdline/>
|
||||||
|
</os>
|
||||||
|
<features>
|
||||||
|
<acpi/>
|
||||||
|
<apic/>
|
||||||
|
<pae/>
|
||||||
|
</features>
|
||||||
|
<clock offset='utc'>
|
||||||
|
<timer name='rtc'/>
|
||||||
|
</clock>
|
||||||
|
<devices>
|
||||||
|
|
||||||
|
|
||||||
|
<serial type='pty'>
|
||||||
|
<target port='0'/>
|
||||||
|
</serial>
|
||||||
|
<console type='pty'>
|
||||||
|
<target port='0'/>
|
||||||
|
</console>
|
||||||
|
|
||||||
|
|
||||||
|
<input bus='ps2' type='mouse'/>
|
||||||
|
|
||||||
|
<graphics autoport='yes' keymap='en-us' listen='127.0.0.1' port='-1' type='vnc'/>
|
||||||
|
<video>
|
||||||
|
<model heads='1' type='cirrus' vram='9216'/>
|
||||||
|
</video>
|
||||||
|
|
||||||
|
|
||||||
|
</devices>
|
||||||
|
|
||||||
|
</domain>
|
@ -8,6 +8,50 @@ describe VagrantPlugins::ProviderLibvirt::Config do
|
|||||||
|
|
||||||
let(:fake_env) { Hash.new }
|
let(:fake_env) { Hash.new }
|
||||||
|
|
||||||
|
describe '#clock_timer' do
|
||||||
|
it 'should handle all options' do
|
||||||
|
expect(
|
||||||
|
subject.clock_timer(
|
||||||
|
:name => 'rtc',
|
||||||
|
:track => 'wall',
|
||||||
|
:tickpolicy => 'delay',
|
||||||
|
:present => 'yes',
|
||||||
|
).length
|
||||||
|
).to be(1)
|
||||||
|
expect(
|
||||||
|
subject.clock_timer(
|
||||||
|
:name => 'tsc',
|
||||||
|
:tickpolicy => 'delay',
|
||||||
|
:frequency => '100',
|
||||||
|
:mode => 'auto',
|
||||||
|
:present => 'yes',
|
||||||
|
).length
|
||||||
|
).to be(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should correctly save the options' do
|
||||||
|
opts = {:name => 'rtc', :track => 'wall'}
|
||||||
|
expect(subject.clock_timer(opts).length).to be(1)
|
||||||
|
|
||||||
|
expect(subject.clock_timers[0]).to eq(opts)
|
||||||
|
|
||||||
|
opts[:name] = 'tsc'
|
||||||
|
expect(subject.clock_timers[0]).to_not eq(opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should error name option is missing' do
|
||||||
|
expect{ subject.clock_timer(:track => "wall") }.to raise_error("Clock timer name must be specified")
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should error if nil value for option supplied' do
|
||||||
|
expect{ subject.clock_timer(:name => "rtc", :track => nil) }.to raise_error("Value of timer option track is nil")
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should error if unrecognized option specified' do
|
||||||
|
expect{ subject.clock_timer(:name => "tsc", :badopt => "value") }.to raise_error("Unknown clock timer option: badopt")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe '#finalize!' do
|
describe '#finalize!' do
|
||||||
it 'is valid with defaults' do
|
it 'is valid with defaults' do
|
||||||
subject.finalize!
|
subject.finalize!
|
||||||
@ -282,5 +326,15 @@ describe VagrantPlugins::ProviderLibvirt::Config do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'clock_timers' do
|
||||||
|
it 'should merge clock_timers' do
|
||||||
|
one.clock_timer(:name => 'rtc', :tickpolicy => 'catchup')
|
||||||
|
two.clock_timer(:name => 'hpet', :present => 'no')
|
||||||
|
|
||||||
|
expect(subject.clock_timers).to include(include(name: 'rtc'),
|
||||||
|
include(name: 'hpet'))
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -36,7 +36,10 @@
|
|||||||
<BBB state='on' />
|
<BBB state='on' />
|
||||||
</hyperv>
|
</hyperv>
|
||||||
</features>
|
</features>
|
||||||
<clock offset='utc'/>
|
<clock offset='variable'>
|
||||||
|
<timer name='t1'/>
|
||||||
|
<timer name='t2' track='b' tickpolicy='c' frequency='d' mode='e' present='yes'/>
|
||||||
|
</clock>
|
||||||
<devices>
|
<devices>
|
||||||
<emulator>/usr/bin/kvm-spice</emulator>
|
<emulator>/usr/bin/kvm-spice</emulator>
|
||||||
<disk type='file' device='disk'>
|
<disk type='file' device='disk'>
|
||||||
|
@ -23,7 +23,8 @@
|
|||||||
<apic/>
|
<apic/>
|
||||||
<pae/>
|
<pae/>
|
||||||
</features>
|
</features>
|
||||||
<clock offset='utc'/>
|
<clock offset='utc'>
|
||||||
|
</clock>
|
||||||
<devices>
|
<devices>
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,7 +23,8 @@
|
|||||||
<apic/>
|
<apic/>
|
||||||
<pae/>
|
<pae/>
|
||||||
</features>
|
</features>
|
||||||
<clock offset='utc'/>
|
<clock offset='utc'>
|
||||||
|
</clock>
|
||||||
<devices>
|
<devices>
|
||||||
|
|
||||||
|
|
||||||
|
@ -33,6 +33,9 @@ describe 'templates/domain' do
|
|||||||
domain.cpu_mode = 'custom'
|
domain.cpu_mode = 'custom'
|
||||||
domain.cpu_feature(name: 'AAA', policy: 'required')
|
domain.cpu_feature(name: 'AAA', policy: 'required')
|
||||||
domain.hyperv_feature(name: 'BBB', state: 'on')
|
domain.hyperv_feature(name: 'BBB', state: 'on')
|
||||||
|
domain.clock_offset = 'variable'
|
||||||
|
domain.clock_timer(name: 't1')
|
||||||
|
domain.clock_timer(name: 't2', track: 'b', tickpolicy: 'c', frequency: 'd', mode: 'e', present: 'yes')
|
||||||
domain.cputopology(sockets: '1', cores: '3', threads: '2')
|
domain.cputopology(sockets: '1', cores: '3', threads: '2')
|
||||||
domain.machine_type = 'pc-compatible'
|
domain.machine_type = 'pc-compatible'
|
||||||
domain.machine_arch = 'x86_64'
|
domain.machine_arch = 'x86_64'
|
||||||
|
@ -23,7 +23,8 @@
|
|||||||
<apic/>
|
<apic/>
|
||||||
<pae/>
|
<pae/>
|
||||||
</features>
|
</features>
|
||||||
<clock offset='utc'/>
|
<clock offset='utc'>
|
||||||
|
</clock>
|
||||||
<devices>
|
<devices>
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,7 +23,8 @@
|
|||||||
<apic/>
|
<apic/>
|
||||||
<pae/>
|
<pae/>
|
||||||
</features>
|
</features>
|
||||||
<clock offset='utc'/>
|
<clock offset='utc'>
|
||||||
|
</clock>
|
||||||
<devices>
|
<devices>
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user