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