Skip to content

Commit

Permalink
Imaging API Complete
Browse files Browse the repository at this point in the history
I want to add some utility functions to aid with imaging but the basic wrappers are now functionally complete.
  • Loading branch information
JamesMc86 committed Oct 13, 2022
1 parent 1337e29 commit 3281177
Show file tree
Hide file tree
Showing 6 changed files with 316 additions and 44 deletions.
6 changes: 5 additions & 1 deletion ni-syscfg/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,8 @@ num-traits = "0.2"

[[bin]]
name = "ni-syscfg"
path = "bin/ni-syscfg.rs"
path = "bin/ni-syscfg.rs"

[[bin]]
name = "imaging-smoke-test"
path = "bin/imaging-smoke-test.rs"
118 changes: 118 additions & 0 deletions ni-syscfg/bin/imaging-smoke-test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
use std::{
io::{self, Write},
path::PathBuf,
};

use ni_syscfg::software::{ImageInfo, NetworkInterfaceSettings};
use ni_syscfg::SessionConfig;

fn main() {
let mut address = String::new();
let mut user = String::new();
let mut password = String::new();

input("Enter the address", &mut address);
input("Enter the username", &mut user);
input("Enter the device password", &mut password);

let session = SessionConfig::new()
.target(&address)
.username(&user)
.unwrap()
.password(&password)
.unwrap()
.connect()
.expect("Session failed");

println!("Preparing local files...");
let unencrypted_image = prepare_image_path("test_unencrypted_image");
let encrypted_image = prepare_image_path("test_encrypted_image");

let image_data = ImageInfo {
title: "test".to_owned(),
id: "".to_owned(),
description: "Test Image".to_owned(),
version: "1.0".to_owned(),
};

//Lets go for an unencrypted first.
println!("Getting unencrypted image...");
session
.get_system_image(
&unencrypted_image,
&image_data,
None,
&["/c", "/C"],
true,
false,
)
.expect("Get Unencrypted Image Failed");
println!("Setting unencrypted image...");
session
.set_system_image(
&unencrypted_image,
None,
&["/c", "/C"],
true,
false,
NetworkInterfaceSettings::PreservePrimaryPreserveOthers,
)
.expect("Set Unecrypted Image Failed");

println!("Checking it doesn't overwrite if it exists.");
let result = session.get_system_image(
&unencrypted_image,
&image_data,
None,
&["/c", "/C"],
true,
false,
);
assert!(result.is_err());

// Now encrpyted
println!("Getting encrypted image...");
session
.get_system_image(
&encrypted_image,
&image_data,
Some("test123"),
&["/c", "/C"],
true,
false,
)
.expect("Get Encrypted Image Failed");
println!("Setting encrypted image...");
session
.set_system_image(
&encrypted_image,
Some("test123"),
&["/c", "/C"],
true,
false,
NetworkInterfaceSettings::PreservePrimaryPreserveOthers,
)
.expect("Set encrypted Image Failed");

println!("Success!");
}

fn input(prompt: &str, output: &mut String) {
print!("{prompt}: ");
io::stdout().flush().unwrap();
io::stdin().read_line(output).unwrap();

//remove new line
output.pop();
#[cfg(target_os = "windows")]
output.pop();
}

fn prepare_image_path(name: &str) -> PathBuf {
let mut path = std::env::temp_dir();
path.push(name);
if path.exists() {
std::fs::remove_dir_all(&path).unwrap();
}
return path;
}
16 changes: 1 addition & 15 deletions ni-syscfg/bin/ni-syscfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,4 @@ use std::path::PathBuf;

use ni_syscfg::SessionConfig;

fn main() {
let session = SessionConfig::new()
.target("192.168.10.102")
.username("admin")
.unwrap()
.password("labview")
.unwrap()
.connect()
.expect("Session failed");
session
.get_software_image(&PathBuf::from(
r"C:\Users\JamesMcNally\Documents\Delete\TestImage",
))
.unwrap();
}
fn main() {}
2 changes: 1 addition & 1 deletion ni-syscfg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ mod hardware_filter;
mod parameters;
mod resources;
mod session;
mod software;
pub mod software;
pub(crate) mod types;

pub use experts::ExpertType;
Expand Down
196 changes: 169 additions & 27 deletions ni-syscfg/src/software.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,64 +2,206 @@
//!
use std::ffi::CString;
use std::os::raw::c_char;
use std::path::Path;

use ni_syscfg_sys::{NISysCfgCreateSystemImageAsFolder, NISysCfgSetSystemImageFromFolder2};
use ni_syscfg_sys::*;

use crate::error::{api_status, Result};
use crate::error::{api_status, NiSystemConfigurationError, Result};
use crate::types::FfiBoolean;
use crate::Session;

type Uuid = String;

struct ImageInfo {
name: String,
id: Uuid,
/// Contains the metadata which can be saved with system images.
pub struct ImageInfo {
/// A title for the image.
pub title: String,
/// A unique ID for the image. Specifies the unique identifier for the system image.
/// If you specify this parameter, it must be in the GUID format "{00000000-0000-0000-0000-000000000000}" where each '0' represents a hexadecimal digit. This should be in the form
pub id: Uuid,
/// A short description for the image.
pub description: String,
/// A version number for the image.
pub version: String,
}

/// The network interface settings that can be applied when setting a system image.
///
/// ## Definitions
///
/// * **Reset**: Set the adapters back to default. Normally DHCP.
/// * **Apply**: Set the adapters to the settings of the system the image was taken to.
/// * **Preserve**: Keep the adapter settings of the current system.
#[repr(i32)]
pub enum NetworkInterfaceSettings {
ResetPrimaryResetOthers = NISysCfgNetworkInterfaceSettings_NISysCfgResetPrimaryResetOthers,
PreservePrimaryResetOthers =
NISysCfgNetworkInterfaceSettings_NISysCfgPreservePrimaryResetOthers,
PreservePrimaryPreserveOthers =
NISysCfgNetworkInterfaceSettings_NISysCfgPreservePrimaryPreserveOthers,
PreservePrimaryApplyOthers =
NISysCfgNetworkInterfaceSettings_NISysCfgPreservePrimaryApplyOthers,
ApplyPrimaryResetOthers = NISysCfgNetworkInterfaceSettings_NISysCfgApplyPrimaryResetOthers,
ApplyPrimaryPreserveOthers =
NISysCfgNetworkInterfaceSettings_NISysCfgApplyPrimaryPreserveOthers,
ApplyPrimaryApplyOthers = NISysCfgNetworkInterfaceSettings_NISysCfgApplyPrimaryApplyOthers,
}

impl Session {
pub fn get_software_image(&self, image: &Path) -> Result<()> {
/// Creates an image of the system in the `image` path.
/// The image is a folder containing metadata and the disk image.
///
/// This wraps the `NISysCfgCreateSystemImageAsFolder` method from the C API.
///
/// # Parameters
/// * `image` should be a path to the directory to be created with the image contents.
/// * `image_info` provides metadata to be saved with the image.
/// * `encryption_passphrase` should be [Some] with a value if you wish to encrypt the image or [None] to save the image unencrypted.
/// * `excluded_files_folders` can contain a list of files and folders which should not be captured in the image.
/// * `auto_restart` Restarts the system into install mode by default before the operation is performed, and restarts back to a running state after the operation is complete.
/// If you choose not to restart automatically, and the system is not in install mode, this function returns an error.
/// * `overwrite_if_exists` defines whether to replace an existing image in the `image` path.
pub fn get_system_image(
&self,
image: &Path,
image_info: &ImageInfo,
encryption_passphrase: Option<&str>,
excluded_files_folders: &[&str],
auto_restart: bool,
overwrite_if_exists: bool,
) -> Result<()> {
let handle = self.handle();
let path = CString::new(image.as_os_str().to_string_lossy().as_ref())?;
let title = std::ptr::null();
let id = std::ptr::null();
let version = std::ptr::null();
let description = std::ptr::null();
let password = encryption_passphrase.map(|password| CString::new(password));
let password_ptr = if let Some(actual_password) = password {
actual_password?.as_ptr()
} else {
std::ptr::null()
};

let title = CString::new(image_info.title.as_str())?;
let id = CString::new(image_info.id.as_str())?;
let version = CString::new(image_info.version.as_str())?;
let description = CString::new(image_info.description.as_str())?;

// Build this list. We do it in two steps to ensure Vec<CString> is owned by the function
// for the duration of the call.
let excluded_c = str_slice_to_cstring(excluded_files_folders)?;
let mut excluded_ptrs = cstring_vec_to_ptr_array(&excluded_c[..]);

unsafe {
api_status(NISysCfgCreateSystemImageAsFolder(
*handle,
title,
id,
version,
description,
FfiBoolean::True as i32,
title.as_ptr(),
id.as_ptr(),
version.as_ptr(),
description.as_ptr(),
FfiBoolean::from(auto_restart) as i32,
path.as_ptr(),
std::ptr::null(),
0,
std::ptr::null_mut(),
FfiBoolean::False as i32,
password_ptr,
excluded_ptrs.len() as u32,
excluded_ptrs.as_mut_ptr(),
FfiBoolean::from(overwrite_if_exists) as i32,
))?;
}
Ok(())
}
pub fn set_software_image(&self, image: &Path) -> Result<()> {

/// Copy a system image to the target from the `image` path.
/// The image is a folder containing metadata and the disk image captured with [Session::get_system_image]
///
/// This wraps the `NISysCfgSetSystemImageFromFolder2` method from the C API.
///
/// # Parameters
/// * `image` should be a path to the directory to be created with the image contents.
/// * `encryption_passphrase` should be [Some] with a value if the image is encrypted or [None] if the image is unencrypted.
/// * `excluded_files_folders` can contain a list of files and folders which should not be ovewritten on the tearget.
/// * `auto_restart` Restarts the system into install mode by default before the operation is performed, and restarts back to a running state after the operation is complete.
/// If you choose not to restart automatically, and the system is not in install mode, the resulting image may not be valid.
/// * `original_system_only` defines whether this should only be applied to the same system the image came from based on the MAC address.
/// * `network_settings` defines the state of the network configuration after the system image has been applied.
pub fn set_system_image(
&self,
image: &Path,
encryption_passphrase: Option<&str>,
excluded_files_folders: &[&str],
auto_restart: bool,
original_system_only: bool,
network_settings: NetworkInterfaceSettings,
) -> Result<()> {
let handle = self.handle();
let path = CString::new(image.as_os_str().to_string_lossy().as_ref())?;
let password = encryption_passphrase.map(|password| CString::new(password));
let password_ptr = if let Some(actual_password) = password {
actual_password?.as_ptr()
} else {
std::ptr::null()
};

// Build this list. We do it in two steps to ensure Vec<CString> is owned by the function
// for the duration of the call.
let excluded_c = str_slice_to_cstring(excluded_files_folders)?;
let mut excluded_ptrs = cstring_vec_to_ptr_array(&excluded_c[..]);

unsafe {
api_status(NISysCfgSetSystemImageFromFolder2(
*handle,
FfiBoolean::True as i32,
FfiBoolean::from(auto_restart) as i32,
path.as_ptr(),
std::ptr::null(),
0,
std::ptr::null_mut(),
FfiBoolean::False as i32,
2,
password_ptr,
excluded_ptrs.len() as u32,
excluded_ptrs.as_mut_ptr(),
FfiBoolean::from(original_system_only) as i32,
network_settings as i32,
))?;
}
Ok(())
}
}

fn str_slice_to_cstring(slice: &[&str]) -> Result<Vec<CString>> {
slice
.iter()
.map(|&item| CString::new(item))
.map(|item| item.map_err(|e| NiSystemConfigurationError::NulStringError(e)))
.collect::<Result<Vec<CString>>>()
}

fn cstring_vec_to_ptr_array(list: &[CString]) -> Vec<*const i8> {
list.iter().map(|cstring| cstring.as_ptr()).collect()
}

#[cfg(test)]
mod test {
use std::ffi::CStr;

use super::{cstring_vec_to_ptr_array, str_slice_to_cstring};

/// Test the conversion to an array of pointers by converting back and confirming contents.
#[test]
fn test_string_list_conversion() {
let original = ["test1", "test2", "test3"];

//First complete our conversion to FFI Types.

let cstring = str_slice_to_cstring(&original).unwrap();
let mut pointers = cstring_vec_to_ptr_array(&cstring);

let length = pointers.len() as u32;
let ptr = pointers.as_mut_ptr();

// This section attempts to replicate what C will do to access this.
let new_strings: Vec<&CStr> = unsafe {
let new_pointers = std::slice::from_raw_parts(ptr, length as usize);
new_pointers
.iter()
.map(|&ptr| CStr::from_ptr(ptr))
.collect()
};

for (&new, original) in new_strings.iter().zip(original) {
let new_string = new.to_str().unwrap();
assert_eq!(new_string, original);
}
}
}
Loading

0 comments on commit 3281177

Please sign in to comment.