File: ipmisimdaemon.py

package info (click to toggle)
gensio 3.0.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 8,364 kB
  • sloc: ansic: 90,328; python: 5,220; sh: 4,935; cpp: 3,258; makefile: 1,418
file content (184 lines) | stat: -rw-r--r-- 5,721 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#
#  gensio - A library for abstracting stream I/O
#  Copyright (C) 2018  Corey Minyard <minyard@acm.org>
#
#  SPDX-License-Identifier: GPL-2.0-only
#

#
# gensio test utilities
#
# This file contains some classes and functions useful for testing
# gensio handling
#

import os
import tempfile
import utils
import signal
import time

default_ipmisim_emu = """
mc_setbmc 0x20\n
\n
mc_add 0x20 0 no-device-sdrs 0x23 9 8 0x9f 0x1291 0xf02 persist_sdr\n
sel_enable 0x20 1000 0x0a\n
\n
mc_enable 0x20\n
"""

default_ipmisim_config = """
name "gensio_sim"\n
\n
set_working_mc 0x20\n
\n
  startlan 1\n
    addr localhost 0\n
\n
    # Maximum privilege limit on the channel.\n
    priv_limit admin\n
\n
    # Allowed IPMI 1.5 authorization types\n
    allowed_auths_callback none md2 md5 straight\n
    allowed_auths_user none md2 md5 straight\n
    allowed_auths_operator none md2 md5 straight\n
    allowed_auths_admin none md2 md5 straight\n
\n
    # guid for IPMI 2.0 authorization  You can also use a string\n
    guid a123456789abcdefa123456789abcdef\n
\n
  endlan\n
\n
  sol "%s" 115200 nortscts\n
\n
  # Start startcmd at startup?  Default is false.\n
  startnow false\n
\n
  # Now add some users.  User 0 is invalid, user 1 is the special "anonymous"\n
  # user and cannot take a username.  Note that the users here are only\n
  # used if the persistent user config doesn't exist.\n
  #    # valid name      passw  priv-lim max-sess allowed-auths (ignored)\n
  user 1 true  ""        "test" user     10       none md2 md5 straight\n
  user 2 true  "ipmiusr" "test" admin    10       none md2 md5 straight\n
"""

class IPMISimDaemon:
    """Create an IPMI Sim daemon instance and start it up

    ipmi_sim is started with the given config data as a config file
    The IPMISIM_EXEC environment variable can be set to tell this code
    to run ipmi_sim with a specific path.
    """

    def __init__(self, o, pipedev,
                 configdata = None, emudata = None, extra_args = ""):
        """Create a running ipmisim program

        The given config data is written to a file and used as the config file.
        It is started with the -r and -d options set, you can supply extra
        options if you like as a string.
        """

        prog = os.getenv("IPMISIM_EXEC")
        if (not prog):
            prog = "ipmi_sim"

        if not configdata:
            configdata = default_ipmisim_config % pipedev
        if not emudata:
            emudata = default_ipmisim_emu

        self.cfile = tempfile.NamedTemporaryFile(mode="w+")
        self.cfile.write(configdata)
        self.cfile.flush()
        self.efile = tempfile.NamedTemporaryFile(mode="w+")
        self.efile.write(emudata)
        self.efile.flush()
        self.o = o

        args = "stdio," + prog + " -p -c " + self.cfile.name + " -f " + self.efile.name + " " + extra_args
        if (utils.debug):
            print("Running: " + args)
        self.handler = utils.HandleData(o, args, name="ipmisim daemon")

        self.io = self.handler.io
        self.io.closeme = True
        self.io.open_s()

        # Uncomment the following or set it yourself to get output from
        # the ipmisim daemon printed.
        #self.handler.debug = 2

        self.pid = utils.remote_id_int(self.io)

        self.handler.debug = 2
        self.handler.set_waitfor("Opened UDP port ")
        if (self.handler.wait_timeout(2000) == 0):
            raise Exception("Timeout waiting for ipmi_sim port")

        self.handler.set_waitfor("\n", keep_data = True)
        if (self.handler.wait_timeout(2000) == 0):
            raise Exception("Timeout waiting(2) for ipmi_sim port")
        self.port = int(self.handler.get_waitfor_kept_data())

        self.handler.set_waitfor("> ")
        if (self.handler.wait_timeout(2000) == 0):
            raise Exception("Timeout waiting for ipmi_sim to start")

        self.handler.ignore_input = True

        # Leave read on so if we enable debug we can see output from the
        # daemon.
        self.io.read_cb_enable(True)
        return

    def __del__(self):
        if self.handler:
            self.terminate()
        del self.io
        return

    def signal(self, sig):
        """"Send a signal to ipmi_sim"""
        os.kill(self.pid, sig)
        return

    def terminate(self):
        """Terminate the running ipmi_sim

        This closes the io and sends a SIGTERM to ipmi_sim and waits
        a bit for it to terminate.  If it does not terminate, send
        SIGTERM a few more times.  If it still refuses to close, send
        a SIGKILL.  If all that fails, raise an exception.
        """
        if (self.handler.debug or utils.debug):
            print("Terminating")
        if self.io.closeme:
            utils.io_close([self.io])
        self.io = None
        self.handler = None
        count = 10
        while (count > 0):
            try:
                if (count < 6):
                    self.signal(signal.SIGTERM)
                else:
                    self.signal(signal.SIGKILL)
            except:
                None
            # It would be really nice if waitpid had a timeout options,
            # in absense of that simulate it, sort of.
            subcount = 500
            while (subcount > 0):
                time.sleep(.01)
                try:
                    pid, rv = os.waitpid(self.pid, os.WNOHANG)
                except:
                    # Call failed, probably process has died.
                    pid = self.pid
                if (pid > 0):
                    self.handler = None
                    return
                subcount -= 1
            count -= 1
        raise Exception("ipmisim did not terminate");