14 January 2015

Rename a host in Ambari (1.7.0)

Ambari might be tricky for some real life operation, like moving a component from a dead node to another (for instance, the App Timeline Server can't be moved to another host if the current host is dead). We end-up in particular corner cases where it's easier to rename a node in db than failing to move the component.

But updating the hostname is not as straight forward as update hosts set host_name = 'new-hostname' where host_name = 'old-hostname' because the primary key in different tables is the hostname.

Updating all hostname in all the required tables in the same transaction does not work either, because the constraint will fail while doing the update. We should defer constraint checks to ensure the transaction is run successfully.
Mysql

If you choose Mysql as the backend DB, lucky you, it's fairly trivial:
SET FOREIGN_KEY_CHECKS=0; 

BEGIN;
UPDATE ambari.clusterhostmapping set host_name = '${new-name}' WHERE host_name = '${old-name}';
UPDATE ambari.configgrouphostmapping set host_name = '${new-name}' WHERE host_name = '${old-name}';
UPDATE ambari.host_role_command set host_name = '${new-name}' WHERE host_name = '${old-name}';
UPDATE ambari.hostcomponentdesiredstate set host_name = '${new-name}' WHERE host_name = '${old-name}';
UPDATE ambari.hostcomponentstate set host_name = '${new-name}' WHERE host_name = '${old-name}';
UPDATE ambari.hostconfigmapping set host_name = '${new-name}' WHERE host_name = '${old-name}';
UPDATE ambari.hoststate set host_name = '${new-name}' WHERE host_name = '${old-name}';
UPDATE ambari.hosts set host_name = '${new-name}' WHERE host_name = '${old-name}';
COMMIT;

SET FOREIGN_KEY_CHECKS=1;
Postgresql

If you have Postgres as the backend DB, well, there's a bit more work to do:
  1. Delete the constraints about hosts.host_name primary key (yes, really)
  2. Update the hostname
  3. Re-created the foreign key constraints
Automatically find the constraint

Connect to postgres with the admin user

sudo su - postgres
psql -d ambari

Run a query which will generate the ALTER command to drop all the constraints on hosts.host_name

SELECT 'ALTER TABLE '||nspname||'.'||relname||' DROP CONSTRAINT '||conname||';'
FROM pg_constraint
INNER JOIN pg_class ON conrelid=pg_class.oid
INNER JOIN pg_namespace ON pg_namespace.oid=pg_class.relnamespace
where conname in ('hstcmponentdesiredstatehstname', 'hostcomponentstate_host_name', 'fk_hoststate_host_name', 'fk_hostconfmapping_host_name', 'fk_host_role_command_host_name', 'fk_cghm_hname', 'clusterhostmapping_cluster_id') ORDER BY CASE WHEN contype='f' THEN 0 ELSE 1 END,contype,nspname,relname,conname;

Run a query which will generate the ALTER command to create all the constraints on hosts.host_name

SELECT 'ALTER TABLE '||nspname||'.'||relname||' ADD CONSTRAINT '||conname||' '|| pg_get_constraintdef(pg_constraint.oid)||';'
FROM pg_constraint
INNER JOIN pg_class ON conrelid=pg_class.oid
INNER JOIN pg_namespace ON pg_namespace.oid=pg_class.relnamespace
where conname in ('hstcmponentdesiredstatehstname', 'hostcomponentstate_host_name', 'fk_hoststate_host_name', 'fk_hostconfmapping_host_name', 'fk_host_role_command_host_name', 'fk_cghm_hname', 'clusterhostmapping_cluster_id')
ORDER BY CASE WHEN contype='f' THEN 0 ELSE 1 END DESC,contype DESC,nspname DESC,relname DESC,conname DESC;

Save the ALTER commands in you favorite text editor for later, or use the ones provided below.
Delete the constraints

 ALTER TABLE ambari.clusterhostmapping DROP CONSTRAINT clusterhostmapping_cluster_id;
 ALTER TABLE ambari.configgrouphostmapping DROP CONSTRAINT fk_cghm_hname;
 ALTER TABLE ambari.host_role_command DROP CONSTRAINT fk_host_role_command_host_name;
 ALTER TABLE ambari.hostcomponentdesiredstate DROP CONSTRAINT hstcmponentdesiredstatehstname;
 ALTER TABLE ambari.hostcomponentstate DROP CONSTRAINT hostcomponentstate_host_name;
 ALTER TABLE ambari.hostconfigmapping DROP CONSTRAINT fk_hostconfmapping_host_name;
 ALTER TABLE ambari.hoststate DROP CONSTRAINT fk_hoststate_host_name;
Update the hostname

Replace ${old-name} and ${new-name} with the appropriate values.
BEGIN;
UPDATE ambari.clusterhostmapping set host_name = '${new-name}' WHERE host_name = '${old-name}';
UPDATE ambari.configgrouphostmapping set host_name = '${new-name}' WHERE host_name = '${old-name}';
UPDATE ambari.host_role_command set host_name = '${new-name}' WHERE host_name = '${old-name}';
UPDATE ambari.hostcomponentdesiredstate set host_name = '${new-name}' WHERE host_name = '${old-name}';
UPDATE ambari.hostcomponentstate set host_name = '${new-name}' WHERE host_name = '${old-name}';
UPDATE ambari.hostconfigmapping set host_name = '${new-name}' WHERE host_name = '${old-name}';
UPDATE ambari.hoststate set host_name = '${new-name}' WHERE host_name = '${old-name}';
UPDATE ambari.hosts set host_name = '${new-name}' WHERE host_name = '${old-name}';
COMMIT;

Recreate the constaints
 ALTER TABLE ambari.hoststate ADD CONSTRAINT fk_hoststate_host_name FOREIGN KEY (host_name) REFERENCES ambari.hosts(host_name);
 ALTER TABLE ambari.hostconfigmapping ADD CONSTRAINT fk_hostconfmapping_host_name FOREIGN KEY (host_name) REFERENCES ambari.hosts(host_name);
 ALTER TABLE ambari.hostcomponentstate ADD CONSTRAINT hostcomponentstate_host_name FOREIGN KEY (host_name) REFERENCES ambari.hosts(host_name);
 ALTER TABLE ambari.hostcomponentdesiredstate ADD CONSTRAINT hstcmponentdesiredstatehstname FOREIGN KEY (host_name) REFERENCES ambari.hosts(host_name);
 ALTER TABLE ambari.host_role_command ADD CONSTRAINT fk_host_role_command_host_name FOREIGN KEY (host_name) REFERENCES ambari.hosts(host_name);
 ALTER TABLE ambari.configgrouphostmapping ADD CONSTRAINT fk_cghm_hname FOREIGN KEY (host_name) REFERENCES ambari.hosts(host_name);
 ALTER TABLE ambari.clusterhostmapping ADD CONSTRAINT clusterhostmapping_cluster_id FOREIGN KEY (host_name) REFERENCES ambari.hosts(host_name);

Done.

Restart ambari-server

Once you updated the hostname, you need to restart ambari-server.
Actually, you might want to shut it down before running the queries to ensure consistent state of the db.

Resources

If you're interested in more generic readings on deferred foreign keys check, I can recommend this one which was from a great inspiration to me:


.

2 comments:

  1. This comment has been removed by a blog administrator.

    ReplyDelete
  2. Hi!

    I used your instructions to add a new host (host died, so I created an EC2 instance of an AMI taken of that host)

    All I had to do :
    old IP of host = 10.10.96.156
    new IP of host = 10.10.96.110

    update hosts set ipv4='10.10.96.110' where host_id=1;
    update hosts set ipv6='10.10.96.110' where host_id=1;

    ReplyDelete