##// END OF EJS Templates
incremental improvements to SSH launchers...
MinRK -
Show More
@@ -60,7 +60,7 b' from IPython.utils.text import EvalFormatter'
60 60 from IPython.utils.traitlets import (
61 61 Any, Integer, CFloat, List, Unicode, Dict, Instance, HasTraits,
62 62 )
63 from IPython.utils.path import get_ipython_module_path
63 from IPython.utils.path import get_ipython_module_path, get_home_dir
64 64 from IPython.utils.process import find_cmd, pycmd2argv, FindCmdError
65 65
66 66 from .win32support import forward_read_events
@@ -217,16 +217,12 b' class BaseLauncher(LoggingConfigurable):'
217 217
218 218 class ClusterAppMixin(HasTraits):
219 219 """MixIn for cluster args as traits"""
220 cluster_args = List([])
221 220 profile_dir=Unicode('')
222 221 cluster_id=Unicode('')
223 def _profile_dir_changed(self, name, old, new):
224 self.cluster_args = []
225 if self.profile_dir:
226 self.cluster_args.extend(['--profile-dir', self.profile_dir])
227 if self.cluster_id:
228 self.cluster_args.extend(['--cluster-id', self.cluster_id])
229 _cluster_id_changed = _profile_dir_changed
222
223 @property
224 def cluster_args(self):
225 return ['--profile-dir', self.profile_dir, '--cluster-id', self.cluster_id]
230 226
231 227 class ControllerMixin(ClusterAppMixin):
232 228 controller_cmd = List(ipcontroller_cmd_argv, config=True,
@@ -243,6 +239,7 b' class EngineMixin(ClusterAppMixin):'
243 239 help="command-line arguments to pass to ipengine"
244 240 )
245 241
242
246 243 #-----------------------------------------------------------------------------
247 244 # Local process launchers
248 245 #-----------------------------------------------------------------------------
@@ -563,6 +560,8 b' class SSHLauncher(LocalProcessLauncher):'
563 560 help="command for starting ssh")
564 561 ssh_args = List(['-tt'], config=True,
565 562 help="args to pass to ssh")
563 scp_cmd = List(['scp'], config=True,
564 help="command for sending files")
566 565 program = List(['date'],
567 566 help="Program to launch via ssh")
568 567 program_args = List([],
@@ -573,6 +572,10 b' class SSHLauncher(LocalProcessLauncher):'
573 572 help="username for ssh")
574 573 location = Unicode('', config=True,
575 574 help="user@hostname location for ssh in one setting")
575 to_fetch = List([], config=True,
576 help="List of (remote, local) files to fetch after starting")
577 to_send = List([], config=True,
578 help="List of (local, remote) files to send before starting")
576 579
577 580 def _hostname_changed(self, name, old, new):
578 581 if self.user:
@@ -586,14 +589,57 b' class SSHLauncher(LocalProcessLauncher):'
586 589 def find_args(self):
587 590 return self.ssh_cmd + self.ssh_args + [self.location] + \
588 591 self.program + self.program_args
592
593 def _send_file(self, local, remote):
594 """send a single file"""
595 remote = "%s:%s" % (self.location, remote)
596 for i in range(10):
597 if not os.path.exists(local):
598 self.log.debug("waiting for %s" % local)
599 time.sleep(1)
600 else:
601 break
602 self.log.info("sending %s to %s", local, remote)
603 check_output(self.scp_cmd + [local, remote])
604
605 def send_files(self):
606 """send our files"""
607 if not self.send_files:
608 return
609 for local_file, remote_file in self.to_send:
610 self._send_file(local_file, remote_file)
611
612 def _fetch_file(self, remote, local):
613 """send a single file"""
614 full_remote = "%s:%s" % (self.location, remote)
615 self.log.info("fetching %s from %s", local, full_remote)
616 for i in range(10):
617 # wait up to 10s for remote file to exist
618 check = check_output(self.ssh_cmd + self.ssh_args + \
619 [self.location, 'test -e', remote, "&& echo 'yes' || echo 'no'"])
620 check = check.strip()
621 if check.strip() == 'no':
622 time.sleep(1)
623 elif check.strip() == 'yes':
624 break
625 check_output(self.scp_cmd + [full_remote, local])
626
627 def fetch_files(self):
628 """override in subclass"""
629 if not self.fetch_files:
630 return
631 for remote_file, local_file in self.to_fetch:
632 self._fetch_file(remote_file, local_file)
589 633
590 634 def start(self, hostname=None, user=None):
591 635 if hostname is not None:
592 636 self.hostname = hostname
593 637 if user is not None:
594 638 self.user = user
595
596 return super(SSHLauncher, self).start()
639
640 self.send_files()
641 super(SSHLauncher, self).start()
642 self.fetch_files()
597 643
598 644 def signal(self, sig):
599 645 if self.state == 'running':
@@ -601,27 +647,67 b' class SSHLauncher(LocalProcessLauncher):'
601 647 self.process.stdin.write('~.')
602 648 self.process.stdin.flush()
603 649
650 class SSHClusterLauncher(SSHLauncher):
651
652 remote_profile_dir = Unicode('', config=True,
653 help="""The remote profile_dir to use.
654
655 If not specified, use calling profile, stripping out possible leading homedir.
656 """)
657
658 def _remote_profie_dir_default(self):
659 """turns /home/you/.ipython/profile_foo into .ipython/profile_foo
660 """
661 home = get_home_dir()
662 if not home.endswith('/'):
663 home = home+'/'
664
665 if self.profile_dir.startswith(home):
666 return self.profile_dir[len(home):]
667 else:
668 return self.profile_dir
669
670 def _cluster_id_changed(self, name, old, new):
671 if new:
672 raise ValueError("cluster id not supported by SSH launchers")
673
674 @property
675 def cluster_args(self):
676 return ['--profile-dir', self.remote_profile_dir]
604 677
605
606 class SSHControllerLauncher(SSHLauncher, ControllerMixin):
678 class SSHControllerLauncher(SSHClusterLauncher, ControllerMixin):
607 679
608 680 # alias back to *non-configurable* program[_args] for use in find_args()
609 681 # this way all Controller/EngineSetLaunchers have the same form, rather
610 682 # than *some* having `program_args` and others `controller_args`
683
684 def _controller_cmd_default(self):
685 return ['ipcontroller']
686
611 687 @property
612 688 def program(self):
613 689 return self.controller_cmd
614
690
615 691 @property
616 692 def program_args(self):
617 693 return self.cluster_args + self.controller_args
618 694
695 def _to_fetch_default(self):
696 return [
697 (os.path.join(self.remote_profile_dir, 'security', cf),
698 os.path.join(self.profile_dir, 'security', cf),)
699 for cf in ('ipcontroller-client.json', 'ipcontroller-engine.json')
700 ]
619 701
620 class SSHEngineLauncher(SSHLauncher, EngineMixin):
702 class SSHEngineLauncher(SSHClusterLauncher, EngineMixin):
621 703
622 704 # alias back to *non-configurable* program[_args] for use in find_args()
623 705 # this way all Controller/EngineSetLaunchers have the same form, rather
624 706 # than *some* having `program_args` and others `controller_args`
707
708 def _engine_cmd_default(self):
709 return ['ipengine']
710
625 711 @property
626 712 def program(self):
627 713 return self.engine_cmd
@@ -629,6 +715,13 b' class SSHEngineLauncher(SSHLauncher, EngineMixin):'
629 715 @property
630 716 def program_args(self):
631 717 return self.cluster_args + self.engine_args
718
719 def _to_send_default(self):
720 return [
721 (os.path.join(self.profile_dir, 'security', cf),
722 os.path.join(self.remote_profile_dir, 'security', cf))
723 for cf in ('ipcontroller-client.json', 'ipcontroller-engine.json')
724 ]
632 725
633 726
634 727 class SSHEngineSetLauncher(LocalEngineSetLauncher):
@@ -669,6 +762,9 b' class SSHEngineSetLauncher(LocalEngineSetLauncher):'
669 762 el = self.launcher_class(work_dir=self.work_dir, config=self.config, log=self.log,
670 763 profile_dir=self.profile_dir, cluster_id=self.cluster_id,
671 764 )
765 if i > 0:
766 # only send files for the first engine on each host
767 el.to_send = []
672 768
673 769 # Copy the engine args over to each engine launcher.
674 770 el.engine_cmd = self.engine_cmd
@@ -681,6 +777,35 b' class SSHEngineSetLauncher(LocalEngineSetLauncher):'
681 777 return dlist
682 778
683 779
780 class SSHProxyEngineSetLauncher(SSHClusterLauncher):
781 """Launcher for calling
782 `ipcluster engines` on a remote machine.
783
784 Requires that remote profile is already configured.
785 """
786
787 n = Integer()
788 ipcluster_cmd = List(['ipcluster'], config=True)
789
790 @property
791 def program(self):
792 return self.ipcluster_cmd + ['engines']
793
794 @property
795 def program_args(self):
796 return ['-n', str(self.n), '--profile-dir', self.remote_profile_dir]
797
798 def _to_send_default(self):
799 return [
800 (os.path.join(self.profile_dir, 'security', cf),
801 os.path.join(self.remote_profile_dir, 'security', cf))
802 for cf in ('ipcontroller-client.json', 'ipcontroller-engine.json')
803 ]
804
805 def start(self, n):
806 self.n = n
807 super(SSHProxyEngineSetLauncher, self).start()
808
684 809
685 810 #-----------------------------------------------------------------------------
686 811 # Windows HPC Server 2008 scheduler launchers
General Comments 0
You need to be logged in to leave comments. Login now