~lutostag/ubuntu/trusty/maas/1.5.2+packagefix

« back to all changes in this revision

Viewing changes to src/provisioningserver/import_images/tgt.py

  • Committer: Package Import Robot
  • Author(s): Andres Rodriguez
  • Date: 2014-03-28 10:43:53 UTC
  • mto: This revision was merged to the branch mainline in revision 57.
  • Revision ID: package-import@ubuntu.com-20140328104353-ekpolg0pm5xnvq2s
Tags: upstream-1.5+bzr2204
ImportĀ upstreamĀ versionĀ 1.5+bzr2204

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright 2013 Canonical Ltd.  This software is licensed under the
2
 
# GNU Affero General Public License version 3 (see the file LICENSE).
3
 
 
4
 
"""Management of iSCSI targets for the ephemerals import script."""
5
 
 
6
 
from __future__ import (
7
 
    absolute_import,
8
 
    print_function,
9
 
    unicode_literals,
10
 
    )
11
 
 
12
 
str = None
13
 
 
14
 
__metaclass__ = type
15
 
__all__ = [
16
 
    'clean_up_info_file',
17
 
    'get_conf_path',
18
 
    'get_target_name',
19
 
    'set_up_data_dir',
20
 
    'tgt_admin_delete',
21
 
    'tgt_admin_update',
22
 
    'write_conf',
23
 
    'write_info_file',
24
 
    ]
25
 
 
26
 
import os
27
 
import os.path
28
 
import re
29
 
import shutil
30
 
from textwrap import dedent
31
 
 
32
 
from provisioningserver.kernel_opts import (
33
 
    ISCSI_TARGET_NAME_PREFIX,
34
 
    prefix_target_name,
35
 
    )
36
 
from provisioningserver.utils import (
37
 
    call_and_check,
38
 
    call_capture_and_check,
39
 
    ensure_dir,
40
 
    write_text_file,
41
 
    )
42
 
 
43
 
# Basic tgt-admin command line.
44
 
TGT_ADMIN = ["tgt-admin", "--conf", "/etc/tgt/targets.conf"]
45
 
 
46
 
# Template for the tgt.conf file in the data directory.
47
 
DATA_DIR_TGT_CONF_TEMPLATE = dedent("""\
48
 
    include {path}/*.conf
49
 
    default-driver iscsi
50
 
    """)
51
 
 
52
 
# Template for individual tgt.conf files in target directories.
53
 
TGT_CONF_TEMPLATE = dedent("""\
54
 
    <target {prefix}:{{target_name}}>
55
 
        readonly 1
56
 
        backing-store "{{image}}"
57
 
    </target>
58
 
    """).format(prefix=ISCSI_TARGET_NAME_PREFIX)
59
 
 
60
 
INFO_TEMPLATE = dedent("""\
61
 
    release={release}
62
 
    label={label}
63
 
    serial={serial}
64
 
    arch={arch}
65
 
    name={name}
66
 
    """)
67
 
 
68
 
 
69
 
def tgt_conf_d(data_dir):
70
 
    """Return the path of a data directory's configuration directory."""
71
 
    return os.path.abspath(os.path.join(data_dir, 'tgt.conf.d'))
72
 
 
73
 
 
74
 
def get_conf_path(data_dir, config_name):
75
 
    """Return the path for an iSCSI config file.
76
 
 
77
 
    :param data_dir: Base data directory.  The config should be in its
78
 
        `tgt.conf.d` subdirectory.
79
 
    :param config_name: Base name for the config.  A ".conf" suffix will be
80
 
        added.
81
 
    """
82
 
    return os.path.join(tgt_conf_d(data_dir), "%s.conf" % config_name)
83
 
 
84
 
 
85
 
def get_target_name(release, version, arch, version_name):
86
 
    """Compose a target's name based on its parameters.
87
 
 
88
 
    The `**kwargs` are inert.  They are only here for calling convenience.
89
 
    """
90
 
    return '-'.join(['maas', release, version, arch, version_name])
91
 
 
92
 
 
93
 
def tgt_admin_delete(name):
94
 
    """Delete a target using `tgt-admin`."""
95
 
    call_and_check(TGT_ADMIN + ["--delete", prefix_target_name(name)])
96
 
 
97
 
 
98
 
class TargetNotCreated(RuntimeError):
99
 
    """tgt-admin failed to create a target."""
100
 
 
101
 
 
102
 
def target_exists(full_name):
103
 
    """Run `tgt --show` to determine whether the given target exists.
104
 
 
105
 
    :param full_name: Full target name, including `ISCSI_TARGET_NAME_PREFIX`.
106
 
    :return: bool.
107
 
    """
108
 
    status = call_capture_and_check(TGT_ADMIN + ["--show"])
109
 
    regex = b'^Target [0-9]+: %s\\s*$' % re.escape(full_name).encode('ascii')
110
 
    match = re.search(regex, status, flags=re.MULTILINE)
111
 
    return match is not None
112
 
 
113
 
 
114
 
def tgt_admin_update(target_dir, target_name):
115
 
    """Update a target using `tgt-admin`.
116
 
 
117
 
    Actually we use this to add new targets.
118
 
    """
119
 
    full_name = prefix_target_name(target_name)
120
 
    call_and_check(TGT_ADMIN + ["--update", full_name])
121
 
    # Check that the target was really created.
122
 
    # Reportedly tgt-admin tends to return 0 even when it fails, so check
123
 
    # actively.
124
 
    if not target_exists(full_name):
125
 
        raise TargetNotCreated("Failed tgt-admin add for %s" % full_name)
126
 
 
127
 
 
128
 
def set_up_data_dir(data_dir):
129
 
    """Create a data directory and its configuration directory."""
130
 
    ensure_dir(tgt_conf_d(data_dir))
131
 
    write_text_file(
132
 
        os.path.join(data_dir, 'tgt.conf'),
133
 
        DATA_DIR_TGT_CONF_TEMPLATE.format(path=tgt_conf_d(data_dir)))
134
 
 
135
 
 
136
 
def write_info_file(target_dir, target_name, release, label, serial, arch):
137
 
    """Write the `info` file based on the given parameters."""
138
 
    text = INFO_TEMPLATE.format(
139
 
        release=release, label=label, serial=serial, arch=arch,
140
 
        name=target_name)
141
 
    info_file = os.path.join(target_dir, 'info')
142
 
    write_text_file(info_file, text)
143
 
 
144
 
 
145
 
def clean_up_info_file(target_dir):
146
 
    """To be called in the event of failure: move `info` file out of the way.
147
 
 
148
 
    The `info` file will be renamed `info.failed`, in the same directory, and
149
 
    any previously existing `info.failed` file is removed.
150
 
    """
151
 
    info_file = os.path.join(target_dir, 'info')
152
 
    if not os.path.exists(info_file):
153
 
        return
154
 
    failed_file = info_file + '.failed'
155
 
    if os.path.isfile(failed_file):
156
 
        os.remove(failed_file)
157
 
    shutil.move(info_file, failed_file)
158
 
 
159
 
 
160
 
def write_conf(path, target_name, image):
161
 
    """Write a `tgt.conf` file."""
162
 
    text = TGT_CONF_TEMPLATE.format(target_name=target_name, image=image)
163
 
    write_text_file(path, text)