Skip to content

Commit 01ebf24

Browse files
committed
Add vcenter module
1 parent eb96bdd commit 01ebf24

File tree

22 files changed

+731
-489
lines changed

22 files changed

+731
-489
lines changed

cloudapi-common/build.gradle.kts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,29 @@ repositories {
1717
}
1818

1919
dependencies {
20+
val ktor_version = "2.1.3"
21+
// ktor client
22+
api("io.ktor:ktor-client-core-jvm:$ktor_version")
23+
api("io.ktor:ktor-client-cio-jvm:$ktor_version")
24+
api("io.ktor:ktor-client-content-negotiation:$ktor_version")
25+
26+
// mysql
2027
val ktorm_version = "3.4.1"
2128
api("org.ktorm:ktorm-core:$ktorm_version")
2229
api("org.ktorm:ktorm-jackson:$ktorm_version")
2330
api("org.ktorm:ktorm-support-mysql:$ktorm_version")
2431
api("org.ktorm:ktorm-support-mysql:3.4.1")
2532

33+
// kotlin-log
34+
api("io.github.microutils:kotlin-logging:2.1.21")
35+
api("ch.qos.logback:logback-classic:1.2.11")
36+
37+
// coroutine
38+
val coroutine_version = "1.6.4"
39+
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version")
40+
api("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:$coroutine_version")
41+
api("org.jetbrains.kotlinx:kotlinx-coroutines-reactive:$coroutine_version")
42+
2643
testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.1")
2744
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.1")
2845
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package cn.edu.buaa.scs.utils
2+
3+
object Constants {
4+
object VCenter {
5+
val port = (System.getenv("VCENTER_PORT") ?: "9977").toInt()
6+
val endpoint = System.getenv("VCENTER_ENDPOINT") ?: "http://localhost:9977"
7+
val username = System.getenv("VCENTER_USERNAME") ?: ""
8+
val password = System.getenv("VCENTER_PASSWORD") ?: ""
9+
}
10+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package cn.edu.buaa.scs.utils
2+
3+
import cn.edu.buaa.scs.error.RemoteServiceException
4+
import io.ktor.client.*
5+
import io.ktor.client.call.*
6+
import io.ktor.client.request.*
7+
import io.ktor.client.statement.*
8+
import io.ktor.http.*
9+
10+
class HttpClientWrapper(val client: HttpClient) {
11+
suspend inline fun <reified T> handleResponse(response: HttpResponse): Result<T> =
12+
if (response.status.isSuccess()) {
13+
Result.success(response.body())
14+
} else {
15+
Result.failure(RemoteServiceException(response.status.value, response.body()))
16+
}
17+
18+
suspend inline fun <reified T> get(path: String): Result<T> {
19+
val response = client.get(path)
20+
return handleResponse(response)
21+
}
22+
23+
suspend inline fun <reified T> post(
24+
path: String,
25+
body: Any? = null,
26+
contentType: ContentType = ContentType.Application.Json,
27+
): Result<T> {
28+
val response = client.post(path) {
29+
contentType(contentType)
30+
setBody(body)
31+
}
32+
return handleResponse(response)
33+
}
34+
35+
suspend inline fun <reified T> delete(
36+
path: String,
37+
body: Any? = null,
38+
contentType: ContentType = ContentType.Application.Json,
39+
): Result<T> {
40+
val response = client.delete(path) {
41+
contentType(contentType)
42+
setBody(body)
43+
}
44+
return handleResponse(response)
45+
}
46+
}

cloudapi-model/src/main/kotlin/cn/edu/buaa/scs/model/VirtualMachine.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@ import org.ktorm.entity.Entity
1010
import org.ktorm.entity.sequenceOf
1111
import org.ktorm.schema.*
1212

13+
fun VirtualMachine.applyExtraInfo(extraInfo: VirtualMachineExtraInfo) {
14+
this.adminId = extraInfo.adminId
15+
this.studentId = extraInfo.studentId
16+
this.teacherId = extraInfo.teacherId
17+
this.isExperimental = extraInfo.isExperimental
18+
this.experimentId = extraInfo.experimentId
19+
this.applyId = extraInfo.applyId
20+
}
21+
1322
data class VirtualMachineExtraInfo(
1423
@JsonProperty("adminID") var adminId: String = "default",
1524
@JsonProperty("studentID") var studentId: String = "default",
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package cn.edu.buaa.scs.vm
2+
3+
import cn.edu.buaa.scs.model.VirtualMachine
4+
5+
data class CreateVmOptions(
6+
val name: String,
7+
val templateUuid: String,
8+
9+
// course related
10+
val adminId: String = "default",
11+
val studentId: String = "default",
12+
val teacherId: String = "default",
13+
val isExperimental: Boolean = false,
14+
val experimentId: Int = 0,
15+
val applyId: String,
16+
17+
val memory: Int, // MB
18+
val cpu: Int,
19+
val disNum: Int = 1,
20+
val diskSize: Long, // bytes
21+
22+
val powerOn: Boolean = false,
23+
)
24+
25+
data class ConfigVmOptions(
26+
val vm: VirtualMachine,
27+
val experimentId: Int?,
28+
val adminId: String?,
29+
val teacherId: String?,
30+
val studentId: String?,
31+
)

cloudapi-web/build.gradle.kts

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -41,26 +41,13 @@ dependencies {
4141
implementation("io.ktor:ktor-server-content-negotiation:$ktor_version")
4242
implementation("io.ktor:ktor-serialization-jackson:$ktor_version")
4343
implementation("io.ktor:ktor-server-status-pages:$ktor_version")
44-
implementation("io.ktor:ktor-client-core-jvm:$ktor_version")
45-
implementation("io.ktor:ktor-client-cio-jvm:$ktor_version")
46-
implementation("io.ktor:ktor-client-content-negotiation:$ktor_version")
4744
testImplementation("io.ktor:ktor-server-tests-jvm:$ktor_version")
4845
testImplementation("io.ktor:ktor-server-test-host-jvm:$ktor_version")
4946

50-
// coroutine
51-
val coroutine_version = "1.6.4"
52-
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version")
53-
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:$coroutine_version")
54-
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactive:$coroutine_version")
55-
5647
// json
5748
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.4")
5849
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.13.4")
5950

60-
// kotlin-log
61-
implementation("io.github.microutils:kotlin-logging:2.1.21")
62-
implementation("ch.qos.logback:logback-classic:1.2.11")
63-
6451
// Redis
6552
implementation("io.lettuce:lettuce-core:6.1.5.RELEASE")
6653

@@ -97,9 +84,6 @@ dependencies {
9784
implementation("io.javaoperatorsdk:operator-framework:$javaOperatorSdk")
9885
annotationProcessor("io.javaoperatorsdk:operator-framework:$javaOperatorSdk")
9986

100-
// vm
101-
implementation("com.vmware.photon.controller:photon-vsphere-adapter-util:0.6.60")
102-
10387
// ssh
10488
implementation("com.hierynomus:sshj:0.33.0")
10589

cloudapi-web/src/main/kotlin/cn/edu/buaa/scs/bugit/GitClient.kt

Lines changed: 26 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@ package cn.edu.buaa.scs.bugit
33
import cn.edu.buaa.scs.application
44
import cn.edu.buaa.scs.error.RemoteServiceException
55
import cn.edu.buaa.scs.project.IProjectManager
6+
import cn.edu.buaa.scs.utils.HttpClientWrapper
67
import cn.edu.buaa.scs.utils.getConfigString
78
import io.ktor.client.*
8-
import io.ktor.client.call.*
99
import io.ktor.client.engine.cio.*
1010
import io.ktor.client.plugins.*
1111
import io.ktor.client.plugins.contentnegotiation.*
1212
import io.ktor.client.request.*
13-
import io.ktor.client.statement.*
1413
import io.ktor.http.*
1514
import io.ktor.serialization.jackson.*
1615
import org.ktorm.jackson.KtormModule
@@ -20,75 +19,39 @@ object GitClient : IProjectManager {
2019
const val gitRepoUrlPrefix = "https://scs.buaa.edu.cn/git"
2120

2221
internal val client by lazy {
23-
HttpClient(CIO) {
24-
val adminToken = application.getConfigString("bugit.adminToken")
25-
defaultRequest {
26-
url {
27-
protocol = URLProtocol.HTTPS
28-
host = application.getConfigString("bugit.host")
29-
path(application.getConfigString("bugit.pathPrefix"))
30-
22+
HttpClientWrapper(
23+
HttpClient(CIO) {
24+
val adminToken = application.getConfigString("bugit.adminToken")
25+
defaultRequest {
26+
url {
27+
protocol = URLProtocol.HTTPS
28+
host = application.getConfigString("bugit.host")
29+
path(application.getConfigString("bugit.pathPrefix"))
30+
}
31+
header(HttpHeaders.Authorization, "token $adminToken")
3132
}
32-
header(HttpHeaders.Authorization, "token $adminToken")
33-
}
34-
install(ContentNegotiation) {
35-
jackson {
36-
registerModule(KtormModule())
33+
install(ContentNegotiation) {
34+
jackson {
35+
registerModule(KtormModule())
36+
}
3737
}
3838
}
39-
}
40-
}
41-
42-
private suspend inline fun <reified T> handleResponse(response: HttpResponse): Result<T> =
43-
if (response.status.isSuccess()) {
44-
Result.success(response.body())
45-
} else {
46-
Result.failure(RemoteServiceException(response.status.value, response.body()))
47-
}
48-
49-
50-
internal suspend inline fun <reified T> get(path: String): Result<T> {
51-
val response = client.get(path)
52-
return handleResponse(response)
53-
}
54-
55-
private suspend inline fun <reified T> post(
56-
path: String,
57-
body: Any? = null,
58-
contentType: ContentType = ContentType.Application.Json,
59-
): Result<T> {
60-
val response = client.post(path) {
61-
contentType(contentType)
62-
setBody(body)
63-
}
64-
return handleResponse(response)
65-
}
66-
67-
private suspend inline fun <reified T> delete(
68-
path: String,
69-
body: Any? = null,
70-
contentType: ContentType = ContentType.Application.Json,
71-
): Result<T> {
72-
val response = client.delete(path) {
73-
contentType(contentType)
74-
setBody(body)
75-
}
76-
return handleResponse(response)
39+
)
7740
}
7841

7942
private suspend fun deleteRepo(username: String, repoName: String): Result<Unit> {
80-
return delete<String>("repos/$username/$repoName").map { }
43+
return client.delete<String>("repos/$username/$repoName").map { }
8144
}
8245

8346
private suspend fun getProject(projectName: String): Result<GitProject> {
84-
return get("orgs/$projectName")
47+
return client.get("orgs/$projectName")
8548
}
8649

8750
override suspend fun createUser(userID: String, realName: String, email: String, password: String): Result<String> {
88-
val exception = get<GitUser>("users/$userID").exceptionOrNull()
51+
val exception = client.get<GitUser>("users/$userID").exceptionOrNull()
8952
return if (exception == null) Result.success(userID)
9053
else if (exception is RemoteServiceException && exception.status == HttpStatusCode.NotFound.value) {
91-
post<String>(
54+
client.post<String>(
9255
"admin/users", CreateUserReq(
9356
username = userID,
9457
fullName = realName,
@@ -114,7 +77,7 @@ object GitClient : IProjectManager {
11477
"",
11578
)
11679
if (getProject(projectName).isSuccess) return Result.success(projectName)
117-
return post<GitProject>("admin/users/$userID/orgs", request).map { it.username }
80+
return client.post<GitProject>("admin/users/$userID/orgs", request).map { it.username }
11881
}
11982

12083
override suspend fun deleteProject(projectName: String): Result<Unit> {
@@ -132,23 +95,23 @@ object GitClient : IProjectManager {
13295
repos.forEach { repo ->
13396
deleteRepo(projectName, repo.name).getOrThrow()
13497
}
135-
delete<String>("admin/users/$projectName").getOrThrow()
98+
client.delete<String>("admin/users/$projectName").getOrThrow()
13699
}
137100
}
138101

139102
override suspend fun addProjectMember(projectName: String, memberID: String): Result<Unit> {
140-
return post("/orgs/$projectName/memberships/$memberID")
103+
return client.post("/orgs/$projectName/memberships/$memberID")
141104
}
142105

143106
override suspend fun removeProjectMember(projectName: String, memberID: String): Result<Unit> {
144-
return delete("/orgs/$projectName/memberships/$memberID")
107+
return client.delete("/orgs/$projectName/memberships/$memberID")
145108
}
146109

147110
suspend fun getRepoListOfProject(projectName: String): Result<List<GitRepo>> {
148-
return get("orgs/$projectName/repos")
111+
return client.get("orgs/$projectName/repos")
149112
}
150113

151114
suspend fun createRepo(projectName: String, req: CreateRepoRequest): Result<GitRepo> {
152-
return post("org/$projectName/repos", req)
115+
return client.post("org/$projectName/repos", req)
153116
}
154117
}

cloudapi-web/src/main/kotlin/cn/edu/buaa/scs/service/Vm.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import cn.edu.buaa.scs.utils.exists
1212
import cn.edu.buaa.scs.utils.user
1313
import cn.edu.buaa.scs.utils.userId
1414
import cn.edu.buaa.scs.vm.*
15-
import com.vmware.vim25.VirtualMachinePowerState
1615
import io.ktor.server.application.*
1716
import io.ktor.server.plugins.*
1817
import io.ktor.server.websocket.*
@@ -266,7 +265,7 @@ class VmService(val call: ApplicationCall) : IService {
266265
var vm = mysql.virtualMachines.find { it.uuid.eq(uuid) } ?: throw NotFoundException("VM not found")
267266
call.user().assertWrite(vm)
268267
// 检查是否已经关机
269-
if (vm.powerState.value.lowercase() != VirtualMachinePowerState.POWERED_OFF.value().lowercase()) {
268+
if (vm.powerState.value.lowercase() != "poweredoff") {
270269
throw BadRequestException("VM is not powered off")
271270
}
272271
if (vm.isTemplate) {

cloudapi-web/src/main/kotlin/cn/edu/buaa/scs/utils/schedule/FanInScheduler.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package cn.edu.buaa.scs.utils.schedule
22

3+
import cn.edu.buaa.scs.utils.logger
34
import kotlinx.coroutines.Dispatchers
45
import kotlinx.coroutines.channels.Channel
56
import kotlinx.coroutines.launch
@@ -13,7 +14,7 @@ interface Event {
1314
* FanInScheduler 从多个协程接收任务, 然后调度给其他协程执行
1415
*/
1516
abstract class FanInScheduler<T : Event>(val name: String) {
16-
private val logger = cn.edu.buaa.scs.utils.logger(name)()
17+
private val logger = logger(name)()
1718
private val defaultBuffer = 100
1819
protected open val channel = Channel<T>(defaultBuffer)
1920
protected open val coroutineDispatcher = Dispatchers.Default
@@ -37,4 +38,4 @@ abstract class FanInScheduler<T : Event>(val name: String) {
3738
* 具体处理每个 event 的业务逻辑
3839
*/
3940
abstract suspend fun work(event: T)
40-
}
41+
}

0 commit comments

Comments
 (0)