/**
* Abiquo community edition
* cloud management application for hybrid clouds
* Copyright (C) 2008-2010 - Abiquo Holdings S.L.
*
* This application is free software; you can redistribute it and/or
* modify it under the terms of the GNU LESSER GENERAL PUBLIC
* LICENSE as published by the Free Software Foundation under
* version 3 of the License
*
* This software 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
* LESSER GENERAL PUBLIC LICENSE v.3 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
package com.abiquo.vsm.migration;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.LineIterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import com.abiquo.vsm.model.PhysicalMachine;
import com.abiquo.vsm.model.VirtualMachine;
import com.abiquo.vsm.model.VirtualMachinesCache;
import com.abiquo.vsm.monitor.Monitor.Type;
import com.abiquo.vsm.redis.dao.RedisDao;
import com.abiquo.vsm.redis.dao.RedisDaoFactory;
public class Migrator
{
private final static Logger logger = LoggerFactory.getLogger(Migrator.class);
public final static String MachineListKey = "tomigrate";
private String host;
private int port;
private int database;
private int machinesCount;
private int subscriptionsCount;
private Map<String, Integer> ports;
private Map<String, Type> hypervisors;
public Migrator(String host, int port, int database)
{
this.host = host;
this.port = port;
this.database = database;
this.machinesCount = 0;
this.subscriptionsCount = 0;
this.ports = new HashMap<String, Integer>();
this.ports.put(Type.VMX_04.name(), 443);
this.ports.put(Type.HYPERV_301.name(), 5985);
this.ports.put(Type.KVM.name(), 8889);
this.ports.put(Type.VBOX.name(), 8889);
this.ports.put(Type.XEN_3.name(), 8889);
this.ports.put(Type.XENSERVER.name(), 9363);
this.hypervisors = new HashMap<String, Type>();
this.hypervisors.put("vmx-04", Type.VMX_04);
this.hypervisors.put("xenserver", Type.XENSERVER);
this.hypervisors.put("xen-3", Type.XEN_3);
this.hypervisors.put("hyperv-301", Type.HYPERV_301);
}
public void migratePersistedModel()
{
RedisWrapper wrapper = new RedisWrapper(host, port, database);
RedisDao dao = RedisDaoFactory.getInstance();
for (String id : wrapper.getAllSubscriptionIds())
{
String address = wrapper.getHypervisorUrl(id);
String type = wrapper.getHypervisorType(id);
String username = wrapper.getUser(id);
String password = wrapper.getPassword(id);
String virtualMachineName = wrapper.getVirtualSystemId(id);
PhysicalMachine machine = insertMachine(dao, address, type, username, password);
if (machine != null)
{
VirtualMachine virtualMachine = dao.findVirtualMachineByName(virtualMachineName);
if (virtualMachine == null)
{
virtualMachine = new VirtualMachine();
virtualMachine.setName(virtualMachineName);
virtualMachine.setPhysicalMachine(machine);
dao.save(virtualMachine);
this.subscriptionsCount++;
logger.info("Subscription migrated: {}", virtualMachineName);
}
wrapper.deleteSubscription(id);
}
}
}
public void migrateNonPersistedModelFromFile(final File file) throws IOException
{
LineIterator iterator = FileUtils.lineIterator(file);
RedisDao dao = RedisDaoFactory.getInstance();
try
{
while (iterator.hasNext())
{
String line = iterator.nextLine();
insertMachineFromCSVLine(dao, line);
}
}
finally
{
LineIterator.closeQuietly(iterator);
}
}
public void migrateNonPersistedModelFromRedis() throws UnknownHostException, IOException
{
RedisDao dao = RedisDaoFactory.getInstance();
Jedis jedis = new Jedis(host, port);
jedis.connect();
jedis.select(database);
long len = jedis.llen(MachineListKey);
for (int i = 0; i < len; i++)
{
insertMachineFromCSVLine(dao, jedis.lindex(MachineListKey, i));
}
jedis.del(MachineListKey);
jedis.disconnect();
}
private void insertMachineFromCSVLine(final RedisDao dao, final String csvLine)
{
String[] fields = csvLine.split(",");
if (fields.length == 5)
{
String ip = fields[0];
String port = fields[1];
String user = fields[2];
String pass = fields[3];
String type = fields[4];
insertMachine(dao, String.format("http://%s:%s/", ip, port), type, user, pass);
}
}
private PhysicalMachine insertMachine(final RedisDao dao, final String address,
final String type, final String username, final String password)
{
PhysicalMachine machine = dao.findPhysicalMachineByAddress(address);
if (machine == null)
{
try
{
URL url = new URL(address);
String finalType = type;
if (this.hypervisors.containsKey(type))
{
finalType = this.hypervisors.get(type).name();
}
String finalURL =
String.format("http://%s:%s/", url.getHost(), this.ports.get(finalType));
machine = dao.findPhysicalMachineByAddress(finalURL);
if (machine == null)
{
VirtualMachinesCache cache = new VirtualMachinesCache();
dao.save(cache);
machine = new PhysicalMachine();
machine.setAddress(finalURL);
machine.setType(finalType);
machine.setUsername(username);
machine.setPassword(password);
machine.setVirtualMachines(cache);
dao.save(machine);
this.machinesCount++;
logger.info("Physical machine migrated: {} {}", machine.getAddress(), machine
.getType());
}
}
catch (MalformedURLException e)
{
logger.error("Invalid physical machine address {}", address);
return null;
}
}
return machine;
}
public int getMachinesCount()
{
return machinesCount;
}
public int getSubscriptionsCount()
{
return subscriptionsCount;
}
private static Options buildOptions()
{
Options options = new Options();
options.addOption("help", "help", false, "Print this usage information.");
options.addOption("f", "file", true, "CSV file with the machines to migrate.");
options.addOption("h", "host", true, "Redis host.");
options.addOption("p", "port", true, "Redis port.");
return options;
}
private static void printUsage()
{
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("java -jar lib/vsm-migration.jar", buildOptions(), true);
}
public static void main(String[] args) throws UnknownHostException, IOException
{
String host = getProperty("abiquo.redis.host", "localhost");
int port = Integer.valueOf(getProperty("abiquo.redis.port", "6379"));
CommandLine command = null;
String filename = null;
try
{
// Parse the command line arguments
command = new PosixParser().parse(buildOptions(), args);
if (command.hasOption("help"))
{
printUsage();
System.exit(0);
}
if (command.hasOption("f"))
{
filename = command.getOptionValue("f");
}
if (command.hasOption("h"))
{
host = command.getOptionValue("h");
}
if (command.hasOption("p"))
{
port = Integer.parseInt(command.getOptionValue("p"));
}
}
catch (Exception e)
{
logger.error("Error while parsing arguments. " + e.getMessage());
printUsage();
System.exit(-1);
}
// Start migration
Migrator migrator = new Migrator(host, port, 0);
logger.info("Migrating from 1.6.8 to 1.7 data model on redis located at {}:{}", host, port);
if (filename == null)
{
migrator.migrateNonPersistedModelFromRedis();
}
else
{
File file = new File(filename);
migrator.migrateNonPersistedModelFromFile(file);
}
migrator.migratePersistedModel();
logger.info("Number of migrated physical machines: {}", migrator.getMachinesCount());
logger.info("Number of migrated subscriptions: {}", migrator.getSubscriptionsCount());
System.exit(0);
}
private static String getProperty(String name, String defaultValue)
{
String value = System.getProperty(name);
return value == null ? defaultValue : value;
}
}