##// END OF EJS Templates
incremental improvements to SSH launchers...
MinRK -
Show More
@@ -60,7 +60,7 b' from IPython.utils.text import EvalFormatter'
60 from IPython.utils.traitlets import (
60 from IPython.utils.traitlets import (
61 Any, Integer, CFloat, List, Unicode, Dict, Instance, HasTraits,
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 from IPython.utils.process import find_cmd, pycmd2argv, FindCmdError
64 from IPython.utils.process import find_cmd, pycmd2argv, FindCmdError
65
65
66 from .win32support import forward_read_events
66 from .win32support import forward_read_events
@@ -217,16 +217,12 b' class BaseLauncher(LoggingConfigurable):'
217
217
218 class ClusterAppMixin(HasTraits):
218 class ClusterAppMixin(HasTraits):
219 """MixIn for cluster args as traits"""
219 """MixIn for cluster args as traits"""
220 cluster_args = List([])
221 profile_dir=Unicode('')
220 profile_dir=Unicode('')
222 cluster_id=Unicode('')
221 cluster_id=Unicode('')
223 def _profile_dir_changed(self, name, old, new):
222
224 self.cluster_args = []
223 @property
225 if self.profile_dir:
224 def cluster_args(self):
226 self.cluster_args.extend(['--profile-dir', self.profile_dir])
225 return ['--profile-dir', self.profile_dir, '--cluster-id', self.cluster_id]
227 if self.cluster_id:
228 self.cluster_args.extend(['--cluster-id', self.cluster_id])
229 _cluster_id_changed = _profile_dir_changed
230
226
231 class ControllerMixin(ClusterAppMixin):
227 class ControllerMixin(ClusterAppMixin):
232 controller_cmd = List(ipcontroller_cmd_argv, config=True,
228 controller_cmd = List(ipcontroller_cmd_argv, config=True,
@@ -243,6 +239,7 b' class EngineMixin(ClusterAppMixin):'
243 help="command-line arguments to pass to ipengine"
239 help="command-line arguments to pass to ipengine"
244 )
240 )
245
241
242
246 #-----------------------------------------------------------------------------
243 #-----------------------------------------------------------------------------
247 # Local process launchers
244 # Local process launchers
248 #-----------------------------------------------------------------------------
245 #-----------------------------------------------------------------------------
@@ -563,6 +560,8 b' class SSHLauncher(LocalProcessLauncher):'
563 help="command for starting ssh")
560 help="command for starting ssh")
564 ssh_args = List(['-tt'], config=True,
561 ssh_args = List(['-tt'], config=True,
565 help="args to pass to ssh")
562 help="args to pass to ssh")
563 scp_cmd = List(['scp'], config=True,
564 help="command for sending files")
566 program = List(['date'],
565 program = List(['date'],
567 help="Program to launch via ssh")
566 help="Program to launch via ssh")
568 program_args = List([],
567 program_args = List([],
@@ -573,6 +572,10 b' class SSHLauncher(LocalProcessLauncher):'
573 help="username for ssh")
572 help="username for ssh")
574 location = Unicode('', config=True,
573 location = Unicode('', config=True,
575 help="user@hostname location for ssh in one setting")
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 def _hostname_changed(self, name, old, new):
580 def _hostname_changed(self, name, old, new):
578 if self.user:
581 if self.user:
@@ -586,14 +589,57 b' class SSHLauncher(LocalProcessLauncher):'
586 def find_args(self):
589 def find_args(self):
587 return self.ssh_cmd + self.ssh_args + [self.location] + \
590 return self.ssh_cmd + self.ssh_args + [self.location] + \
588 self.program + self.program_args
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 def start(self, hostname=None, user=None):
634 def start(self, hostname=None, user=None):
591 if hostname is not None:
635 if hostname is not None:
592 self.hostname = hostname
636 self.hostname = hostname
593 if user is not None:
637 if user is not None:
594 self.user = user
638 self.user = user
595
639
596 return super(SSHLauncher, self).start()
640 self.send_files()
641 super(SSHLauncher, self).start()
642 self.fetch_files()
597
643
598 def signal(self, sig):
644 def signal(self, sig):
599 if self.state == 'running':
645 if self.state == 'running':
@@ -601,27 +647,67 b' class SSHLauncher(LocalProcessLauncher):'
601 self.process.stdin.write('~.')
647 self.process.stdin.write('~.')
602 self.process.stdin.flush()
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
678 class SSHControllerLauncher(SSHClusterLauncher, ControllerMixin):
606 class SSHControllerLauncher(SSHLauncher, ControllerMixin):
607
679
608 # alias back to *non-configurable* program[_args] for use in find_args()
680 # alias back to *non-configurable* program[_args] for use in find_args()
609 # this way all Controller/EngineSetLaunchers have the same form, rather
681 # this way all Controller/EngineSetLaunchers have the same form, rather
610 # than *some* having `program_args` and others `controller_args`
682 # than *some* having `program_args` and others `controller_args`
683
684 def _controller_cmd_default(self):
685 return ['ipcontroller']
686
611 @property
687 @property
612 def program(self):
688 def program(self):
613 return self.controller_cmd
689 return self.controller_cmd
614
690
615 @property
691 @property
616 def program_args(self):
692 def program_args(self):
617 return self.cluster_args + self.controller_args
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 # alias back to *non-configurable* program[_args] for use in find_args()
704 # alias back to *non-configurable* program[_args] for use in find_args()
623 # this way all Controller/EngineSetLaunchers have the same form, rather
705 # this way all Controller/EngineSetLaunchers have the same form, rather
624 # than *some* having `program_args` and others `controller_args`
706 # than *some* having `program_args` and others `controller_args`
707
708 def _engine_cmd_default(self):
709 return ['ipengine']
710
625 @property
711 @property
626 def program(self):
712 def program(self):
627 return self.engine_cmd
713 return self.engine_cmd
@@ -629,6 +715,13 b' class SSHEngineLauncher(SSHLauncher, EngineMixin):'
629 @property
715 @property
630 def program_args(self):
716 def program_args(self):
631 return self.cluster_args + self.engine_args
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 class SSHEngineSetLauncher(LocalEngineSetLauncher):
727 class SSHEngineSetLauncher(LocalEngineSetLauncher):
@@ -669,6 +762,9 b' class SSHEngineSetLauncher(LocalEngineSetLauncher):'
669 el = self.launcher_class(work_dir=self.work_dir, config=self.config, log=self.log,
762 el = self.launcher_class(work_dir=self.work_dir, config=self.config, log=self.log,
670 profile_dir=self.profile_dir, cluster_id=self.cluster_id,
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 # Copy the engine args over to each engine launcher.
769 # Copy the engine args over to each engine launcher.
674 el.engine_cmd = self.engine_cmd
770 el.engine_cmd = self.engine_cmd
@@ -681,6 +777,35 b' class SSHEngineSetLauncher(LocalEngineSetLauncher):'
681 return dlist
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 # Windows HPC Server 2008 scheduler launchers
811 # Windows HPC Server 2008 scheduler launchers
General Comments 0
You need to be logged in to leave comments. Login now