| #!/usr/bin/env python |
| # ----------------------------------------------------------------------- |
| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you 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 sys |
| |
| version_min = [2, 7] |
| version_info = sys.version_info |
| version_error = False |
| if(version_info[0] < version_min[0]): |
| version_error = True |
| elif(version_info[0] == version_min[0]): |
| if(version_info[1] < version_min[1]): |
| version_error = True |
| if(version_error): |
| print('Python minimum requirement is version '+str(version_min[0])+'.'+str(version_min[1])) |
| sys.exit(1) |
| |
| import argparse |
| import os |
| |
| from argparse import RawDescriptionHelpFormatter |
| |
| from ducc_util import * |
| |
| class DuccRsync(DuccUtil): |
| |
| merge = True |
| |
| key_ducc_head = 'ducc.head' |
| key_ducc_head_reliable_list = 'ducc.head.reliable.list' |
| |
| rsync_cmd = 'rsync' |
| rsync_flags = '-e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" -avz --links --delete --ignore-errors --timeout 20' |
| |
| symlink_cmd = 'rsync' |
| symlink_flags = '-e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" -vz --links --delete --ignore-errors --timeout 20' |
| |
| ssh_cmd = 'ssh' |
| ssh_flags = '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' |
| |
| mkdir_cmd = 'mkdir' |
| mkdir_flags = '-p' |
| |
| head_dirs_list = [ |
| 'admin', |
| 'apache-uima', |
| 'bin', |
| 'cassandra-server', |
| 'cron', |
| 'docs', |
| 'duccling', |
| 'examples', |
| #'history', |
| 'INSTALL', |
| 'issuesFixed', |
| 'lib', |
| 'LICENSE', |
| #'logs', |
| 'NOTICE', |
| 'README', |
| 'RELEASE_NOTES.html', |
| 'resources', |
| 'resources.private', |
| #'state', |
| 'state/duccling.version', |
| 'webserver', |
| ] |
| |
| agent_dirs_list = [ |
| 'admin', |
| 'apache-uima', |
| 'bin', |
| #'cassandra-server', |
| 'cron', |
| 'docs', |
| 'duccling', |
| 'examples', |
| #'history', |
| 'INSTALL', |
| 'issuesFixed', |
| 'lib', |
| 'LICENSE', |
| #'logs', |
| 'NOTICE', |
| 'README', |
| 'RELEASE_NOTES.html', |
| 'resources', |
| #'resources.private', |
| #'state', |
| #'webserver', |
| ] |
| |
| agent_files_list = [ |
| 'resources.private/ducc-broker-credentials.properties', |
| 'state/duccling.version', |
| ] |
| |
| dual_dirs_list = head_dirs_list |
| |
| head_dirs = ' '.join(head_dirs_list) |
| agent_dirs = ' '.join(agent_dirs_list) |
| dual_dirs = ' '.join(dual_dirs_list) |
| |
| agent_files = ' '.join(agent_files_list) |
| |
| def __init__(self): |
| DuccUtil.__init__(self, self.merge) |
| |
| def _fn(self): |
| fpath = __file__.split('/') |
| flen = len(fpath) |
| return fpath[flen-1] |
| |
| # extra help! |
| def get_epilog(self): |
| epilog = '' |
| epilog = epilog+'Purpose: synchronize DUCC directory(s) from present node to other head & agent nodes.' |
| return epilog |
| |
| # parse command line |
| def get_args(self): |
| self.help_head_nodes = 'Blank separated list of head nodes to synchronize, default:"None"; specify "all" for '+' '.join(self.head_nodes) |
| self.help_agent_nodes = 'Blank separated list of agent nodes to synchronize, default:"None"; specify "all" for '+' '.join(self.agent_nodes) |
| |
| self.help_head_dirs = 'Blank separated list of head directories (and files) to synchronize, default="'+self.head_dirs+'"' |
| self.help_agent_dirs = 'Blank separated list of agent directories (and files) to synchronize, default="'+self.agent_dirs+'"' |
| |
| self.help_debug = 'Display debugging messages.' |
| self.help_quiet = 'Suppress informational messages.' |
| |
| self.parser = argparse.ArgumentParser(formatter_class=RawDescriptionHelpFormatter,epilog=self.get_epilog()) |
| |
| self.parser.add_argument('--head-nodes' , action='store' , help=self.help_head_nodes) |
| self.parser.add_argument('--agent-nodes' , action='store' , help=self.help_agent_nodes) |
| self.parser.add_argument('--head-dirs' , action='store' , help=self.help_head_dirs) |
| self.parser.add_argument('--agent-dirs' , action='store' , help=self.help_agent_dirs) |
| |
| self.parser.add_argument('--debug' , action='store_true', help=self.help_debug) |
| self.parser.add_argument('--quiet' , action='store_true', help=self.help_quiet) |
| |
| self.args = self.parser.parse_args() |
| |
| if(self.args.head_nodes == None): |
| if(self.args.agent_nodes == None): |
| raise Exception('--head-nodes and/or --agent-nodes must be specified') |
| |
| # conditionally add node to list of nodes to be updated |
| def add(self,list,node): |
| if(node == self.localhost): |
| pass |
| elif(node in list): |
| pass |
| else: |
| list.append(node) |
| |
| # create list of head nodes |
| def get_head_node_list(self): |
| node_list = [] |
| # head |
| head = self.ducc_properties.get(self.key_ducc_head) |
| #reliable |
| reliable_string = self.ducc_properties.get(self.key_ducc_head_reliable_list) |
| reliable = reliable_string.split() |
| if(len(reliable) > 0): |
| for node in reliable: |
| self.add(node_list,node) |
| else: |
| self.add(node_list,head) |
| return node_list |
| |
| # create list of head nodes |
| def get_agent_node_list(self): |
| node_list = [] |
| ducc_nodes = 'ducc.nodes' |
| nodefiles = ducc_nodes.split(); |
| map = {} |
| for nodefile in nodefiles: |
| nodes, map = self.read_nodefile(nodefile,map) |
| for key in map: |
| value = map[key] |
| for node in value: |
| self.add(node_list,node) |
| return node_list |
| |
| # use user specified head nodes, else from ducc.properties |
| def resolve_head_nodes(self): |
| if(self.args.head_nodes == None): |
| self.head_nodes = None |
| elif(self.args.head_nodes.lower() != 'all'): |
| self.head_nodes = self.args.head_nodes.split() |
| |
| # use user specified agent nodes, else from ducc.nodes |
| def resolve_agent_nodes(self): |
| if(self.args.agent_nodes == None): |
| self.agent_nodes = None |
| elif(self.args.agent_nodes.lower() != 'all'): |
| self.agent_nodes = self.args.agent_nodes.split() |
| |
| # update nodes that are head+agent |
| def update_dual(self,user): |
| rc = 0 |
| if(self.head_nodes != None): |
| if(self.agent_nodes != None): |
| for node in self.head_nodes: |
| if(node in self.agent_nodes): |
| if(self.args.debug): |
| print 'update head+agent: '+node |
| self.mkdir(node,user) |
| rc = self.rsync_dirs(node,user,self.dual_dirs) |
| return rc |
| |
| # update nodes that are head only |
| def update_heads(self,user): |
| rc = 0 |
| update = True |
| if(self.head_nodes != None): |
| for node in self.head_nodes: |
| if(self.agent_nodes != None): |
| if(node in self.agent_nodes): |
| # dual should have done this one |
| update = False |
| if(update): |
| if(self.args.debug): |
| print 'update head: '+node |
| self.mkdir(node,user) |
| rc = self.rsync_dirs(node,user,self.head_dirs) |
| return rc |
| |
| # update nodes that are agent only |
| def update_agents(self,user): |
| rc = 0 |
| update = True |
| if(self.agent_nodes != None): |
| for node in self.agent_nodes: |
| if(self.head_nodes != None): |
| if(node in self.head_nodes): |
| # dual should have done this one |
| update = False |
| if(update): |
| if(self.args.debug): |
| print 'update agent: '+node |
| self.mkdir(node,user) |
| rc = self.rsync_dirs(node,user,self.agent_dirs) |
| if(rc == 0): |
| rc = self.rsync_files(node,user,self.agent_files) |
| return rc |
| |
| # update the specified node dirs |
| def mkdir(self,node,user): |
| rc = 0 |
| rmt = self.mkdir_cmd+' '+self.mkdir_flags+' '+DUCC_HOME |
| cmd = self.ssh_cmd+' '+self.ssh_flags+' '+user+'@'+node+' '+rmt |
| proc = subprocess.Popen(cmd, shell=True, bufsize=0, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) |
| lines = [] |
| for line in proc.stdout: |
| lines.append(line.strip()) |
| proc.wait() |
| rc = proc.returncode |
| if(rc != 0): |
| self.rsync_display(rc, cmd, lines) |
| elif(self.args.debug): |
| self.rsync_display(rc, cmd, lines) |
| return rc |
| |
| # update the specified node dirs |
| def rsync_dirs(self,node,user,subdirs): |
| rc = 0 |
| for subdir in subdirs.split(): |
| if(not self.args.quiet): |
| print 'rsync '+user+' '+node+' '+subdir |
| dir = os.path.join(DUCC_HOME,subdir) |
| cmd = self.rsync_cmd+' '+self.rsync_flags+' '+dir+' '+user+'@'+node+':'+DUCC_HOME |
| proc = subprocess.Popen(cmd, shell=True, bufsize=0, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) |
| lines = [] |
| for line in proc.stdout: |
| lines.append(line.strip()) |
| proc.wait() |
| rc = proc.returncode |
| if(rc != 0): |
| self.rsync_display(rc, cmd, lines) |
| break |
| elif(self.args.debug): |
| self.rsync_display(rc, cmd, lines) |
| self.symlink(user,node) |
| return rc |
| |
| # update the specified node files |
| def rsync_files(self,node,user,subfiles): |
| rc = 0 |
| for subfile in subfiles.split(): |
| if(not self.args.quiet): |
| print 'rsync '+user+' '+node+' '+subfile |
| file = os.path.join(DUCC_HOME,subfile) |
| # head = abs path of this script (.../admin) |
| head, tail = os.path.split(file) |
| rmt = self.mkdir_cmd+' '+self.mkdir_flags+' '+head |
| cmd = self.ssh_cmd+' '+self.ssh_flags+' '+user+'@'+node+' '+rmt |
| proc = subprocess.Popen(cmd, shell=True, bufsize=0, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) |
| lines = [] |
| for line in proc.stdout: |
| lines.append(line.strip()) |
| proc.wait() |
| rc = proc.returncode |
| if(rc != 0): |
| self.rsync_display(rc, cmd, lines) |
| break |
| elif(self.args.debug): |
| self.rsync_display(rc, cmd, lines) |
| # rsync file |
| cmd = self.rsync_cmd+' '+self.rsync_flags+' '+file+' '+user+'@'+node+':'+head |
| proc = subprocess.Popen(cmd, shell=True, bufsize=0, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) |
| lines = [] |
| for line in proc.stdout: |
| lines.append(line.strip()) |
| proc.wait() |
| rc = proc.returncode |
| if(rc != 0): |
| self.rsync_display(rc, cmd, lines) |
| break |
| elif(self.args.debug): |
| self.rsync_display(rc, cmd, lines) |
| self.symlink(user,node) |
| return rc |
| |
| def rsync_display(self, rc, cmd, lines): |
| print rc, cmd |
| for line in lines: |
| print line |
| |
| def symlink(self,user,node): |
| # head = abs path of this script (.../admin) |
| head, tail = os.path.split(sys.argv[0]) |
| # ducc_home = abs path of DUCC_HOME |
| ducc_home, tail = os.path.split(head) |
| if(len(ducc_home) > 0): |
| user_home = os.path.expanduser('~') |
| ducc_runtime = os.path.join(user_home,'ducc_runtime') |
| rmt = 'ln -s '+ducc_home+' '+ducc_runtime |
| cmd = self.ssh_cmd+' '+self.ssh_flags+' '+user+'@'+node+' '+rmt |
| proc = subprocess.Popen(cmd, shell=True, bufsize=0, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) |
| lines = [] |
| for line in proc.stdout: |
| lines.append(line.strip()) |
| proc.wait() |
| rc = proc.returncode |
| #if(rc != 0): |
| #self.rsync_display(rc, cmd, lines) |
| |
| # do rsync of dir(s) from present node to peer(s) |
| def main(self, argv): |
| try: |
| self.head_nodes = self.get_head_node_list() |
| self.agent_nodes = self.get_agent_node_list() |
| self.get_args() |
| self.resolve_head_nodes() |
| self.resolve_agent_nodes() |
| user = find_ducc_uid() |
| rc = 0 |
| if(rc == 0): |
| rc = self.update_dual(user) |
| if(rc == 0): |
| rc = self.update_heads(user) |
| if(rc == 0): |
| rc = self.update_agents(user) |
| except Exception,e: |
| print e |
| |
| if __name__ == '__main__': |
| instance = DuccRsync() |
| instance.main(sys.argv[1:]) |