Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
dCache
cta
Commits
eab120f5
Commit
eab120f5
authored
Sep 08, 2014
by
Eric Cano
Browse files
Changed printout of timings so that they appear in decimal notation without exponent.
Also removed slashes from parameter names.
parent
2f905c4b
Changes
10
Hide whitespace changes
Inline
Side-by-side
castor/log/LogContext.hpp
View file @
eab120f5
...
...
@@ -24,6 +24,7 @@
#pragma once
#include <ostream>
#include <cstdio>
#include "castor/log/Logger.hpp"
namespace
castor
{
...
...
@@ -159,6 +160,14 @@ class ScopedParamContainer{
m_names
.
push_back
(
s
);
return
*
this
;
}
ScopedParamContainer
&
addTiming
(
const
std
::
string
&
s
,
double
t
){
char
buf
[
100
];
std
::
snprintf
(
buf
,
sizeof
(
buf
),
"%f"
,
t
);
m_context
.
pushOrReplace
(
Param
(
s
,
buf
));
m_names
.
push_back
(
s
);
return
*
this
;
}
private:
LogContext
&
m_context
;
...
...
castor/tape/tapeserver/daemon/DiskReadTask.cpp
View file @
eab120f5
...
...
@@ -157,12 +157,12 @@ void DiskReadTask::circulateAllBlocks(size_t fromBlockId, MemBlock * mb){
//------------------------------------------------------------------------------
void
DiskReadTask
::
logWithStat
(
int
level
,
const
std
::
string
&
msg
,
log
::
LogContext
&
lc
){
log
::
ScopedParamContainer
params
(
lc
);
params
.
add
(
"transferTime"
,
m_stats
.
transferTime
)
.
add
(
"checksumingTime"
,
m_stats
.
checksumingTime
)
.
add
(
"waitDataTime"
,
m_stats
.
waitDataTime
)
.
add
(
"waitReportingTime"
,
m_stats
.
waitReportingTime
)
.
add
(
"checkingErrorTime"
,
m_stats
.
checkingErrorTime
)
.
add
(
"openingTime"
,
m_stats
.
openingTime
)
params
.
add
Timing
(
"transferTime"
,
m_stats
.
transferTime
)
.
add
Timing
(
"checksumingTime"
,
m_stats
.
checksumingTime
)
.
add
Timing
(
"waitDataTime"
,
m_stats
.
waitDataTime
)
.
add
Timing
(
"waitReportingTime"
,
m_stats
.
waitReportingTime
)
.
add
Timing
(
"checkingErrorTime"
,
m_stats
.
checkingErrorTime
)
.
add
Timing
(
"openingTime"
,
m_stats
.
openingTime
)
.
add
(
"payloadTransferSpeedMB/s"
,
1.0
*
m_stats
.
dataVolume
/
1024
/
1024
/
m_stats
.
transferTime
)
.
add
(
"FILEID"
,
m_migratedFile
->
fileid
())
...
...
castor/tape/tapeserver/daemon/DiskReadThreadPool.cpp
View file @
eab120f5
...
...
@@ -125,10 +125,10 @@ void DiskReadThreadPool::addThreadStats(const DiskStats& other){
//------------------------------------------------------------------------------
void
DiskReadThreadPool
::
logWithStat
(
int
level
,
const
std
::
string
&
message
){
log
::
ScopedParamContainer
params
(
m_lc
);
params
.
add
(
"poolTransferTime"
,
m_pooldStat
.
transferTime
)
.
add
(
"poolWaitFreeMemoryTime"
,
m_pooldStat
.
waitFreeMemoryTime
)
.
add
(
"poolCheckingErrorTime"
,
m_pooldStat
.
checkingErrorTime
)
.
add
(
"poolOpeningTime"
,
m_pooldStat
.
openingTime
)
params
.
add
Timing
(
"poolTransferTime"
,
m_pooldStat
.
transferTime
)
.
add
Timing
(
"poolWaitFreeMemoryTime"
,
m_pooldStat
.
waitFreeMemoryTime
)
.
add
Timing
(
"poolCheckingErrorTime"
,
m_pooldStat
.
checkingErrorTime
)
.
add
Timing
(
"poolOpeningTime"
,
m_pooldStat
.
openingTime
)
.
add
(
"poolFileCount"
,
m_pooldStat
.
filesCount
)
.
add
(
"poolDataVolumeInMB"
,
1.0
*
m_pooldStat
.
dataVolume
/
1024
/
1024
)
.
add
(
"Average_Pool_PayloadTransferSpeedMB/s"
,
...
...
@@ -174,10 +174,10 @@ void DiskReadThreadPool::DiskReadWorkerThread::run() {
void
DiskReadThreadPool
::
DiskReadWorkerThread
::
logWithStat
(
int
level
,
const
std
::
string
&
message
){
log
::
ScopedParamContainer
params
(
m_lc
);
params
.
add
(
"threadTransferTime"
,
m_threadStat
.
transferTime
)
.
add
(
"threadWaitFreeMemoryTime"
,
m_threadStat
.
waitFreeMemoryTime
)
.
add
(
"threadCheckingErrorTime"
,
m_threadStat
.
checkingErrorTime
)
.
add
(
"threadOpeningTime"
,
m_threadStat
.
openingTime
)
params
.
add
Timing
(
"threadTransferTime"
,
m_threadStat
.
transferTime
)
.
add
Timing
(
"threadWaitFreeMemoryTime"
,
m_threadStat
.
waitFreeMemoryTime
)
.
add
Timing
(
"threadCheckingErrorTime"
,
m_threadStat
.
checkingErrorTime
)
.
add
Timing
(
"threadOpeningTime"
,
m_threadStat
.
openingTime
)
.
add
(
"threaDataVolumeInMB"
,
1.0
*
m_threadStat
.
dataVolume
/
1024
/
1024
)
.
add
(
"threadPayloadTransferSpeedMB/s"
,
1.0
*
m_threadStat
.
dataVolume
/
1024
/
1024
/
m_threadStat
.
transferTime
);
...
...
castor/tape/tapeserver/daemon/DiskWriteTask.cpp
View file @
eab120f5
...
...
@@ -192,13 +192,13 @@ const DiskStats DiskWriteTask::getTaskStats() const{
//------------------------------------------------------------------------------
void
DiskWriteTask
::
logWithStat
(
int
level
,
const
std
::
string
&
msg
,
log
::
LogContext
&
lc
){
log
::
ScopedParamContainer
params
(
lc
);
params
.
add
(
"transferTime"
,
m_stats
.
transferTime
)
.
add
(
"checksumingTime"
,
m_stats
.
checksumingTime
)
.
add
(
"waitDataTime"
,
m_stats
.
waitDataTime
)
.
add
(
"waitReportingTime"
,
m_stats
.
waitReportingTime
)
.
add
(
"checkingErrorTime"
,
m_stats
.
checkingErrorTime
)
.
add
(
"openingTime"
,
m_stats
.
openingTime
)
.
add
(
"closingTime"
,
m_stats
.
closingTime
)
params
.
add
Timing
(
"transferTime"
,
m_stats
.
transferTime
)
.
add
Timing
(
"checksumingTime"
,
m_stats
.
checksumingTime
)
.
add
Timing
(
"waitDataTime"
,
m_stats
.
waitDataTime
)
.
add
Timing
(
"waitReportingTime"
,
m_stats
.
waitReportingTime
)
.
add
Timing
(
"checkingErrorTime"
,
m_stats
.
checkingErrorTime
)
.
add
Timing
(
"openingTime"
,
m_stats
.
openingTime
)
.
add
Timing
(
"closingTime"
,
m_stats
.
closingTime
)
.
add
(
"payloadTransferSpeedMB/s"
,
1.0
*
m_stats
.
dataVolume
/
1024
/
1024
/
m_stats
.
transferTime
)
.
add
(
"FILEID"
,
m_recallingFile
->
fileid
())
...
...
castor/tape/tapeserver/daemon/DiskWriteThreadPool.cpp
View file @
eab120f5
...
...
@@ -115,14 +115,14 @@ void DiskWriteThreadPool::addThreadStats(const DiskStats& other){
//------------------------------------------------------------------------------
void
DiskWriteThreadPool
::
logWithStat
(
int
level
,
const
std
::
string
&
message
){
log
::
ScopedParamContainer
params
(
m_lc
);
params
.
add
(
"poolTransferTime"
,
m_pooldStat
.
transferTime
)
.
add
(
"poolChecksumingTime"
,
m_pooldStat
.
checksumingTime
)
.
add
(
"poolWaitDataTime"
,
m_pooldStat
.
waitDataTime
)
.
add
(
"poolWaitReportingTime"
,
m_pooldStat
.
waitReportingTime
)
.
add
(
"poolCheckingErrorTime"
,
m_pooldStat
.
checkingErrorTime
)
.
add
(
"poolOpeningTime"
,
m_pooldStat
.
openingTime
)
params
.
add
Timing
(
"poolTransferTime"
,
m_pooldStat
.
transferTime
)
.
add
Timing
(
"poolChecksumingTime"
,
m_pooldStat
.
checksumingTime
)
.
add
Timing
(
"poolWaitDataTime"
,
m_pooldStat
.
waitDataTime
)
.
add
Timing
(
"poolWaitReportingTime"
,
m_pooldStat
.
waitReportingTime
)
.
add
Timing
(
"poolCheckingErrorTime"
,
m_pooldStat
.
checkingErrorTime
)
.
add
Timing
(
"poolOpeningTime"
,
m_pooldStat
.
openingTime
)
.
add
(
"poolFileCount"
,
m_pooldStat
.
filesCount
)
.
add
(
"poolClosingTime"
,
m_pooldStat
.
closingTime
)
.
add
Timing
(
"poolClosingTime"
,
m_pooldStat
.
closingTime
)
.
add
(
"poolDataVolumeInMB"
,
1.0
*
m_pooldStat
.
dataVolume
/
1024
/
1024
)
.
add
(
"poolPayloadTransferSpeedMB/s"
,
1.0
*
m_pooldStat
.
dataVolume
/
1024
/
1024
/
m_pooldStat
.
transferTime
);
...
...
@@ -180,14 +180,14 @@ void DiskWriteThreadPool::DiskWriteWorkerThread::run() {
void
DiskWriteThreadPool
::
DiskWriteWorkerThread
::
logWithStat
(
int
level
,
const
std
::
string
&
msg
)
{
log
::
ScopedParamContainer
params
(
m_lc
);
params
.
add
(
"threadTransferTime"
,
m_threadStat
.
transferTime
)
.
add
(
"threadChecksumingTime"
,
m_threadStat
.
checksumingTime
)
.
add
(
"threadWaitDataTime"
,
m_threadStat
.
waitDataTime
)
.
add
(
"threadWaitReportingTime"
,
m_threadStat
.
waitReportingTime
)
params
.
add
Timing
(
"threadTransferTime"
,
m_threadStat
.
transferTime
)
.
add
Timing
(
"threadChecksumingTime"
,
m_threadStat
.
checksumingTime
)
.
add
Timing
(
"threadWaitDataTime"
,
m_threadStat
.
waitDataTime
)
.
add
Timing
(
"threadWaitReportingTime"
,
m_threadStat
.
waitReportingTime
)
.
add
(
"threadFileCount"
,
m_threadStat
.
filesCount
)
.
add
(
"threadCheckingErrorTime"
,
m_threadStat
.
checkingErrorTime
)
.
add
(
"threadOpeningTime"
,
m_threadStat
.
openingTime
)
.
add
(
"threadClosingTime"
,
m_threadStat
.
closingTime
)
.
add
Timing
(
"threadCheckingErrorTime"
,
m_threadStat
.
checkingErrorTime
)
.
add
Timing
(
"threadOpeningTime"
,
m_threadStat
.
openingTime
)
.
add
Timing
(
"threadClosingTime"
,
m_threadStat
.
closingTime
)
.
add
(
"threaDataVolumeInMB"
,
1.0
*
m_threadStat
.
dataVolume
/
1024
/
1024
)
.
add
(
"threadPayloadTransferSpeedMB/s"
,
1.0
*
m_threadStat
.
dataVolume
/
1024
/
1024
/
m_threadStat
.
transferTime
);
...
...
castor/tape/tapeserver/daemon/TapeReadSingleThread.cpp
View file @
eab120f5
...
...
@@ -148,7 +148,7 @@ void castor::tape::tapeserver::daemon::TapeReadSingleThread::run() {
m_stats
.
mountTime
+=
timer
.
secs
(
utils
::
Timer
::
resetCounter
);
{
castor
::
log
::
ScopedParamContainer
scoped
(
m_logContext
);
scoped
.
add
(
"mountTime"
,
m_stats
.
mountTime
);
scoped
.
add
Timing
(
"mountTime"
,
m_stats
.
mountTime
);
m_logContext
.
log
(
LOG_INFO
,
"Tape mounted and drive ready"
);
}
// Then we have to initialise the tape read session
...
...
@@ -157,7 +157,7 @@ void castor::tape::tapeserver::daemon::TapeReadSingleThread::run() {
//and then report
{
castor
::
log
::
ScopedParamContainer
scoped
(
m_logContext
);
scoped
.
add
(
"positionTime"
,
m_stats
.
positionTime
);
scoped
.
add
Timing
(
"positionTime"
,
m_stats
.
positionTime
);
m_logContext
.
log
(
LOG_INFO
,
"Tape read session session successfully started"
);
}
m_initialProcess
.
tapeMountedForRead
();
...
...
@@ -220,22 +220,22 @@ void castor::tape::tapeserver::daemon::TapeReadSingleThread::logWithStat(
double
sessionTime
)
{
params
.
add
(
"type"
,
"read"
)
.
add
(
"VID"
,
m_volInfo
.
vid
)
.
add
(
"mountTime"
,
m_stats
.
mountTime
)
.
add
(
"positionTime"
,
m_stats
.
positionTime
)
.
add
(
"waitInstructionsTime"
,
m_stats
.
waitInstructionsTime
)
.
add
(
"checksumingTime"
,
m_stats
.
checksumingTime
)
.
add
(
"transferTime"
,
m_stats
.
transferTime
)
.
add
(
"waitFreeMemoryTime"
,
m_stats
.
waitFreeMemoryTime
)
.
add
(
"waitReportingTime"
,
m_stats
.
waitReportingTime
)
.
add
(
"unloadTime"
,
m_stats
.
unloadTime
)
.
add
(
"unmountTime"
,
m_stats
.
unmountTime
)
.
add
Timing
(
"mountTime"
,
m_stats
.
mountTime
)
.
add
Timing
(
"positionTime"
,
m_stats
.
positionTime
)
.
add
Timing
(
"waitInstructionsTime"
,
m_stats
.
waitInstructionsTime
)
.
add
Timing
(
"checksumingTime"
,
m_stats
.
checksumingTime
)
.
add
Timing
(
"transferTime"
,
m_stats
.
transferTime
)
.
add
Timing
(
"waitFreeMemoryTime"
,
m_stats
.
waitFreeMemoryTime
)
.
add
Timing
(
"waitReportingTime"
,
m_stats
.
waitReportingTime
)
.
add
Timing
(
"unloadTime"
,
m_stats
.
unloadTime
)
.
add
Timing
(
"unmountTime"
,
m_stats
.
unmountTime
)
.
add
(
"dataVolume"
,
m_stats
.
dataVolume
)
.
add
(
"headerVolume"
,
m_stats
.
headerVolume
)
.
add
(
"files"
,
m_stats
.
filesCount
)
.
add
(
"dataBandwidthMB
/
s"
,
1.0
*
m_stats
.
dataVolume
.
add
(
"dataBandwidthMB
p
s"
,
1.0
*
m_stats
.
dataVolume
/
1000
/
1000
/
sessionTime
)
.
add
(
"driveBandwidthMB
/
s"
,
1.0
*
(
m_stats
.
dataVolume
+
m_stats
.
headerVolume
)
.
add
(
"driveBandwidthMB
p
s"
,
1.0
*
(
m_stats
.
dataVolume
+
m_stats
.
headerVolume
)
/
1000
/
1000
/
sessionTime
)
.
add
(
"sessionTime"
,
sessionTime
);
.
add
Timing
(
"sessionTime"
,
sessionTime
);
m_logContext
.
log
(
level
,
msg
);
}
castor/tape/tapeserver/daemon/TapeReadTask.hpp
View file @
eab120f5
...
...
@@ -127,12 +127,12 @@ public:
// Log the successful transfer
double
fileTime
=
localTime
.
secs
();
log
::
ScopedParamContainer
params
(
lc
);
params
.
add
(
"positionTime"
,
localStats
.
positionTime
)
.
add
(
"transferTime"
,
localStats
.
transferTime
)
.
add
(
"waitFreeMemoryTime"
,
localStats
.
waitFreeMemoryTime
)
.
add
(
"waitReportingTime"
,
localStats
.
waitReportingTime
)
params
.
add
Timing
(
"positionTime"
,
localStats
.
positionTime
)
.
add
Timing
(
"transferTime"
,
localStats
.
transferTime
)
.
add
Timing
(
"waitFreeMemoryTime"
,
localStats
.
waitFreeMemoryTime
)
.
add
Timing
(
"waitReportingTime"
,
localStats
.
waitReportingTime
)
.
add
(
"dataVolume"
,
localStats
.
dataVolume
)
.
add
(
"totalTime"
,
fileTime
)
.
add
Timing
(
"totalTime"
,
fileTime
)
.
add
(
"driveTransferSpeedMiB/s"
,
(
localStats
.
dataVolume
+
localStats
.
headerVolume
)
/
1024
/
1024
...
...
castor/tape/tapeserver/daemon/TapeSingleThreadInterface.hpp
View file @
eab120f5
...
...
@@ -120,7 +120,7 @@ protected:
m_rmc
.
mountTape
(
m_volInfo
.
vid
,
m_drive
.
librarySlot
,
mode
);
const
std
::
string
modeAsString
=
std
::
string
(
"R"
)
+
((
mode
==
legacymsg
::
RmcProxy
::
MOUNT_MODE_READWRITE
)
?
"W"
:
""
);
scoped
.
add
(
"RMCMountTime"
,
timer
.
secs
()).
add
(
"mode"
,
modeAsString
);
scoped
.
add
Timing
(
"RMCMountTime"
,
timer
.
secs
()).
add
(
"mode"
,
modeAsString
);
m_logContext
.
log
(
LOG_INFO
,
"Tape Mounted"
);
}
...
...
castor/tape/tapeserver/daemon/TapeWriteSingleThread.cpp
View file @
eab120f5
...
...
@@ -91,7 +91,7 @@ tapeFlush(const std::string& message,uint64_t bytes,uint64_t files,
log
::
ScopedParamContainer
params
(
m_logContext
);
params
.
add
(
"files"
,
files
)
.
add
(
"bytes"
,
bytes
)
.
add
(
"flushTime"
,
flushTime
);
.
add
Timing
(
"flushTime"
,
flushTime
);
m_logContext
.
log
(
LOG_INFO
,
message
);
m_stats
.
flushTime
+=
flushTime
;
...
...
@@ -145,7 +145,7 @@ void castor::tape::tapeserver::daemon::TapeWriteSingleThread::run() {
m_stats
.
mountTime
+=
timer
.
secs
(
utils
::
Timer
::
resetCounter
);
{
castor
::
log
::
ScopedParamContainer
scoped
(
m_logContext
);
scoped
.
add
(
"mountTime"
,
m_stats
.
mountTime
);
scoped
.
add
Timing
(
"mountTime"
,
m_stats
.
mountTime
);
m_logContext
.
log
(
LOG_INFO
,
"Tape mounted and drive ready"
);
}
...
...
@@ -154,7 +154,7 @@ void castor::tape::tapeserver::daemon::TapeWriteSingleThread::run() {
m_stats
.
positionTime
+=
timer
.
secs
(
utils
::
Timer
::
resetCounter
);
{
castor
::
log
::
ScopedParamContainer
scoped
(
m_logContext
);
scoped
.
add
(
"positionTime"
,
m_stats
.
positionTime
);
scoped
.
add
Timing
(
"positionTime"
,
m_stats
.
positionTime
);
m_logContext
.
log
(
LOG_INFO
,
"Write session initialised, tape VID checked and drive positioned for writing"
);
}
...
...
@@ -230,23 +230,23 @@ int level,const std::string& msg, log::ScopedParamContainer& params,
double
sessionTime
){
params
.
add
(
"type"
,
"write"
)
.
add
(
"VID"
,
m_volInfo
.
vid
)
.
add
(
"mountTime"
,
m_stats
.
mountTime
)
.
add
(
"positionTime"
,
m_stats
.
positionTime
)
.
add
(
"waitInstructionsTime"
,
m_stats
.
waitInstructionsTime
)
.
add
(
"checksumingTime"
,
m_stats
.
checksumingTime
)
.
add
(
"transferTime"
,
m_stats
.
transferTime
)
.
add
(
"waitDataTime"
,
m_stats
.
waitDataTime
)
.
add
(
"waitReportingTime"
,
m_stats
.
waitReportingTime
)
.
add
(
"flushTime"
,
m_stats
.
flushTime
)
.
add
(
"unloadTime"
,
m_stats
.
unloadTime
)
.
add
(
"unmountTime"
,
m_stats
.
unmountTime
)
.
add
Timing
(
"mountTime"
,
m_stats
.
mountTime
)
.
add
Timing
(
"positionTime"
,
m_stats
.
positionTime
)
.
add
Timing
(
"waitInstructionsTime"
,
m_stats
.
waitInstructionsTime
)
.
add
Timing
(
"checksumingTime"
,
m_stats
.
checksumingTime
)
.
add
Timing
(
"transferTime"
,
m_stats
.
transferTime
)
.
add
Timing
(
"waitDataTime"
,
m_stats
.
waitDataTime
)
.
add
Timing
(
"waitReportingTime"
,
m_stats
.
waitReportingTime
)
.
add
Timing
(
"flushTime"
,
m_stats
.
flushTime
)
.
add
Timing
(
"unloadTime"
,
m_stats
.
unloadTime
)
.
add
Timing
(
"unmountTime"
,
m_stats
.
unmountTime
)
.
add
(
"dataVolume"
,
m_stats
.
dataVolume
)
.
add
(
"headerVolume"
,
m_stats
.
headerVolume
)
.
add
(
"files"
,
m_stats
.
filesCount
)
.
add
(
"dataBandwidthMB
/
s"
,
1.0
*
m_stats
.
dataVolume
.
add
(
"dataBandwidthMB
p
s"
,
1.0
*
m_stats
.
dataVolume
/
1000
/
1000
/
sessionTime
)
.
add
(
"driveBandwidthMB
/
s"
,
1.0
*
(
m_stats
.
dataVolume
+
m_stats
.
headerVolume
)
.
add
(
"driveBandwidthMB
p
s"
,
1.0
*
(
m_stats
.
dataVolume
+
m_stats
.
headerVolume
)
/
1000
/
1000
/
sessionTime
)
.
add
(
"sessionTime"
,
sessionTime
);
.
add
Timing
(
"sessionTime"
,
sessionTime
);
m_logContext
.
log
(
level
,
msg
);
}
castor/tape/tapeserver/daemon/TapeWriteTask.cpp
View file @
eab120f5
...
...
@@ -235,13 +235,13 @@ namespace daemon {
void
TapeWriteTask
::
logWithStats
(
int
level
,
const
std
::
string
&
msg
,
double
fileTime
,
log
::
LogContext
&
lc
)
const
{
log
::
ScopedParamContainer
params
(
lc
);
params
.
add
(
"transferTime"
,
m_taskStats
.
transferTime
)
.
add
(
"checksumingTime"
,
m_taskStats
.
checksumingTime
)
.
add
(
"waitDataTime"
,
m_taskStats
.
waitDataTime
)
.
add
(
"waitReportingTime"
,
m_taskStats
.
waitReportingTime
)
params
.
add
Timing
(
"transferTime"
,
m_taskStats
.
transferTime
)
.
add
Timing
(
"checksumingTime"
,
m_taskStats
.
checksumingTime
)
.
add
Timing
(
"waitDataTime"
,
m_taskStats
.
waitDataTime
)
.
add
Timing
(
"waitReportingTime"
,
m_taskStats
.
waitReportingTime
)
.
add
(
"dataVolume"
,
m_taskStats
.
dataVolume
)
.
add
(
"headerVolume"
,
m_taskStats
.
headerVolume
)
.
add
(
"totalTime"
,
fileTime
)
.
add
Timing
(
"totalTime"
,
fileTime
)
.
add
(
"driveTransferSpeedMiB/s"
,
(
m_taskStats
.
dataVolume
+
m_taskStats
.
headerVolume
)
/
1024
/
1024
...
...
Write
Preview
Supports
Markdown
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