Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
J
jumpserver
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
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
ops
jumpserver
Commits
d323c9df
Commit
d323c9df
authored
Sep 18, 2016
by
ibuler
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Modify asset create
parent
acfe8950
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
89 additions
and
40 deletions
+89
-40
forms.py
apps/assets/forms.py
+25
-3
models.py
apps/assets/models.py
+35
-21
asset_create_update.html
apps/assets/templates/assets/asset_create_update.html
+8
-8
asset_list.html
apps/assets/templates/assets/asset_list.html
+2
-2
views.py
apps/assets/views.py
+12
-6
utils.py
apps/common/utils.py
+7
-0
django.po
apps/locale/zh/LC_MESSAGES/django.po
+0
-0
No files found.
apps/assets/forms.py
View file @
d323c9df
# coding:utf-8
# coding:utf-8
from
django
import
forms
from
django
import
forms
from
.models
import
IDC
,
Asset
,
AssetGroup
,
AdminUser
,
SystemUser
from
.models
import
IDC
,
Asset
,
AssetGroup
,
AdminUser
,
SystemUser
,
Tag
from
django.utils.translation
import
gettext_lazy
as
_
from
django.utils.translation
import
gettext_lazy
as
_
...
@@ -23,13 +23,35 @@ from django.utils.translation import gettext_lazy as _
...
@@ -23,13 +23,35 @@ from django.utils.translation import gettext_lazy as _
#
#
class
AssetCreateForm
(
forms
.
ModelForm
):
class
AssetCreateForm
(
forms
.
ModelForm
):
tags
=
forms
.
CharField
(
label
=
_
(
'Tags'
),
widget
=
forms
.
TextInput
(
attrs
=
{
'id'
:
'tags'
}),
help_text
=
'Use `,` split'
)
def
__init__
(
self
,
*
args
,
**
kwargs
):
instance
=
kwargs
.
get
(
'instance'
)
if
instance
:
initial
=
kwargs
.
get
(
'initial'
,
{})
tags
=
Tag
.
objects
.
filter
(
asset
=
instance
)
tags_value
=
','
.
join
([
tag
.
value
for
tag
in
tags
])
initial
[
'tags'
]
=
tags_value
super
(
AssetCreateForm
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
def
_save_m2m
(
self
):
tags
=
self
.
cleaned_data
[
'tags'
]
if
tags
:
value_list
=
tags
.
split
(
','
)
self
.
instance
.
tags
.
all
()
.
delete
()
Tag
.
objects
.
bulk_create
(
[
Tag
(
value
=
value
)
for
value
in
value_list
]
)
class
Meta
:
class
Meta
:
model
=
Asset
model
=
Asset
fields
=
[
fields
=
[
'hostname'
,
'ip'
,
'port'
,
'type'
,
'
zone'
,
'
comment'
,
'admin_user'
,
'system_users'
,
'idc'
,
'groups'
'hostname'
,
'ip'
,
'port'
,
'type'
,
'comment'
,
'admin_user'
,
'system_users'
,
'idc'
,
'groups'
]
]
widgets
=
{
widgets
=
{
'groups'
:
forms
.
SelectMultiple
(
attrs
=
{
'class'
:
'select2'
,
'groups'
:
forms
.
SelectMultiple
(
attrs
=
{
'class'
:
'select2'
,
'data-placeholder'
:
_
(
'Select asset groups'
)}),
'data-placeholder'
:
_
(
'Select asset groups'
)}),
...
...
apps/assets/models.py
View file @
d323c9df
# coding:utf-8
# coding:utf-8
from
__future__
import
unicode_literals
,
absolute_import
from
__future__
import
unicode_literals
,
absolute_import
import
functools
from
django.db
import
models
from
django.db
import
models
import
logging
import
logging
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.utils.translation
import
ugettext_lazy
as
_
...
@@ -25,6 +26,10 @@ class IDC(models.Model):
...
@@ -25,6 +26,10 @@ class IDC(models.Model):
def
__unicode__
(
self
):
def
__unicode__
(
self
):
return
self
.
name
return
self
.
name
@classmethod
def
initial
(
cls
):
return
cls
.
objects
.
get_or_create
(
name
=
_
(
'Default'
),
created_by
=
_
(
'System'
),
comment
=
_
(
'Default IDC'
))[
0
]
class
Meta
:
class
Meta
:
db_table
=
'idc'
db_table
=
'idc'
...
@@ -77,13 +82,12 @@ class AssetExtend(models.Model):
...
@@ -77,13 +82,12 @@ class AssetExtend(models.Model):
(
_
(
'env'
),
_
(
'Production'
)),
(
_
(
'env'
),
_
(
'Production'
)),
(
_
(
'env'
),
_
(
'Development'
)),
(
_
(
'env'
),
_
(
'Development'
)),
(
_
(
'env'
),
_
(
'Testing'
)),
(
_
(
'env'
),
_
(
'Testing'
)),
(
_
(
'zone'
),
_
(
'Default'
)),
):
):
cls
.
objects
.
create
(
key
=
k
,
value
=
v
,
created_by
=
'System'
)
cls
.
objects
.
create
(
key
=
k
,
value
=
v
,
created_by
=
'System'
)
class
Meta
:
class
Meta
:
db_table
=
'asset_extend'
db_table
=
'asset_extend'
index
_together
=
(
'key'
,
'value'
)
unique
_together
=
(
'key'
,
'value'
)
class
AdminUser
(
models
.
Model
):
class
AdminUser
(
models
.
Model
):
...
@@ -272,12 +276,16 @@ class AssetGroup(models.Model):
...
@@ -272,12 +276,16 @@ class AssetGroup(models.Model):
continue
continue
class
Asset
(
models
.
Model
):
def
get_default_extend
(
key
,
value
):
STATUS_DEFAULT
=
AssetExtend
.
objects
.
get_or_create
(
key
=
'status'
,
value
=
_
(
'In use'
))
return
AssetExtend
.
objects
.
get_or_create
(
key
=
key
,
value
=
value
)[
0
]
TYPE_DEFAULT
=
AssetExtend
.
objects
.
get_or_create
(
key
=
'type'
,
value
=
_
(
'Server'
))
ZONE_DEFAULT
=
AssetExtend
.
objects
.
get_or_create
(
key
=
'zone'
,
value
=
_
(
'Default'
))
def
get_default_idc
():
return
IDC
.
initial
()
ip
=
models
.
CharField
(
max_length
=
32
,
verbose_name
=
_
(
'IP'
))
class
Asset
(
models
.
Model
):
ip
=
models
.
GenericIPAddressField
(
max_length
=
32
,
verbose_name
=
_
(
'IP'
))
other_ip
=
models
.
CharField
(
max_length
=
255
,
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
'Other IP'
))
other_ip
=
models
.
CharField
(
max_length
=
255
,
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
'Other IP'
))
remote_card_ip
=
models
.
CharField
(
max_length
=
16
,
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
'Remote card IP'
))
remote_card_ip
=
models
.
CharField
(
max_length
=
16
,
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
'Remote card IP'
))
hostname
=
models
.
CharField
(
max_length
=
128
,
blank
=
True
,
verbose_name
=
_
(
'Hostname'
))
hostname
=
models
.
CharField
(
max_length
=
128
,
blank
=
True
,
verbose_name
=
_
(
'Hostname'
))
...
@@ -286,7 +294,9 @@ class Asset(models.Model):
...
@@ -286,7 +294,9 @@ class Asset(models.Model):
admin_user
=
models
.
ForeignKey
(
AdminUser
,
null
=
True
,
blank
=
True
,
related_name
=
'assets'
,
admin_user
=
models
.
ForeignKey
(
AdminUser
,
null
=
True
,
blank
=
True
,
related_name
=
'assets'
,
on_delete
=
models
.
SET_NULL
,
verbose_name
=
_
(
"Admin user"
))
on_delete
=
models
.
SET_NULL
,
verbose_name
=
_
(
"Admin user"
))
system_users
=
models
.
ManyToManyField
(
SystemUser
,
blank
=
True
,
related_name
=
'assets'
,
verbose_name
=
_
(
"System User"
))
system_users
=
models
.
ManyToManyField
(
SystemUser
,
blank
=
True
,
related_name
=
'assets'
,
verbose_name
=
_
(
"System User"
))
idc
=
models
.
ForeignKey
(
IDC
,
null
=
True
,
related_name
=
'assets'
,
on_delete
=
models
.
SET_NULL
,
verbose_name
=
_
(
'IDC'
))
idc
=
models
.
ForeignKey
(
IDC
,
null
=
True
,
related_name
=
'assets'
,
on_delete
=
models
.
SET_NULL
,
verbose_name
=
_
(
'IDC'
),
default
=
get_default_idc
)
mac_address
=
models
.
CharField
(
max_length
=
20
,
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
"Mac address"
))
mac_address
=
models
.
CharField
(
max_length
=
20
,
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
"Mac address"
))
brand
=
models
.
CharField
(
max_length
=
64
,
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
'Brand'
))
brand
=
models
.
CharField
(
max_length
=
64
,
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
'Brand'
))
cpu
=
models
.
CharField
(
max_length
=
64
,
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
'CPU'
))
cpu
=
models
.
CharField
(
max_length
=
64
,
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
'CPU'
))
...
@@ -296,14 +306,15 @@ class Asset(models.Model):
...
@@ -296,14 +306,15 @@ class Asset(models.Model):
cabinet_no
=
models
.
CharField
(
max_length
=
32
,
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
'Cabinet number'
))
cabinet_no
=
models
.
CharField
(
max_length
=
32
,
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
'Cabinet number'
))
cabinet_pos
=
models
.
IntegerField
(
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
'Cabinet position'
))
cabinet_pos
=
models
.
IntegerField
(
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
'Cabinet position'
))
number
=
models
.
CharField
(
max_length
=
32
,
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
'Asset number'
))
number
=
models
.
CharField
(
max_length
=
32
,
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
'Asset number'
))
status
=
models
.
ForeignKey
(
AssetExtend
,
null
=
True
,
blank
=
True
,
related_name
=
"status_asset"
,
status
=
models
.
ForeignKey
(
AssetExtend
,
null
=
True
,
blank
=
True
,
default
=
STATUS_DEFAULT
,
verbose_name
=
_
(
'Asset status'
))
related_name
=
"status_asset"
,
verbose_name
=
_
(
'Asset status'
),
default
=
functools
.
partial
(
get_default_extend
,
'status'
,
'In use'
))
type
=
models
.
ForeignKey
(
AssetExtend
,
null
=
True
,
limit_choices_to
=
{
'key'
:
'type'
},
type
=
models
.
ForeignKey
(
AssetExtend
,
null
=
True
,
limit_choices_to
=
{
'key'
:
'type'
},
default
=
TYPE_DEFAULT
,
related_name
=
"type_asset"
,
verbose_name
=
_
(
'Asset type'
))
related_name
=
"type_asset"
,
verbose_name
=
_
(
'Asset type'
),
default
=
functools
.
partial
(
get_default_extend
,
'type'
,
'Server'
))
env
=
models
.
ForeignKey
(
AssetExtend
,
null
=
True
,
limit_choices_to
=
{
'key'
:
'env'
},
env
=
models
.
ForeignKey
(
AssetExtend
,
null
=
True
,
limit_choices_to
=
{
'key'
:
'env'
},
related_name
=
"env_asset"
,
verbose_name
=
_
(
'Asset environment'
))
related_name
=
"env_asset"
,
verbose_name
=
_
(
'Asset environment'
),
zone
=
models
.
ForeignKey
(
AssetExtend
,
null
=
True
,
limit_choices_to
=
{
'key'
:
'zone'
},
default
=
ZONE_DEFAULT
,
default
=
functools
.
partial
(
get_default_extend
,
'env'
,
'Production'
))
related_name
=
"zone_asset"
,
verbose_name
=
_
(
'Asset zone'
))
sn
=
models
.
CharField
(
max_length
=
128
,
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
'Serial number'
))
sn
=
models
.
CharField
(
max_length
=
128
,
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
'Serial number'
))
created_by
=
models
.
CharField
(
max_length
=
32
,
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
'Created by'
))
created_by
=
models
.
CharField
(
max_length
=
32
,
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
'Created by'
))
is_active
=
models
.
BooleanField
(
default
=
True
,
verbose_name
=
_
(
'Is active'
))
is_active
=
models
.
BooleanField
(
default
=
True
,
verbose_name
=
_
(
'Is active'
))
...
@@ -318,7 +329,7 @@ class Asset(models.Model):
...
@@ -318,7 +329,7 @@ class Asset(models.Model):
class
Meta
:
class
Meta
:
db_table
=
'asset'
db_table
=
'asset'
index
_together
=
(
'ip'
,
'port'
)
unique
_together
=
(
'ip'
,
'port'
)
@classmethod
@classmethod
def
generate_fake
(
cls
,
count
=
100
):
def
generate_fake
(
cls
,
count
=
100
):
...
@@ -345,17 +356,15 @@ class Asset(models.Model):
...
@@ -345,17 +356,15 @@ class Asset(models.Model):
class
Tag
(
models
.
Model
):
class
Tag
(
models
.
Model
):
key
=
models
.
CharField
(
max_length
=
64
,
blank
=
True
,
verbose_name
=
_
(
'KEY'
))
value
=
models
.
CharField
(
max_length
=
64
,
verbose_name
=
_
(
'VALUE'
))
value
=
models
.
CharField
(
max_length
=
64
,
verbose_name
=
_
(
'VALUE'
))
asset
=
models
.
ForeignKey
(
Asset
,
null
=
True
,
blank
=
True
,
on_delete
=
models
.
SET_NULL
,
verbose_name
=
_
(
'Asset'
))
asset
=
models
.
ForeignKey
(
Asset
,
related_name
=
'tags'
,
on_delete
=
models
.
CASCADE
,
verbose_name
=
_
(
'Asset'
))
created_by
=
models
.
CharField
(
max_length
=
32
,
blank
=
True
,
verbose_name
=
_
(
"Created by"
))
date_created
=
models
.
DateTimeField
(
auto_now
=
True
,
null
=
True
)
def
__unicode__
(
self
):
def
__unicode__
(
self
):
return
self
.
key
return
self
.
value
class
Meta
:
class
Meta
:
db_table
=
'label'
db_table
=
'tag'
unique_together
=
(
'value'
,
'asset'
)
def
initial
():
def
initial
():
...
@@ -366,3 +375,8 @@ def initial():
...
@@ -366,3 +375,8 @@ def initial():
def
generate_fake
():
def
generate_fake
():
for
cls
in
(
AssetGroup
,
IDC
,
AdminUser
,
SystemUser
,
Asset
):
for
cls
in
(
AssetGroup
,
IDC
,
AdminUser
,
SystemUser
,
Asset
):
cls
.
generate_fake
()
cls
.
generate_fake
()
def
flush_all
():
for
cls
in
(
AssetGroup
,
AssetExtend
,
IDC
,
AdminUser
,
SystemUser
,
Asset
):
cls
.
objects
.
all
()
.
delete
()
apps/assets/templates/assets/asset_create_update.html
View file @
d323c9df
...
@@ -16,7 +16,6 @@
...
@@ -16,7 +16,6 @@
{{ form.ip|bootstrap_horizontal }}
{{ form.ip|bootstrap_horizontal }}
{{ form.port|bootstrap_horizontal }}
{{ form.port|bootstrap_horizontal }}
{{ form.type|bootstrap_horizontal }}
{{ form.type|bootstrap_horizontal }}
{{ form.zone|bootstrap_horizontal }}
<div
class=
"hr-line-dashed"
></div>
<div
class=
"hr-line-dashed"
></div>
<h3>
{% trans 'Group' %}
</h3>
<h3>
{% trans 'Group' %}
</h3>
...
@@ -30,13 +29,14 @@
...
@@ -30,13 +29,14 @@
<div
class=
"hr-line-dashed"
></div>
<div
class=
"hr-line-dashed"
></div>
<h3>
{% trans 'Other' %}
</h3>
<h3>
{% trans 'Other' %}
</h3>
<div
class=
"form-group"
>
{#
<div
class=
"form-group"
>
#}
<label
for=
"tags"
class=
"col-sm-2 control-label"
>
Tags
</label>
{#
<label
for=
"tags"
class=
"col-sm-2 control-label"
>
Tags
</label>
#}
<div
class=
"col-sm-9 col-lg-9 "
style=
"background-color: #fff"
>
{#
<div
class=
"col-sm-9 col-lg-9 "
style=
"background-color: #fff"
>
#}
<input
id=
"tags"
name=
"tags"
type=
"text"
class=
"form-control"
>
{#
<input
id=
"tags"
name=
"tags"
type=
"text"
class=
"form-control"
>
#}
<p
class=
"help-block"
>
{% trans 'Tips: Use `,` split' %}
</p>
{#
<p
class=
"help-block"
>
{% trans 'Tips: Use `,` split' %}
</p>
#}
</div>
{#
</div>
#}
</div>
{#
</div>
#}
{{ form.tags|bootstrap_horizontal }}
{{ form.comment|bootstrap_horizontal }}
{{ form.comment|bootstrap_horizontal }}
<div
class=
"hr-line-dashed"
></div>
<div
class=
"hr-line-dashed"
></div>
...
...
apps/assets/templates/assets/asset_list.html
View file @
d323c9df
...
@@ -41,8 +41,8 @@
...
@@ -41,8 +41,8 @@
{% endif %}
{% endif %}
</td>
</td>
<td
class=
"text-center"
>
<td
class=
"text-center"
>
<a
href=
"{% url 'assets:asset-update' pk=
user
.id %}"
class=
"btn btn-xs btn-info"
>
{% trans 'Update' %}
</a>
<a
href=
"{% url 'assets:asset-update' pk=
asset
.id %}"
class=
"btn btn-xs btn-info"
>
{% trans 'Update' %}
</a>
<a
href=
"{% url 'assets:asset-delete' pk=
user
.id %}"
class=
"btn btn-xs btn-danger del"
>
{% trans 'Delete' %}
</a>
-
<a
href=
"{% url 'assets:asset-delete' pk=
asset
.id %}"
class=
"btn btn-xs btn-danger del"
>
{% trans 'Delete' %}
</a>
-
{#
<a
href=
"{% url '' %}"
>
{% trans 'Delete' %}
</a>
#}
{#
<a
href=
"{% url '' %}"
>
{% trans 'Delete' %}
</a>
#}
</td>
</td>
</tr>
</tr>
...
...
apps/assets/views.py
View file @
d323c9df
...
@@ -10,6 +10,8 @@ from django.urls import reverse_lazy
...
@@ -10,6 +10,8 @@ from django.urls import reverse_lazy
from
django.contrib.messages.views
import
SuccessMessageMixin
from
django.contrib.messages.views
import
SuccessMessageMixin
from
django.views.generic.detail
import
DetailView
,
SingleObjectMixin
from
django.views.generic.detail
import
DetailView
,
SingleObjectMixin
from
common.utils
import
int_seq
from
.models
import
Asset
,
AssetGroup
,
IDC
,
AssetExtend
,
AdminUser
,
SystemUser
,
Tag
from
.models
import
Asset
,
AssetGroup
,
IDC
,
AssetExtend
,
AdminUser
,
SystemUser
,
Tag
from
.forms
import
AssetCreateForm
,
AssetGroupForm
,
IDCForm
,
AdminUserForm
,
SystemUserForm
from
.forms
import
AssetCreateForm
,
AssetGroupForm
,
IDCForm
,
AdminUserForm
,
SystemUserForm
from
.hands
import
AdminUserRequiredMixin
from
.hands
import
AdminUserRequiredMixin
...
@@ -21,6 +23,11 @@ class AssetListView(ListView):
...
@@ -21,6 +23,11 @@ class AssetListView(ListView):
context_object_name
=
'asset_list'
context_object_name
=
'asset_list'
template_name
=
'assets/asset_list.html'
template_name
=
'assets/asset_list.html'
def
get_queryset
(
self
):
queryset
=
super
(
AssetListView
,
self
)
.
get_queryset
()
queryset
=
sorted
(
queryset
,
key
=
lambda
asset
:
int_seq
(
asset
.
ip
.
split
(
'.'
)))
return
queryset
def
get_context_data
(
self
,
**
kwargs
):
def
get_context_data
(
self
,
**
kwargs
):
context
=
{
context
=
{
'app'
:
'Assets'
,
'app'
:
'Assets'
,
...
@@ -36,12 +43,10 @@ class AssetCreateView(AdminUserRequiredMixin, CreateView):
...
@@ -36,12 +43,10 @@ class AssetCreateView(AdminUserRequiredMixin, CreateView):
template_name
=
'assets/asset_create_update.html'
template_name
=
'assets/asset_create_update.html'
success_url
=
reverse_lazy
(
'assets:asset-list'
)
success_url
=
reverse_lazy
(
'assets:asset-list'
)
def
form_valid
(
self
,
form
):
# def form_valid(self, form):
asset
=
form
.
save
(
commit
=
False
)
# asset = form.save()
key
=
self
.
request
.
POST
.
get
(
'key'
,
''
)
# print(self.request.POST.get('tags'))
value
=
self
.
request
.
POST
.
get
(
'value'
,
''
)
# return super(AssetCreateView, self).form_valid(form)
asset
.
save
()
return
super
(
AssetCreateView
,
self
)
.
form_valid
(
form
)
def
get_context_data
(
self
,
**
kwargs
):
def
get_context_data
(
self
,
**
kwargs
):
context
=
{
context
=
{
...
@@ -58,6 +63,7 @@ class AssetUpdateView(UpdateView):
...
@@ -58,6 +63,7 @@ class AssetUpdateView(UpdateView):
class
AssetDeleteView
(
DeleteView
):
class
AssetDeleteView
(
DeleteView
):
model
=
Asset
model
=
Asset
template_name
=
'assets/delete_confirm.html'
success_url
=
reverse_lazy
(
'assets:asset-list'
)
success_url
=
reverse_lazy
(
'assets:asset-list'
)
...
...
apps/common/utils.py
View file @
d323c9df
...
@@ -100,3 +100,10 @@ def search_object_attr(obj, value='', attr_list=None, ignore_case=False):
...
@@ -100,3 +100,10 @@ def search_object_attr(obj, value='', attr_list=None, ignore_case=False):
def
get_logger
(
name
=
None
):
def
get_logger
(
name
=
None
):
return
logging
.
getLogger
(
'jumpserver.
%
s'
%
name
)
return
logging
.
getLogger
(
'jumpserver.
%
s'
%
name
)
def
int_seq
(
seq
):
try
:
return
map
(
int
,
seq
)
except
ValueError
:
return
seq
apps/locale/zh/LC_MESSAGES/django.po
View file @
d323c9df
This diff is collapsed.
Click to expand it.
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