using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Runtime.Serialization.Json; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; using Zerto.Zvm.Api.Interfaces; using Zerto.Zvm.Api.Interfaces.Alerts; using Zerto.Zvm.Api.Interfaces.Events; using Zerto.Zvm.Api.Interfaces.Tasks; using Zerto.Zvm.Api.Interfaces.Vpgs; |
namespace ApiTestExample { public class DemoProgram { private const string c_reponseInXml = "application/xml"; private const string c_reponseInJson = "application/json"; private const string c_authorizationHeader = "x-zerto-session"; private string m_baseAddress; private string m_format; private string m_sessionId; //private static string m_responseFormat = c_reponseInXml; public DemoProgram(string baseAddress, string responseFormat) { m_baseAddress = baseAddress; m_format = responseFormat; m_sessionId = string.Empty; } |
public static void Main(string[] args) { // Input if (args.Length != 5 && args.Length != 4) { Console.WriteLine("Usage: DemoProgram <ZVM IP> <ZVM Port> <user> <password> [<login type 0 = windows or 1 = vCenter or SCVMM>]"); return; } string zvmip = args[0]; string zvmport = args[1]; string username = args[2]; string password = args[3]; string loginType = args.Length > 4 ? args[4] : "1"; // Connection setting for http ServicePointManager.Expect100Continue = true; ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3; ServicePointManager.ServerCertificateValidationCallback = (obj, certificate, chain, errors) => true; // Construct Object with URL // note the required '/' at the end of the uri DemoProgram demo = new DemoProgram("https://" + zvmip + ":" + zvmport + "/v1/", c_reponseInJson); // Actions: try { //list the APIs - execute request on the base address demo.ListApis(); //only APIs listing and starting a session don't require being logged-in demo.LogInAndSetSessionId(username, password, type); // A simple Get action demo.GetEvents(); // A Put action using a filter demo.DismissAllAlerts(); // Performing a failover test demo.FailoverTestVpg(); // Logout demo.LogOut(); Console.WriteLine("\n\nPress any key to exit"); Console.ReadKey(); } catch (WebException wex) { Console.WriteLine(wex); } } |
#region Actions that do not require a session private void ListApis() { string result = ExecuteHttpGet(m_baseAddress, m_format, null); Console.WriteLine("The result is a list of uri addresses each representing an API:"); Console.WriteLine("====================================================="); List<StringResourceLink> uris = (List<StringResourceLink>)DeserializeObjectFromJson(result, typeof(List<StringResourceLink>)); foreach (var stringResourceLink in uris) { Console.WriteLine(stringResourceLink.ResourceLocation); } } #endregion |
#region login related private void LogInAndSetSessionId(string domainUserName, string domainPassword, string loginType) { string addSessionUri = m_baseAddress + "session/add"; HttpWebRequest request = WebRequest.Create(addSessionUri) as HttpWebRequest; //logging-in is about ADDING a session with the POST http command request.Method = "POST"; request.Timeout = 10000; request.ContentLength = 0; string encodedCredentialsInBasicAuthenticationFormat = GetCredentialsForBasicAuthentication(domainUserName, domainPassword); request.Headers.Add("Authorization", encodedCredentialsInBasicAuthenticationFormat); // Set the content type of the data being posted request.ContentType = m_format; request.Accept = m_format; // Body byte[] byte1 = new ASCIIEncoding().GetBytes("{\"AuthenticationMethod\":"+ loginType + "}"); request.ContentLength = byte1.Length; Stream newStream = request.GetRequestStream(); newStream.Write(byte1, 0, byte1.Length); HttpWebResponse httpResponse = request.GetResponse() as HttpWebResponse; if (httpResponse.StatusCode == HttpStatusCode.OK) { //After a successful login your session-id is returned in the 'x-zerto-session' header m_sessionId = httpResponse.Headers.Get(c_authorizationHeader); } else { Console.WriteLine("request {0} failed with code: {1}, {2}", request, httpResponse.StatusCode, httpResponse.StatusDescription); } } private static string GetCredentialsForBasicAuthentication(string domainUserName, string domainPassword) { string prepareUserNamePassword = string.Format("{0}:{1}", domainUserName, domainPassword); byte[] credentialsBytes = Encoding.ASCII.GetBytes(prepareUserNamePassword); string encodedBasicAuthentication = Convert.ToBase64String(credentialsBytes); return "Basic " + encodedBasicAuthentication; //note that the space is mandatory } |
private void LogOut() { string addSessionUri = m_baseAddress + "/session"; HttpWebRequest request = WebRequest.Create(addSessionUri) as HttpWebRequest; //logging-out is about DELETING a session, with the DELETE http command request.Method = "DELETE"; request.Timeout = 10000; //you need to set the id of the session to be deleted --> logged-out request.Headers.Add(c_authorizationHeader, m_sessionId); HttpWebResponse httpResponse = request.GetResponse() as HttpWebResponse; if (httpResponse.StatusCode == HttpStatusCode.OK) { m_sessionId = string.Empty; Console.WriteLine("\nLogged out - by deleting the session"); } else { Console.WriteLine("Request {0} failed with code: {1}, {2}", request, httpResponse.StatusCode, httpResponse.StatusDescription); } } #endregion |
#region example flows /* * Example - get all events, filtering is not used */ private void GetEvents() { Console.WriteLine("\nRequest the list of events (no filter is operated):"); Console.WriteLine("==================================================="); string apiAddress = m_baseAddress + "events"; string result = ExecuteHttpGet(apiAddress, m_format, m_sessionId); List<EventApi> events = (List<EventApi>)DeserializeObjectFromJson(result, typeof(List<EventApi>)); Console.WriteLine("Found " + events.Count + " events."); // Go Over all events fetching them one by one for (int i = 0;i < events.Count;i++) { Console.WriteLine((i+1) + ". " + events[i].Description); } } |
/* * Example - dismiss all the 'warning' level alerts that are not already dismissed */ private void DismissAllAlerts() { Console.WriteLine("\nDismiss all alerts that are warning level and not dismissed."); Console.WriteLine("==========================================================="); // Do Get fetch all alerts with certain filter string apiAddress = m_baseAddress + "alerts?isDismissed=false&level=Error"; string result = ExecuteHttpGet(apiAddress, m_format, m_sessionId); // Deserialize response into zerto api object List<AlertApi> alerts = (List<AlertApi>)DeserializeObjectFromJson(result, typeof(List<AlertApi>)); Console.WriteLine("Found " + alerts.Count + " alerts matching filter."); int count = 0; // Go Over all alerts fetch and dismiss them one by one foreach (var alert in alerts) { try { Console.WriteLine("Dismiss alert : " + alert.Description); string apiAlertAddress = m_baseAddress + "alerts/" + alert.Link.Identifier + "/dismiss"; ExecuteHttpPost(apiAlertAddress, m_format, m_sessionId, null); count++; } catch (Exception e) { Console.WriteLine("error " + e.Message + "."); } Console.WriteLine("Dismissed " + count + " alerts."); } } |
/* * Example - Failover Test a VPG */ private void FailoverTestVpg() { Console.WriteLine("\nDo a Failover Test for the first VPG"); Console.WriteLine("==================================================="); // Fetch all vpgs - convert data to a zerto api object string apiAddress = m_baseAddress + "vpgs"; string result = ExecuteHttpGet(apiAddress, m_format, m_sessionId); List<VpgApi> vpgs = (List<VpgApi>)DeserializeObjectFromJson(result, typeof(List<VpgApi>)); Console.WriteLine("Found " + vpgs.Count + " vpgs."); // if any vpgs are retrieved, take first one for a failover test if (vpgs.Count > 0) { VpgApi vpg = vpgs[0]; Console.WriteLine("Work on Vpg: " + vpg.VpgName + "."); // Fetch all checkpoints for the VPG string cpApiAddress = m_baseAddress + "vpgs/" + vpg.Link.Identifier + "/checkpoints"; string cpRes = ExecuteHttpGet(cpApiAddress, m_format, m_sessionId); List<CheckpointApi> cps = (List<CheckpointApi>)DeserializeObjectFromJson(cpRes, typeof(List<CheckpointApi>)); Console.WriteLine("Found " + cps.Count + " valid checkpoints."); if (cps.Count > 0) { // Post a request for failover test with the latest checkpoint FailOverTestStartDataApi fotData = new FailOverTestStartDataApi(cps.Last().CheckpointIdentifier); Console.WriteLine("Start Failover Test."); string fotApiAddress = m_baseAddress + "vpgs/" + vpg.Link.Identifier + "/failovertest"; // Wait till the task is done bool isFotOk = FollowTask(ExecuteHttpPost(fotApiAddress, m_format, m_sessionId, SerializeObjectToJson(fotData, typeof(FailOverTestStartDataApi))), "Start Failover Test"); if (isFotOk) { Console.WriteLine("Failover is in Test."); // Post a request to stop the failover test StopFailoverTestDataApi stopFotData = new StopFailoverTestDataApi(true, "OK"); Console.WriteLine("Stop Failover Test."); string sfotApiAddress = m_baseAddress + "vpgs/" + vpg.Link.Identifier + "/failoverteststop"; bool isStopFotOk = FollowTask(ExecuteHttpPost(sfotApiAddress, m_format, m_sessionId, SerializeObjectToJson(stopFotData, typeof(StopFailoverTestDataApi))), "Stop Failover Test"); Console.WriteLine(isStopFotOk ? "Failover Test stopped successfully." : "Failover Test not stopped!"); } else { Console.WriteLine("Could not start Failover Test."); } } else { Console.WriteLine("Vpg has no Checkpoints - cannot Failover Test."); } } Console.WriteLine("Testing completed."); } |
/* * Example Follow a task id until the task finishes */ private bool FollowTask(string taskId, string operation) { bool isSuccess = false; if (taskId != null) { taskId = (string)DeserializeObjectFromJson(taskId, typeof(string)); // NOTICE output is in json string so needs to be deserialized bool isRunning = true; do { string taskApiAddress = m_baseAddress + "tasks/" + taskId; string taskRes = ExecuteHttpGet(taskApiAddress, m_format, m_sessionId); TaskApi taskApi = (TaskApi)DeserializeObjectFromJson(taskRes, typeof(TaskApi)); switch (taskApi.Status.State) { case TaskStatusEnumApi.InProgress: Console.WriteLine(operation + " Task is at " + (taskApi.Status.Progress) + "%"); break; case TaskStatusEnumApi.Cancelling: Console.WriteLine(operation + " Task is aborting."); break; case TaskStatusEnumApi.Failed: Console.WriteLine(operation + " Task failed."); isRunning = false; break; case TaskStatusEnumApi.Completed: Console.WriteLine(operation + " Task done."); isRunning = false; isSuccess = true; break; default: Console.WriteLine(operation + " Task is waiting."); break; } Thread.Sleep(10000); } while (isRunning); } else { Console.WriteLine(operation + " Task failed to start."); } return isSuccess; } #endregion |
#region General http private static string ParseHttpResponse(HttpWebRequest request, string format) { string apiResult = null; try { HttpWebResponse httpResponse = request.GetResponse() as HttpWebResponse; if (httpResponse.StatusCode == HttpStatusCode.OK) { using (Stream responseStream = httpResponse.GetResponseStream()) { using (StreamReader reader = new StreamReader(responseStream)) { apiResult = reader.ReadToEnd(); } } if (format == c_reponseInXml) { apiResult = PrettyPrintXmlData(apiResult); } } else { Console.WriteLine("request {0} failed with code: {1}, {2}", request, httpResponse.StatusCode, httpResponse.StatusDescription); } } catch (WebException wex) { Console.WriteLine(wex); } catch (Exception ex) { Console.WriteLine(ex); } return apiResult; } |
public static string ExecuteHttpGet(string url, string format, string sessionId) { HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest; request.Timeout = 20000; request.Method = "GET"; request.ContentType = "charset=UTF-8"; request.Accept = format; //Must add the session-id to every API call, except to listing of APIs and logging-in if (sessionId != null) { request.Headers.Add(c_authorizationHeader, sessionId); } string apiResult = ParseHttpResponse(request, format); return apiResult; } |
public static string ExecuteHttpPost(string url, string format, string sessionId, string bodyData) { HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest; //logging-in is about ADDING a session - using the POST http command request.Method = "POST"; request.Timeout = 10000; request.ContentLength = 0; request.Headers.Add("Authorization", sessionId); request.Accept = format; request.ContentType = format; // Set the data and content length of the string being posted. byte[] byte1 = new ASCIIEncoding().GetBytes(bodyData); request.ContentLength = byte1.Length; Stream newStream = request.GetRequestStream(); newStream.Write(byte1, 0, byte1.Length); string apiResult = ParseHttpResponse(request, format); return apiResult; } |
private static string PrettyPrintXmlData(String xmlString) { String formattedXmlString = string.Empty; using (MemoryStream mStream = new MemoryStream()) { using (XmlTextWriter writer = new XmlTextWriter(mStream, Encoding.Unicode)) { var xDoc = new XmlDocument(); try { xDoc.LoadXml(xmlString); writer.Formatting = Formatting.Indented; xDoc.WriteContentTo(writer); writer.Flush(); mStream.Flush(); mStream.Position = 0; using (StreamReader reader = new StreamReader(mStream)) { formattedXmlString = reader.ReadToEnd(); } } catch (XmlException) { } } } return formattedXmlString; } #endregion |
#region serialize / deserialize private string SerializeObjectToJson(Object o, Type type) { string output; using (var stream = new MemoryStream()) { DataContractJsonSerializer ser = new DataContractJsonSerializer(type); ser.WriteObject(stream, o); stream.Position = 0; using (var reader = new StreamReader(stream)) { output = reader.ReadToEnd(); } } return output; } private static Stream GenerateStreamFromString(string s) { return new MemoryStream(Encoding.UTF8.GetBytes(s)); } |
private Object DeserializeObjectFromJson(string jsonStr, Type type) { if (jsonStr == null) { return null; } Object output; using (Stream stream = GenerateStreamFromString(jsonStr)) { stream.Position = 0; DataContractJsonSerializer ser = new DataContractJsonSerializer(type); output = ser.ReadObject(stream); } return output; } #endregion } } |
The result is a list of uri addresses each representing an API: ===================================================== https://162.30.77.10:9669/v1/events https://162.30.77.10:9669/v1/vpgs https://162.30.77.10:9669/v1/vms https://162.30.77.10:9669/v1/vras https://162.30.77.10:9669/v1/peersites https://162.30.77.10:9669/v1/session https://162.30.77.10:9669/v1/tasks https://162.30.77.10:9669/v1/serviceprofiles https://162.30.77.10:9669/v1/virtualizationsites https://162.30.77.10:9669/v1/zorgs https://162.30.77.10:9669/v1/localsite https://162.30.77.10:9669/v1/alerts |
Request the list of events (no filters are used): =================================================== Found 21 Events. 1. Stop Failover Test. Success. Completed successfully: OK 2. Protection group failover test. Success. Completed successfully. 3. Stop Failover Test. Success. Completed successfully: OK 4. Protection group failover test. Success. Completed successfully. 5. Stop Failover Test. Success. Completed successfully: OK 6. Protection group failover test. Success. Completed successfully. 7. Stop Failover Test. Success. Completed successfully. 8. Protection group failover test. Success. Completed successfully. 9. Stop Failover Test. Success. Completed successfully. 10. Protection group failover test. Success. Completed successfully. 11. Stop Failover Test. Success. Completed successfully. 12. Protection group failover test. Success. Completed successfully. 13. Stop Failover Test. Success. Completed successfully. 14. Protection group failover test. Success. Completed successfully. 15. Stop Failover Test. Success. Completed successfully. 16. Alert turned on at 08/06/2015 14:53:52: The VPG MyVPG has been protected for 15 minutes but the journal history is only 3 minutes. 17. Protection group failover test. Success. Completed successfully. 18. Alert turned off at 08/06/2015 14:40:02 after 0:0:10: Synchronizing with site Site4-Ent2-P1. 19. Pairing. Zerto Virtual Manager = 127.0.0.1, Port = 9081. Success. Completed successfully. 20. Alert turned on at 08/06/2015 14:39:52: Synchronizing with site Site4-Ent2-P1. 21. Protection group creation. Success. Completed successfully. |
Dismiss all alerts that are warning level and not dismissed. =================================================== Found 1 alerts matching filter. Dismiss alert : The VPG MyVPG has been protected for 1 hour but the journal history is only 4 minutes. Dismissed 1 alerts. |
Do a Failover Test for the first VPG =================================================== Found 1 vpgs. Work on Vpg: MyVPG. Found 60 valid checkpoints. Start Failover Test. Start Failover Test Task is at 0% Start Failover Test Task is at 6% Start Failover Test Task is at 43% Start Failover Test Task is at 43% Start Failover Test Task is at 43% Start Failover Test Task is at 43% Start Failover Test Task is at 81% Start Failover Test Task done. Failover is in Test. Stop Failover Test. Stop Failover Test Task is at 0% Stop Failover Test Task is at 19% Stop Failover Test Task is at 19% Stop Failover Test Task is at 95% Stop Failover Test Task done. Failover Test stopped successfully. Testing completed. |
Logged out - by deleting the session |