Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
CONF
Project
Project
Details
Activity
Releases
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
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
UvA
CONF
Commits
e34bbc8d
Commit
e34bbc8d
authored
Sep 16, 2019
by
Spiros Koulouzis
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
added format for provision
parent
92af4962
Changes
17
Show whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
125 additions
and
216 deletions
+125
-216
ProvisionService.java
...in/java/nl/uva/sne/drip/api/service/ProvisionService.java
+20
-130
PlannerController.java
...n/java/nl/uva/sne/drip/api/v1/rest/PlannerController.java
+4
-42
ProvisionController.java
...java/nl/uva/sne/drip/api/v1/rest/ProvisionController.java
+24
-12
ProvisionResponse.java
...drip/drip/commons/data/v1/external/ProvisionResponse.java
+0
-30
.gitignore
drip-deployer/.idea/.gitignore
+2
-0
drip-deployer.iml
drip-deployer/.idea/drip-deployer.iml
+11
-0
profiles_settings.xml
drip-deployer/.idea/inspectionProfiles/profiles_settings.xml
+6
-0
misc.xml
drip-deployer/.idea/misc.xml
+4
-0
modules.xml
drip-deployer/.idea/modules.xml
+8
-0
vcs.xml
drip-deployer/.idea/vcs.xml
+6
-0
.gitignore
drip-provisioner/.idea/.gitignore
+3
-0
drip-provisioner.iml
drip-provisioner/.idea/drip-provisioner.iml
+11
-0
profiles_settings.xml
...rovisioner/.idea/inspectionProfiles/profiles_settings.xml
+6
-0
misc.xml
drip-provisioner/.idea/misc.xml
+4
-0
modules.xml
drip-provisioner/.idea/modules.xml
+8
-0
vcs.xml
drip-provisioner/.idea/vcs.xml
+6
-0
workspace.xml
drip_planner2/.idea/workspace.xml
+2
-2
No files found.
drip-api/src/main/java/nl/uva/sne/drip/api/service/ProvisionService.java
View file @
e34bbc8d
...
...
@@ -449,12 +449,9 @@ public class ProvisionService {
}
for
(
int
i
=
0
;
i
<
lines
.
length
;
i
++)
{
// DeployParameter deployParam = new DeployParameter();
String
[]
parts
=
lines
[
i
].
split
(
" "
);
String
deployIP
=
parts
[
0
];
String
deployUser
=
parts
[
1
];
// String deployCertPath = parts[2];
// String cloudCertificateName = FilenameUtils.removeExtension(FilenameUtils.getBaseName(deployCertPath));
String
deployRole
=
parts
[
2
];
String
nodeName
=
nodeNames
.
get
(
i
);
...
...
@@ -474,137 +471,10 @@ public class ProvisionService {
outputs
.
put
(
name
,
keyOutput
);
}
}
// switch (name) {
// case "deploy_parameters":
// break;
// case "public_user_key":
// Key key = new Key();
// key.setKey(p.getValue());
// key.setName(p.getAttributes().get("name"));
// key.setType(Key.KeyType.PUBLIC);
// userKey.setPublicKey(key);
// break;
// case "private_user_key":
// key = new Key();
// key.setKey(p.getValue());
// key.setName(p.getAttributes().get("name"));
// key.setType(Key.KeyType.PRIVATE);
// userKey.setPrivateKey(key);
// break;
// case "private_deployer_key":
// key = new Key();
// key.setKey(p.getValue());
// key.setName(p.getAttributes().get("name"));
// key.setType(Key.KeyType.PRIVATE);
// deployerKey.setPrivateKey(key);
// break;
// case "public_deployer_key":
// key = new Key();
// key.setKey(p.getValue());
// key.setName(p.getAttributes().get("name"));
// key.setType(Key.KeyType.PUBLIC);
// deployerKey.setPublicKey(key);
// break;
// case "public_cloud_key":
// key = new Key();
// key.setKey(p.getValue());
// key.setName(p.getAttributes().get("name"));
// key.setType(Key.KeyType.PUBLIC);
// key.setAttributes(p.getAttributes());
// publicCloudKeys.put(p.getAttributes().get("key_pair_id"), key);
// break;
//
// case "private_cloud_key":
// key = new Key();
// key.setKey(p.getValue());
// key.setName(p.getAttributes().get("name"));
// key.setType(Key.KeyType.PRIVATE);
// key.setAttributes(p.getAttributes());
// privateCloudKeys.put(p.getAttributes().get("key_pair_id"), key);
// break;
//
// default:
// value = p.getValue();
// if (kvMap == null) {
// kvMap = new HashMap();
// }
// kvMap.put(name, Converter.ymlString2Map(value));
// break;
// }
}
topologyTemplate
.
put
(
"outputs"
,
outputs
);
// List<String> userKeyIds = null;
// if (provisionRequest != null) {
// userKeyIds = provisionRequest.getUserKeyPairIDs();
// } else {
//// userKeyIds = provisionResponse.getUserKeyPairIDs();
// }
// if (saveUserKeys) {
// if (userKeyIds != null && !userKeyIds.isEmpty()) {
// } else {
// userKeyIds = new ArrayList<>();
// if (userKey.getPublicKey() != null) {
// userKey = keyPairService.save(userKey);
// userKeyIds.add(userKey.getId());
// }
// }
// }
// ArrayList<String> deployerKeyIds = null;
// if (saveDeployerKeyI) {
// deployerKeyIds = new ArrayList<>();
// if (deployerKey.getPublicKey() != null) {
// deployerKey = keyPairService.save(deployerKey);
// deployerKeyIds.add(deployerKey.getId());
// }
// }
// ArrayList<String> cloudKeyPairIDs = new ArrayList<>();
// List<KeyPair> allPirs = keyPairService.findAll();
// for (String id : publicCloudKeys.keySet()) {
// boolean save = true;
// String key_pair_id = privateCloudKeys.get(id).getAttributes().get("key_pair_id");
// for (KeyPair p : allPirs) {
// Key pk = p.getPrivateKey();
// if (pk != null && pk.getAttributes() != null && pk.getAttributes().containsKey("key_pair_id")) {
// if (key_pair_id.equals(pk.getAttributes().get("key_pair_id"))) {
// save = false;
// break;
// }
// }
// }
// if (save) {
// KeyPair cloudPair = new KeyPair();
// cloudPair.setPrivateKey(privateCloudKeys.get(id));
// cloudPair.setPublicKey(publicCloudKeys.get(id));
// cloudPair.setKeyPairId(id);
// cloudPair = keyPairService.save(cloudPair);
// cloudKeyPairIDs.add(cloudPair.getId());
// }
//
// }
// ArrayList<String> existingCloudKeyPairIDs = provisionResponse.getCloudKeyPairIDs();
// if (existingCloudKeyPairIDs != null) {
// existingCloudKeyPairIDs.addAll(cloudKeyPairIDs);
// } else {
// existingCloudKeyPairIDs = cloudKeyPairIDs;
// }
// provisionResponse.setCloudKeyPairIDs(existingCloudKeyPairIDs);
// provisionResponse.setDeployParameters(deployParameters);
// provisionResponse.setKvMap(kvMap);
provisionResponse
.
setKvMap
(
toscaPlan
);
// if (provisionRequest != null) {
//// provisionResponse.setCloudCredentialsIDs(provisionRequest.getCloudCredentialsIDs());
// provisionResponse.setPlanID(provisionRequest.getPlanID());
// }
//
// if (userKeyIds != null) {
//// provisionResponse.setUserKeyPairIDs(userKeyIds);
// }
// if (deployerKeyIds != null) {
//// provisionResponse.setDeployerKeyPairIDs(deployerKeyIds);
// }
provisionResponse
=
save
(
provisionResponse
);
return
provisionResponse
;
}
...
...
@@ -645,4 +515,24 @@ public class ProvisionService {
}
return
plan
;
}
public
String
get
(
String
id
,
String
format
)
throws
JSONException
{
ProvisionResponse
pro
=
findOne
(
id
);
Map
<
String
,
Object
>
map
=
pro
.
getKeyValue
();
if
(
format
!=
null
&&
format
.
equals
(
"yml"
))
{
String
ymlStr
=
Converter
.
map2YmlString
(
map
);
ymlStr
=
ymlStr
.
replaceAll
(
"\\uff0E"
,
"."
);
return
ymlStr
;
}
if
(
format
!=
null
&&
format
.
equals
(
"json"
))
{
String
jsonStr
=
Converter
.
map2JsonString
(
map
);
jsonStr
=
jsonStr
.
replaceAll
(
"\\uff0E"
,
"."
);
return
jsonStr
;
}
String
ymlStr
=
Converter
.
map2YmlString
(
map
);
ymlStr
=
ymlStr
.
replaceAll
(
"\\uff0E"
,
"."
);
return
ymlStr
;
}
}
drip-api/src/main/java/nl/uva/sne/drip/api/v1/rest/PlannerController.java
View file @
e34bbc8d
...
...
@@ -100,24 +100,7 @@ public class PlannerController {
return
null
;
}
// @RequestMapping(value = "/plan/", method = RequestMethod.POST)
// @RolesAllowed({UserService.USER, UserService.ADMIN})
// public @ResponseBody
// String plan(@RequestBody PlanRequest planRequest) {
//
// try {
// PlanResponse plan = plannerService.getPlan(planRequest.getToscaID(),
// planRequest.getManagerType(), planRequest.getVmUserName(),
// planRequest.getCloudProvider(), planRequest.getOsType(), planRequest.getDomain());
// if (plan == null) {
// throw new NotFoundException("Could not make plan");
// }
// return plan.getId();
// } catch (JSONException | IOException | TimeoutException | InterruptedException ex) {
// Logger.getLogger(PlannerController.class.getName()).log(Level.SEVERE, null, ex);
// }
// return null;
// }
/**
* Gets a plan
*
...
...
@@ -190,32 +173,11 @@ public class PlannerController {
return
ids
;
}
@RequestMapping
(
value
=
"/post/{name}"
,
method
=
RequestMethod
.
POST
)
@RolesAllowed
({
UserService
.
USER
,
UserService
.
ADMIN
})
public
@ResponseBody
String
postTop
(
@RequestBody
String
toscaContents
,
@PathVariable
(
"name"
)
String
name
)
{
return
plannerService
.
saveStringContents
(
toscaContents
,
0
,
name
);
}
// @RequestMapping(value = "/post/{level}/{name}/{id}", method = RequestMethod.POST)
// @RequestMapping(value = "/post/{name}", method = RequestMethod.POST)
// @RolesAllowed({UserService.USER, UserService.ADMIN})
// public @ResponseBody
// String postLow(@RequestBody String toscaContents, @PathVariable("level") String level, @PathVariable("name") String name, @PathVariable("id") String id) {
// int intLevel = Integer.valueOf(level);
// if (intLevel == 0) {
// String postTop(@RequestBody String toscaContents, @PathVariable("name") String name) {
// return plannerService.saveStringContents(toscaContents, 0, name);
// }
//
// PlanResponse topPlan = plannerService.findOne(id);
// Set<String> lowIDs = topPlan.getLoweLevelPlanIDs();
// if (lowIDs == null) {
// lowIDs = new HashSet<>();
// }
// String lowPlanID = plannerService.saveStringContents(toscaContents, intLevel, name);
// lowIDs.add(lowPlanID);
// topPlan.setLoweLevelPlansIDs(lowIDs);
// topPlan = plannerService.save(topPlan);
// return topPlan.getId();
// }
}
drip-api/src/main/java/nl/uva/sne/drip/api/v1/rest/ProvisionController.java
View file @
e34bbc8d
...
...
@@ -41,6 +41,7 @@ import nl.uva.sne.drip.drip.commons.data.v1.external.ScaleRequest;
import
org.json.JSONException
;
import
org.springframework.web.bind.annotation.PathVariable
;
import
org.springframework.web.bind.annotation.RequestBody
;
import
org.springframework.web.bind.annotation.RequestParam
;
/**
* This controller is responsible for obtaining resources from cloud providers
...
...
@@ -67,33 +68,41 @@ public class ProvisionController {
@RequestMapping
(
value
=
"/providers"
,
method
=
RequestMethod
.
GET
)
@RolesAllowed
({
UserService
.
USER
,
UserService
.
ADMIN
})
@StatusCodes
({
@ResponseCode
(
code
=
404
,
condition
=
"object not found"
),
@ResponseCode
(
code
=
404
,
condition
=
"object not found"
)
,
@ResponseCode
(
code
=
200
,
condition
=
"object exists"
)
})
public
@ResponseBody
String
[]
getSuportedProviders
()
{
return
new
String
[]{
"egi"
,
"ec2"
};
return
new
String
[]{
"egi"
,
"ec2"
,
"exogeni"
};
}
/**
* Gets the ProvisionRequest
*
* @param id. The id of the ProvisionRequest
* @param format
* @return the requested ProvisionRequest
*/
@RequestMapping
(
value
=
"/{id}"
,
method
=
RequestMethod
.
GET
)
@RequestMapping
(
value
=
"/{id}"
,
method
=
RequestMethod
.
GET
,
params
=
{
"format"
}
)
@RolesAllowed
({
UserService
.
USER
,
UserService
.
ADMIN
})
@StatusCodes
({
@ResponseCode
(
code
=
404
,
condition
=
"object not found"
),
@ResponseCode
(
code
=
404
,
condition
=
"object not found"
)
,
@ResponseCode
(
code
=
200
,
condition
=
"object exists"
)
})
public
@ResponseBody
ProvisionResponse
get
(
@PathVariable
(
"id"
)
String
id
)
{
ProvisionResponse
pro
=
provisionService
.
findOne
(
id
);
String
get
(
@PathVariable
(
"id"
)
String
id
,
@RequestParam
(
value
=
"format"
)
String
format
)
{
try
{
String
pro
=
provisionService
.
get
(
id
,
format
);
if
(
pro
==
null
)
{
throw
new
NotFoundException
();
}
return
provisionService
.
findOne
(
id
);
return
pro
;
}
catch
(
JSONException
ex
)
{
Logger
.
getLogger
(
ProvisionController
.
class
.
getName
()).
log
(
Level
.
SEVERE
,
null
,
ex
);
}
return
null
;
}
/**
...
...
@@ -105,7 +114,8 @@ public class ProvisionController {
@RequestMapping
(
value
=
"/{id}"
,
method
=
RequestMethod
.
DELETE
)
@RolesAllowed
({
UserService
.
USER
,
UserService
.
ADMIN
})
@StatusCodes
({
@ResponseCode
(
code
=
404
,
condition
=
"object not found"
),
@ResponseCode
(
code
=
404
,
condition
=
"object not found"
)
,
@ResponseCode
(
code
=
200
,
condition
=
"delete successful"
)
})
public
@ResponseBody
...
...
@@ -169,7 +179,8 @@ public class ProvisionController {
@RequestMapping
(
value
=
"/provision"
,
method
=
RequestMethod
.
POST
)
@RolesAllowed
({
UserService
.
USER
,
UserService
.
ADMIN
})
@StatusCodes
({
@ResponseCode
(
code
=
400
,
condition
=
"Plan not found or credentials not found"
),
@ResponseCode
(
code
=
400
,
condition
=
"Plan not found or credentials not found"
)
,
@ResponseCode
(
code
=
200
,
condition
=
"provision success"
)
})
public
@ResponseBody
...
...
@@ -205,7 +216,8 @@ public class ProvisionController {
@RequestMapping
(
value
=
"/post/provision"
,
method
=
RequestMethod
.
POST
)
@RolesAllowed
({
UserService
.
USER
,
UserService
.
ADMIN
})
@StatusCodes
({
@ResponseCode
(
code
=
400
,
condition
=
"Plan not found or credentials not found or something important"
),
@ResponseCode
(
code
=
400
,
condition
=
"Plan not found or credentials not found or something important"
)
,
@ResponseCode
(
code
=
200
,
condition
=
"provision success"
)
})
public
@ResponseBody
...
...
drip-commons/src/main/java/nl/uva/sne/drip/drip/commons/data/v1/external/ProvisionResponse.java
View file @
e34bbc8d
...
...
@@ -47,34 +47,4 @@ public class ProvisionResponse extends KeyValueHolder {
this
.
planID
=
planID
;
}
// private List<DeployParameter> deployParameters;
// private ArrayList<String> cloudKeyPairIDs;
//
// /**
// * The deploy parameters.
// *
// * @return the deployParameters
// */
// public List<DeployParameter> getDeployParameters() {
// return deployParameters;
// }
//
// /**
// * @param deployParameters the deployParameters to set
// */
// public void setDeployParameters(List<DeployParameter> deployParameters) {
// this.deployParameters = deployParameters;
// }
//
// public void setCloudKeyPairIDs(ArrayList<String> cloudKeyPairIDs) {
// this.cloudKeyPairIDs = cloudKeyPairIDs;
// }
//
// /**
// * @return the cloudKeyPairIDs
// */
// public ArrayList<String> getCloudKeyPairIDs() {
// return cloudKeyPairIDs;
// }
}
drip-deployer/.idea/.gitignore
0 → 100644
View file @
e34bbc8d
# Default ignored files
/workspace.xml
\ No newline at end of file
drip-deployer/.idea/drip-deployer.iml
0 → 100644
View file @
e34bbc8d
<?xml version="1.0" encoding="UTF-8"?>
<module
type=
"PYTHON_MODULE"
version=
"4"
>
<component
name=
"NewModuleRootManager"
>
<content
url=
"file://$MODULE_DIR$"
/>
<orderEntry
type=
"inheritedJdk"
/>
<orderEntry
type=
"sourceFolder"
forTests=
"false"
/>
</component>
<component
name=
"TestRunnerService"
>
<option
name=
"PROJECT_TEST_RUNNER"
value=
"Unittests"
/>
</component>
</module>
\ No newline at end of file
drip-deployer/.idea/inspectionProfiles/profiles_settings.xml
0 → 100644
View file @
e34bbc8d
<component
name=
"InspectionProjectProfileManager"
>
<settings>
<option
name=
"USE_PROJECT_PROFILE"
value=
"false"
/>
<version
value=
"1.0"
/>
</settings>
</component>
\ No newline at end of file
drip-deployer/.idea/misc.xml
0 → 100644
View file @
e34bbc8d
<?xml version="1.0" encoding="UTF-8"?>
<project
version=
"4"
>
<component
name=
"ProjectRootManager"
version=
"2"
project-jdk-name=
"Python 3.6"
project-jdk-type=
"Python SDK"
/>
</project>
\ No newline at end of file
drip-deployer/.idea/modules.xml
0 → 100644
View file @
e34bbc8d
<?xml version="1.0" encoding="UTF-8"?>
<project
version=
"4"
>
<component
name=
"ProjectModuleManager"
>
<modules>
<module
fileurl=
"file://$PROJECT_DIR$/.idea/drip-deployer.iml"
filepath=
"$PROJECT_DIR$/.idea/drip-deployer.iml"
/>
</modules>
</component>
</project>
\ No newline at end of file
drip-deployer/.idea/vcs.xml
0 → 100644
View file @
e34bbc8d
<?xml version="1.0" encoding="UTF-8"?>
<project
version=
"4"
>
<component
name=
"VcsDirectoryMappings"
>
<mapping
directory=
"$PROJECT_DIR$/.."
vcs=
"Git"
/>
</component>
</project>
\ No newline at end of file
drip-provisioner/.idea/.gitignore
0 → 100644
View file @
e34bbc8d
# Default ignored files
/workspace.xml
\ No newline at end of file
drip-provisioner/.idea/drip-provisioner.iml
0 → 100644
View file @
e34bbc8d
<?xml version="1.0" encoding="UTF-8"?>
<module
type=
"PYTHON_MODULE"
version=
"4"
>
<component
name=
"NewModuleRootManager"
>
<content
url=
"file://$MODULE_DIR$"
/>
<orderEntry
type=
"inheritedJdk"
/>
<orderEntry
type=
"sourceFolder"
forTests=
"false"
/>
</component>
<component
name=
"TestRunnerService"
>
<option
name=
"PROJECT_TEST_RUNNER"
value=
"Unittests"
/>
</component>
</module>
\ No newline at end of file
drip-provisioner/.idea/inspectionProfiles/profiles_settings.xml
0 → 100644
View file @
e34bbc8d
<component
name=
"InspectionProjectProfileManager"
>
<settings>
<option
name=
"USE_PROJECT_PROFILE"
value=
"false"
/>
<version
value=
"1.0"
/>
</settings>
</component>
\ No newline at end of file
drip-provisioner/.idea/misc.xml
0 → 100644
View file @
e34bbc8d
<?xml version="1.0" encoding="UTF-8"?>
<project
version=
"4"
>
<component
name=
"ProjectRootManager"
version=
"2"
project-jdk-name=
"Python 3.6"
project-jdk-type=
"Python SDK"
/>
</project>
\ No newline at end of file
drip-provisioner/.idea/modules.xml
0 → 100644
View file @
e34bbc8d
<?xml version="1.0" encoding="UTF-8"?>
<project
version=
"4"
>
<component
name=
"ProjectModuleManager"
>
<modules>
<module
fileurl=
"file://$PROJECT_DIR$/.idea/drip-provisioner.iml"
filepath=
"$PROJECT_DIR$/.idea/drip-provisioner.iml"
/>
</modules>
</component>
</project>
\ No newline at end of file
drip-provisioner/.idea/vcs.xml
0 → 100644
View file @
e34bbc8d
<?xml version="1.0" encoding="UTF-8"?>
<project
version=
"4"
>
<component
name=
"VcsDirectoryMappings"
>
<mapping
directory=
"$PROJECT_DIR$/.."
vcs=
"Git"
/>
</component>
</project>
\ No newline at end of file
drip_planner2/.idea/workspace.xml
View file @
e34bbc8d
...
...
@@ -2,8 +2,8 @@
<project
version=
"4"
>
<component
name=
"ChangeListManager"
>
<list
default=
"true"
id=
"462ede19-adfe-472b-975e-fefefa973fe0"
name=
"Default Changelist"
comment=
"slolved cap error"
>
<change
beforePath=
"$PROJECT_DIR$/../drip-api/src/main/java/nl/uva/sne/drip/api/service/DeployService.java"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/../drip-api/src/main/java/nl/uva/sne/drip/api/service/DeployService.java"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/../drip-api/src/main/java/nl/uva/sne/drip/api/service/ProvisionService.java"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/../drip-api/src/main/java/nl/uva/sne/drip/api/service/ProvisionService.java"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/.idea/workspace.xml"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/.idea/workspace.xml"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/venv/lib/python3.6/site-packages/easy-install.pth"
beforeDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/venv/lib/python3.6/site-packages/prettytable.py"
beforeDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/venv/lib/python3.6/site-packages/pyparsing.py"
beforeDir=
"false"
/>
...
...
@@ -33,7 +33,7 @@
<property
name=
"SHARE_PROJECT_CONFIGURATION_FILES"
value=
"true"
/>
<property
name=
"TODO_SCOPE"
value=
"All Places"
/>
<property
name=
"full.screen.before.presentation.mode"
value=
"false"
/>
<property
name=
"last_opened_file_path"
value=
"$PROJECT_DIR$"
/>
<property
name=
"last_opened_file_path"
value=
"$PROJECT_DIR$
/../drip-provisioner
"
/>
<property
name=
"settings.editor.selected.configurable"
value=
"com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable"
/>
</component>
<component
name=
"RunDashboard"
>
...
...
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