commit 3ef4e29564b556922b4737c5bcd8fa891f3c09a1
Author: Vincent Untz <vuntz@suse.com>
Date:   Tue Aug 19 15:34:40 2014 +0200

    Clarify variable names

diff --git a/lib/helper-neutron-migration b/lib/helper-neutron-migration
index cef3896..aa81188 100644
--- a/lib/helper-neutron-migration
+++ b/lib/helper-neutron-migration
@@ -21,12 +21,12 @@ UPGRADE_DATA_DIR=/var/lib/crowbar/upgrade/suse-cloud-3-to-5
 prepare_neutron_migration() {
 
   # Get nodes that have the neutron_server role
-  local neutron_nodes=$(cat ${UPGRADE_DATA_DIR}/neutron-server.nodes)
+  local neutron_servers=$(cat ${UPGRADE_DATA_DIR}/neutron-server.nodes)
 
-  for i in $neutron_nodes; do
-    ssh $i 'neutron-db-manage --config-file /etc/neutron/neutron.conf \
+  for neutron_server in $neutron_servers; do
+    ssh "$neutron_server" 'neutron-db-manage --config-file /etc/neutron/neutron.conf \
             --config-file /etc/neutron/plugins/ml2/ml2_conf.ini stamp havana'
-    # We only need to do this on one node; On a HA-setup, $neutron_nodes can
+    # We only need to do this on one node; On a HA-setup, $neutron_servers can
     # have mutltiple entries
     break
   done
@@ -35,12 +35,12 @@ prepare_neutron_migration() {
 finish_neutron_migration() {
 
   # Get nodes that have the neutron_server role
-  local neutron_nodes=$(cat ${UPGRADE_DATA_DIR}/neutron-server.nodes)
+  local neutron_servers=$(cat ${UPGRADE_DATA_DIR}/neutron-server.nodes)
 
-  for i in $neutron_nodes; do
-    ssh $i 'neutron-db-manage --config-file /etc/neutron/neutron.conf \
+  for neutron_server in $neutron_servers; do
+    ssh "$neutron_server" 'neutron-db-manage --config-file /etc/neutron/neutron.conf \
             --config-file /etc/neutron/plugins/ml2/ml2_conf.ini upgrade head'
-    # We only need to do this on one node; On a HA-setup, $neutron_nodes can
+    # We only need to do this on one node; On a HA-setup, $neutron_servers can
     # have mutltiple entries
     break
   done
commit 7d7e165c9ff04c953c5729265279bae77c2577db
Author: Vincent Untz <vuntz@suse.com>
Date:   Tue Aug 19 15:56:19 2014 +0200

    Remove duplicated agents in neutron database before doing db migration
    
    Otherwise, we can hit an error like this during the migration:
    
    sqlalchemy.exc.IntegrityError: (IntegrityError) could not create unique index
    "uniq_agents0agent_type0host"
    DETAIL:  Key (agent_type, host)=(Open vSwitch agent, d00-19-b9-e2-22-b5) is
    duplicated.
     'ALTER TABLE agents ADD CONSTRAINT uniq_agents0agent_type0host UNIQUE
    (agent_type, host)' {}
    
    https://bugzilla.novell.com/show_bug.cgi?id=892503

diff --git a/lib/helper-neutron-migration b/lib/helper-neutron-migration
index aa81188..b95fb06 100644
--- a/lib/helper-neutron-migration
+++ b/lib/helper-neutron-migration
@@ -26,7 +26,11 @@ prepare_neutron_migration() {
   for neutron_server in $neutron_servers; do
     ssh "$neutron_server" 'neutron-db-manage --config-file /etc/neutron/neutron.conf \
             --config-file /etc/neutron/plugins/ml2/ml2_conf.ini stamp havana'
-    # We only need to do this on one node; On a HA-setup, $neutron_servers can
+
+    scp "`dirname $0`/neutron-icehouse-migration-helper" "${neutron_server}:/var/lib/neutron"
+    ssh "$neutron_server" "/var/lib/neutron/neutron-icehouse-migration-helper"
+
+    # We only need to doi this on one node; On a HA-setup, $neutron_servers can
     # have mutltiple entries
     break
   done
diff --git a/lib/neutron-icehouse-migration-helper b/lib/neutron-icehouse-migration-helper
new file mode 100755
index 0000000..32ed5db
--- /dev/null
+++ b/lib/neutron-icehouse-migration-helper
@@ -0,0 +1,70 @@
+#!/usr/bin/python
+#
+# Copyright 2014, SUSE
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import ConfigParser
+import os
+import sqlalchemy
+import sys
+
+if len(sys.argv) != 1:
+   print >>sys.stderr, 'Usage: %s' % sys.argv[0]
+   sys.exit(1)
+
+def get_db_uri(file, group, option):
+    if not os.path.exists(file):
+        return None
+
+    config = ConfigParser.SafeConfigParser()
+    config.read(file)
+    try:
+        return config.get(group, option)
+    except ConfigParser.Error as e:
+        print >>sys.stderr, 'Cannot find database connection information in %s: %s' % (file, e)
+        return None
+
+
+db_uri = get_db_uri('/etc/neutron/neutron.conf', 'database', 'connection')
+if not db_uri:
+    print >>sys.stderr, 'Cannot find database connection information'
+    sys.exit(1)
+
+db = sqlalchemy.create_engine(db_uri)
+try:
+    connection = db.connect()
+except sqlalchemy.exc.SQLAlchemyError as e:
+    print >>sys.stderr, 'Cannot connect to database: %s' % e
+    sys.exit(1)
+
+unique = {}
+to_delete = []
+
+results = connection.execute("SELECT id, agent_type, host, heartbeat_timestamp FROM agents;")
+for row in results:
+    key = (row['host'], row['agent_type'])
+    agents = unique.get(key, [])
+    agents.append((row['host'], row['agent_type'], row['heartbeat_timestamp'], row['id']))
+    unique[key] = agents
+
+for key, value in unique.iteritems():
+    value.sort()
+    value.pop()
+    for (host, agent_type, timestamp, agent_id) in value:
+        to_delete.append((agent_id, agent_type, host))
+
+for agent_id, agent_type, host in to_delete:
+    print 'Deleting duplicated agent "%s" for (host=%s, agent_type=%s)...' % (agent_id, host, agent_type)
+    connection.execute(sqlalchemy.sql.text('DELETE FROM agents WHERE id = :agent'), agent = agent_id)
