Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
P
pve-manager
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
pve-manager
Commits
f090efb3
Commit
f090efb3
authored
Oct 17, 2011
by
Dietmar Maurer
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
implement vzrestore
This just calls create_vm (restore is a special case of create).
parent
bc03b811
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
206 additions
and
192 deletions
+206
-192
OpenVZ.pm
PVE/API2/OpenVZ.pm
+122
-19
OpenVZ.pm
PVE/OpenVZ.pm
+13
-3
OpenVZ.pm
PVE/VZDump/OpenVZ.pm
+1
-23
Makefile
bin/Makefile
+5
-0
vzrestore
bin/vzrestore
+64
-147
Utils.js
www/manager/Utils.js
+1
-0
No files found.
PVE/API2/OpenVZ.pm
View file @
f090efb3
...
...
@@ -3,10 +3,12 @@ package PVE::API2::OpenVZ;
use
strict
;
use
warnings
;
use
File::
Basename
;
use
File::
Path
;
use
POSIX
qw (LONG_MAX);
use
Cwd
'
abs_path
';
use
PVE::
SafeSyslog
;
use
PVE::
Tools
qw(extract_param)
;
use
PVE::
Tools
qw(extract_param
run_command
)
;
use
PVE::
Cluster
qw(cfs_lock_file cfs_read_file)
;
use
PVE::
Storage
;
use
PVE::
RESTHandler
;
...
...
@@ -84,11 +86,94 @@ __PACKAGE__->register_method({
}});
my
$restore_openvz
=
sub
{
my
(
$archive
,
$vmid
,
$force
)
=
@_
;
my
$vzconf
=
PVE::OpenVZ::
read_global_vz_config
();
my
$conffile
=
PVE::OpenVZ::
config_file
(
$vmid
);
my
$cfgdir
=
dirname
(
$conffile
);
my
$private
=
$vzconf
->
{
privatedir
};
$private
=~
s/\$VEID/$vmid/
;
my
$root
=
$vzconf
->
{
rootdir
};
$root
=~
s/\$VEID/$vmid/
;
print
"
you choose to force overwriting VPS config file, private and root directories.
\n
"
if
$force
;
die
"
unable to create CT
$vmid
- container already exists
\n
"
if
!
$force
&&
-
f
$conffile
;
die
"
unable to create CT
$vmid
- directory '
$private
' already exists
\n
"
if
!
$force
&&
-
d
$private
;
die
"
unable to create CT
$vmid
- directory '
$root
' already exists
\n
"
if
!
$force
&&
-
d
$root
;
my
$conf
;
eval
{
rmtree
$private
if
-
d
$private
;
rmtree
$root
if
-
d
$root
;
mkpath
$private
||
die
"
unable to create private dir '
$private
'
";
mkpath
$root
||
die
"
unable to create private dir '
$private
'
";
my
$cmd
=
['
tar
',
'
xpf
',
$archive
,
'
--totals
',
'
--sparse
',
'
-C
',
$private
];
if
(
$archive
eq
'
-
')
{
print
"
extracting archive from STDIN
\n
";
run_command
(
$cmd
,
input
=>
"
<&STDIN
");
}
else
{
print
"
extracting archive '
$archive
'
\n
";
run_command
(
$cmd
);
}
my
$backup_cfg
=
"
$private
/etc/vzdump/vps.conf
";
if
(
-
f
$backup_cfg
)
{
print
"
restore configuration to '
$conffile
'
\n
";
$conf
=
PVE::Tools::
file_get_contents
(
$backup_cfg
);
$conf
=~
s/VE_ROOT=.*/VE_ROOT=\"$root\"/
;
$conf
=~
s/VE_PRIVATE=.*/VE_PRIVATE=\"$private\"/
;
$conf
=~
s/host_ifname=veth[0-9]+\./host_ifname=veth${vmid}\./g
;
PVE::Tools::
file_set_contents
(
$conffile
,
$conf
);
foreach
my
$s
(
PVE::OpenVZ::
SCRIPT_EXT
)
{
my
$tfn
=
"
$cfgdir
/
${vmid}
.
$s
";
my
$sfn
=
"
$private
/etc/vzdump/vps.
$s
";
if
(
-
f
$sfn
)
{
my
$sc
=
PVE::Tools::
file_get_contents
(
$sfn
);
PVE::Tools::
file_set_contents
(
$tfn
,
$sc
);
}
}
}
rmtree
"
$private
/etc/vzdump
";
};
my
$err
=
$@
;
if
(
$err
)
{
rmtree
$private
;
rmtree
$root
;
unlink
$conffile
;
foreach
my
$s
(
PVE::OpenVZ::
SCRIPT_EXT
)
{
unlink
"
$cfgdir
/
${vmid}
.
$s
";
}
die
$err
;
}
return
$conf
;
};
# create_vm is also used by vzrestore
__PACKAGE__
->
register_method
({
name
=>
'
create_vm
',
path
=>
'',
method
=>
'
POST
',
description
=>
"
Create
new
container.
",
description
=>
"
Create
or restore a
container.
",
protected
=>
1
,
proxyto
=>
'
node
',
parameters
=>
{
...
...
@@ -97,7 +182,7 @@ __PACKAGE__->register_method({
node
=>
get_standard_option
('
pve-node
'),
vmid
=>
get_standard_option
('
pve-vmid
'),
ostemplate
=>
{
description
=>
"
The OS template.
",
description
=>
"
The OS template
or backup file
.
",
type
=>
'
string
',
maxLength
=>
255
,
},
...
...
@@ -106,6 +191,16 @@ __PACKAGE__->register_method({
type
=>
'
string
',
description
=>
"
Sets root password inside container.
",
},
force
=>
{
optional
=>
1
,
type
=>
'
boolean
',
description
=>
"
Allow to overwrite existing container.
",
},
restore
=>
{
optional
=>
1
,
type
=>
'
boolean
',
description
=>
"
Mark this as restore task.
",
},
}),
},
returns
=>
{
...
...
@@ -132,23 +227,29 @@ __PACKAGE__->register_method({
my
$basecfg_fn
=
PVE::OpenVZ::
config_file
(
$vmid
);
die
"
container
$vmid
already exists
\n
"
if
-
f
$basecfg_fn
;
if
(
$param
->
{
force
})
{
die
"
cant overwrite mounted container
\n
"
if
PVE::OpenVZ::
check_mounted
(
$vmid
);
}
else
{
die
"
container
$vmid
already exists
\n
"
if
-
f
$basecfg_fn
;
}
my
$ostemplate
=
extract_param
(
$param
,
'
ostemplate
');
$ostemplate
=~
s
|^
/var/li
b
/vz/
template
/cache/
|
local
:
vztmpl
/|
;
my
$archive
;
if
(
$ostemplate
!~
m|^local:vztmpl/|
)
{
$ostemplate
=
"
local:vztmpl/
${ostemplate}
";
if
(
$ostemplate
eq
'
-
')
{
die
"
pipe requires cli environment
\n
"
if
$rpcenv
->
{
type
}
ne
'
cli
';
$archive
=
'
-
';
}
else
{
if
(
PVE::Storage::
parse_volume_id
(
$ostemplate
,
1
))
{
$archive
=
PVE::Storage::
path
(
$stcfg
,
$ostemplate
);
}
else
{
$archive
=
abs_path
(
$ostemplate
);
}
die
"
can't find file '
$archive
'
\n
"
if
!
-
f
$archive
;
}
my
$tpath
=
PVE::Storage::
path
(
$stcfg
,
$ostemplate
);
die
"
can't find OS template '
$ostemplate
'
\n
"
if
!
-
f
$tpath
;
# hack: openvz does not support full paths
$tpath
=
basename
(
$tpath
);
$tpath
=~
s/\.tar\.gz$//
;
if
(
!
defined
(
$param
->
{
searchdomain
})
&&
!
defined
(
$param
->
{
nameserver
}))
{
...
...
@@ -168,19 +269,21 @@ __PACKAGE__->register_method({
my
$rawconf
=
PVE::OpenVZ::
generate_raw_config
(
$pve_base_ovz_config
,
$conf
);
my
$realcmd
=
sub
{
PVE::Tools::
file_set_contents
(
$basecfg_fn
,
$rawconf
);
PVE::Cluster::
check_cfs_quorum
();
my
$cmd
=
['
vzctl
',
'
--skiplock
',
'
create
',
$vmid
,
'
--ostemplate
',
$tpath
];
my
$realcmd
=
sub
{
&
$restore_openvz
(
$archive
,
$vmid
,
$param
->
{
force
});
PVE::Tools::
run_command
(
$cmd
);
PVE::Tools::
file_set_contents
(
$basecfg_fn
,
$rawconf
)
if
!
$param
->
{
restore
};
# hack: vzctl '--userpasswd' starts the CT, but we want
# to avoid that for create
PVE::OpenVZ::
set_rootpasswd
(
$vmid
,
$password
)
if
defined
(
$password
);
};
return
$rpcenv
->
fork_worker
('
vzcreate
',
$vmid
,
$user
,
$realcmd
);
return
$rpcenv
->
fork_worker
(
$param
->
{
restore
}
?
'
vzrestore
'
:
'
vzcreate
',
$vmid
,
$user
,
$realcmd
);
};
return
PVE::OpenVZ::
lock_container
(
$vmid
,
$code
);
...
...
PVE/OpenVZ.pm
View file @
f090efb3
...
...
@@ -15,8 +15,12 @@ use PVE::JSONSchema;
use
Digest::
SHA1
;
use
Encode
;
use
constant
SCRIPT_EXT
=>
qw (start
stop
mount
umount
);
my
$cpuinfo
=
PVE::ProcFSTools::
read_cpuinfo
();
my
$nodename
=
PVE::INotify::
nodename
();
my
$global_vzconf
=
read_global_vz_config
();
my
$res_unlimited
=
LONG_MAX
;
sub
config_list
{
my
$vmlist
=
PVE::Cluster::
get_vmlist
();
...
...
@@ -59,6 +63,15 @@ sub load_config {
return
$conf
;
}
sub
check_mounted
{
my
(
$vmid
)
=
@_
;
my
$root
=
$global_vzconf
->
{
rootdir
};
$root
=~
s/\$VEID/$vmid/
;
return
(
-
d
"
$root
/etc
"
||
-
d
"
$root
/proc
");
}
sub
read_user_beancounters
{
my
$ubc
=
{};
if
(
my
$fh
=
IO::
File
->
new
("
/proc/user_beancounters
",
"
r
"))
{
...
...
@@ -431,9 +444,6 @@ sub read_global_vz_config {
return
$res
;
};
my
$global_vzconf
=
read_global_vz_config
();
my
$res_unlimited
=
LONG_MAX
;
sub
parse_netif
{
my
(
$data
)
=
@_
;
...
...
PVE/VZDump/OpenVZ.pm
View file @
f090efb3
package
PVE::VZDump::
OpenVZ
;
# Copyright (C) 2007-2009 Proxmox Server Solutions GmbH
#
# Copyright: vzdump is under GNU GPL, the GNU General Public License.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 dated June, 1991.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
# MA 02110-1301, USA.
#
# Author: Dietmar Maurer <dietmar@proxmox.com>
use
strict
;
use
warnings
;
use
File::
Path
;
...
...
@@ -30,8 +10,6 @@ use PVE::OpenVZ;
use
base
qw (PVE::VZDump::Plugin);
use
constant
SCRIPT_EXT
=>
qw (start
stop
mount
umount
);
my
$load_vz_conf
=
sub
{
my
(
$self
,
$vmid
)
=
@_
;
...
...
@@ -246,7 +224,7 @@ sub assemble {
mkpath
"
$dir
/etc/vzdump/
";
$self
->
cmd
("
cp '
$conffile
' '
$dir
/etc/vzdump/vps.conf'
");
my
$cfgdir
=
dirname
(
$conffile
);
foreach
my
$s
(
SCRIPT_EXT
)
{
foreach
my
$s
(
PVE::OpenVZ::
SCRIPT_EXT
)
{
my
$fn
=
"
$cfgdir
/
$vmid
.
$s
";
$self
->
cmd
("
cp '
$fn
' '
$dir
/etc/vzdump/vps.
$s
'
")
if
-
f
$fn
;
}
...
...
bin/Makefile
View file @
f090efb3
...
...
@@ -17,6 +17,7 @@ SCRIPTS = \
MANS
=
\
pvectl.1
\
vzdump.1
\
vzrestore.1
\
pvestatd.1
\
pvedaemon.1
\
pveversion.1
\
...
...
@@ -37,6 +38,9 @@ pvectl.1.pod: pvectl
vzdump.1.pod
:
vzdump
perl
-I
.. ./vzdump printmanpod
>
$@
vzrestore.1.pod
:
vzrestore
perl
-I
.. ./vzrestore printmanpod
>
$@
.PHONY
:
install
install
:
${SCRIPTS} ${MANS}
perl
-I
.. ./pvesh verifyapi
...
...
@@ -47,6 +51,7 @@ install: ${SCRIPTS} ${MANS}
install
-d
${
PODDIR
}
install
-m
0644 pvectl.1.pod
${
PODDIR
}
install
-m
0644 vzdump.1.pod
${
PODDIR
}
install
-m
0644 vzrestore.1.pod
${
PODDIR
}
set
-e
&&
for
i
in
${
SUBDIRS
}
;
do
${
MAKE
}
-C
$$
i
$@
;
done
.PHONY
:
distclean
...
...
bin/vzrestore
View file @
f090efb3
#!/usr/bin/perl -w
#
# Copyright (C) 2007-2009 Proxmox Server Solutions GmbH
#
# Copyright: vzdump is under GNU GPL, the GNU General Public License.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 dated June, 1991.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
# MA 02110-1301, USA.
#
# Author: Dietmar Maurer <dietmar@proxmox.com>
#
use
strict
;
use
Getopt::
Long
;
use
Sys::
Syslog
;
use
File::
Path
;
use
PVE::
VZDump
;
use
PVE::VZDump::
OpenVZ
;
use
PVE::
SafeSyslog
;
use
PVE::
Tools
qw(extract_param)
;
use
PVE::
INotify
;
use
PVE::
RPCEnvironment
;
use
PVE::
CLIHandler
;
use
PVE::
JSONSchema
qw(get_standard_option)
;
use
PVE::API2::
OpenVZ
;
$ENV
{
LANG
}
=
"
C
";
# avoid locale related issues/warnings
use
Data::
Dumper
;
# fixme: remove
openlog
('
vzdump
',
'
cons,pid
',
'
daemon
'
);
use
base
qw(PVE::CLIHandler
)
;
my
$force
=
0
;
$ENV
{'
PATH
'}
=
'
/sbin:/bin:/usr/sbin:/usr/bin
'
;
sub
print_usage
{
my
$msg
=
shift
;
initlog
('
vzrestore
');
print
STDERR
"
ERROR:
$msg
\n\n
"
if
$msg
;
die
"
please run as root
\n
"
if
$>
!=
0
;
print
STDERR
"
usage: $0 [OPTIONS] <ARCHIVE> <VMID>
\n
";
print
STDERR
"
\n
";
print
STDERR
"
\t
--force overwrite existing conf file, private and root directory
\n\n
";
}
PVE::INotify::
inotify_init
();
if
(
!
GetOptions
('
force
'
=>
\
$force
))
{
print_usage
();
exit
(
-
1
);
}
my
$rpcenv
=
PVE::
RPCEnvironment
->
init
('
cli
');
if
(
$#ARGV
!=
1
)
{
print_usage
();
exit
(
-
1
);
}
$rpcenv
->
init_request
();
$rpcenv
->
set_language
(
$ENV
{
LANG
});
$rpcenv
->
set_user
('
root@pam
');
my
$archive
=
shift
;
my
$vmid
=
PVE::VZDump::
check_vmids
((
shift
))
->
[
0
];
__PACKAGE__
->
register_method
({
name
=>
'
vzrestore
',
path
=>
'
vzrestore
',
method
=>
'
POST
',
description
=>
"
Restore OpenVZ containers.
",
parameters
=>
{
additionalProperties
=>
0
,
properties
=>
{
vmid
=>
get_standard_option
('
pve-vmid
'),
backup
=>
{
description
=>
"
The backup file.
",
type
=>
'
string
',
maxLength
=>
255
,
},
force
=>
{
optional
=>
1
,
type
=>
'
boolean
',
description
=>
"
Allow to overwrite existing container.
",
},
},
},
returns
=>
{
type
=>
'
string
',
},
code
=>
sub
{
my
(
$param
)
=
@_
;
$SIG
{
INT
}
=
$SIG
{
TERM
}
=
$SIG
{
QUIT
}
=
$SIG
{
HUP
}
=
$SIG
{
PIPE
}
=
sub
{
die
"
interrupted by signal
\n
";
};
my
$backup
=
extract_param
(
$param
,
'
backup
');
sub
debugmsg
{
PVE::VZDump::
debugmsg
(
@_
);
}
# just a shortcut
$param
->
{
ostemplate
}
=
$backup
;
sub
run_command
{
PVE::VZDump::
run_command
(
undef
,
@_
);
}
# just a shortcut
$param
->
{
node
}
=
PVE::INotify::
nodename
();
sub
restore_openvz
{
my
(
$archive
,
$vmid
)
=
@_
;
$param
->
{
restore
}
=
1
;
my
$vzconf
=
PVE::VZDump::OpenVZ::
read_global_vz_config
(
);
my
$cfgdir
=
PVE::VZDump::OpenVZ::
VZDIR
.
"
/conf
";
return
PVE::API2::
OpenVZ
->
create_vm
(
$param
);
}});
my
$conffile
=
"
$cfgdir
/
${vmid}
.conf
";
my
$private
=
$vzconf
->
{
privatedir
};
$private
=~
s/\$VEID/$vmid/
;
my
$root
=
$vzconf
->
{
rootdir
};
$root
=~
s/\$VEID/$vmid/
;
my
$cmddef
=
[
__PACKAGE__
,
'
vzrestore
',
['
backup
',
'
vmid
'],
undef
,
sub
{
my
$upid
=
shift
;
my
$status
=
PVE::Tools::
upid_read_status
(
$upid
);
exit
(
$status
eq
'
OK
'
?
0
:
-
1
);
}];
print
"
you choose to force overwriting VPS config file, private and root directories.
\n
"
if
$force
;
push
@ARGV
,
'
help
'
if
!
scalar
(
@ARGV
)
;
die
"
unable to restore VM '
$vmid
' - VM already exists
\n
"
if
!
$force
&&
-
f
$conffile
;
;
die
"
unable to restore VPS '
${vmid}
' -
"
.
"
directory '
$private
' already exists
\n
"
if
!
$force
&&
-
d
$private
;
die
"
unable to restore VPS '
${vmid}
' -
"
.
"
directory '
$root
' already exists
\n
"
if
!
$force
&&
-
d
$root
;
PVE::CLIHandler::
handle_simple_cmd
(
$cmddef
,
\
@ARGV
,
undef
,
$0
);
eval
{
mkpath
$private
||
die
"
unable to create private dir '
$private
'
";
mkpath
$root
||
die
"
unable to create private dir '
$private
'
";
my
$cmd
=
"
tar xpf
$archive
--totals --sparse -C
$private
";
if
(
$archive
eq
'
-
')
{
debugmsg
('
info
',
"
extracting archive from STDIN
");
run_command
(
$cmd
,
input
=>
"
<&STDIN
");
}
else
{
debugmsg
('
info
',
"
extracting archive '
$archive
'
");
run_command
(
$cmd
);
}
debugmsg
('
info
',
"
extracting configuration to '
$conffile
'
");
my
$qroot
=
$vzconf
->
{
rootdir
};
$qroot
=~
s
|
/|\\\/
|
g
;
my
$qprivate
=
$vzconf
->
{
privatedir
};
$qprivate
=~
s
|
/|\\\/
|
g
;
my
$scmd
=
"
sed -r -e 's/VE_ROOT=.*/VE_ROOT=
\\\"
$qroot
\\\"
/' -e 's/VE_PRIVATE=.*/VE_PRIVATE=
\\\"
$qprivate
\\\"
/' -e 's/host_ifname=veth[0-9]+
\
./host_ifname=veth
${vmid}
./' <'
$private
/etc/vzdump/vps.conf' >'
$conffile
'
";
run_command
(
$scmd
);
foreach
my
$s
(
PVE::VZDump::OpenVZ::
SCRIPT_EXT
)
{
my
$tfn
=
"
$cfgdir
/
${vmid}
.
$s
";
my
$sfn
=
"
$private
/etc/vzdump/vps.
$s
";
if
(
-
f
$sfn
)
{
run_command
("
cp '
$sfn
' '
$tfn
'
");
}
}
rmtree
"
$private
/etc/vzdump
";
};
my
$err
=
$@
;
if
(
$err
)
{
rmtree
$private
;
rmtree
$root
;
unlink
$conffile
;
die
$err
;
}
}
my
$plugin
=
PVE::VZDump::
OpenVZ
->
new
();
if
(
$archive
ne
'
-
')
{
my
$firstfile
=
PVE::VZDump::
read_firstfile
(
$archive
);
if
(
$firstfile
eq
'
qemu-server.conf
')
{
die
"
ERROR: please use 'qmrestore' to restore QemuServer VMs
\n
";
}
}
my
$lock
=
$plugin
->
lock_vm
(
$vmid
);
eval
{
debugmsg
('
info
',
"
restore openvz backup '
$archive
' using ID
$vmid
",
undef
,
1
);
restore_openvz
(
$archive
,
$vmid
);
debugmsg
('
info
',
"
restore openvz backup '
$archive
' successful
",
undef
,
1
);
};
my
$err
=
$@
;
$plugin
->
unlock_vm
(
$vmid
);
if
(
$err
)
{
debugmsg
('
err
',
"
restore openvz backup '
$archive
' failed -
$err
",
undef
,
1
);
exit
(
-
1
);
}
exit
(
0
);
exit
0
;
__END__
=head1 NAME
vzrestore - restore OpenVZ vzdump backups
=head1 SYNOPSIS
vzrestore <archive> <VMID>
=include synopsis
=head1 DESCRIPTION
Restore
the OpenVZ vzdump backup <archive> to virtual machine <VMID>
.
Restore
s OpenVZ vzdump backups
.
=head1 SEE ALSO
vzdump(1) qmrestore(1)
vzdump(1) qmrestore(1)
=include pve_copyright
www/manager/Utils.js
View file @
f090efb3
...
...
@@ -377,6 +377,7 @@ Ext.define('PVE.Utils', { statics: {
qmsuspend
:
'
Suspend VM {0}
'
,
qmresume
:
'
Resume VM {0}
'
,
vzcreate
:
'
Create CT {0}
'
,
vzrestore
:
'
Restore CT {0}
'
,
vzdestroy
:
'
Destroy CT {0}
'
,
vzstart
:
'
Start CT {0}
'
,
vzstop
:
'
Stop CT {0}
'
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment