Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
RECIRCLE
/
interface-openstack
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
2
Merge Requests
4
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
d7f5e29b
authored
Jul 03, 2019
by
Belákovics Ádám
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'vm'
parents
9e46f1ed
51b00d53
Pipeline
#731
failed with stage
in 38 seconds
Changes
5
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
196 additions
and
63 deletions
+196
-63
implementation/vm/instance.py
+82
-39
interface/vm/instance.py
+9
-12
interface/vm/resources.py
+19
-5
main.py
+31
-7
tests/vmtest.py
+55
-0
No files found.
implementation/vm/instance.py
View file @
d7f5e29b
from
interface.vm.instance
import
InstanceInterface
from
interface.vm.instance
import
InstanceInterface
from
interface.vm.resources
import
Instance
from
interface.vm.resources
import
Instance
,
Flavor
from
openstack.exceptions
import
SDKException
from
openstack.exceptions
import
SDKException
from
novaclient
import
client
from
novaclient
import
client
import
logging
import
logging
...
@@ -24,8 +24,8 @@ def openstackError(func):
...
@@ -24,8 +24,8 @@ def openstackError(func):
try
:
try
:
return
func
(
*
args
,
**
kw
)
return
func
(
*
args
,
**
kw
)
except
SDKException
as
e
:
except
SDKException
as
e
:
logging
.
error
(
e
.
get_error_message
()
)
logging
.
error
(
e
)
new_e
=
Exception
(
e
.
get_error_message
()
)
new_e
=
Exception
(
e
)
new_e
.
OpenStackError
=
True
new_e
.
OpenStackError
=
True
raise
new_e
raise
new_e
return
wrap_OpenStackError
return
wrap_OpenStackError
...
@@ -38,24 +38,27 @@ class OSVirtualMachineManager(InstanceInterface):
...
@@ -38,24 +38,27 @@ class OSVirtualMachineManager(InstanceInterface):
self
.
openstack
=
cloud
self
.
openstack
=
cloud
@openstackError
@openstackError
def
create_base_vm
(
self
,
name
,
resource
,
networks
,
block_dev_map
):
def
create_base_vm
(
self
,
name
,
flavor
,
networks
,
block_dev_map
):
flavor
=
self
.
get_flavor
(
resource
)
devices
=
[]
new_server
=
self
.
compute
.
create_server
(
name
=
name
,
b_device
=
block_dev_map
.
__dict__
flavorRef
=
flavor
.
id
,
devices
.
append
(
b_device
)
networks
=
networks
,
flavor
=
self
.
get_flavor
(
flavor
)
block_device_mapping
=
block_dev_map
new_server
=
self
.
openstack
.
compute
.
create_server
(
name
=
name
,
)
flavorRef
=
flavor
.
id
,
return
new_server
networks
=
networks
,
block_device_mapping
=
devices
)
return
self
.
convert_server_to_instance
(
new_server
)
@openstackError
@openstackError
def
create_vm_from_template
(
self
,
name
,
image
,
resource
,
networks
):
def
create_vm_from_template
(
self
,
name
,
image
,
flavor
,
networks
):
self
.
create_multiple_vm_from_template
(
name
,
image
,
resource
,
networks
,
1
)
return
self
.
create_multiple_vm_from_template
(
name
,
image
,
flavor
,
networks
,
1
)
@openstackError
@openstackError
def
create_multiple_vm_from_template
(
self
,
name
,
image
,
resource
,
networks
,
def
create_multiple_vm_from_template
(
self
,
name
,
image
,
flavor
,
networks
,
number
,
**
args
):
number
,
**
args
):
compute
=
self
.
openstack
.
compute
compute
=
self
.
openstack
.
compute
flav
=
compute
.
find_flavor
(
resource
)
flav
=
compute
.
find_flavor
(
flavor
)
image
=
compute
.
find_image
(
image
)
image
=
compute
.
find_image
(
image
)
if
not
image
:
if
not
image
:
...
@@ -67,76 +70,104 @@ class OSVirtualMachineManager(InstanceInterface):
...
@@ -67,76 +70,104 @@ class OSVirtualMachineManager(InstanceInterface):
networks
=
networks
,
networks
=
networks
,
min_count
=
number
,
min_count
=
number
,
)
)
return
self
.
convert_server_to_instance
(
new_server
)
new_server
=
self
.
openstack
.
compute
.
wait_for_server
(
new_server
)
@openstackError
def
create_flavor
(
self
,
name
,
ram
,
vcpus
,
initial_disk
):
flavor
=
self
.
openstack
.
compute
.
create_flavor
(
name
=
name
,
ram
=
ram
,
vcpus
=
vcpus
,
disk
=
initial_disk
)
return
Flavor
(
flavor
.
name
,
flavor
.
id
,
flavor
.
ram
,
flavor
.
vcpus
,
flavor
.
disk
)
return
new_server
@openstackError
def
get_flavor
(
self
,
flavor_id
):
flavor
=
self
.
openstack
.
compute
.
find_flavor
(
flavor_id
)
return
Flavor
(
flavor
.
name
,
flavor
.
id
,
flavor
.
ram
,
flavor
.
vcpus
,
flavor
.
disk
)
@openstackError
def
delete_flavor
(
self
,
flavor_id
):
flavor
=
self
.
openstack
.
compute
.
find_flavor
(
flavor_id
)
self
.
openstack
.
compute
.
delete_flavor
(
flavor
)
@openstackError
def
list_flavors
(
self
):
flavors
=
[]
for
flavor
in
self
.
openstack
.
compute
.
flavors
():
flavors
.
append
(
Flavor
(
flavor
.
name
,
flavor
.
id
,
flavor
.
ram
,
flavor
.
vcpus
,
flavor
.
disk
))
return
flavors
@openstackError
@openstackError
def
get_vm
(
self
,
name_or_id
=
None
):
def
get_vm
(
self
,
name_or_id
=
None
):
if
not
name_or_id
:
if
not
name_or_id
:
raise
ValueError
(
"Name or id doesn't given"
)
raise
ValueError
(
"Name or id doesn't given"
)
server_instance
=
self
.
openstack
.
get_server
(
name_or_id
)
server_instance
=
self
.
openstack
.
compute
.
get_server
(
name_or_id
)
if
not
server_instance
:
if
not
server_instance
:
raise
ValueError
(
"Could not get the vm"
)
raise
ValueError
(
"Could not get the vm"
)
return
se
rver_instance
return
se
lf
.
convert_server_to_instance
(
server_instance
)
@openstackError
@openstackError
def
start_vm
(
self
,
name_or_id
=
None
):
def
start_vm
(
self
,
name_or_id
=
None
):
if
name_or_id
:
if
name_or_id
:
instance
=
self
.
get_vm
(
name_or_id
)
instance
=
self
.
openstack
.
compute
.
get_server
(
name_or_id
)
self
.
openstack
.
compute
.
start_server
(
instance
)
self
.
openstack
.
compute
.
start_server
(
instance
)
@openstackError
@openstackError
def
stop_vm
(
self
,
name_or_id
=
None
):
def
stop_vm
(
self
,
name_or_id
=
None
):
if
name_or_id
:
if
name_or_id
:
instance
=
self
.
get_vm
(
name_or_id
)
instance
=
self
.
openstack
.
compute
.
get_server
(
name_or_id
)
self
.
openstack
.
compute
.
stop_server
(
instance
)
self
.
openstack
.
compute
.
stop_server
(
instance
)
@openstackError
@openstackError
def
suspend_vm
(
self
,
name_or_id
=
None
):
def
suspend_vm
(
self
,
name_or_id
=
None
):
if
name_or_id
:
if
name_or_id
:
instance
=
self
.
get_vm
(
name_or_id
)
instance
=
self
.
openstack
.
compute
.
get_server
(
name_or_id
)
self
.
openstack
.
compute
.
suspend_server
(
instance
)
self
.
openstack
.
compute
.
suspend_server
(
instance
)
@openstackError
@openstackError
def
wake_up_vm
(
self
,
name_or_id
=
None
):
def
wake_up_vm
(
self
,
name_or_id
=
None
):
if
name_or_id
:
if
name_or_id
:
instance
=
self
.
get_vm
(
name_or_id
)
instance
=
self
.
openstack
.
compute
.
get_server
(
name_or_id
)
self
.
openstack
.
compute
.
resume_server
(
instance
)
self
.
openstack
.
compute
.
resume_server
(
instance
)
@openstackError
@openstackError
def
reboot_vm
(
self
,
name_or_id
):
def
reboot_vm
(
self
,
name_or_id
):
if
name_or_id
:
if
name_or_id
:
instance
=
self
.
get_vm
(
name_or_id
)
instance
=
self
.
openstack
.
compute
.
get_server
(
name_or_id
)
self
.
openstack
.
compute
.
reboot_server
(
instance
,
reboot_type
=
'SOFT'
)
self
.
openstack
.
compute
.
reboot_server
(
instance
,
reboot_type
=
'SOFT'
)
@openstackError
@openstackError
def
reset_vm
(
self
,
name_or_id
):
def
reset_vm
(
self
,
name_or_id
):
if
name_or_id
:
if
name_or_id
:
instance
=
self
.
get_vm
(
name_or_id
)
instance
=
self
.
openstack
.
compute
.
get_server
(
name_or_id
)
self
.
openstack
.
compute
.
reboot_server
(
instance
,
reboot_type
=
'HARD'
)
self
.
openstack
.
compute
.
reboot_server
(
instance
,
reboot_type
=
'HARD'
)
@openstackError
@openstackError
def
destroy_vm
(
self
,
name_or_id
):
def
destroy_vm
(
self
,
name_or_id
):
if
name_or_id
:
if
name_or_id
:
instance
=
self
.
get_vm
(
name_or_id
)
instance
=
self
.
openstack
.
compute
.
get_server
(
name_or_id
)
self
.
openstack
.
compute
.
delete_server
(
instance
)
self
.
openstack
.
compute
.
delete_server
(
instance
)
@openstackError
@openstackError
def
get_status
(
self
,
name_or_id
):
def
get_status
(
self
,
name_or_id
):
if
name_or_id
:
if
name_or_id
:
instance
=
self
.
get_vm
(
name_or_id
)
instance
=
self
.
openstack
.
compute
.
get_server
(
name_or_id
)
return
instance
.
status
return
instance
.
status
@openstackError
@openstackError
def
list_all_vm
(
self
):
def
list_all_vm
(
self
):
return
self
.
openstack
.
compute
.
servers
()
servers
=
[]
for
server
in
self
.
openstack
.
compute
.
servers
():
servers
.
append
(
self
.
convert_server_to_instance
(
server
))
return
servers
@openstackError
@openstackError
def
resize_vm
(
self
,
name_or_id
,
resource
):
def
resize_vm
(
self
,
name_or_id
,
resource
):
if
name_or_id
:
if
name_or_id
:
instance
=
self
.
get_vm
(
name_or_id
)
instance
=
self
.
openstack
.
get_server
(
name_or_id
)
flavor
=
self
.
openstack
.
compute
.
find_flavor
(
resource
[
'name'
])
flavor
=
self
.
openstack
.
compute
.
find_flavor
(
resource
[
'name'
])
self
.
openstack
.
compute
.
resize_server
(
instance
,
flavor
)
self
.
openstack
.
compute
.
resize_server
(
instance
,
flavor
)
...
@@ -144,28 +175,40 @@ class OSVirtualMachineManager(InstanceInterface):
...
@@ -144,28 +175,40 @@ class OSVirtualMachineManager(InstanceInterface):
@openstackError
@openstackError
def
create_template
(
self
,
name_or_id
,
template_name
,
metadata
=
None
):
def
create_template
(
self
,
name_or_id
,
template_name
,
metadata
=
None
):
if
name_or_id
:
if
name_or_id
:
instance
=
self
.
get_vm
(
name_or_id
)
instance
=
self
.
openstack
.
compute
.
get_server
(
name_or_id
)
self
.
openstack
.
compute
.
create_server_image
(
instance
,
template_name
,
metadata
)
self
.
openstack
.
compute
.
create_server_image
(
instance
,
template_name
,
metadata
)
def
get_vnc_console
(
self
,
name_o
r_id
):
def
get_vnc_console
(
self
,
serve
r_id
):
with
client
.
Client
(
"2"
,
session
=
sess
)
as
nova
:
with
client
.
Client
(
"2"
,
session
=
sess
)
as
nova
:
if
name_o
r_id
:
if
serve
r_id
:
instance
=
nova
.
servers
.
get
(
name_o
r_id
)
instance
=
nova
.
servers
.
get
(
serve
r_id
)
return
instance
.
get_vnc_console
(
"novnc"
)
return
instance
.
get_vnc_console
(
"novnc"
)
def
attach_volume
(
self
,
name_or_id
,
amount
):
@openstackError
raise
NotImplementedError
def
attach_volume
(
self
,
server_id
,
volume_id
,
device
=
None
):
self
.
openstack
.
compute
.
create_volume_attachment
(
server_id
,
{
"volumeId"
:
volume_id
,
})
@openstackError
def
detach_volume
(
self
,
server_id
,
volume_id
,
device
=
None
):
self
.
openstack
.
compute
.
delete_volume_attachment
(
server_id
,
{
"volumeId"
:
volume_id
,
"device"
:
device
})
def
convert_server_to_instance
(
self
,
server
):
def
convert_server_to_instance
(
self
,
server
):
if
not
server
.
image
:
if
not
server
.
image
:
image_id
=
None
image_id
=
None
else
:
else
:
image_id
=
server
.
image
.
id
image_id
=
server
.
image
_
id
return
Instance
(
id
=
server
.
id
,
return
Instance
(
id
=
server
.
id
,
resource
=
server
.
flavor
.
id
,
flavor
=
server
.
flavor_
id
,
name
=
server
.
name
,
name
=
server
.
name
,
image_id
=
image_id
,
image_id
=
image_id
,
disks
=
server
.
volumes
,
disks
=
server
.
attached_
volumes
,
status
=
server
.
status
,
status
=
server
.
status
,
launched_at
=
server
.
launched_at
,
launched_at
=
server
.
launched_at
,
terminated_at
=
server
.
terminated_at
,
terminated_at
=
server
.
terminated_at
,
...
...
interface/vm/instance.py
View file @
d7f5e29b
...
@@ -7,10 +7,13 @@ It should be implemented for using other providers e. g. OpenStack
...
@@ -7,10 +7,13 @@ It should be implemented for using other providers e. g. OpenStack
class
InstanceInterface
:
class
InstanceInterface
:
def
create_
vm_from_template
(
self
,
template
,
resource
):
def
create_
base_vm
(
self
,
name
,
flavor
,
networks
,
block_device_mapping
):
raise
NotImplementedError
raise
NotImplementedError
def
create_multiple_vm_from_template
(
self
,
template
,
resource
,
number
):
def
create_vm_from_template
(
self
,
name
,
image
,
flavor
,
networks
):
raise
NotImplementedError
def
create_multiple_vm_from_template
(
self
,
image
,
flavor
,
networks
,
number
):
raise
NotImplementedError
raise
NotImplementedError
def
get_vm
(
self
,
name_or_id
):
def
get_vm
(
self
,
name_or_id
):
...
@@ -40,9 +43,6 @@ class InstanceInterface:
...
@@ -40,9 +43,6 @@ class InstanceInterface:
def
destroy_vm
(
self
,
name_or_id
):
def
destroy_vm
(
self
,
name_or_id
):
raise
NotImplementedError
raise
NotImplementedError
def
migrate_vm
(
self
,
name_or_id
,
to
):
raise
NotImplementedError
def
get_status
(
self
,
name_or_id
):
def
get_status
(
self
,
name_or_id
):
raise
NotImplementedError
raise
NotImplementedError
...
@@ -52,17 +52,14 @@ class InstanceInterface:
...
@@ -52,17 +52,14 @@ class InstanceInterface:
def
install_ssh_key
(
self
,
name_or_id
,
key
):
def
install_ssh_key
(
self
,
name_or_id
,
key
):
raise
NotImplementedError
raise
NotImplementedError
def
save_as_templat
e
(
self
,
name_or_id
):
def
get_vnc_consol
e
(
self
,
name_or_id
):
raise
NotImplementedError
raise
NotImplementedError
def
get_vnc_console
(
self
,
name_or_id
):
def
change_password
(
self
,
name_or_id
):
raise
NotImplementedError
raise
NotImplementedError
# def change_password(self, name_or_id):
def
get_password
(
self
,
name_or_id
):
# raise NotImplementedError
raise
NotImplementedError
#
# def get_password(self, name_or_id):
# raise NotImplementedError
def
resize_vm
(
self
,
name_or_id
):
def
resize_vm
(
self
,
name_or_id
):
raise
NotImplementedError
raise
NotImplementedError
...
...
interface/vm/resources.py
View file @
d7f5e29b
...
@@ -10,11 +10,11 @@ class Instance:
...
@@ -10,11 +10,11 @@ class Instance:
ssh_keys
=
None
ssh_keys
=
None
console_access_url
=
None
console_access_url
=
None
def
__init__
(
self
,
id
,
name
,
resource
,
image_id
,
def
__init__
(
self
,
id
,
name
,
flavor
,
image_id
,
status
,
status
,
addresses
,
launched_at
,
terminated_at
,
disks
=
None
):
addresses
,
launched_at
,
terminated_at
,
disks
=
None
):
self
.
id
=
id
self
.
id
=
id
self
.
name
=
name
self
.
name
=
name
self
.
resource
=
resource
self
.
flavor
=
flavor
self
.
image
=
image_id
self
.
image
=
image_id
self
.
disks
=
disks
self
.
disks
=
disks
self
.
status
=
status
self
.
status
=
status
...
@@ -34,6 +34,20 @@ class Flavor:
...
@@ -34,6 +34,20 @@ class Flavor:
self
.
vcpus
=
vcpus
self
.
vcpus
=
vcpus
self
.
initial_disk
=
disk
self
.
initial_disk
=
disk
def
JSON
(
self
):
return
json
.
dumps
(
self
.
__dict__
)
class
Volume
:
class
BlockDeviceMapping
:
pass
def
__init__
(
self
,
boot_index
,
uuid
,
source_type
,
volume_size
,
destination_type
,
delete_on_termination
,
disk_bus
):
self
.
boot_index
=
boot_index
self
.
uuid
=
uuid
self
.
source_type
=
source_type
self
.
volume_size
=
volume_size
self
.
destination_type
=
destination_type
self
.
delete_on_termination
=
delete_on_termination
self
.
disk_bus
=
disk_bus
def
JSON
(
self
):
return
json
.
dumps
(
self
.
__dict__
)
main.py
View file @
d7f5e29b
# This file is for testing the openstack api access
# This file is for testing the openstack api access
from
implementation.vm.instance
import
OSVirtualMachineManager
from
implementation.vm.instance
import
OSVirtualMachineManager
from
interface.vm.resources
import
BlockDeviceMapping
,
Flavor
import
openstack
import
openstack
# openstack.enable_logging(debug=True)
# openstack.enable_logging(debug=True)
conn
=
openstack
.
connect
(
cloud
=
'openstack'
)
conn
=
openstack
.
connect
(
cloud
=
'openstack'
)
block_dev_map
=
BlockDeviceMapping
(
boot_index
=
0
,
uuid
=
"da51253f-867c-472d-8ce0-81e7b7126d60"
,
source_type
=
"image"
,
volume_size
=
10
,
destination_type
=
"volume"
,
delete_on_termination
=
True
,
disk_bus
=
"scsi"
)
networks
=
[{
"uuid"
:
"c03d0d4b-413e-4cc6-9ebe-c0b5ca0dac3a"
}]
interface
=
OSVirtualMachineManager
(
conn
)
interface
=
OSVirtualMachineManager
(
conn
)
resource
=
{
"name"
:
"m1.tiny"
,
"ram"
:
1024
,
"cpu"
:
2
}
print
(
'#'
*
40
)
print
(
'#'
*
40
)
print
(
resource
)
# interface.create_vm_from_template('new_server', resource, "cirros-0.4.0-x86_64-disk")
# interface.create_vm_from_template('new_server', resource, "cirros-0.4.0-x86_64-disk")
# interface.stop_vm('a6bc504f-a422-4492-b429-e5dad2df12f4')
# interface.stop_vm('a6bc504f-a422-4492-b429-e5dad2df12f4')
server
=
interface
.
get_vm
(
"New test"
)
# server = interface.openstack.compute.get_server("8e94c162-f8ed-4872-b9e5-50bf33040b5b")
#
# print(server)
# print(interface.delete_flavor("flavorTest"))
for
flavor
in
interface
.
list_flavors
():
print
(
flavor
.
JSON
())
for
vm
in
interface
.
list_all_vm
():
print
(
vm
.
JSON
())
print
(
"*"
*
50
)
print
(
interface
.
create_base_vm
(
"BaseTest"
,
"2"
,
networks
,
block_dev_map
)
.
JSON
())
# print(interface.convert_server_to_instance(server).JSON())
print
(
interface
.
convert_server_to_instance
(
server
)
.
JSON
(
))
# print(interface.openstack.compute.get_server("8e94c162-f8ed-4872-b9e5-50bf33040b5b"
))
print
(
interface
.
get_status
(
"Uborka Test"
))
# print(server.JSON(
))
print
(
interface
.
get_vnc_console
(
"8e94c162-f8ed-4872-b9e5-50bf33040b5b"
))
#
print(interface.get_vnc_console("8e94c162-f8ed-4872-b9e5-50bf33040b5b"))
# print(interface.convert_server_to_instance(server))
# print(interface.convert_server_to_instance(server))
...
@@ -37,4 +62,3 @@ print(interface.get_vnc_console("8e94c162-f8ed-4872-b9e5-50bf33040b5b"))
...
@@ -37,4 +62,3 @@ print(interface.get_vnc_console("8e94c162-f8ed-4872-b9e5-50bf33040b5b"))
# interface.wake_up_vm(server.id)
# interface.wake_up_vm(server.id)
# interface.destroy_vm(server.id)
# interface.destroy_vm(server.id)
# conn.compute.change_server_password(server, "root")
tests/vmtest.py
0 → 100644
View file @
d7f5e29b
import
unittest
from
unittest.mock
import
MagicMock
from
implementation.vm.instance
import
OSVirtualMachineManager
from
interface.vm.resources
import
Instance
servers
=
[
MagicMock
(
id
=
"test1"
,
name
=
"test1"
,
flavorRef
=
"flav1"
,
imageRef
=
"image1"
,
networks
=
[{
"uuid"
:
"network1"
}],
terminated_at
=
"20200320-15-31"
,
launched_at
=
"20190510-13-22"
,
disks
=
[
"disk1"
],
status
=
"ACTIVE"
,
addresses
=
[{
"mac"
:
"12345678AB"
,
"ipv4"
:
"1.1.1.1"
}]
)
]
class
MockOpenStackCompute
(
MagicMock
):
def
setUp
(
self
):
self
.
compute
=
MagicMock
()
self
.
compute
.
create_server
=
MagicMock
(
return_value
=
servers
[
0
])
self
.
compute
.
find_flavor
=
MagicMock
(
return_value
=
MagicMock
(
id
=
"uuid1"
))
self
.
compute
.
find_image
=
MagicMock
(
return_value
=
MagicMock
(
id
=
"uuid2"
))
self
.
compute
.
wait_for_server
=
MagicMock
(
return_value
=
True
)
self
.
compute
.
list_servers
=
MagicMock
(
return_value
=
servers
)
class
InstanceCreateTestCase
(
unittest
.
TestCase
):
def
setUp
(
self
):
self
.
conn
=
MockOpenStackCompute
()
self
.
conn
.
setUp
()
self
.
manager
=
OSVirtualMachineManager
(
self
.
conn
)
def
tearDown
(
self
):
pass
def
test_create_from_template
(
self
):
instance
=
self
.
manager
.
create_vm_from_template
(
'test'
,
'imageid'
,
'flavorid'
,
[
'networkid1'
])
self
.
conn
.
compute
.
create_server
.
assert_called
()
def
test_create_from_template_params
(
self
):
self
.
manager
.
create_vm_from_template
(
'test'
,
'imageid'
,
'flavorid'
,
[
'networkid1'
])
self
.
conn
.
compute
.
create_server
.
assert_called_with
(
name
=
'test'
,
flavorRef
=
"uuid1"
,
imageRef
=
"uuid2"
,
networks
=
[
'networkid1'
],
min_count
=
1
)
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