tencent_sdk/services/
cvm.rs

1use crate::{
2    client::{TencentCloudAsync, TencentCloudBlocking},
3    core::{Endpoint, TencentCloudResult},
4    services::Filter,
5};
6use serde::{Deserialize, Serialize};
7use serde_json::{json, Value};
8use std::borrow::Cow;
9use std::collections::HashMap;
10
11#[derive(Debug, Deserialize)]
12pub struct DescribeInstancesResponse {
13    #[serde(rename = "Response")]
14    pub response: DescribeInstancesResult,
15}
16
17#[derive(Debug, Deserialize)]
18pub struct DescribeInstancesResult {
19    #[serde(rename = "TotalCount")]
20    pub total_count: Option<u64>,
21    #[serde(rename = "InstanceSet")]
22    #[serde(default)]
23    pub instance_set: Vec<InstanceSummary>,
24    #[serde(rename = "RequestId")]
25    pub request_id: String,
26}
27
28#[derive(Debug, Deserialize)]
29pub struct InstanceSummary {
30    #[serde(rename = "InstanceId")]
31    pub instance_id: Option<String>,
32    #[serde(rename = "InstanceName")]
33    pub instance_name: Option<String>,
34    #[serde(rename = "InstanceState")]
35    pub instance_state: Option<String>,
36    #[serde(rename = "InstanceType")]
37    pub instance_type: Option<String>,
38    #[serde(rename = "Cpu")]
39    pub cpu: Option<u64>,
40    #[serde(rename = "Memory")]
41    pub memory: Option<u64>,
42    #[serde(rename = "PrivateIpAddresses")]
43    pub private_ip_addresses: Option<Vec<String>>,
44    #[serde(rename = "PublicIpAddresses")]
45    pub public_ip_addresses: Option<Vec<String>>,
46    #[serde(default)]
47    pub placement: Option<InstancePlacement>,
48    #[serde(default)]
49    pub system_disk: Option<DiskSummary>,
50    #[serde(default)]
51    pub data_disks: Option<Vec<DiskSummary>>,
52    #[serde(flatten, default)]
53    pub extra: HashMap<String, Value>,
54}
55
56#[derive(Debug, Deserialize)]
57pub struct InstancePlacement {
58    #[serde(rename = "Zone")]
59    pub zone: Option<String>,
60    #[serde(rename = "ProjectId")]
61    pub project_id: Option<i64>,
62    #[serde(flatten, default)]
63    pub extra: HashMap<String, Value>,
64}
65
66#[derive(Debug, Deserialize)]
67pub struct DiskSummary {
68    #[serde(rename = "DiskType")]
69    pub disk_type: Option<String>,
70    #[serde(rename = "DiskSize")]
71    pub disk_size: Option<u64>,
72    #[serde(flatten, default)]
73    pub extra: HashMap<String, Value>,
74}
75
76#[derive(Serialize)]
77#[serde(rename_all = "PascalCase")]
78struct DescribeInstancesPayload<'a> {
79    #[serde(skip_serializing_if = "Option::is_none")]
80    filters: Option<&'a [Filter<'a>]>,
81    #[serde(skip_serializing_if = "Option::is_none")]
82    limit: Option<u32>,
83    #[serde(skip_serializing_if = "Option::is_none")]
84    offset: Option<u32>,
85}
86
87/// Request wrapper for CVM `DescribeInstances`.
88pub struct DescribeInstances<'a> {
89    pub region: Option<&'a str>,
90    pub filters: Option<Vec<Filter<'a>>>,
91    pub limit: Option<u32>,
92    pub offset: Option<u32>,
93}
94
95impl<'a> Default for DescribeInstances<'a> {
96    fn default() -> Self {
97        Self::new()
98    }
99}
100
101impl<'a> DescribeInstances<'a> {
102    /// Create an empty request configuration.
103    pub fn new() -> Self {
104        Self {
105            region: None,
106            filters: None,
107            limit: None,
108            offset: None,
109        }
110    }
111
112    /// Set the target region for the request.
113    pub fn with_region(mut self, region: &'a str) -> Self {
114        self.region = Some(region);
115        self
116    }
117
118    /// Add a filter to the request.
119    pub fn push_filter(mut self, filter: Filter<'a>) -> Self {
120        self.filters.get_or_insert_with(Vec::new).push(filter);
121        self
122    }
123
124    /// Override the maximum number of items to return.
125    pub fn with_limit(mut self, limit: u32) -> Self {
126        self.limit = Some(limit);
127        self
128    }
129
130    /// Provide an offset for pagination.
131    pub fn with_offset(mut self, offset: u32) -> Self {
132        self.offset = Some(offset);
133        self
134    }
135}
136
137impl<'a> Endpoint for DescribeInstances<'a> {
138    type Output = DescribeInstancesResponse;
139
140    fn service(&self) -> Cow<'static, str> {
141        Cow::Borrowed("cvm")
142    }
143
144    fn action(&self) -> Cow<'static, str> {
145        Cow::Borrowed("DescribeInstances")
146    }
147
148    fn version(&self) -> Cow<'static, str> {
149        Cow::Borrowed("2017-03-12")
150    }
151
152    fn region(&self) -> Option<Cow<'_, str>> {
153        self.region.map(Cow::Borrowed)
154    }
155
156    fn payload(&self) -> Value {
157        let filters = self.filters.as_deref();
158        serde_json::to_value(DescribeInstancesPayload {
159            filters,
160            limit: self.limit,
161            offset: self.offset,
162        })
163        .expect("serialize DescribeInstances payload")
164    }
165}
166
167#[derive(Debug, Deserialize)]
168pub struct GenericActionResponse {
169    #[serde(rename = "Response")]
170    pub response: GenericActionResult,
171}
172
173#[derive(Debug, Deserialize)]
174pub struct GenericActionResult {
175    #[serde(rename = "RequestId")]
176    pub request_id: String,
177}
178
179/// Request payload for `ResetInstancesPassword`.
180pub struct ResetInstancesPassword<'a> {
181    pub region: &'a str,
182    pub instance_ids: &'a [&'a str],
183    pub password: &'a str,
184    pub username: Option<&'a str>,
185    pub force_stop: Option<bool>,
186}
187
188impl<'a> Endpoint for ResetInstancesPassword<'a> {
189    type Output = GenericActionResponse;
190
191    fn service(&self) -> Cow<'static, str> {
192        Cow::Borrowed("cvm")
193    }
194
195    fn action(&self) -> Cow<'static, str> {
196        Cow::Borrowed("ResetInstancesPassword")
197    }
198
199    fn version(&self) -> Cow<'static, str> {
200        Cow::Borrowed("2017-03-12")
201    }
202
203    fn region(&self) -> Option<Cow<'_, str>> {
204        Some(Cow::Borrowed(self.region))
205    }
206
207    fn payload(&self) -> Value {
208        let mut payload = json!({
209            "InstanceIds": self.instance_ids,
210            "Password": self.password
211        });
212
213        if let Some(username) = self.username {
214            payload["UserName"] = json!(username);
215        }
216        if let Some(force_stop) = self.force_stop {
217            payload["ForceStop"] = json!(force_stop);
218        }
219        payload
220    }
221}
222
223#[derive(Debug, Deserialize)]
224pub struct DescribeInstanceVncUrlResponse {
225    #[serde(rename = "Response")]
226    pub response: DescribeInstanceVncUrlResult,
227}
228
229#[derive(Debug, Deserialize)]
230pub struct DescribeInstanceVncUrlResult {
231    #[serde(rename = "InstanceVncUrl")]
232    pub instance_vnc_url: Option<String>,
233    #[serde(rename = "RequestId")]
234    pub request_id: String,
235}
236
237/// Request payload for `DescribeInstanceVncUrl`.
238pub struct DescribeInstanceVncUrl<'a> {
239    pub region: &'a str,
240    pub instance_id: &'a str,
241}
242
243impl<'a> Endpoint for DescribeInstanceVncUrl<'a> {
244    type Output = DescribeInstanceVncUrlResponse;
245
246    fn service(&self) -> Cow<'static, str> {
247        Cow::Borrowed("cvm")
248    }
249
250    fn action(&self) -> Cow<'static, str> {
251        Cow::Borrowed("DescribeInstanceVncUrl")
252    }
253
254    fn version(&self) -> Cow<'static, str> {
255        Cow::Borrowed("2017-03-12")
256    }
257
258    fn region(&self) -> Option<Cow<'_, str>> {
259        Some(Cow::Borrowed(self.region))
260    }
261
262    fn payload(&self) -> Value {
263        json!({ "InstanceId": self.instance_id })
264    }
265}
266
267/// Request payload for `StartInstances`.
268pub struct StartInstances<'a> {
269    pub region: &'a str,
270    pub instance_ids: &'a [&'a str],
271}
272
273impl<'a> Endpoint for StartInstances<'a> {
274    type Output = GenericActionResponse;
275
276    fn service(&self) -> Cow<'static, str> {
277        Cow::Borrowed("cvm")
278    }
279
280    fn action(&self) -> Cow<'static, str> {
281        Cow::Borrowed("StartInstances")
282    }
283
284    fn version(&self) -> Cow<'static, str> {
285        Cow::Borrowed("2017-03-12")
286    }
287
288    fn region(&self) -> Option<Cow<'_, str>> {
289        Some(Cow::Borrowed(self.region))
290    }
291
292    fn payload(&self) -> Value {
293        json!({ "InstanceIds": self.instance_ids })
294    }
295}
296
297/// Request payload for `RebootInstances`.
298pub struct RebootInstances<'a> {
299    pub region: &'a str,
300    pub instance_ids: &'a [&'a str],
301    pub force_reboot: Option<bool>,
302}
303
304impl<'a> Endpoint for RebootInstances<'a> {
305    type Output = GenericActionResponse;
306
307    fn service(&self) -> Cow<'static, str> {
308        Cow::Borrowed("cvm")
309    }
310
311    fn action(&self) -> Cow<'static, str> {
312        Cow::Borrowed("RebootInstances")
313    }
314
315    fn version(&self) -> Cow<'static, str> {
316        Cow::Borrowed("2017-03-12")
317    }
318
319    fn region(&self) -> Option<Cow<'_, str>> {
320        Some(Cow::Borrowed(self.region))
321    }
322
323    fn payload(&self) -> Value {
324        let mut payload = json!({ "InstanceIds": self.instance_ids });
325        if let Some(force_reboot) = self.force_reboot {
326            payload["ForceReboot"] = json!(force_reboot);
327        }
328        payload
329    }
330}
331
332/// Request payload for `StopInstances`.
333pub struct StopInstances<'a> {
334    pub region: &'a str,
335    pub instance_ids: &'a [&'a str],
336    pub stop_type: Option<&'a str>,
337}
338
339impl<'a> Endpoint for StopInstances<'a> {
340    type Output = GenericActionResponse;
341
342    fn service(&self) -> Cow<'static, str> {
343        Cow::Borrowed("cvm")
344    }
345
346    fn action(&self) -> Cow<'static, str> {
347        Cow::Borrowed("StopInstances")
348    }
349
350    fn version(&self) -> Cow<'static, str> {
351        Cow::Borrowed("2017-03-12")
352    }
353
354    fn region(&self) -> Option<Cow<'_, str>> {
355        Some(Cow::Borrowed(self.region))
356    }
357
358    fn payload(&self) -> Value {
359        let mut payload = json!({ "InstanceIds": self.instance_ids });
360        if let Some(stop_type) = self.stop_type {
361            payload["StopType"] = json!(stop_type);
362        }
363        payload
364    }
365}
366
367/// Request payload for `ModifyInstancesProject`.
368pub struct ModifyInstancesProject<'a> {
369    pub region: &'a str,
370    pub instance_ids: &'a [&'a str],
371    pub project_id: u64,
372}
373
374impl<'a> Endpoint for ModifyInstancesProject<'a> {
375    type Output = GenericActionResponse;
376
377    fn service(&self) -> Cow<'static, str> {
378        Cow::Borrowed("cvm")
379    }
380
381    fn action(&self) -> Cow<'static, str> {
382        Cow::Borrowed("ModifyInstancesProject")
383    }
384
385    fn version(&self) -> Cow<'static, str> {
386        Cow::Borrowed("2017-03-12")
387    }
388
389    fn region(&self) -> Option<Cow<'_, str>> {
390        Some(Cow::Borrowed(self.region))
391    }
392
393    fn payload(&self) -> Value {
394        json!({
395            "InstanceIds": self.instance_ids,
396            "ProjectId": self.project_id
397        })
398    }
399}
400
401/// Invoke `DescribeInstances` asynchronously.
402pub async fn describe_instances_async(
403    client: &TencentCloudAsync,
404    request: &DescribeInstances<'_>,
405) -> TencentCloudResult<DescribeInstancesResponse> {
406    client.request(request).await
407}
408
409/// Invoke `DescribeInstances` with the blocking client.
410pub fn describe_instances_blocking(
411    client: &TencentCloudBlocking,
412    request: &DescribeInstances<'_>,
413) -> TencentCloudResult<DescribeInstancesResponse> {
414    client.request(request)
415}
416
417/// Reset instances password asynchronously.
418pub async fn reset_instances_password_async(
419    client: &TencentCloudAsync,
420    request: &ResetInstancesPassword<'_>,
421) -> TencentCloudResult<GenericActionResponse> {
422    client.request(request).await
423}
424
425/// Reset instances password with the blocking client.
426pub fn reset_instances_password_blocking(
427    client: &TencentCloudBlocking,
428    request: &ResetInstancesPassword<'_>,
429) -> TencentCloudResult<GenericActionResponse> {
430    client.request(request)
431}
432
433/// Fetch CVM VNC URL asynchronously.
434pub async fn describe_instance_vnc_url_async(
435    client: &TencentCloudAsync,
436    request: &DescribeInstanceVncUrl<'_>,
437) -> TencentCloudResult<DescribeInstanceVncUrlResponse> {
438    client.request(request).await
439}
440
441/// Fetch CVM VNC URL with the blocking client.
442pub fn describe_instance_vnc_url_blocking(
443    client: &TencentCloudBlocking,
444    request: &DescribeInstanceVncUrl<'_>,
445) -> TencentCloudResult<DescribeInstanceVncUrlResponse> {
446    client.request(request)
447}
448
449/// Start CVM instances asynchronously.
450pub async fn start_instances_async(
451    client: &TencentCloudAsync,
452    request: &StartInstances<'_>,
453) -> TencentCloudResult<GenericActionResponse> {
454    client.request(request).await
455}
456
457/// Start CVM instances with the blocking client.
458pub fn start_instances_blocking(
459    client: &TencentCloudBlocking,
460    request: &StartInstances<'_>,
461) -> TencentCloudResult<GenericActionResponse> {
462    client.request(request)
463}
464
465/// Reboot CVM instances asynchronously.
466pub async fn reboot_instances_async(
467    client: &TencentCloudAsync,
468    request: &RebootInstances<'_>,
469) -> TencentCloudResult<GenericActionResponse> {
470    client.request(request).await
471}
472
473/// Reboot CVM instances with the blocking client.
474pub fn reboot_instances_blocking(
475    client: &TencentCloudBlocking,
476    request: &RebootInstances<'_>,
477) -> TencentCloudResult<GenericActionResponse> {
478    client.request(request)
479}
480
481/// Stop CVM instances asynchronously.
482pub async fn stop_instances_async(
483    client: &TencentCloudAsync,
484    request: &StopInstances<'_>,
485) -> TencentCloudResult<GenericActionResponse> {
486    client.request(request).await
487}
488
489/// Stop CVM instances with the blocking client.
490pub fn stop_instances_blocking(
491    client: &TencentCloudBlocking,
492    request: &StopInstances<'_>,
493) -> TencentCloudResult<GenericActionResponse> {
494    client.request(request)
495}
496
497/// Change the project of CVM instances asynchronously.
498pub async fn modify_instances_project_async(
499    client: &TencentCloudAsync,
500    request: &ModifyInstancesProject<'_>,
501) -> TencentCloudResult<GenericActionResponse> {
502    client.request(request).await
503}
504
505/// Change the project of CVM instances with the blocking client.
506pub fn modify_instances_project_blocking(
507    client: &TencentCloudBlocking,
508    request: &ModifyInstancesProject<'_>,
509) -> TencentCloudResult<GenericActionResponse> {
510    client.request(request)
511}
512
513#[cfg(test)]
514mod tests {
515    use super::*;
516    use crate::services::Filter;
517
518    #[test]
519    fn describe_instances_payload_supports_filters() {
520        let filters = vec![
521            Filter::new("instance-id", ["ins-123"]),
522            Filter::new("zone", ["ap-shanghai-1"]),
523        ];
524        let request = DescribeInstances {
525            region: Some("ap-shanghai"),
526            filters: Some(filters.clone()),
527            limit: Some(20),
528            offset: Some(0),
529        };
530
531        let payload = request.payload();
532        assert_eq!(payload["Filters"][0]["Name"], json!("instance-id"));
533        assert_eq!(payload["Filters"][1]["Values"], json!(["ap-shanghai-1"]));
534        assert_eq!(payload["Limit"], json!(20));
535        assert_eq!(payload["Offset"], json!(0));
536    }
537
538    #[test]
539    fn deserialize_generic_action_response() {
540        let payload = r#"{
541            "Response": {
542                "RequestId": "req-abc"
543            }
544        }"#;
545        let parsed: GenericActionResponse = serde_json::from_str(payload).unwrap();
546        assert_eq!(parsed.response.request_id, "req-abc");
547    }
548
549    #[test]
550    fn deserialize_vnc_url_response() {
551        let payload = r#"{
552            "Response": {
553                "InstanceVncUrl": "https://example.com",
554                "RequestId": "req-xyz"
555            }
556        }"#;
557        let parsed: DescribeInstanceVncUrlResponse = serde_json::from_str(payload).unwrap();
558        assert_eq!(
559            parsed.response.instance_vnc_url.as_deref(),
560            Some("https://example.com")
561        );
562    }
563
564    #[test]
565    fn describe_instances_builder_accumulates_filters() {
566        let request = DescribeInstances::new()
567            .with_region("ap-guangzhou")
568            .push_filter(Filter::new("zone", ["ap-guangzhou-1"]))
569            .with_limit(10)
570            .with_offset(5);
571
572        assert_eq!(request.region, Some("ap-guangzhou"));
573        let filters = request.filters.as_ref().expect("filters set");
574        assert_eq!(filters.len(), 1);
575        assert_eq!(filters[0].name, "zone");
576        assert_eq!(filters[0].values[0], "ap-guangzhou-1");
577        assert_eq!(request.limit, Some(10));
578        assert_eq!(request.offset, Some(5));
579    }
580}