upcloud_sdk/types/
server.rs

1use serde::{Deserialize, Serialize};
2use crate::types::common::{Labels, Label, Tags};
3
4pub const VIDEO_MODEL_VGA: &str = "vga";
5pub const VIDEO_MODEL_CIRRUS: &str = "cirrus";
6
7pub const NIC_MODEL_E1000: &str = "e1000";
8pub const NIC_MODEL_VIRTIO: &str = "virtio";
9pub const NIC_MODEL_RTL8139: &str = "rtl8139";
10
11pub const STOP_TYPE_SOFT: &str = "soft";
12pub const STOP_TYPE_HARD: &str = "hard";
13
14pub const PASSWORD_DELIVERY_NONE: &str = "none";
15pub const PASSWORD_DELIVERY_EMAIL: &str = "email";
16pub const PASSWORD_DELIVERY_SMS: &str = "sms";
17
18pub const CREATE_SERVER_STORAGE_DEVICE_ACTION_CREATE: &str = "create";
19pub const CREATE_SERVER_STORAGE_DEVICE_ACTION_CLONE: &str = "clone";
20pub const CREATE_SERVER_STORAGE_DEVICE_ACTION_ATTACH: &str = "attach";
21
22pub enum ServerState {
23    Started,
24    Stopped,
25    Maintenance,
26    Error,
27}
28
29impl ServerState {
30    pub fn as_str(&self) -> &'static str {
31        match self {
32            Self::Started => "started",
33            Self::Stopped => "stopped",
34            Self::Maintenance => "maintenance",
35            Self::Error => "error",
36        }
37    }
38}
39
40#[derive(Debug, Deserialize)]
41pub struct GetServerResponse {
42    pub servers: ServerList,
43}
44
45#[derive(Debug, Deserialize)]
46pub struct GetServerDetailsResponse {
47    pub server: ServerDetails,
48}
49
50#[derive(Debug, Deserialize)]
51pub struct CreateServerResponse {
52    pub server: ServerDetails,
53}
54
55#[derive(Debug, Clone, Deserialize)]
56pub struct ServerList {
57    pub server: Vec<Server>,
58}
59
60#[derive(Debug, Default, Serialize, Clone, Deserialize)]
61pub struct Server {
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub core_number: Option<String>,
64    #[serde(skip_serializing_if = "Option::is_none")]
65    pub created: Option<i64>,
66    #[serde(default)]
67    pub host: i64,
68    pub hostname: String,
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub labels: Option<Labels>,
71    pub license: f64,
72    pub memory_amount: String,
73    pub plan: String,
74    #[serde(default)]
75    pub plan_ipv4_bytes: String,
76    #[serde(default)]
77    pub plan_ipv6_bytes: String,
78    #[serde(default)]
79    pub server_group: Option<String>,
80    #[serde(default)]
81    pub simple_backup: String,
82    pub state: String,
83    #[serde(default)]
84    pub tags: Tags,
85    pub title: String,
86    pub uuid: String,
87    pub zone: String,
88}
89
90#[derive(Debug, Default, Clone, Deserialize)]
91pub struct ServerDetails {
92    #[serde(flatten)]
93    pub server: Server,
94    pub boot_order: Option<String>,
95    #[serde(skip_serializing_if = "Option::is_none")]
96    pub firewall: Option<String>,
97    #[serde(rename = "ip_addresses")]
98    pub ip_addresses: Option<IPAddressWrapper>,
99    pub networking: Option<CreateServerNetworking>,
100    pub nic_model: Option<String>,
101    #[serde(skip_serializing_if = "Option::is_none")]
102    pub password: Option<String>,
103    #[serde(skip_serializing_if = "Option::is_none", rename = "storage_devices")]
104    pub storage_devices: Option<StorageDeviceListWrapper>,
105    pub video_model: Option<String>,
106    #[serde(serialize_with = "serialize_bool_as_yes_no", deserialize_with = "deserialize_yes_no_as_bool")]
107    pub remote_access_enabled: bool,
108    pub remote_access_password: String,
109}
110
111#[derive(Debug, Serialize, Clone, Deserialize)]
112pub struct StorageDeviceListWrapper {
113    pub storage_device: Vec<ServerStorageDevice>,
114}
115
116impl Default for StorageDeviceListWrapper {
117    fn default() -> Self {
118        Self {
119            storage_device: Vec::new()
120        }
121    }
122}
123
124#[derive(Debug, Serialize, Deserialize)]
125pub struct IPAddress {
126    pub access: String,
127    pub family: String,
128    pub address: String,
129    pub ptr_record: String,
130    pub server: String,
131    pub floating: Option<bool>,
132    pub mac: Option<String>,
133    pub network: Option<String>,
134}
135
136#[derive(Debug, Serialize, Clone, Deserialize)]
137pub struct ServerStorageDevice {
138    pub address: String,
139    #[serde(rename = "part_of_plan")]
140    pub part_of_plan: String,
141    pub labels: Vec<Label>,
142    pub storage: String,
143    #[serde(rename = "storage_size")]
144    pub storage_size: i32,
145    #[serde(rename = "storage_encrypted")]
146    pub storage_encrypted: String,
147    #[serde(rename = "storage_tier")]
148    pub storage_tier: String,
149    #[serde(rename = "storage_title")]
150    pub storage_title: String,
151    #[serde(skip_serializing_if = "Option::is_none", rename = "type")]
152    pub storage_type: Option<String>,
153    #[serde(rename = "boot_disk")]
154    pub boot_disk: String,
155}
156
157#[derive(Debug, Serialize, Deserialize)]    
158pub struct NetworkInterface {
159    #[serde(skip_serializing_if = "Option::is_none")]
160    pub index: Option<i32>,
161    #[serde(skip_serializing_if = "Option::is_none")]
162    pub ip_addresses: Option<Vec<IPAddress>>,
163    #[serde(skip_serializing_if = "Option::is_none", rename = "type")]
164    pub interface_type: Option<String>,
165    #[serde(skip_serializing_if = "Option::is_none")]
166    pub network: Option<String>,
167    #[serde(skip_serializing_if = "Option::is_none")]
168    pub mac: Option<String>,
169    #[serde(skip_serializing_if = "Option::is_none")]
170    pub source_ip_filtering: Option<bool>,
171    #[serde(skip_serializing_if = "Option::is_none")]
172    pub bootable: Option<bool>,
173}
174
175#[derive(Debug, Serialize, Deserialize)]
176pub struct ServerNetworking {
177    pub interfaces: Vec<NetworkInterface>,
178}
179
180#[derive(Debug, Default, Serialize)]
181pub struct ServerRequest {
182    #[serde(skip_serializing_if = "Option::is_none")]
183    pub avoid_host: Option<i32>,
184    #[serde(skip_serializing_if = "Option::is_none")]
185    pub host: Option<i32>,
186    #[serde(skip_serializing_if = "Option::is_none")]
187    pub boot_order: Option<String>,
188    #[serde(skip_serializing_if = "Option::is_none")]
189    pub core_number: Option<i32>,
190    #[serde(skip_serializing_if = "Option::is_none")]
191    pub firewall: Option<String>,
192    pub hostname: String,
193    #[serde(skip_serializing_if = "Option::is_none")]
194    pub labels: Option<Labels>,
195    pub login_user: LoginUser,
196    #[serde(skip_serializing_if = "Option::is_none")]
197    pub memory_amount: Option<i32>,
198    #[serde(skip_serializing_if = "Option::is_none")]
199    pub metadata: Option<String>,
200    #[serde(skip_serializing_if = "Option::is_none")]
201    pub nic_model: Option<String>,
202    #[serde(skip_serializing_if = "Option::is_none")]
203    pub networking: Option<CreateServerNetworking>,
204    #[serde(skip_serializing_if = "Option::is_none")]
205    pub password_delivery: Option<String>,
206    #[serde(skip_serializing_if = "Option::is_none")]
207    pub plan: Option<String>,
208    #[serde(skip_serializing_if = "Option::is_none")]
209    pub server_group: Option<String>,
210    #[serde(skip_serializing_if = "Option::is_none")]
211    pub simple_backup: Option<String>,
212    #[serde(rename = "storage_devices")]
213    pub storage_devices: StorageDeviceWrapper,
214    #[serde(skip_serializing_if = "Option::is_none")]
215    pub timezone: Option<String>,
216    pub title: String,
217    #[serde(skip_serializing_if = "Option::is_none")]
218    pub user_data: Option<String>,
219    #[serde(skip_serializing_if = "Option::is_none")]
220    pub video_model: Option<String>,
221    #[serde(skip_serializing_if = "Option::is_none")]
222    pub remote_access_enabled: Option<bool>,
223    #[serde(skip_serializing_if = "Option::is_none")]
224    pub remote_access_type: Option<String>,
225    #[serde(skip_serializing_if = "Option::is_none")]
226    pub remote_access_password: Option<String>,
227    pub zone: String,
228}
229
230#[derive(Debug, Default, Serialize)]
231pub struct CreateServerRequest {
232    pub server: ServerRequest,
233}
234
235impl CreateServerRequest {
236    pub fn new() -> Self {
237        Self::default()
238    }
239
240    pub fn with_zone(mut self, zone: impl Into<String>) -> Self {
241        self.server.zone = zone.into();
242        self
243    }
244
245    pub fn with_hostname(mut self, hostname: impl Into<String>) -> Self {
246        self.server.hostname = hostname.into();
247        self
248    }
249
250    pub fn with_title(mut self, title: impl Into<String>) -> Self {
251        self.server.title = title.into();
252        self
253    }
254
255    pub fn with_plan(mut self, plan: impl Into<String>) -> Self {
256        self.server.plan = Some(plan.into());
257        self
258    }
259
260    pub fn with_core_number(mut self, core_number: i32) -> Self {
261        self.server.core_number = Some(core_number);
262        self
263    }
264
265    pub fn with_memory_amount(mut self, memory_amount: i32) -> Self {
266        self.server.memory_amount = Some(memory_amount);
267        self
268    }
269
270    pub fn with_storage_device(mut self, device: CreateServerStorageDevice) -> Self {
271        self.server.storage_devices.storage_device.push(device);
272        self
273    }
274
275    pub fn with_networking(mut self, networking: CreateServerNetworking) -> Self {
276        self.server.networking = Some(networking);
277        self
278    }
279
280    pub fn with_login_user(mut self, login_user: LoginUser) -> Self {
281        self.server.login_user = login_user;
282        self
283    }
284
285    pub fn with_user_data(mut self, user_data: String) -> Self {
286        self.server.user_data = Some(user_data);
287        self
288    }
289
290    pub fn with_avoid_host(mut self, avoid_host: i32) -> Self {
291        self.server.avoid_host = Some(avoid_host);
292        self
293    }
294
295    pub fn with_host(mut self, host: i32) -> Self {
296        self.server.host = Some(host);
297        self
298    }
299
300    pub fn with_boot_order(mut self, boot_order: String) -> Self {
301        self.server.boot_order = Some(boot_order);
302        self
303    }
304
305    pub fn with_firewall(mut self, firewall: String) -> Self {
306        self.server.firewall = Some(firewall);
307        self
308    }
309
310    pub fn with_labels(mut self, labels: Labels) -> Self {
311        self.server.labels = Some(labels);
312        self
313    }
314
315    pub fn with_metadata(mut self, metadata: impl Into<String>) -> Self {
316        self.server.metadata = Some(metadata.into());
317        self
318    }
319
320    pub fn with_nic_model(mut self, nic_model: String) -> Self {
321        self.server.nic_model = Some(nic_model);
322        self
323    }
324
325    pub fn with_password_delivery(mut self, password_delivery: String) -> Self {
326        self.server.password_delivery = Some(password_delivery);
327        self
328    }
329
330    pub fn with_server_group(mut self, server_group: String) -> Self {
331        self.server.server_group = Some(server_group);
332        self
333    }
334
335    pub fn with_simple_backup(mut self, simple_backup: String) -> Self {
336        self.server.simple_backup = Some(simple_backup);
337        self
338    }
339
340    pub fn with_timezone(mut self, timezone: String) -> Self {
341        self.server.timezone = Some(timezone);
342        self
343    }
344
345    pub fn with_video_model(mut self, video_model: String) -> Self {
346        self.server.video_model = Some(video_model);
347        self
348    }
349
350    pub fn with_remote_access(
351        mut self,
352        enabled: bool,
353        access_type: Option<String>,
354        password: Option<String>,
355    ) -> Self {
356        self.server.remote_access_enabled = Some(enabled);
357        self.server.remote_access_type = access_type;
358        self.server.remote_access_password = password;
359        self
360    }
361
362    pub fn build(mut self) -> CreateServerRequest {
363        // By default, we only have an interface with a public IP address
364        if self.server.networking.is_none() {
365            self.server.networking = Some(
366                CreateServerNetworking::new()
367                    .with_interface(
368                        CreateServerInterface::new("public")
369                            .with_ip_address("IPv4", None)
370                            .with_index(1)
371                    )
372            );
373        }
374        CreateServerRequest {
375            server: self.server
376        }
377    }
378}
379
380#[derive(Debug, Default, Serialize)]
381pub struct CreateServerStorageDevice {
382    pub action: String,
383    pub address: Option<String>,
384    #[serde(serialize_with = "serialize_bool_as_yes_no", deserialize_with = "deserialize_yes_no_as_bool")]
385    pub encrypted: Option<bool>,
386    pub storage: String,
387    pub title: Option<String>,
388    // Storage size in gigabytes
389    pub size: Option<i32>,
390    pub tier: Option<String>,
391    #[serde(rename = "type")]
392    pub storage_type: Option<String>,
393}
394
395impl CreateServerStorageDevice {
396    pub fn new(action: impl Into<String>, storage: impl Into<String>) -> Self {
397        Self {
398            action: action.into(),
399            storage: storage.into(),
400            ..Default::default()
401        }
402    }
403
404    pub fn from_template(template_uuid: impl Into<String>) -> Self {
405        Self::new(
406            CREATE_SERVER_STORAGE_DEVICE_ACTION_CLONE.to_string(),
407            template_uuid.into()
408        )
409            .with_title("System Disk")
410    }
411
412    pub fn with_size(mut self, size: i32) -> Self {
413        self.size = Some(size);
414        self
415    }
416
417    pub fn with_tier(mut self, tier: impl Into<String>) -> Self {
418        self.tier = Some(tier.into());
419        self
420    }
421
422    pub fn with_title(mut self, title: impl Into<String>) -> Self {
423        self.title = Some(title.into());
424        self
425    }
426
427    pub fn with_encrypted(mut self, encrypted: bool) -> Self {
428        self.encrypted = Some(encrypted);
429        self
430    }
431
432    pub fn with_address(mut self, address: impl Into<String>) -> Self {
433        self.address = Some(address.into());
434        self
435    }
436}
437
438#[derive(Debug, Serialize, Clone, Deserialize)]
439pub struct CreateServerNetworking {
440    pub interfaces: InterfaceWrapper,
441}
442
443#[derive(Debug, Default, Serialize, Clone, Deserialize)]
444pub struct InterfaceWrapper {
445    pub interface: Vec<CreateServerInterface>,
446}
447
448#[derive(Debug, Serialize, Clone, Deserialize)]
449pub struct CreateServerInterface {
450    pub ip_addresses: IPAddressWrapper,
451    #[serde(rename = "type")]
452    pub interface_type: String,
453    #[serde(skip_serializing_if = "Option::is_none")]
454    pub network: Option<String>,
455    #[serde(skip_serializing_if = "Option::is_none")]
456    pub source_ip_filtering: Option<String>,
457    #[serde(skip_serializing_if = "Option::is_none")]
458    pub bootable: Option<String>,
459    #[serde(skip_serializing_if = "Option::is_none")]
460    pub index: Option<i32>,
461}
462
463#[derive(Debug, Serialize, Clone, Deserialize)]
464pub struct CreateServerIPAddress {
465    pub family: Option<String>,
466    pub address: Option<String>,
467}
468
469#[derive(Debug, Default, Clone, Serialize, Deserialize)]
470pub struct IPAddressWrapper {
471    pub ip_address: Vec<CreateServerIPAddress>,
472}
473
474#[derive(Debug, Serialize)]
475pub struct LoginUser {
476    pub username: String,
477    #[serde(skip_serializing_if = "Option::is_none")]
478    pub ssh_keys: Option<SSHKey>,
479    #[serde(skip_serializing_if = "Option::is_none")]
480    pub create_password: Option<String>,
481}
482
483#[derive(Debug, Serialize)]
484pub struct SSHKey {
485    pub ssh_key: Vec<String>,
486}
487
488impl LoginUser {
489    pub fn new(username: impl Into<String>) -> Self {
490        Self {
491            username: username.into(),
492            ssh_keys: None,
493            create_password: Some("yes".to_string()),
494        }
495    }
496
497    pub fn with_ssh_key(self, key: impl Into<String>) -> Self {
498        self.with_ssh_keys(vec![key.into()])
499    }
500
501    pub fn with_ssh_keys(mut self, keys: impl Into<Vec<String>>) -> Self {
502        match &mut self.ssh_keys {
503            Some(ssh_keys) => ssh_keys.ssh_key.extend(keys.into()),
504            None => self.ssh_keys = Some(SSHKey { ssh_key: keys.into() }),
505        }
506        self
507    }
508
509    pub fn with_create_password(mut self, create: bool) -> Self {
510        self.create_password = Some(if create { "yes" } else { "no" }.to_string());
511        self
512    }
513}
514
515impl Default for LoginUser {
516    fn default() -> Self {
517        Self {
518            username: "root".to_string(),
519            ssh_keys: None,
520            create_password: Some("yes".to_string()),
521        }
522    }
523}
524
525#[derive(Debug, Serialize)]
526pub struct StartServerRequest {
527    pub host: Option<i32>,
528    pub avoid_host: Option<i32>,
529}
530
531#[derive(Debug, Deserialize)]
532pub struct StartServerResponse {
533    pub server: ServerDetails,
534}
535
536#[derive(Debug, Serialize)]
537pub struct StopServerRequest {
538    pub stop_type: Option<String>,
539    pub timeout: Option<i64>,
540}
541
542#[derive(Debug, Deserialize)]
543pub struct StopServerResponse {
544    pub server: ServerDetails,
545}
546
547#[derive(Debug, Serialize)]
548pub struct RestartServerRequest {
549    pub stop_type: Option<String>,
550    pub timeout: Option<i64>,
551    pub timeout_action: Option<String>,
552    pub host: Option<i32>,
553}
554
555#[derive(Debug, Deserialize)]
556pub struct RestartServerResponse {
557    pub server: ServerDetails,
558}
559
560#[derive(Debug, Serialize)]
561pub struct ModifyServerRequest {
562    pub boot_order: Option<String>,
563    pub core_number: Option<i32>,
564    pub memory_amount: Option<i32>,
565    pub title: Option<String>,
566    pub zone: Option<String>,
567    // Add other fields as needed
568}
569
570#[derive(Debug, Deserialize)]
571pub struct ModifyServerResponse {
572    pub server: ServerDetails,
573}
574
575#[derive(Debug, Serialize)]
576pub struct StorageDeviceWrapper {
577    pub storage_device: Vec<CreateServerStorageDevice>,
578}
579
580impl Default for StorageDeviceWrapper {
581    fn default() -> Self {
582        Self {
583            storage_device: Vec::new()
584        }
585    }
586}
587
588fn deserialize_yes_no_as_bool<'de, D>(deserializer: D) -> Result<bool, D::Error>
589where
590    D: serde::Deserializer<'de>,
591{
592    let s = String::deserialize(deserializer)?;
593    Ok(s == "yes")
594}
595
596fn serialize_bool_as_yes_no<S>(value: &Option<bool>, serializer: S) -> Result<S::Ok, S::Error>
597where
598    S: serde::Serializer,
599{
600    match value {
601        Some(true) => serializer.serialize_str("yes"),
602        Some(false) => serializer.serialize_str("no"),
603        None => serializer.serialize_none(),
604    }
605}
606
607// Add these builder implementations
608impl CreateServerNetworking {
609    pub fn new() -> Self {
610        Self {
611            interfaces: InterfaceWrapper { interface: Vec::new() }
612        }
613    }
614
615    pub fn with_interface(mut self, interface: CreateServerInterface) -> Self {
616        self.interfaces.interface.push(interface);
617        self
618    }
619}
620
621impl CreateServerInterface {
622    pub fn new(interface_type: impl Into<String>) -> Self {
623        Self {
624            ip_addresses: IPAddressWrapper { ip_address: Vec::new() },
625            interface_type: interface_type.into(),
626            network: None,
627            source_ip_filtering: None,
628            bootable: None,
629            index: None,
630        }
631    }
632
633    pub fn with_ip_address(mut self, family: impl Into<String>, address: Option<String>) -> Self {
634        self.ip_addresses.ip_address.push(CreateServerIPAddress {
635            family: Some(family.into()),
636            address,
637        });
638        self
639    }
640
641    pub fn with_source_ip_filtering(mut self, enabled: bool) -> Self {
642        self.source_ip_filtering = Some(if enabled { "yes" } else { "no" }.to_string());
643        self
644    }
645
646    pub fn with_bootable(mut self, bootable: bool) -> Self {
647        self.bootable = Some(if bootable { "yes" } else { "no" }.to_string());
648        self
649    }
650
651    pub fn with_index(mut self, index: i32) -> Self {
652        self.index = Some(index);
653        self
654    }
655}