/* * Lokomo OneCMDB - An Open Source Software for Configuration * Management of Datacenter Resources * * Copyright (C) 2006 Lokomo Systems AB * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. * * Lokomo Systems AB can be contacted via e-mail: info@lokomo.com or via * paper mail: Lokomo Systems AB, Sv�rdv�gen 27, SE-182 33 * Danderyd, Sweden. * */ package org.onecmdb.utils.wsdl; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.net.URI; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.TimeZone; import org.onecmdb.core.IRfcResult; import org.onecmdb.core.internal.ccb.workers.RfcResult; import org.onecmdb.core.internal.model.primitivetypes.DateTimeType; import org.onecmdb.core.utils.IBeanProvider; import org.onecmdb.core.utils.bean.AttributeBean; import org.onecmdb.core.utils.bean.CiBean; import org.onecmdb.core.utils.bean.ValueBean; import org.onecmdb.core.utils.wsdl.IOneCMDBWebService; import org.onecmdb.core.utils.wsdl.OneCMDBServiceFactory; import org.onecmdb.core.utils.xml.XmlGenerator; import org.onecmdb.core.utils.xml.XmlParser; public class OneCMDBSync implements Runnable { private String syncPath; private String serviceURL; private String username; private String pwd; private String token; private String group; private String name; private IOneCMDBWebService service; private boolean doDelete; private IRfcResult result; private IRfcResult deleteResult = new RfcResult(); public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSyncPath() { return syncPath; } public void setSyncPath(String path) { this.syncPath = path; } public String getServiceURL() { return serviceURL; } public void setServiceURL(String service) { this.serviceURL = service; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } public String getToken() { return token; } public void setToken(String token) { this.token = token; } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } private IOneCMDBWebService getService() throws Exception { if (this.service == null) { this.service = OneCMDBServiceFactory.getWebService(serviceURL); if (this.token == null) { this.token = this.service.auth(getUsername(), getPwd()); } } return(service); } public void setService(IOneCMDBWebService service) { this.service = service; } public void run() { // Search for all files in inPath. File in = new File(getImportPath()); if (!in.isDirectory()) { throw new IllegalArgumentException("Import Path '" + getImportPath() + "' is not a directory!"); } File out = new File(getImportStatusPath()); if (!out.isDirectory()) { throw new IllegalArgumentException("Complete Path '" + getImportStatusPath() + "' is not a directory!"); } try { parseInputPath(in, in, out); } catch (Throwable e) { throw new IllegalArgumentException(e); } } private String getImportStatusPath() { return(syncPath + "/status"); } private String getImportPath() { return(syncPath + "/import"); } private String getDeltaPath() { return(syncPath + "/delta"); } public boolean isDoDelete() { return doDelete; } public void setDoDelete(boolean doDelete) { this.doDelete = doDelete; } private List<CiBean> getTemplateBeans() { List<CiBean> templates = new ArrayList<CiBean>(); CiBean sync = new CiBean("Root", "Synchronisation", true); sync.setDisplayNameExpression("Synchronisation"); CiBean importEntry = new CiBean("Synchronisation", "ImportEntry", true); importEntry.setDisplayNameExpression("${group}/${content}"); importEntry.addAttribute(new AttributeBean("Rejected", "isRejected", "xs:boolean", null, false)); importEntry.addAttribute(new AttributeBean("Cause", "rejectCause", "xs:string", null, false)); importEntry.addAttribute(new AttributeBean("TX ID", "txId", "xs:string", null, false)); importEntry.addAttribute(new AttributeBean("Entry Name", "content", "xs:anyURI", null, false)); importEntry.addAttribute(new AttributeBean("Status", "status", "xs:string", null, false)); importEntry.addAttribute(new AttributeBean("Group", "group", "ImportGroup", "PointsTo", true)); importEntry.addAttribute(new AttributeBean("CI Added", "added", "xs:integer", null, false)); importEntry.addAttribute(new AttributeBean("CI Deleted", "deleted", "xs:integer", null, false)); importEntry.addAttribute(new AttributeBean("CI Modified", "modified", "xs:integer", null, false)); importEntry.addAttribute(new AttributeBean("Started", "start", "xs:dateTime", null, false)); importEntry.addAttribute(new AttributeBean("Ended", "stop", "xs:dateTime", null, false)); importEntry.addAttribute(new AttributeBean("Total CI", "totalCI", "xs:integer", null, false)); importEntry.addAttributeValue(new ValueBean("content", "ImportEntry", false)); CiBean importGroup = new CiBean("Synchronisation", "ImportGroup", true); importGroup.setDisplayNameExpression("${name}"); AttributeBean gName = new AttributeBean("name", "xs:string", null, false); gName.setDisplayName("Name"); importGroup.addAttribute(gName); importGroup.addAttributeValue(new ValueBean("name", "ImportGroup", false)); templates.add(sync); templates.add(importEntry); templates.add(importGroup); return(templates); } private void parseInputPath(File in, File inRoot, File outRoot) throws Exception { System.out.println("Parse: " + in.toURI()); if (in.isDirectory()) { File content[] = in.listFiles(); for (int i = 0; i < content.length; i++) { File sub = content[i]; parseInputPath(sub, inRoot, outRoot); } return; } System.out.println("Found: " + in.toURI()); String url = in.toURI().toString(); if (!url.endsWith(".xml")) { System.out.println(url + " :Import files must end width '.xml' skipping..."); return; } String inRootPath = inRoot.toURI().toString(); String outRootPath = outRoot.toURI().toString(); String fileOffsetPath = url.substring(inRootPath.length()); String outPath = outRootPath + fileOffsetPath; File statusFile = new File(new URI(outPath)); if (statusFile.exists() && (statusFile.lastModified() > in.lastModified())) { System.out.println("Already done: " + url); return; } if (statusFile.exists()) { System.out.println(url + "File modified, re-import...."); } // Add this file to input list... XmlParser parser = new XmlParser(); parser.setURL(url); List<CiBean> beans = null; try { beans = parser.getBeans(); } catch (Throwable t) { System.out.println("Wrong format in file: " + url); return; } // Update security group on all CIS. if (group != null) { for (CiBean bean : beans) { bean.setGroup(group); } } // If we receive empty result set ignore, eq. don't remove all! if (beans.size() > 0) { // Create diff files, and generate delete entries. List<CiBean> deletedBeans = createDelta(in, fileOffsetPath, parser, statusFile); if (!doDelete) { deletedBeans = Collections.EMPTY_LIST; } this.result = getService().update(token, beans.toArray(new CiBean[0]), null); if (this.result.isRejected()) { System.out.println("FAILED: Import <" + in.getPath() + "> :" + result.getRejectCause()); } else { System.out.println("OK: Import <" + in.getPath() + ">"); } // Handle Delete if (deletedBeans.size() > 0) { this.deleteResult = getService().update(token, null, deletedBeans.toArray(new CiBean[0])); if (deleteResult.isRejected()) { System.out.println("FAILED: DELETE <" + in.getPath() + "> :" + result.getRejectCause()); } else { System.out.println("OK: DELETE <" + in.getPath() + ">"); } } if (!this.result.isRejected()) { // Move file to outRoot... statusFile.getParentFile().mkdirs(); // Copy content.. try { copyFile(in, statusFile); } catch (Exception e) { System.out.println("Can't save import file " + in.getPath() + " to " + statusFile.getPath() + ":" + e); } } } else { this.result = new RfcResult(); } String groupName = this.name; if (groupName == null) { groupName = in.getPath(); } CiBean importGroup = new CiBean(); importGroup.setDerivedFrom("ImportGroup"); importGroup.setAlias("ImportGroup-" + groupName.hashCode()); importGroup.setTemplate(false); importGroup.setGroup(group); importGroup.addAttributeValue(new ValueBean("name", groupName, false)); // Add this file so we keep track of it... String deletedCIs = "0"; if (deleteResult != null) { if (deleteResult.getCiDeleted() != null) { deletedCIs = deleteResult.getCiDeleted().toString(); } } CiBean importEntry = new CiBean(); importEntry.setDerivedFrom("ImportEntry"); importEntry.setAlias("ImportContent-" + in.toURI().toString().hashCode()); importEntry.setTemplate(false); importEntry.setGroup(group); importEntry.addAttributeValue(new ValueBean("isRejected", "" + result.isRejected(), false)); importEntry.addAttributeValue(new ValueBean("rejectCause", "" + result.getRejectCause(), false)); importEntry.addAttributeValue(new ValueBean("txId", "" + result.getTxId(), false)); importEntry.addAttributeValue(new ValueBean("content", fileOffsetPath, false)); importEntry.addAttributeValue(new ValueBean("status", "Imported", false)); importEntry.addAttributeValue(new ValueBean("group", importGroup.getAlias(), true)); importEntry.addAttributeValue(new ValueBean("added", "" + (result.getCiAdded() == null ? 0 : result.getCiAdded()) , false)); importEntry.addAttributeValue(new ValueBean("deleted", deletedCIs, false)); importEntry.addAttributeValue(new ValueBean("modified", "" + (result.getCiModified() == null ? 0 : result.getCiModified()) + "" , false)); importEntry.addAttributeValue(new ValueBean("totalCI", "" + beans.size(), false)); Date start = result.getStart(); if (start == null) { start = new Date(); } importEntry.addAttributeValue(new ValueBean("start", DateTimeType.parseDate(start), false)); Date stop = result.getStop(); if (stop == null) { stop = new Date(); } importEntry.addAttributeValue(new ValueBean("stop", DateTimeType.parseDate(stop), false)); List<CiBean> templates = getTemplateBeans(); templates.add(importEntry); templates.add(importGroup); IRfcResult statusResult = getService().update(token, templates.toArray(new CiBean[0]), null); if (statusResult.isRejected()) { System.out.println("FAILED: Status Import <" + importEntry.getAlias() + "> :" + statusResult.getRejectCause()); } } private List<CiBean> createDelta(File in, String fileOffsetPath, IBeanProvider imported, File statusFile) throws Exception { File delatPath = new File(getDeltaPath()); File deltaFile = new File(getDeltaPath(), fileOffsetPath); // Move file to outRoot... deltaFile.getParentFile().mkdirs(); if (!statusFile.exists()) { // Store all beans as new. copyFile(in, new File(deltaFile.getPath() + "-NEW-" + getTS() + ".xml")); return(Collections.EMPTY_LIST); } // Add this file to input list... XmlParser parser = new XmlParser(); parser.setURL(statusFile.toURL().toExternalForm()); List<CiBean> newBeans = new ArrayList<CiBean>(); List<CiBean> deletedBeans = new ArrayList<CiBean>(); // Find new for (CiBean bean : imported.getBeans()) { if (parser.getBean(bean.getAlias()) == null) { newBeans.add(bean); } } // Find removed. for (CiBean bean : parser.getBeans()) { if (imported.getBean(bean.getAlias()) == null) { deletedBeans.add(bean); } } // Flush them out. XmlGenerator gen = new XmlGenerator(); gen.setOutput(deltaFile.getPath() + "-NEW-" + getTS() + ".xml"); gen.setBeans(newBeans); gen.process(); XmlGenerator gen2 = new XmlGenerator(); gen2.setOutput(deltaFile.getPath() + "-DELETED-" + getTS() + ".xml"); gen2.setBeans(deletedBeans); gen2.process(); return(deletedBeans); } private String getTS() { SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd-HH-mm-ss"); format.setTimeZone(TimeZone.getDefault()); return(format.format(new Date())); } private void copyFile(File in, File out) throws Exception { FileInputStream fis = new FileInputStream(in); FileOutputStream fos = new FileOutputStream(out); try { byte[] buf = new byte[8192]; int i = 0; while ((i = fis.read(buf)) != -1) { fos.write(buf, 0, i); } } catch (Exception e) { throw e; } finally { try { if (fis != null) { fis.close(); } } finally { if (fos != null) { fos.close(); } } } } private static String ARGS[][] = { {"url", "WSDL URL excluding ?WSDL", "http://localhost:8080/webservice/onecmdb"}, {"user", "The user to login as.", "admin"}, {"pwd", "The user to login as.", "123"}, {"syncPath", "Path to syncronization directory", null}, {"group", "Group alias that all imorted files should belong to", null}, {"token", "Used instead of username/pwd", null}, {"name", "Group all import entry by this name", "Default Import Group"}, }; public static void main(String argv[]) { SimpleArg arg = new SimpleArg(ARGS); String url = arg.getArg(ARGS[0][0], argv); String username = arg.getArg(ARGS[1][0], argv); String pwd = arg.getArg(ARGS[2][0], argv); String syncPath = arg.getArg("syncPath", argv); String group = arg.getArg("group", argv); String token = arg.getArg("token", argv); String name = arg.getArg("name", argv); OneCMDBSync sync = new OneCMDBSync(); sync.setServiceURL(url); sync.setSyncPath(syncPath); sync.setGroup(group); sync.setToken(token); sync.setUsername(username); sync.setPwd(pwd); sync.setName(name); try { sync.run(); } catch (Throwable t) { t.printStackTrace(); } } public IRfcResult getResult() { return(result); } public IRfcResult getDeleteResult() { return(deleteResult); } }