Merge ~os369510/maas/+git/maas:master into maas:master

Proposed by jeremyszu
Status: Rejected
Rejected by: Adam Collard
Proposed branch: ~os369510/maas/+git/maas:master
Merge into: maas:master
Diff against target: 129 lines (+105/-0)
2 files modified
src/provisioningserver/drivers/power/registry.py (+2/-0)
src/provisioningserver/drivers/power/ssh.py (+103/-0)
Reviewer Review Type Date Requested Status
MAAS Lander Approve
MAAS Maintainers Pending
Review via email: mp+375994@code.launchpad.net

Commit message

Add SSH Power driver

Description of the change

In our development environment, we need to daily deploy the daily built image to various platforms. (such as workstation, laptop, IoT board, etc...)

There's many platforms not support either:
1. Power on when AC detected (which means can not be wake up by PDU).
2. Wake on LAN.

and also these platforms won't be power-off after last deployment completed.
Therefore, here is the idea to power control device through ssh command.

Requirements:
1. Generate ssh keys on MaaS server (e.g. put ssh key in /var/lib/maas/id_rsa).
2. Add the pair public key of above one to MaaS account.
3. Assign a static IP for target device.
4. Need to know a super user name on target device.

Then the device would be reboot after 5 secs (to prevent got ssh connection closed error) when deploying image if the device has been deployed by MaaS in previous stage.

I've already test it with version 2.5.0-7442-gdf68e30a5-0ubuntu1~18.04.1.

I'm new in MaaS world and would like to know more opinions.
Please let me know if anything I'm missing.

To post a comment you must log in.
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b master lp:~os369510/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci-jenkins.internal:8080/job/maas/job/branch-tester/6996/console
COMMIT: 2b908804f95049bce87509dac8f14b2248c0f4b9

review: Needs Fixing
Revision history for this message
jeremyszu (os369510) wrote :

Hi Maintainers,

I can not access this link 'http://maas-ci-jenkins.internal:8080/job/maas/job/branch-tester/6996/console', could you please share more detail for building failed?

~os369510/maas/+git/maas:master updated
81e273a... by jeremyszu

Add SSH Power driver

Applied change based on lint suggestion.

Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b master lp:~os369510/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci-jenkins.internal:8080/job/maas/job/branch-tester/7044/console
COMMIT: 81e273a95028115b6ee45d5ca823aea122f4f485

review: Needs Fixing
~os369510/maas/+git/maas:master updated
234306d... by jeremyszu

Add SSH Power driver

Change the order of importing library

Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b master lp:~os369510/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 234306d206d270802d9397a35da84e14e4784d53

review: Approve
Revision history for this message
jeremyszu (os369510) wrote :

@Adam,

Does this MP missing anything? or any improvements need to be applied?
Would you please let me know whether missing anything?

Unmerged commits

234306d... by jeremyszu

Add SSH Power driver

Change the order of importing library

81e273a... by jeremyszu

Add SSH Power driver

Applied change based on lint suggestion.

2b90880... by jeremyszu

Add SSH Power driver

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/provisioningserver/drivers/power/registry.py b/src/provisioningserver/drivers/power/registry.py
2index 8ea5b27..cd2f8cf 100644
3--- a/src/provisioningserver/drivers/power/registry.py
4+++ b/src/provisioningserver/drivers/power/registry.py
5@@ -23,6 +23,7 @@ from provisioningserver.drivers.power.openbmc import OpenBMCPowerDriver
6 from provisioningserver.drivers.power.recs import RECSPowerDriver
7 from provisioningserver.drivers.power.redfish import RedfishPowerDriver
8 from provisioningserver.drivers.power.seamicro import SeaMicroPowerDriver
9+from provisioningserver.drivers.power.ssh import SSHPowerDriver
10 from provisioningserver.drivers.power.ucsm import UCSMPowerDriver
11 from provisioningserver.drivers.power.virsh import VirshPowerDriver
12 from provisioningserver.drivers.power.vmware import VMwarePowerDriver
13@@ -67,6 +68,7 @@ power_drivers = [
14 VirshPowerDriver(),
15 VMwarePowerDriver(),
16 WedgePowerDriver(),
17+ SSHPowerDriver(),
18 ]
19 for driver in power_drivers:
20 PowerDriverRegistry.register_item(driver.name, driver)
21diff --git a/src/provisioningserver/drivers/power/ssh.py b/src/provisioningserver/drivers/power/ssh.py
22new file mode 100644
23index 0000000..974950c
24--- /dev/null
25+++ b/src/provisioningserver/drivers/power/ssh.py
26@@ -0,0 +1,103 @@
27+# Copyright 2015-2016 Canonical Ltd. This software is licensed under the
28+# GNU Affero General Public License version 3 (see the file LICENSE).
29+
30+"""SSH Power Driver.
31+
32+Issue command to control power through SSH.
33+"""
34+
35+__all__ = []
36+
37+from subprocess import PIPE, Popen
38+
39+from twisted.internet.defer import maybeDeferred
40+
41+from provisioningserver.drivers import make_setting_field
42+from provisioningserver.drivers.power import PowerActionError, PowerDriver
43+from provisioningserver.logger import get_maas_logger
44+from provisioningserver.utils import shell
45+from provisioningserver.utils.shell import get_env_with_locale
46+
47+maaslog = get_maas_logger("drivers.power.ssh")
48+
49+
50+class SSHPowerDriver(PowerDriver):
51+ """Power control through issuing SSH command."""
52+
53+ name = "ssh"
54+ chassis = True
55+ description = "Issue Command through SSH"
56+ settings = [
57+ make_setting_field("username", "Username", required=True),
58+ make_setting_field(
59+ "device_address", "IP for Target Device", required=True
60+ ),
61+ make_setting_field(
62+ "key_path", "Paired key on MaaS server", required=True
63+ ),
64+ ]
65+ ip_extractor = None
66+ queryable = False
67+
68+ def detect_missing_packages(self):
69+ binary, package = ["ssh", "openssh-client"]
70+ if not shell.has_command_available(binary):
71+ return [package]
72+ return []
73+
74+ @classmethod
75+ def run_process(cls, command):
76+ """Run SSH command in subprocess."""
77+ proc = Popen(
78+ command.split(),
79+ stdout=PIPE,
80+ stderr=PIPE,
81+ env=get_env_with_locale(),
82+ )
83+ stdout, stderr = proc.communicate()
84+ stdout = stdout.decode("utf-8")
85+ stderr = stderr.decode("utf-8")
86+ if proc.returncode != 0:
87+ raise PowerActionError(
88+ "APC Power Driver external process error for command %s: %s: %s"
89+ % (command, stdout, stderr)
90+ )
91+
92+ def on(self, system_id, context):
93+ """Override `on` as we do not need retry logic."""
94+ return maybeDeferred(self.power_on, system_id, context)
95+
96+ def off(self, system_id, context):
97+ """Override `off` as we do not need retry logic."""
98+ return maybeDeferred(self.power_off, system_id, context)
99+
100+ def query(self, system_id, context):
101+ """Override `query` as we do not need retry logic."""
102+ return maybeDeferred(self.power_query, system_id, context)
103+
104+ def power_on(self, system_id, context):
105+ """Power on machine through ssh."""
106+ maaslog.info("You need to power on %s manually.", system_id)
107+ self.run_process(
108+ "ssh "
109+ + "-o StrictHostKeyChecking=no "
110+ + "-o UserKnownHostsFile=/dev/null "
111+ + "-i "
112+ + context["key_path"]
113+ + " -t "
114+ + context["username"]
115+ + "@"
116+ + context["device_address"]
117+ + " (sleep 5; sudo reboot) &"
118+ )
119+
120+ def power_off(self, system_id, context):
121+ """Power off machine manually."""
122+ maaslog.info("You need to power off %s manually.", system_id)
123+
124+ def power_query(self, system_id, context):
125+ """Power query machine manually."""
126+ maaslog.info(
127+ "You need to check power state of %s manually.", system_id
128+ )
129+ return "unknown"

Subscribers

People subscribed via source and target branches