Initial rtslib commit
Signed-off-by: Nicholas A. Bellinger <nab@risingtidesystems.com>
This commit is contained in:
661
COPYING
Normal file
661
COPYING
Normal file
@@ -0,0 +1,661 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
85
Makefile
Normal file
85
Makefile
Normal file
@@ -0,0 +1,85 @@
|
||||
# This file is part of RTSLib Community Edition.
|
||||
# Copyright (c) 2011 by RisingTide Systems LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, version 3 (AGPLv3).
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
NAME = rtslib
|
||||
LIB = /usr/share
|
||||
DOC = ${LIB}/doc/
|
||||
SETUP = ./setup.py
|
||||
CLEAN = ./bin/clean
|
||||
GENDOC = ./bin/gendoc
|
||||
|
||||
all: usage
|
||||
usage:
|
||||
@echo "Usage:"
|
||||
@echo " make install - Install rtslib"
|
||||
@echo " make installdocs - Install the documentation"
|
||||
@echo "Developer targets:"
|
||||
@echo " make packages - Generate the Debian and RPM packages"
|
||||
@echo " make doc - Generate the documentation"
|
||||
@echo " make clean - Cleanup the local repository"
|
||||
@echo " make sdist - Build the source tarball"
|
||||
@echo " make bdist - Build the installable tarball"
|
||||
|
||||
install:
|
||||
${SETUP} install
|
||||
|
||||
doc:
|
||||
./bin/gen_changelog
|
||||
${GENDOC}
|
||||
|
||||
installdocs: doc
|
||||
@test -e ${DOC} || \
|
||||
echo "Could not find ${DOC}; check the makefile variables."
|
||||
@test -e ${DOC}
|
||||
cp -r doc/* ${DOC}/${NAME}/
|
||||
|
||||
clean:
|
||||
${CLEAN}
|
||||
./bin/gen_changelog_cleanup
|
||||
|
||||
packages: clean doc
|
||||
dpkg-buildpackage -rfakeroot | tee dpkg-buildpackage.log
|
||||
./bin/gen_changelog_cleanup
|
||||
grep "source version" dpkg-buildpackage.log | awk '{print $$4}' > dpkg-buildpackage.version
|
||||
@test -e dist || mkdir dist
|
||||
mv ../${NAME}_$$(cat dpkg-buildpackage.version).dsc dist
|
||||
mv ../${NAME}_$$(cat dpkg-buildpackage.version)_*.changes dist
|
||||
mv ../${NAME}_$$(cat dpkg-buildpackage.version).tar.gz dist
|
||||
mv ../*${NAME}*$$(cat dpkg-buildpackage.version)*.deb dist
|
||||
@test -e build || mkdir build
|
||||
cd build; alien --scripts -k -g -r ../dist/rtslib-doc_$$(cat ../dpkg-buildpackage.version)_all.deb
|
||||
cd build/rtslib-doc-*; mkdir usr/share/doc/packages
|
||||
cd build/rtslib-doc-*; mv usr/share/doc/rtslib-doc usr/share/doc/packages/
|
||||
cd build/rtslib-doc-*; perl -pi -e "s,/usr/share/doc/rtslib-doc,/usr/share/doc/packages/rtslib-doc,g" *.spec
|
||||
cd build/rtslib-doc-*; perl -pi -e "s,%%{ARCH},noarch,g" *.spec
|
||||
cd build/rtslib-doc-*; perl -pi -e "s,%post,%posttrans,g" *.spec
|
||||
cd build/rtslib-doc-*; rpmbuild --buildroot $$PWD -bb *.spec
|
||||
cd build; alien --scripts -k -g -r ../dist/python-rtslib_$$(cat ../dpkg-buildpackage.version)_all.deb; cd ..
|
||||
cd build/python-rtslib-*; mkdir usr/share/doc/packages
|
||||
cd build/python-rtslib-*; mv usr/share/doc/python-rtslib usr/share/doc/packages/
|
||||
cd build/python-rtslib-*; perl -pi -e "s,/usr/share/doc/python-rtslib,/usr/share/doc/packages/python-rtslib,g" *.spec
|
||||
cd build/python-rtslib-*; perl -pi -e 's/Group:/Requires: python >= 2.5\nGroup:/g' *.spec
|
||||
cd build/python-rtslib-*; perl -pi -e "s,%%{ARCH},noarch,g" *.spec
|
||||
cd build/python-rtslib-*; perl -pi -e "s,%post,%posttrans,g" *.spec
|
||||
cd build/python-rtslib-*; rpmbuild --buildroot $$PWD -bb *.spec
|
||||
mv build/*.rpm dist
|
||||
rm dpkg-buildpackage.log dpkg-buildpackage.version
|
||||
|
||||
sdist: clean doc
|
||||
${SETUP} sdist
|
||||
|
||||
bdist: clean doc
|
||||
${SETUP} bdist
|
||||
|
24
README
Normal file
24
README
Normal file
@@ -0,0 +1,24 @@
|
||||
# This file is part of RTSLib Community Edition.
|
||||
# Copyright (c) 2011 by RisingTide Systems LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, version 3 (AGPLv3).
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
RTSLib Community Edition is a python library that provides an object API to
|
||||
RisingTide Systems generic SCSI Target as well as third-party target fabric
|
||||
modules written for it and backend storage objects.
|
||||
|
||||
It is useful for developing 3rd-party applications, as well as serving as a
|
||||
foundation for RisingTide Systems userspace tools.
|
||||
|
||||
For more information, see the rtslib API reference, available in both html
|
||||
and pdf formats as a separate package.
|
30
bin/clean
Executable file
30
bin/clean
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This file is part of RTSLib Community Edition.
|
||||
# Copyright (c) 2011 by RisingTide Systems LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, version 3 (AGPLv3).
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
rm -v rtslib/*.pyc rtslib/*.html 2>/dev/null
|
||||
rm -rvf doc doc/*.pdf doc/html doc/pdf doc/*.html 2>/dev/null
|
||||
rm -rfv rtslib.egg-info MANIFEST build dist 2>/dev/null
|
||||
rm -rvf pdf html 2>/dev/null
|
||||
rm -rvf debian/tmp 2>/dev/null
|
||||
rm -v build-stamp 2>/dev/null
|
||||
rm -v dpkg-buildpackage.log dpkg-buildpackage.version 2>/dev/null
|
||||
rm -rfv *.rpm 2>/dev/null
|
||||
rm -v debian/files debian/*.log debian/*.substvars 2>/dev/null
|
||||
rm -rv debian/rtslib-doc/ debian/python2.5-rtslib/ debian/python2.6-rtslib/ debian/python-rtslib/ 2>/dev/null
|
||||
rm -rv results 2>/dev/null
|
||||
echo "Finished cleanup."
|
||||
|
45
bin/gen_changelog
Executable file
45
bin/gen_changelog
Executable file
@@ -0,0 +1,45 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This file is part of RTSLib Community Edition.
|
||||
# Copyright (c) 2011 by RisingTide Systems LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, version 3 (AGPLv3).
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
NAME=$(git config --get user.name)
|
||||
EMAIL=$(git config --get user.email)
|
||||
DATE=$(date +'%a, %d %b %Y %H:%M:%S %z')
|
||||
|
||||
SRCPKG=$(cat debian/control | grep ^Source | awk '{print $2}')
|
||||
|
||||
LAST_TAG=$(git tag | tail -1)
|
||||
if [ -z $LAST_TAG ]; then
|
||||
LAST_TAG='0.0'
|
||||
fi
|
||||
|
||||
COMMIT=$(git log ${LAST_TAG}..HEAD | head -1 | colrm 1 7 | colrm 8)
|
||||
TS=$(date +%Y%m%d%H%M%S)
|
||||
|
||||
if [ -z $COMMIT ]; then
|
||||
VERSION="${LAST_TAG}"
|
||||
else
|
||||
VERSION="${LAST_TAG}-${TS}.${COMMIT}"
|
||||
fi
|
||||
|
||||
sed -i "s/__version__ = .*/__version__ = '${VERSION}'/g" ${SRCPKG}/__init__.py
|
||||
|
||||
echo "${SRCPKG} (${VERSION}) unstable; urgency=low" > debian/changelog
|
||||
echo >> debian/changelog
|
||||
echo " * Generated package." >> debian/changelog
|
||||
echo >> debian/changelog
|
||||
echo " -- ${NAME} <${EMAIL}> ${DATE}" >> debian/changelog
|
||||
|
20
bin/gen_changelog_cleanup
Executable file
20
bin/gen_changelog_cleanup
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This file is part of RTSLib Community Edition.
|
||||
# Copyright (c) 2011 by RisingTide Systems LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, version 3 (AGPLv3).
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
SRCPKG=$(cat debian/control | grep ^Source | awk '{print $2}')
|
||||
rm -f debian/changelog
|
||||
sed -i "s/__version__ = .*/__version__ = 'GIT_VERSION'/g" ${SRCPKG}/__init__.py
|
31
bin/gendoc
Executable file
31
bin/gendoc
Executable file
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This file is part of RTSLib Community Edition.
|
||||
# Copyright (c) 2011 by RisingTide Systems LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, version 3 (AGPLv3).
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
path=$PWD
|
||||
options='-n rtslib --exclude configobj rtslib/*.py'
|
||||
|
||||
rm -rf doc &>/dev/null
|
||||
mkdir doc
|
||||
epydoc --no-sourcecode --pdf -v $options
|
||||
mkdir doc/pdf 2>/dev/null
|
||||
mv pdf/api.pdf doc/pdf/rtslib-API-reference.pdf
|
||||
rm pdf -rf
|
||||
epydoc --no-sourcecode --html $options
|
||||
mv html doc/
|
||||
perl -pi -e "s/<\?/<!/g" doc/html/*.html
|
||||
perl -pi -e "s/\?>/>/g" doc/html/*.html
|
||||
cp README COPYING doc/
|
14
debian/README.Debian
vendored
Normal file
14
debian/README.Debian
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
Copyright (c) 2011 by RisingTide Systems LLC
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, version 3 (AGPLv3).
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
1
debian/compat
vendored
Normal file
1
debian/compat
vendored
Normal file
@@ -0,0 +1 @@
|
||||
7
|
19
debian/control
vendored
Normal file
19
debian/control
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Source: rtslib
|
||||
Section: python
|
||||
Priority: optional
|
||||
Maintainer: Jerome Martin <jxm@risingtidesystems.com>
|
||||
Build-Depends: debhelper(>= 7.0.1), python2.5, python2.6, python-epydoc
|
||||
Standards-Version: 3.8.1
|
||||
|
||||
Package: python-rtslib
|
||||
Architecture: all
|
||||
Depends: python (>= 2.5)|python2.5|python2.6, python-configobj
|
||||
Suggests: rtslib-doc
|
||||
Conflicts: rtsadmin-frozen
|
||||
Description: RisingTide Systems generic SCSI target API in python.
|
||||
|
||||
Package: rtslib-doc
|
||||
Section: doc
|
||||
Architecture: all
|
||||
Recommends: iceweasel | www-browser
|
||||
Description: RisingTide Systems generic SCSI target API documentation.
|
13
debian/copyright
vendored
Normal file
13
debian/copyright
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
This package was originally debianized by Jerome Martin <jxm@risingtidesystems.com>
|
||||
on Fri Nov 18 12:00:01 UTC 2009. It is currently maintained by Jerome Martin
|
||||
<jxm@risingtidesystems.com>.
|
||||
|
||||
Upstream Author: Jerome Martin <jxm@risingtidesystems.com>
|
||||
|
||||
Copyright:
|
||||
|
||||
Copyright (c) 2009 by RisingTide Systems LLC.
|
||||
All rights reserved.
|
||||
For licensing information, please contact us.
|
||||
Not for redistribution.
|
||||
|
2
debian/python-rtslib.dirs
vendored
Normal file
2
debian/python-rtslib.dirs
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
usr/share/python-support
|
||||
var/target/fabric
|
2
debian/python-rtslib.docs
vendored
Normal file
2
debian/python-rtslib.docs
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
README
|
||||
COPYING
|
2
debian/python-rtslib.install
vendored
Normal file
2
debian/python-rtslib.install
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
lib/rtslib usr/share/python-support
|
||||
specs/* var/target/fabric
|
17
debian/python-rtslib.postinst
vendored
Executable file
17
debian/python-rtslib.postinst
vendored
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/bin/sh
|
||||
for lib in lib lib64; do
|
||||
for python in python2.5 python2.6; do
|
||||
if [ -e /usr/${lib}/${python} ]; then
|
||||
if [ ! -e /usr/${lib}/${python}/rtslib ]; then
|
||||
mkdir /usr/${lib}/${python}/rtslib
|
||||
for source in /usr/share/python-support/rtslib/rtslib/*.py; do
|
||||
ln -sf ${source} /usr/${lib}/${python}/rtslib/
|
||||
done
|
||||
python_path=$(which ${python} 2>/dev/null)
|
||||
if [ ! -z $python_path ]; then
|
||||
${python} -c "import compileall; compileall.compile_dir('/usr/${lib}/${python}/rtslib', force=1)"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
done
|
3
debian/python-rtslib.preinst
vendored
Executable file
3
debian/python-rtslib.preinst
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
rm -f /usr/share/python-support/rtslib/rtslib/*.pyc
|
||||
rm -f /usr/share/python-support/rtslib/rtslib/*.pyo
|
8
debian/python-rtslib.prerm
vendored
Executable file
8
debian/python-rtslib.prerm
vendored
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
for lib in lib lib64; do
|
||||
for python in python2.5 python2.6; do
|
||||
if [ -e /usr/${lib}/${python}/rtslib ]; then
|
||||
rm -rf /usr/${lib}/${python}/rtslib
|
||||
fi
|
||||
done
|
||||
done
|
1
debian/pyversions
vendored
Normal file
1
debian/pyversions
vendored
Normal file
@@ -0,0 +1 @@
|
||||
2.5-
|
4
debian/rtslib-doc.docs
vendored
Normal file
4
debian/rtslib-doc.docs
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
README
|
||||
COPYING
|
||||
doc/pdf
|
||||
doc/html
|
49
debian/rules
vendored
Executable file
49
debian/rules
vendored
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
build_dir = build
|
||||
install_dir = debian/tmp
|
||||
setup = /usr/bin/python ./setup.py --quiet
|
||||
|
||||
binary: binary-indep
|
||||
|
||||
binary-arch:
|
||||
|
||||
binary-indep: build install
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
dh_installchangelogs
|
||||
dh_installdocs
|
||||
dh_installman
|
||||
dh_install --list-missing --sourcedir $(install_dir)
|
||||
dh_fixperms
|
||||
dh_compress -X.py
|
||||
dh_installdeb
|
||||
dh_gencontrol
|
||||
dh_md5sums
|
||||
dh_builddeb
|
||||
|
||||
install: build
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
dh_installdirs
|
||||
cp -R specs $(install_dir)
|
||||
|
||||
build: build-stamp
|
||||
build-stamp:
|
||||
dh_testdir
|
||||
$(setup) build --build-base $(build_dir) install --no-compile --install-purelib $(install_dir)/lib/rtslib --install-scripts $(install_dir)/bin
|
||||
echo "2.5, 2.6" > $(install_dir)/lib/rtslib/.version
|
||||
touch build-stamp
|
||||
|
||||
clean:
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
rm -f build-stamp
|
||||
$(setup) clean
|
||||
find . -name "*.pyc" | xargs rm -f
|
||||
find . -name "*.pyo" | xargs rm -f
|
||||
rm -rf $(build_dir) $(install_dir)
|
||||
dh_clean
|
||||
|
||||
.PHONY: binary binary-indep install build clean
|
||||
|
35
rtslib/__init__.py
Normal file
35
rtslib/__init__.py
Normal file
@@ -0,0 +1,35 @@
|
||||
'''
|
||||
This file is part of RTSLib Community Edition.
|
||||
Copyright (c) 2011 by RisingTide Systems LLC
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, version 3 (AGPLv3).
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
'''
|
||||
|
||||
import utils
|
||||
|
||||
from root import RTSRoot
|
||||
from utils import RTSLibError, RTSLibBrokenLink
|
||||
|
||||
from target import LUN, MappedLUN
|
||||
from target import NodeACL, NetworkPortal, TPG, Target, FabricModule
|
||||
|
||||
from tcm import FileIOBackstore, IBlockBackstore
|
||||
from tcm import FileIOStorageObject, IBlockStorageObject
|
||||
from tcm import PSCSIBackstore, RDDRBackstore, RDMCPBackstore
|
||||
from tcm import PSCSIStorageObject, RDDRStorageObject, RDMCPStorageObject
|
||||
|
||||
__version__ = 'GIT_VERSION'
|
||||
__author__ = "Jerome Martin <jxm@risingtidesystems.com>"
|
||||
__url__ = "http://www.risingtidesystems.com"
|
||||
__description__ = "API for RisingTide Systems generic SCSI target."
|
||||
__license__ = __doc__
|
465
rtslib/loop.py
Normal file
465
rtslib/loop.py
Normal file
@@ -0,0 +1,465 @@
|
||||
'''
|
||||
Implements the RTS SAS loopback classes.
|
||||
|
||||
This file is part of RTSLib Community Edition.
|
||||
Copyright (c) 2011 by RisingTide Systems LLC
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, version 3 (AGPLv3).
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
'''
|
||||
|
||||
import re
|
||||
import os
|
||||
import glob
|
||||
import uuid
|
||||
import shutil
|
||||
|
||||
# rtslib modules
|
||||
from root import RTSRoot
|
||||
from node import CFSNode
|
||||
from utils import RTSLibError, RTSLibBrokenLink
|
||||
from utils import generate_wwn, fwrite, fread
|
||||
|
||||
class LUN(CFSNode):
|
||||
'''
|
||||
This is an interface to RTS Target LUNs in configFS.
|
||||
A LUN is identified by its parent Nexus and LUN index.
|
||||
'''
|
||||
|
||||
# LUN private stuff
|
||||
|
||||
def __init__(self, parent_nexus, lun, storage_object=None, alias=None):
|
||||
'''
|
||||
A LUN object can be instanciated in two ways:
|
||||
- B{Creation mode}: If I{storage_object} is specified, the
|
||||
underlying configFS object will be created with that parameter.
|
||||
No LUN with the same I{lun} index can pre-exist in the parent
|
||||
Nexus in that mode, or instanciation will fail.
|
||||
- B{Lookup mode}: If I{storage_object} is not set, then the LUN
|
||||
will be bound to the existing configFS LUN object of the parent
|
||||
Nexus having the specified I{lun} index. The underlying configFS
|
||||
object must already exist in that mode.
|
||||
|
||||
@param parent_nexus: The parent Nexus object.
|
||||
@type parent_nexus: Nexus
|
||||
@param lun: The LUN index.
|
||||
@type lun: 0-255
|
||||
@param storage_object: The storage object to be exported as a LUN.
|
||||
@type storage_object: StorageObject subclass
|
||||
@param alias: An optional parameter to manually specify the LUN alias.
|
||||
You probably do not need this.
|
||||
@type alias: string
|
||||
@return: A LUN object.
|
||||
'''
|
||||
super(LUN, self).__init__()
|
||||
|
||||
if isinstance(parent_nexus, Nexus):
|
||||
self._parent_nexus = parent_nexus
|
||||
else:
|
||||
raise RTSLibError("Invalid parent Nexus.")
|
||||
|
||||
try:
|
||||
lun = int(lun)
|
||||
except ValueError:
|
||||
raise RTSLibError("Invalid LUN index: %s" % str(lun))
|
||||
else:
|
||||
if lun > 255 or lun < 0:
|
||||
raise RTSLibError("Invalid LUN index, it must be "
|
||||
+ "between 0 and 255: %d" % lun)
|
||||
self._lun = lun
|
||||
|
||||
self._path = "%s/lun/lun_%d" % (self.parent_nexus.path, self.lun)
|
||||
|
||||
if storage_object is None and alias is not None:
|
||||
raise RTSLibError("The alias parameter has no meaning "
|
||||
+ "without the storage_object parameter.")
|
||||
|
||||
if storage_object is not None:
|
||||
self._create_in_cfs_ine('create')
|
||||
try:
|
||||
self._configure(storage_object, alias)
|
||||
except:
|
||||
self.delete()
|
||||
raise
|
||||
else:
|
||||
self._create_in_cfs_ine('lookup')
|
||||
|
||||
def __str__(self):
|
||||
try:
|
||||
storage_object = self.storage_object
|
||||
except RTSLibBrokenLink:
|
||||
desc = "[BROKEN STORAGE LINK]"
|
||||
else:
|
||||
backstore = storage_object.backstore
|
||||
soname = storage_object.name
|
||||
if backstore.plugin.startswith("rd"):
|
||||
path = "ramdisk"
|
||||
else:
|
||||
path = storage_object.udev_path
|
||||
desc = "-> %s%d '%s' (%s)" \
|
||||
% (backstore.plugin, backstore.index, soname, path)
|
||||
return "LUN %d %s" % (self.lun, desc)
|
||||
|
||||
def _create_in_cfs_ine(self, mode):
|
||||
super(LUN, self)._create_in_cfs_ine(mode)
|
||||
|
||||
def _configure(self, storage_object, alias):
|
||||
self._check_self()
|
||||
if alias is None:
|
||||
alias = str(uuid.uuid4())[-10:]
|
||||
else:
|
||||
alias = str(alias).strip()
|
||||
if '/' in alias:
|
||||
raise RTSLibError("Invalid alias: %s", alias)
|
||||
destination = "%s/%s" % (self.path, alias)
|
||||
from tcm import StorageObject
|
||||
if isinstance(storage_object, StorageObject):
|
||||
if storage_object.exists:
|
||||
source = storage_object.path
|
||||
else:
|
||||
raise RTSLibError("The storage_object does not exist "
|
||||
+ "in configFS.")
|
||||
else:
|
||||
raise RTSLibError("Invalid storage object.")
|
||||
|
||||
os.symlink(source, destination)
|
||||
|
||||
def _get_alias(self):
|
||||
self._check_self()
|
||||
alias = None
|
||||
for path in os.listdir(self.path):
|
||||
if os.path.islink("%s/%s" % (self.path, path)):
|
||||
alias = os.path.basename(path)
|
||||
break
|
||||
if alias is None:
|
||||
raise RTSLibBrokenLink("Broken LUN in configFS, no " \
|
||||
+ "storage object attached.")
|
||||
else:
|
||||
return alias
|
||||
|
||||
def _get_storage_object(self):
|
||||
self._check_self()
|
||||
alias_path = None
|
||||
for path in os.listdir(self.path):
|
||||
if os.path.islink("%s/%s" % (self.path, path)):
|
||||
alias_path = os.path.realpath("%s/%s" % (self.path, path))
|
||||
break
|
||||
if alias_path is None:
|
||||
raise RTSLibBrokenLink("Broken LUN in configFS, no " \
|
||||
+ "storage object attached.")
|
||||
rtsroot = RTSRoot()
|
||||
for storage_object in rtsroot.storage_objects:
|
||||
if storage_object.path == alias_path:
|
||||
return storage_object
|
||||
raise RTSLibBrokenLink("Broken storage object link in LUN.")
|
||||
|
||||
def _get_parent_nexus(self):
|
||||
return self._parent_nexus
|
||||
|
||||
def _get_lun(self):
|
||||
return self._lun
|
||||
|
||||
def _get_alua_metadata_path(self):
|
||||
return "%s/lun_%d" % (self.parent_nexus.alua_metadata_path, self.lun)
|
||||
|
||||
# LUN public stuff
|
||||
|
||||
def delete(self):
|
||||
'''
|
||||
If the underlying configFS object does not exists, this method does
|
||||
nothing. If the underlying configFS object exists, this method attempts
|
||||
to delete it.
|
||||
'''
|
||||
self._check_self()
|
||||
try:
|
||||
link = self.alias
|
||||
except RTSLibBrokenLink:
|
||||
pass
|
||||
else:
|
||||
if os.path.islink("%s/%s" % (self.path, link)):
|
||||
os.unlink("%s/%s" % (self.path, link))
|
||||
|
||||
super(LUN, self).delete()
|
||||
if os.path.isdir(self.alua_metadata_path):
|
||||
shutil.rmtree(self.alua_metadata_path)
|
||||
|
||||
alua_metadata_path = property(_get_alua_metadata_path,
|
||||
doc="Get the ALUA metadata directory path for the LUN.")
|
||||
parent_nexus = property(_get_parent_nexus,
|
||||
doc="Get the parent Nexus object.")
|
||||
lun = property(_get_lun,
|
||||
doc="Get the LUN index as an int.")
|
||||
storage_object = property(_get_storage_object,
|
||||
doc="Get the storage object attached to the LUN.")
|
||||
alias = property(_get_alias,
|
||||
doc="Get the LUN alias.")
|
||||
|
||||
class Nexus(CFSNode):
|
||||
'''
|
||||
This is a an interface to Target Portal Groups in configFS.
|
||||
A Nexus is identified by its parent Target object and its nexus Tag.
|
||||
To a Nexus object is attached a list of NetworkPortals.
|
||||
'''
|
||||
|
||||
# Nexus private stuff
|
||||
|
||||
def __init__(self, parent_target, tag, mode='any'):
|
||||
'''
|
||||
@param parent_target: The parent Target object of the Nexus.
|
||||
@type parent_target: Target
|
||||
@param tag: The Nexus Tag (TPGT).
|
||||
@type tag: int > 0
|
||||
@param mode:An optionnal string containing the object creation mode:
|
||||
- I{'any'} means the configFS object will be either looked up or
|
||||
created.
|
||||
- I{'lookup'} means the object MUST already exist configFS.
|
||||
- I{'create'} means the object must NOT already exist in configFS.
|
||||
@type mode:string
|
||||
@return: A Nexus object.
|
||||
'''
|
||||
|
||||
super(Nexus, self).__init__()
|
||||
|
||||
try:
|
||||
self._tag = int(tag)
|
||||
except ValueError:
|
||||
raise RTSLibError("Invalid Tag.")
|
||||
|
||||
if tag < 1:
|
||||
raise RTSLibError("Invalig Tag, it must be >0.")
|
||||
|
||||
if isinstance(parent_target, Target):
|
||||
self._parent_target = parent_target
|
||||
else:
|
||||
raise RTSLibError("Invalid parent Target.")
|
||||
|
||||
self._path = "%s/tpgt_%d" % (self.parent_target.path, self.tag)
|
||||
self._create_in_cfs_ine(mode)
|
||||
|
||||
def __str__(self):
|
||||
try:
|
||||
initiator = self.initiator
|
||||
except RTSLibError:
|
||||
initiator = "[BROKEN]"
|
||||
return "Nexus %d / initiator %s" % (self.tag, initiator)
|
||||
|
||||
def _get_initiator(self):
|
||||
nexus_path = self._path + "/nexus"
|
||||
if os.path.isfile(nexus_path):
|
||||
try:
|
||||
initiator = fread(nexus_path)
|
||||
except IOError, msg:
|
||||
raise RTSLibError("Cannot read Nexus initiator address "
|
||||
+ "(>=4.0 style, %s): %s."
|
||||
% (nexus_path, msg))
|
||||
else:
|
||||
try:
|
||||
initiator = os.listdir(nexus_path)[0]
|
||||
except IOError, msg:
|
||||
raise RTSLibError("Cannot read Nexus initiator address "
|
||||
+ "(<4.0 style, %s): %s."
|
||||
% (nexus_path, msg))
|
||||
return initiator.strip()
|
||||
|
||||
def _get_tag(self):
|
||||
return self._tag
|
||||
|
||||
def _get_parent_target(self):
|
||||
return self._parent_target
|
||||
|
||||
def _create_in_cfs_ine(self, mode):
|
||||
super(Nexus, self)._create_in_cfs_ine(mode)
|
||||
|
||||
if not os.path.isdir(self.alua_metadata_path):
|
||||
os.makedirs(self.alua_metadata_path)
|
||||
|
||||
if self._fresh:
|
||||
initiator = generate_wwn('naa')
|
||||
nexus_path = self._path + "/nexus"
|
||||
if os.path.isfile(nexus_path):
|
||||
try:
|
||||
fwrite(nexus_path, initiator)
|
||||
except IOError, msg:
|
||||
raise RTSLibError("Cannot create Nexus initiator "
|
||||
+ "(>=4.0 style, %s): %s."
|
||||
% (nexus_path, msg))
|
||||
else:
|
||||
try:
|
||||
os.makedirs(nexus_path + "/" + initiator)
|
||||
except IOError, msg:
|
||||
raise RTSLibError("Cannot create Nexus initiator."
|
||||
+ "(<4.0 style, %s): %s."
|
||||
% (nexus_path, msg))
|
||||
|
||||
def _list_luns(self):
|
||||
self._check_self()
|
||||
luns = []
|
||||
lun_dirs = [os.path.basename(path)
|
||||
for path in os.listdir("%s/lun" % self.path)]
|
||||
for lun_dir in lun_dirs:
|
||||
lun = lun_dir.split('_')[1]
|
||||
lun = int(lun)
|
||||
luns.append(LUN(self, lun))
|
||||
return luns
|
||||
|
||||
def _control(self, command):
|
||||
self._check_self()
|
||||
path = "%s/control" % self.path
|
||||
fwrite(path, "%s\n" % str(command))
|
||||
|
||||
def _get_alua_metadata_path(self):
|
||||
return "%s/%s+%d" \
|
||||
% (self.alua_metadata_dir,
|
||||
self.parent_target.naa, self.tag)
|
||||
|
||||
# Nexus public stuff
|
||||
|
||||
def delete(self):
|
||||
'''
|
||||
Recursively deletes a Nexus object.
|
||||
This will delete all attached LUN, and then the Nexus itself.
|
||||
'''
|
||||
self._check_self()
|
||||
for lun in self.luns:
|
||||
lun.delete()
|
||||
|
||||
# TODO: check that ALUA MD removal works while removing Nexus
|
||||
if os.path.isdir(self.alua_metadata_path):
|
||||
shutil.rmtree(self.alua_metadata_path)
|
||||
|
||||
nexus_path = self._path + "/nexus"
|
||||
if os.path.isfile(nexus_path):
|
||||
try:
|
||||
fwrite(nexus_path, "NULL")
|
||||
except IOError, msg:
|
||||
raise RTSLibError("Cannot delete Nexus initiator "
|
||||
+ "(>=4.0 style, %s): %s."
|
||||
% (nexus_path, msg))
|
||||
else:
|
||||
try:
|
||||
os.rmdir(nexus_path + "/" + self.initiator)
|
||||
except IOError, msg:
|
||||
raise RTSLibError("Cannot delete Nexus initiator."
|
||||
+ "(<4.0 style, %s): %s."
|
||||
% (nexus_path, msg))
|
||||
|
||||
super(Nexus, self).delete()
|
||||
|
||||
def lun(self, lun, storage_object=None, alias=None):
|
||||
'''
|
||||
Same as LUN() but without specifying the parent_nexus.
|
||||
'''
|
||||
self._check_self()
|
||||
return LUN(self, lun=lun, storage_object=storage_object, alias=alias)
|
||||
|
||||
alua_metadata_path = property(_get_alua_metadata_path,
|
||||
doc="Get the ALUA metadata directory path " \
|
||||
+ "for the Nexus.")
|
||||
tag = property(_get_tag,
|
||||
doc="Get the Nexus Tag as an int.")
|
||||
initiator = property(_get_initiator,
|
||||
doc="Get the Nexus initiator address as a string.")
|
||||
parent_target = property(_get_parent_target,
|
||||
doc="Get the parent Target object to which the " \
|
||||
+ "Nexus is attached.")
|
||||
luns = property(_list_luns,
|
||||
doc="Get the list of LUN objects currently attached " \
|
||||
+ "to the Nexus.")
|
||||
|
||||
class Target(CFSNode):
|
||||
'''
|
||||
This is an interface to loopback SAS Targets in configFS.
|
||||
A Target is identified by its naa SAS address.
|
||||
To a Target is attached a list of Nexus objects.
|
||||
'''
|
||||
|
||||
# Target private stuff
|
||||
|
||||
def __init__(self, naa=None, mode='any'):
|
||||
'''
|
||||
@param naa: The optionnal Target's address.
|
||||
If no address or an empty address is specified, one will be
|
||||
generated for you.
|
||||
@type naa: string
|
||||
@param mode:An optionnal string containing the object creation mode:
|
||||
- I{'any'} means the configFS object will be either looked up
|
||||
or created.
|
||||
- I{'lookup'} means the object MUST already exist configFS.
|
||||
- I{'create'} means the object must NOT already exist in configFS.
|
||||
@type mode:string
|
||||
@return: A Target object.
|
||||
'''
|
||||
|
||||
super(Target, self).__init__()
|
||||
|
||||
if naa is None:
|
||||
naa = generate_wwn('naa')
|
||||
else:
|
||||
naa = str(naa).lower().strip()
|
||||
self._naa = naa
|
||||
self._path = "%s/loopback/%s" % (self.configfs_dir, self._naa)
|
||||
if not self:
|
||||
if not re.match(
|
||||
"naa\.[0-9]+", naa) \
|
||||
or re.search(' ', naa) \
|
||||
or re.search('_', naa):
|
||||
raise RTSLibError("Invalid naa: %s"
|
||||
% naa)
|
||||
self._create_in_cfs_ine(mode)
|
||||
|
||||
def __str__(self):
|
||||
return "SAS loopback %s" % self.naa
|
||||
|
||||
def _list_nexuses(self):
|
||||
self._check_self()
|
||||
nexuses = []
|
||||
nexus_dirs = glob.glob("%s/tpgt*" % self.path)
|
||||
for nexus_dir in nexus_dirs:
|
||||
tag = os.path.basename(nexus_dir).split('_')[1]
|
||||
tag = int(tag)
|
||||
nexuses.append(Nexus(self, tag, 'lookup'))
|
||||
return nexuses
|
||||
|
||||
def _get_naa(self):
|
||||
return self._naa
|
||||
|
||||
# Target public stuff
|
||||
|
||||
def delete(self):
|
||||
'''
|
||||
Recursively deletes a Target object.
|
||||
This will delete all attached Nexus objects and then the Target itself.
|
||||
'''
|
||||
self._check_self()
|
||||
for nexus in self.nexuses:
|
||||
nexus.delete()
|
||||
super(Target, self).delete()
|
||||
|
||||
def nexus(self, tag, mode='any'):
|
||||
'''
|
||||
Same as Nexus() but without the parent_target parameter.
|
||||
'''
|
||||
self._check_self()
|
||||
return Nexus(self, tag=tag, mode=mode)
|
||||
|
||||
naa = property(_get_naa,
|
||||
doc="Get the naa of the Target object as a string.")
|
||||
nexuses = property(_list_nexuses,
|
||||
doc="Get the list of Nexus objects currently "
|
||||
+ "attached to the Target.")
|
||||
|
||||
def _test():
|
||||
import doctest
|
||||
doctest.testmod()
|
||||
|
||||
if __name__ == "__main__":
|
||||
_test()
|
236
rtslib/node.py
Normal file
236
rtslib/node.py
Normal file
@@ -0,0 +1,236 @@
|
||||
'''
|
||||
Implements the base CFSNode class and a few inherited variants.
|
||||
|
||||
This file is part of RTSLib Community Edition.
|
||||
Copyright (c) 2011 by RisingTide Systems LLC
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, version 3 (AGPLv3).
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
'''
|
||||
|
||||
import os
|
||||
import stat
|
||||
from utils import fread, fwrite, RTSLibError
|
||||
|
||||
class CFSNode(object):
|
||||
|
||||
# Where do we store the fabric modules spec files ?
|
||||
spec_dir = "/var/target/fabric"
|
||||
# Where is the configfs base LIO directory ?
|
||||
configfs_dir = '/sys/kernel/config/target'
|
||||
# TODO: Make the ALUA path generic, not iscsi-centric
|
||||
# What is the ALUA directory ?
|
||||
alua_metadata_dir = "/var/target/alua/iSCSI"
|
||||
|
||||
# CFSNode private stuff
|
||||
|
||||
def __init__(self):
|
||||
self._path = self.configfs_dir
|
||||
|
||||
def __nonzero__(self):
|
||||
if os.path.isdir(self.path):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def __str__(self):
|
||||
return self.path
|
||||
|
||||
def _get_path(self):
|
||||
return self._path
|
||||
|
||||
def _create_in_cfs_ine(self, mode):
|
||||
'''
|
||||
Creates the configFS node if it does not already exists depending on
|
||||
the mode.
|
||||
any -> makes sure it exists, also works if the node already does exists
|
||||
lookup -> make sure it does NOT exists
|
||||
create -> create the node which must not exists beforehand
|
||||
Upon success (no exception raised), self._fresh is True if a node was
|
||||
created, else self._fresh is False.
|
||||
'''
|
||||
if mode not in ['any', 'lookup', 'create']:
|
||||
raise RTSLibError("Invalid mode: %s" % mode)
|
||||
if self and mode == 'create':
|
||||
raise RTSLibError("This %s already exists in configFS."
|
||||
% self.__class__.__name__)
|
||||
elif not self and mode == 'lookup':
|
||||
raise RTSLibError("No such %s in configfs: %s."
|
||||
% (self.__class__.__name__, self.path))
|
||||
if not self:
|
||||
os.mkdir(self.path)
|
||||
self._fresh = True
|
||||
else:
|
||||
self._fresh = False
|
||||
|
||||
def _exists(self):
|
||||
return bool(self)
|
||||
|
||||
def _check_self(self):
|
||||
if not self:
|
||||
raise RTSLibError("This %s does not exist in configFS."
|
||||
% self.__class__.__name__)
|
||||
|
||||
def _is_fresh(self):
|
||||
return self._fresh
|
||||
|
||||
def _list_files(self, path, writable=None):
|
||||
'''
|
||||
List files under a path depending on their owner's write permissions.
|
||||
@param path: The path under which the files are expected to be. If the
|
||||
path itself is not a directory, an empty list will be returned.
|
||||
@type path: str
|
||||
@param writable: If None (default), returns all parameters, if True,
|
||||
returns read-write parameters, if False, returns just the read-only
|
||||
parameters.
|
||||
@type writable: bool or None
|
||||
@return: List of file names filtered according to their write perms.
|
||||
'''
|
||||
if not os.path.isdir(path):
|
||||
return []
|
||||
|
||||
if writable is None:
|
||||
names = os.listdir(path)
|
||||
elif writable:
|
||||
names = [name for name in os.listdir(path)
|
||||
if (os.stat("%s/%s" % (path, name))[stat.ST_MODE] \
|
||||
& stat.S_IWUSR)]
|
||||
else:
|
||||
names = [os.path.basename(name) for name in os.listdir(path)
|
||||
if not (os.stat("%s/%s" % (path, name))[stat.ST_MODE] \
|
||||
& stat.S_IWUSR)]
|
||||
names.sort()
|
||||
return names
|
||||
|
||||
# CFSNode public stuff
|
||||
|
||||
def list_parameters(self, writable=None):
|
||||
'''
|
||||
@param writable: If None (default), returns all parameters, if True,
|
||||
returns read-write parameters, if False, returns just the read-only
|
||||
parameters.
|
||||
@type writable: bool or None
|
||||
@return: The list of existing RFC-3720 parameter names.
|
||||
'''
|
||||
self._check_self()
|
||||
path = "%s/param" % self.path
|
||||
return self._list_files(path, writable)
|
||||
|
||||
def list_attributes(self, writable=None):
|
||||
'''
|
||||
@param writable: If None (default), returns all attributes, if True,
|
||||
returns read-write attributes, if False, returns just the read-only
|
||||
attributes.
|
||||
@type writable: bool or None
|
||||
@return: A list of existing attribute names as strings.
|
||||
'''
|
||||
self._check_self()
|
||||
path = "%s/attrib" % self.path
|
||||
return self._list_files(path, writable)
|
||||
|
||||
def set_attribute(self, attribute, value):
|
||||
'''
|
||||
Sets the value of a named attribute.
|
||||
The attribute must exist in configFS.
|
||||
@param attribute: The attribute's name. It is case-sensitive.
|
||||
@type attribute: string
|
||||
@param value: The attribute's value.
|
||||
@type value: string
|
||||
'''
|
||||
self._check_self()
|
||||
path = "%s/attrib/%s" % (self.path, str(attribute))
|
||||
if not os.path.isfile(path):
|
||||
raise RTSLibError("Cannot find attribute: %s."
|
||||
% str(attribute))
|
||||
else:
|
||||
try:
|
||||
fwrite(path, "%s\n" % str(value))
|
||||
except IOError:
|
||||
raise RTSLibError("Cannot set attribute %s."
|
||||
% str(attribute))
|
||||
|
||||
def get_attribute(self, attribute):
|
||||
'''
|
||||
@param attribute: The attribute's name. It is case-sensitive.
|
||||
@return: The named attribute's value, as a string.
|
||||
'''
|
||||
self._check_self()
|
||||
path = "%s/attrib/%s" % (self.path, str(attribute))
|
||||
if not os.path.isfile(path):
|
||||
raise RTSLibError("Cannot find attribute: %s."
|
||||
% str(attribute))
|
||||
else:
|
||||
return fread(path).strip()
|
||||
|
||||
def set_parameter(self, parameter, value):
|
||||
'''
|
||||
Sets the value of a named RFC-3720 parameter.
|
||||
The parameter must exist in configFS.
|
||||
@param parameter: The RFC-3720 parameter's name. It is case-sensitive.
|
||||
@type parameter: string
|
||||
@param value: The parameter's value.
|
||||
@type value: string
|
||||
'''
|
||||
self._check_self()
|
||||
path = "%s/param/%s" % (self.path, str(parameter))
|
||||
if not os.path.isfile(path):
|
||||
raise RTSLibError("Cannot find parameter: %s."
|
||||
% str(parameter))
|
||||
else:
|
||||
try:
|
||||
fwrite(path, "%s \n" % str(value))
|
||||
except IOError:
|
||||
raise RTSLibError("Cannot set parameter %s."
|
||||
% str(parameter))
|
||||
|
||||
def get_parameter(self, parameter):
|
||||
'''
|
||||
@param parameter: The RFC-3720 parameter's name. It is case-sensitive.
|
||||
@type parameter: string
|
||||
@return: The named parameter value as a string.
|
||||
'''
|
||||
self._check_self()
|
||||
path = "%s/param/%s" % (self.path, str(parameter))
|
||||
if not os.path.isfile(path):
|
||||
raise RTSLibError("Cannot find RFC-3720 parameter: %s."
|
||||
% str(parameter))
|
||||
else:
|
||||
return fread(path).rstrip()
|
||||
|
||||
def delete(self):
|
||||
'''
|
||||
If the underlying configFS object does not exists, this method does
|
||||
nothing. If the underlying configFS object exists, this method attempts
|
||||
to delete it.
|
||||
'''
|
||||
if self:
|
||||
os.rmdir(self.path)
|
||||
|
||||
path = property(_get_path,
|
||||
doc="Get the configFS object path.")
|
||||
exists = property(_exists,
|
||||
doc="Is True as long as the underlying configFS object exists. " \
|
||||
+ "If the underlying configFS objects gets deleted " \
|
||||
+ "either by calling the delete() method, or by any " \
|
||||
+ "other means, it will be False.")
|
||||
is_fresh = property(_is_fresh,
|
||||
doc="Is True if the underlying configFS object has been created " \
|
||||
+ "when instanciating this particular object. Is " \
|
||||
+ "False if this object instanciation just looked " \
|
||||
+ "up the underlying configFS object.")
|
||||
|
||||
def _test():
|
||||
import doctest
|
||||
doctest.testmod()
|
||||
|
||||
if __name__ == "__main__":
|
||||
_test()
|
164
rtslib/root.py
Normal file
164
rtslib/root.py
Normal file
@@ -0,0 +1,164 @@
|
||||
'''
|
||||
Implements the RTSRoot class.
|
||||
|
||||
This file is part of RTSLib Community Edition.
|
||||
Copyright (c) 2011 by RisingTide Systems LLC
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, version 3 (AGPLv3).
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
'''
|
||||
|
||||
import re
|
||||
import os
|
||||
import glob
|
||||
|
||||
from node import CFSNode
|
||||
from target import Target, FabricModule
|
||||
from tcm import FileIOBackstore, IBlockBackstore
|
||||
from tcm import PSCSIBackstore, RDDRBackstore, RDMCPBackstore
|
||||
from utils import RTSLibError, RTSLibBrokenLink, flatten_nested_list, modprobe
|
||||
|
||||
class RTSRoot(CFSNode):
|
||||
'''
|
||||
This is an interface to the root of the configFS object tree.
|
||||
Is allows one to start browsing Target and Backstore objects,
|
||||
as well as helper methods to return arbitrary objects from the
|
||||
configFS tree.
|
||||
|
||||
>>> import rtslib.root as root
|
||||
>>> rtsroot = root.RTSRoot()
|
||||
>>> rtsroot.path
|
||||
'/sys/kernel/config/target'
|
||||
>>> rtsroot.exists
|
||||
True
|
||||
>>> rtsroot.targets # doctest: +ELLIPSIS
|
||||
[...]
|
||||
>>> rtsroot.backstores # doctest: +ELLIPSIS
|
||||
[...]
|
||||
>>> rtsroot.tpgs # doctest: +ELLIPSIS
|
||||
[...]
|
||||
>>> rtsroot.storage_objects # doctest: +ELLIPSIS
|
||||
[...]
|
||||
>>> rtsroot.network_portals # doctest: +ELLIPSIS
|
||||
[...]
|
||||
|
||||
'''
|
||||
|
||||
# The core target/tcm kernel module
|
||||
target_core_mod = 'target_core_mod'
|
||||
|
||||
# RTSRoot private stuff
|
||||
def __init__(self):
|
||||
'''
|
||||
Instanciate an RTSRoot object. Basically checks for configfs setup and
|
||||
base kernel modules (tcm )
|
||||
'''
|
||||
super(RTSRoot, self).__init__()
|
||||
modprobe(self.target_core_mod)
|
||||
self._create_in_cfs_ine('any')
|
||||
|
||||
def _list_targets(self):
|
||||
self._check_self()
|
||||
targets = set([])
|
||||
for fabric_module in self.fabric_modules:
|
||||
targets.update(fabric_module.targets)
|
||||
return targets
|
||||
|
||||
def _list_backstores(self):
|
||||
self._check_self()
|
||||
backstores = set([])
|
||||
if os.path.isdir("%s/core" % self.path):
|
||||
backstore_dirs = glob.glob("%s/core/*_*" % self.path)
|
||||
for backstore_dir in [os.path.basename(path)
|
||||
for path in backstore_dirs]:
|
||||
regex = re.search("([a-z]+[_]*[a-z]+)(_)([0-9]+)",
|
||||
backstore_dir)
|
||||
if regex:
|
||||
if regex.group(1) == "fileio":
|
||||
backstores.add(
|
||||
FileIOBackstore(int(regex.group(3)), 'lookup'))
|
||||
elif regex.group(1) == "pscsi":
|
||||
backstores.add(
|
||||
PSCSIBackstore(int(regex.group(3)), 'lookup'))
|
||||
elif regex.group(1) == "iblock":
|
||||
backstores.add(
|
||||
IBlockBackstore(int(regex.group(3)), 'lookup'))
|
||||
elif regex.group(1) == "rd_dr":
|
||||
backstores.add(
|
||||
RDDRBackstore(int(regex.group(3)), 'lookup'))
|
||||
elif regex.group(1) == "rd_mcp":
|
||||
backstores.add(
|
||||
RDMCPBackstore(int(regex.group(3)), 'lookup'))
|
||||
return backstores
|
||||
|
||||
def _list_storage_objects(self):
|
||||
self._check_self()
|
||||
return set(flatten_nested_list([backstore.storage_objects
|
||||
for backstore in self.backstores]))
|
||||
|
||||
def _list_tpgs(self):
|
||||
self._check_self()
|
||||
return set(flatten_nested_list([t.tpgs for t in self.targets]))
|
||||
|
||||
def _list_node_acls(self):
|
||||
self._check_self()
|
||||
return set(flatten_nested_list([t.node_acls for t in self.tpgs]))
|
||||
|
||||
def _list_network_portals(self):
|
||||
self._check_self()
|
||||
return set(flatten_nested_list([t.network_portals for t in self.tpgs]))
|
||||
|
||||
def _list_luns(self):
|
||||
self._check_self()
|
||||
return set(flatten_nested_list([t.luns for t in self.tpgs]))
|
||||
|
||||
def _list_fabric_modules(self):
|
||||
self._check_self()
|
||||
mod_names = [mod_name[:-5] for mod_name in os.listdir(self.spec_dir)
|
||||
if mod_name.endswith('.spec')]
|
||||
modules = [FabricModule(mod_name) for mod_name in mod_names]
|
||||
return modules
|
||||
|
||||
def _list_loaded_fabric_modules(self):
|
||||
return [fm for fm in self._list_fabric_modules() if fm.exists]
|
||||
|
||||
def __str__(self):
|
||||
return "rtsadmin"
|
||||
|
||||
# RTSRoot public stuff
|
||||
|
||||
backstores = property(_list_backstores,
|
||||
doc="Get the list of Backstore objects.")
|
||||
targets = property(_list_targets,
|
||||
doc="Get the list of Target objects.")
|
||||
tpgs = property(_list_tpgs,
|
||||
doc="Get the list of all the existing TPG objects.")
|
||||
node_acls = property(_list_node_acls,
|
||||
doc="Get the list of all the existing NodeACL objects.")
|
||||
network_portals = property(_list_network_portals,
|
||||
doc="Get the list of all the existing Network Portal objects.")
|
||||
storage_objects = property(_list_storage_objects,
|
||||
doc="Get the list of all the existing Storage objects.")
|
||||
luns = property(_list_luns,
|
||||
doc="Get the list of all existing LUN objects.")
|
||||
fabric_modules = property(_list_fabric_modules,
|
||||
doc="Get the list of all FabricModule objects.")
|
||||
loaded_fabric_modules = property(_list_loaded_fabric_modules,
|
||||
doc="Get the list of all loaded FabricModule objects.")
|
||||
|
||||
def _test():
|
||||
'''Run the doctests.'''
|
||||
import doctest
|
||||
doctest.testmod()
|
||||
|
||||
if __name__ == "__main__":
|
||||
_test()
|
1157
rtslib/target.py
Normal file
1157
rtslib/target.py
Normal file
File diff suppressed because it is too large
Load Diff
1130
rtslib/tcm.py
Normal file
1130
rtslib/tcm.py
Normal file
File diff suppressed because it is too large
Load Diff
630
rtslib/utils.py
Normal file
630
rtslib/utils.py
Normal file
@@ -0,0 +1,630 @@
|
||||
'''
|
||||
Provides various utility functions.
|
||||
|
||||
This file is part of RTSLib Community Edition.
|
||||
Copyright (c) 2011 by RisingTide Systems LLC
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, version 3 (AGPLv3).
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
'''
|
||||
|
||||
import re
|
||||
import os
|
||||
import stat
|
||||
import uuid
|
||||
import glob
|
||||
import socket
|
||||
import subprocess
|
||||
|
||||
from array import array
|
||||
from fcntl import ioctl
|
||||
from struct import pack, unpack
|
||||
|
||||
class RTSLibError(Exception):
|
||||
'''
|
||||
Generic rtslib error.
|
||||
'''
|
||||
pass
|
||||
|
||||
class RTSLibBrokenLink(RTSLibError):
|
||||
'''
|
||||
Broken link in configfs, i.e. missing LUN storage object.
|
||||
'''
|
||||
pass
|
||||
|
||||
def flatten_nested_list(nested_list):
|
||||
'''
|
||||
Function to flatten a nested list.
|
||||
|
||||
>>> import rtslib.utils as utils
|
||||
>>> utils.flatten_nested_list([[1,2,3,[4,5,6]],[7,8],[[[9,10]],[11,]]])
|
||||
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
|
||||
|
||||
@param nested_list: A nested list (list of lists of lists etc.)
|
||||
@type nested_list: list
|
||||
@return: A list with only non-list elements
|
||||
'''
|
||||
return list(gen_list_item(nested_list))
|
||||
|
||||
def gen_list_item(nested_list):
|
||||
'''
|
||||
The generator for flatten_nested_list().
|
||||
It returns one by one items that are not a list, and recurses when
|
||||
he finds an item that is a list.
|
||||
'''
|
||||
for item in nested_list:
|
||||
if type(item) is list:
|
||||
for nested_item in gen_list_item(item):
|
||||
yield nested_item
|
||||
else:
|
||||
yield item
|
||||
|
||||
def fwrite(path, string):
|
||||
'''
|
||||
This function writes a string to a file, and takes care of
|
||||
opening it and closing it. If the file does not exists, it
|
||||
will be created.
|
||||
|
||||
>>> from rtslib.utils import *
|
||||
>>> fwrite("/tmp/test", "hello")
|
||||
>>> fread("/tmp/test")
|
||||
'hello'
|
||||
|
||||
@param path: The file to write to.
|
||||
@type path: string
|
||||
@param string: The string to write to the file.
|
||||
@type string: string
|
||||
|
||||
'''
|
||||
path = os.path.realpath(str(path))
|
||||
file_fd = open(path, 'w')
|
||||
try:
|
||||
file_fd.write("%s" % string)
|
||||
finally:
|
||||
file_fd.close()
|
||||
|
||||
def fread(path):
|
||||
'''
|
||||
This function reads the contents of a file.
|
||||
It takes care of opening and closing it.
|
||||
|
||||
>>> from rtslib.utils import *
|
||||
>>> fwrite("/tmp/test", "hello")
|
||||
>>> fread("/tmp/test")
|
||||
'hello'
|
||||
>>> fread("/tmp/notexistingfile") # doctest: +ELLIPSIS
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
IOError: [Errno 2] No such file or directory: '/tmp/notexistingfile'
|
||||
|
||||
@param path: The path to the file to read from.
|
||||
@type path: string
|
||||
@return: A string containing the file's contents.
|
||||
|
||||
'''
|
||||
path = os.path.realpath(str(path))
|
||||
string = ""
|
||||
file_fd = open(path, 'r')
|
||||
try:
|
||||
string = file_fd.read()
|
||||
finally:
|
||||
file_fd.close()
|
||||
|
||||
return string
|
||||
|
||||
def is_dev_in_use(path):
|
||||
'''
|
||||
This function will check if the device or file referenced by path is
|
||||
already mounted or used as a storage object backend. It works by trying to
|
||||
open the path with O_EXCL flag, which will fail if someone else already
|
||||
did. Note that the file is closed before the function returns, so this
|
||||
does not guaranteed the device will still be available after the check.
|
||||
@param path: path to the file of device to check
|
||||
@type path: string
|
||||
@return: A boolean, True is we cannot get exclusive descriptor on the path,
|
||||
False if we can.
|
||||
'''
|
||||
path = os.path.realpath(str(path))
|
||||
try:
|
||||
file_fd = os.open(path, os.O_EXCL|os.O_NDELAY)
|
||||
except OSError:
|
||||
return True
|
||||
else:
|
||||
os.close(file_fd)
|
||||
return False
|
||||
|
||||
def is_disk_partition(path):
|
||||
'''
|
||||
Try to find out if path is a partition of a TYPE_DISK device.
|
||||
Handles both /dev/sdaX and /dev/disk/by-*/*-part? schemes.
|
||||
'''
|
||||
regex = re.match(r'([a-z/]+)([1-9]+)$', path)
|
||||
if not regex:
|
||||
regex = re.match(r'(/dev/disk/.+)(-part[1-9]+)$', path)
|
||||
if not regex:
|
||||
return False
|
||||
else:
|
||||
if get_block_type(regex.group(1)) == 0:
|
||||
return True
|
||||
|
||||
def get_disk_size(path):
|
||||
'''
|
||||
This function returns the size in bytes of a disk-type
|
||||
block device, or None if path does not point to a disk-
|
||||
type device.
|
||||
'''
|
||||
(major, minor) = get_block_numbers(path)
|
||||
if major is None:
|
||||
return None
|
||||
# list of [major, minor, #blocks (1K), name
|
||||
partitions = [ x.split()[0:4]
|
||||
for x in fread("/proc/partitions").split("\n")[2:] if x]
|
||||
size = None
|
||||
for partition in partitions:
|
||||
if partition[0:2] == [str(major), str(minor)]:
|
||||
size = int(partition[2]) * 1024
|
||||
break
|
||||
return size
|
||||
|
||||
def get_block_numbers(path):
|
||||
'''
|
||||
This function returns a (major,minor) tuple for the block
|
||||
device found at path, or (None, None) if path is
|
||||
not a block device.
|
||||
'''
|
||||
dev = os.path.realpath(path)
|
||||
try:
|
||||
mode = os.stat(dev)
|
||||
except OSError:
|
||||
return (None, None)
|
||||
|
||||
if not stat.S_ISBLK(mode[stat.ST_MODE]):
|
||||
return (None, None)
|
||||
|
||||
major = os.major(mode.st_rdev)
|
||||
minor = os.minor(mode.st_rdev)
|
||||
return (major, minor)
|
||||
|
||||
def get_block_type(path):
|
||||
'''
|
||||
This function returns a block device's type.
|
||||
Example: 0 is TYPE_DISK
|
||||
If no match is found, None is returned.
|
||||
|
||||
>>> from rtslib.utils import *
|
||||
>>> get_block_type("/dev/sda")
|
||||
0
|
||||
>>> get_block_type("/dev/sr0")
|
||||
5
|
||||
>>> get_block_type("/dev/scd0")
|
||||
5
|
||||
>>> get_block_type("/dev/nodevicehere") is None
|
||||
True
|
||||
|
||||
@param path: path to the block device
|
||||
@type path: string
|
||||
@return: An int for the block device type, or None if not a block device.
|
||||
'''
|
||||
dev = os.path.realpath(path)
|
||||
# TODO: Make adding new majors on-the-fly possible, using some config file
|
||||
# for instance, maybe an additionnal list argument, or even a match all
|
||||
# mode for overrides ?
|
||||
|
||||
# Make sure we are dealing with a block device
|
||||
(major, minor) = get_block_numbers(dev)
|
||||
if major is None:
|
||||
return None
|
||||
|
||||
# Treat disk partitions as TYPE_DISK
|
||||
if is_disk_partition(path):
|
||||
return 0
|
||||
|
||||
# Assume that the assigned experimental major range devices are TYPE_DISK
|
||||
if 239 < major < 255:
|
||||
return 0
|
||||
|
||||
# DRBD devices do not report type but can be treated as TYPE_DISK
|
||||
if major == 147:
|
||||
return 0
|
||||
|
||||
# TODO: This should no be there as block device 30 is normally
|
||||
# 'Philips LMS CM-205 CD-ROM' in the Linux devices list
|
||||
# Cirtas devices do not report type but can be treated as TYPE_DISK
|
||||
if major == 30:
|
||||
return 0
|
||||
|
||||
# Same for LVM LVs, but as we cannot use major here
|
||||
# (it varies accross distros), use the realpath to check
|
||||
if os.path.dirname(dev) == "/dev/mapper":
|
||||
return 0
|
||||
|
||||
# list of (major, minor, type) tuples
|
||||
blocks = [(fread("%s/dev" % fdev).strip().split(':')[0],
|
||||
fread("%s/dev" % fdev).strip().split(':')[1],
|
||||
fread("%s/device/type" % fdev).strip())
|
||||
for fdev in glob.glob("/sys/block/*")
|
||||
if os.path.isfile("%s/device/type" % fdev)]
|
||||
|
||||
for block in blocks:
|
||||
if int(block[0]) == major and int(block[1]) == minor:
|
||||
return int(block[2])
|
||||
|
||||
return None
|
||||
|
||||
def list_scsi_hbas():
|
||||
'''
|
||||
This function returns the list of HBA indexes for existing SCSI HBAs.
|
||||
'''
|
||||
return list(set([int(device.partition(":")[0])
|
||||
for device in os.listdir("/sys/bus/scsi/devices")
|
||||
if re.match("[0-9:]+", device)]))
|
||||
|
||||
def convert_scsi_path_to_hctl(path):
|
||||
'''
|
||||
This function returns the SCSI ID in H:C:T:L form for the block
|
||||
device being mapped to the udev path specified.
|
||||
If no match is found, None is returned.
|
||||
|
||||
>>> import rtslib.utils as utils
|
||||
>>> utils.convert_scsi_path_to_hctl('/dev/scd0')
|
||||
(2, 0, 0, 0)
|
||||
>>> utils.convert_scsi_path_to_hctl('/dev/sr0')
|
||||
(2, 0, 0, 0)
|
||||
>>> utils.convert_scsi_path_to_hctl('/dev/sda')
|
||||
(3, 0, 0, 0)
|
||||
>>> utils.convert_scsi_path_to_hctl('/dev/sda1')
|
||||
>>> utils.convert_scsi_path_to_hctl('/dev/sdb')
|
||||
(3, 0, 1, 0)
|
||||
>>> utils.convert_scsi_path_to_hctl('/dev/sdc')
|
||||
(3, 0, 2, 0)
|
||||
|
||||
@param path: The udev path to the SCSI block device.
|
||||
@type path: string
|
||||
@return: An (host, controller, target, lun) tuple of integer
|
||||
values representing the SCSI ID of the device, or None if no
|
||||
match is found.
|
||||
'''
|
||||
dev = os.path.realpath(path)
|
||||
scsi_devices = [os.path.basename(scsi_dev).split(':')
|
||||
for scsi_dev in glob.glob("/sys/class/scsi_device/*")]
|
||||
for (host, controller, target, lun) in scsi_devices:
|
||||
scsi_dev = convert_scsi_hctl_to_path(host, controller, target, lun)
|
||||
if dev == scsi_dev:
|
||||
return (int(host), int(controller), int(target), int(lun))
|
||||
|
||||
return None
|
||||
|
||||
def convert_scsi_hctl_to_path(host, controller, target, lun):
|
||||
'''
|
||||
This function returns a udev path pointing to the block device being
|
||||
mapped to the SCSI device that has the provided H:C:T:L.
|
||||
|
||||
>>> import rtslib.utils as utils
|
||||
>>> utils.convert_scsi_hctl_to_path(0,0,0,0)
|
||||
''
|
||||
>>> utils.convert_scsi_hctl_to_path(2,0,0,0) # doctest: +ELLIPSIS
|
||||
'/dev/s...0'
|
||||
>>> utils.convert_scsi_hctl_to_path(3,0,2,0)
|
||||
'/dev/sdc'
|
||||
|
||||
@param host: The SCSI host id.
|
||||
@type host: int
|
||||
@param controller: The SCSI controller id.
|
||||
@type controller: int
|
||||
@param target: The SCSI target id.
|
||||
@type target: int
|
||||
@param lun: The SCSI Logical Unit Number.
|
||||
@type lun: int
|
||||
@return: A string for the canonical path to the device, or empty string.
|
||||
'''
|
||||
try:
|
||||
host = int(host)
|
||||
controller = int(controller)
|
||||
target = int(target)
|
||||
lun = int(lun)
|
||||
except ValueError:
|
||||
raise RTSLibError(
|
||||
"The host, controller, target and lun parameter must be integers.")
|
||||
|
||||
scsi_dev_path = "/sys/class/scsi_device"
|
||||
sysfs_names = [os.path.basename(name) for name
|
||||
in glob.glob("%s/%d:%d:%d:%d/device/block:*"
|
||||
% (scsi_dev_path, host, controller, target, lun))]
|
||||
if len(sysfs_names) == 0:
|
||||
sysfs_names = [os.path.basename(name) for name
|
||||
in glob.glob("%s/%d:%d:%d:%d/device/block/*"
|
||||
% (scsi_dev_path, host, controller, target, lun))]
|
||||
if len(sysfs_names) > 0:
|
||||
for name in sysfs_names:
|
||||
name1 = name.partition(":")[2].strip()
|
||||
if name1:
|
||||
name = name1
|
||||
dev = os.path.realpath("/dev/%s" % name)
|
||||
try:
|
||||
mode = os.stat(dev)[stat.ST_MODE]
|
||||
except OSError:
|
||||
pass
|
||||
if stat.S_ISBLK(mode):
|
||||
return dev
|
||||
else:
|
||||
return ''
|
||||
|
||||
def convert_human_to_bytes(hsize, kilo=1024):
|
||||
'''
|
||||
This function converts human-readable amounts of bytes to bytes.
|
||||
It understands the following units :
|
||||
- I{B} or no unit present for Bytes
|
||||
- I{k}, I{K}, I{kB}, I{KB} for kB (kilobytes)
|
||||
- I{m}, I{M}, I{mB}, I{MB} for MB (megabytes)
|
||||
- I{g}, I{G}, I{gB}, I{GB} for GB (gigabytes)
|
||||
- I{t}, I{T}, I{tB}, I{TB} for TB (terabytes)
|
||||
|
||||
Note: The definition of I{kilo} defaults to 1kB = 1024Bytes.
|
||||
Strictly speaking, those should not be called I{kB} but I{kiB}.
|
||||
You can override that with the optional kilo parameter.
|
||||
|
||||
Example:
|
||||
|
||||
>>> import rtslib.utils as utils
|
||||
>>> utils.convert_human_to_bytes("1k")
|
||||
1024
|
||||
>>> utils.convert_human_to_bytes("1k", 1000)
|
||||
1000
|
||||
>>> utils.convert_human_to_bytes("1MB")
|
||||
1048576
|
||||
>>> utils.convert_human_to_bytes("12kB")
|
||||
12288
|
||||
|
||||
@param hsize: The human-readable version of the Bytes amount to convert
|
||||
@type hsize: string or int
|
||||
@param kilo: Optionnal base for the kilo prefix
|
||||
@type kilo: int
|
||||
@return: An int representing the human-readable string converted to bytes
|
||||
'''
|
||||
size = str(hsize).replace("g","G").replace("K","k")
|
||||
size = size.replace("m","M").replace("t","T")
|
||||
if not re.match("^[0-9]+[T|G|M|k]?[B]?$", size):
|
||||
raise RTSLibError("Cannot interpret size, wrong format: %s" % hsize)
|
||||
|
||||
size = size.rstrip('B')
|
||||
|
||||
units = ['k', 'M', 'G', 'T']
|
||||
try:
|
||||
power = units.index(size[-1]) + 1
|
||||
except ValueError:
|
||||
power = 0
|
||||
size = int(size)
|
||||
else:
|
||||
size = int(size[:-1])
|
||||
|
||||
size = size * int(kilo) ** power
|
||||
return size
|
||||
|
||||
def generate_wwn(wwn_type):
|
||||
'''
|
||||
Generates a random WWN of the specified type:
|
||||
- unit_serial: T10 WWN Unit Serial.
|
||||
- iqn: iSCSI IQN
|
||||
- naa: SAS NAA address
|
||||
@param wwn_type: The WWN address type.
|
||||
@type wwn_type: str
|
||||
@returns: A string containing the WWN.
|
||||
'''
|
||||
wwn_type = wwn_type.lower()
|
||||
if wwn_type == 'free':
|
||||
return str(uuid.uuid4())
|
||||
if wwn_type == 'unit_serial':
|
||||
return str(uuid.uuid4())
|
||||
elif wwn_type == 'iqn':
|
||||
localname = socket.gethostname().split(".")[0]
|
||||
localarch = os.uname()[4].replace("_","")
|
||||
prefix = "iqn.2003-01.org.linux-iscsi.%s.%s" % (localname, localarch)
|
||||
prefix = prefix.strip().lower()
|
||||
serial = "sn.%s" % str(uuid.uuid4())[24:]
|
||||
return "%s:%s" % (prefix, serial)
|
||||
elif wwn_type == 'naa':
|
||||
sas_address = "naa.6001405%s" % str(uuid.uuid4())[:10]
|
||||
return sas_address.replace('-', '')
|
||||
else:
|
||||
raise ValueError("Unknown WWN type: %s." % wwn_type)
|
||||
|
||||
def is_valid_wwn(wwn_type, wwn, wwn_list=None):
|
||||
'''
|
||||
Returns True if the wwn is a valid wwn of type wwn_type.
|
||||
@param wwn_type: The WWN address type.
|
||||
@type wwn_type: str
|
||||
@param wwn: The WWN address to check.
|
||||
@type wwn: str
|
||||
@param wwn_list: An optional list of wwns to check the wwn parameter from.
|
||||
@type wwn_list: list of str
|
||||
@returns: bool.
|
||||
'''
|
||||
wwn_type = wwn_type.lower()
|
||||
|
||||
if wwn_list is not None and wwn not in wwn_list:
|
||||
return False
|
||||
elif wwn_type == 'free':
|
||||
return True
|
||||
elif wwn_type == 'iqn' \
|
||||
and re.match("iqn\.[0-9]{4}-[0-1][0-9]\..*\..*", wwn) \
|
||||
and not re.search(' ', wwn) \
|
||||
and not re.search('_', wwn):
|
||||
return True
|
||||
elif wwn_type == 'naa' \
|
||||
and re.match("naa\.[0-9A-Fa-f]{16}$", wwn):
|
||||
return True
|
||||
elif wwn_type == 'unit_serial' \
|
||||
and re.match(
|
||||
"[0-9A-Fa-f]{8}(-[0-9A-Fa-f]{4}){3}-[0-9A-Fa-f]{12}$", wwn):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def list_available_kernel_modules():
|
||||
'''
|
||||
List all loadable kernel modules as registered by depmod
|
||||
'''
|
||||
kver = os.uname()[2]
|
||||
depfile = "/lib/modules/%s/modules.dep" % kver
|
||||
return [module.split(".")[0] for module in
|
||||
re.findall(r"[a-zA-Z0-9_-]+\.ko:", fread(depfile))]
|
||||
|
||||
def list_loaded_kernel_modules():
|
||||
'''
|
||||
List all currently loaded kernel modules
|
||||
'''
|
||||
return [line.split(" ")[0] for line in
|
||||
fread("/proc/modules").split('\n') if line]
|
||||
|
||||
def modprobe(module):
|
||||
'''
|
||||
Load the specified kernel module if needed.
|
||||
@param module: The name of the kernel module to be loaded.
|
||||
@type module: str
|
||||
@return: Whether of not we had to load the module.
|
||||
'''
|
||||
if module not in list_loaded_kernel_modules():
|
||||
if module in list_available_kernel_modules():
|
||||
try:
|
||||
exec_argv(["modprobe", module])
|
||||
except:
|
||||
raise RTSLibError("Kernel module %s exists "
|
||||
% module + "but fails to load: %s")
|
||||
else:
|
||||
return True
|
||||
else:
|
||||
raise RTSLibError("Kernel module %s does not exists on disk "
|
||||
% module + "and is not loaded.")
|
||||
else:
|
||||
return False
|
||||
|
||||
def exec_argv(argv, strip=True, shell=False):
|
||||
'''
|
||||
Executes a command line given as an argv table and either:
|
||||
- raise an exception if return != 0
|
||||
- return the output
|
||||
If strip is True, then output lines will be stripped.
|
||||
If shell is True, the argv must be a string that will be evaluated by the
|
||||
shell, instead of the argv list.
|
||||
|
||||
'''
|
||||
process = subprocess.Popen(argv,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
shell=shell)
|
||||
(stdoutdata, stderrdata) = process.communicate()
|
||||
# Remove indents, trailing space and empty lines in output.
|
||||
if strip:
|
||||
stdoutdata = "\n".join([line.strip()
|
||||
for line in stdoutdata.split("\n")
|
||||
if line.strip()])
|
||||
stderrdata = "\n".join([line.strip()
|
||||
for line in stderrdata.split("\n")
|
||||
if line.strip()])
|
||||
if process.returncode != 0:
|
||||
raise RTSLibError(stderrdata)
|
||||
else:
|
||||
return stdoutdata
|
||||
|
||||
def list_eth_names(max_eth=1024):
|
||||
'''
|
||||
List the max_eth first local ethernet interfaces names from SIOCGIFCONF
|
||||
struct.
|
||||
'''
|
||||
SIOCGIFCONF = 0x8912
|
||||
if os.uname()[4].endswith("_64"):
|
||||
offset = 40
|
||||
else:
|
||||
offset = 32
|
||||
bytes = 32 * max_eth
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
ifaces = array('B', '\0' * bytes)
|
||||
packed = pack('iL', bytes, ifaces.buffer_info()[0])
|
||||
outbytes = unpack('iL', ioctl(sock.fileno(), SIOCGIFCONF, packed))[0]
|
||||
names = ifaces.tostring()
|
||||
return [names[i:i+offset].split('\0', 1)[0]
|
||||
for i in range(0, outbytes, offset)]
|
||||
|
||||
def list_eth_ips(ifnames=None):
|
||||
'''
|
||||
List the IP addresses of a list of ethernet interfaces from the SIOCGIFADDR
|
||||
struct. If ifname is omitted, list all IPs of all ifaces excepted for lo.
|
||||
'''
|
||||
SIOCGIFADDR = 0x8915
|
||||
if ifnames is None:
|
||||
ifnames = [iface for iface in list_eth_names() if iface != 'lo']
|
||||
ips = []
|
||||
for ifname in ifnames:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
packed = pack('256s', ifname[:15])
|
||||
ips.append(socket.inet_ntoa(ioctl(sock.fileno(),
|
||||
SIOCGIFADDR,
|
||||
packed)[20:24]))
|
||||
return flatten_nested_list(ips)
|
||||
|
||||
def get_main_ip():
|
||||
'''
|
||||
Try to guess the local machine non-loopback IP.
|
||||
If available, local hostname resolution is used (if non-loopback),
|
||||
else try to find an other non-loopback IP on configured NICs.
|
||||
If no usable IP address is found, returns None.
|
||||
'''
|
||||
# socket.gethostbyname does no have a timeout parameter
|
||||
# Let's use a thread to implement that in the background
|
||||
from threading import Thread
|
||||
from Queue import Queue, Empty
|
||||
|
||||
def start_thread(func):
|
||||
thread = Thread(target = func)
|
||||
thread.setDaemon(True)
|
||||
thread.start()
|
||||
|
||||
def gethostbyname_timeout(hostname, timeout = 1):
|
||||
queue = Queue(1)
|
||||
|
||||
def try_gethostbyname(hostname):
|
||||
try:
|
||||
hostname = socket.gethostbyname(hostname)
|
||||
except socket.gaierror:
|
||||
hostname = None
|
||||
return hostname
|
||||
|
||||
def queue_try_gethostbyname():
|
||||
queue.put(try_gethostbyname(hostname))
|
||||
|
||||
start_thread(queue_try_gethostbyname)
|
||||
|
||||
try:
|
||||
result = queue.get(block = True, timeout = timeout)
|
||||
except Empty:
|
||||
result = None
|
||||
return result
|
||||
|
||||
local_ips = list_eth_ips()
|
||||
# try to get a resolution in less than 1 second
|
||||
host_ip = gethostbyname_timeout(socket.gethostname())
|
||||
# Put the host IP in first position of the IP list if it exists
|
||||
if host_ip in local_ips:
|
||||
local_ips.remove(host_ip)
|
||||
local_ips.insert(0, host_ip)
|
||||
for ip_addr in local_ips:
|
||||
if not ip_addr.startswith("127.") and ip_addr.strip():
|
||||
return ip_addr
|
||||
return None
|
||||
|
||||
def _test():
|
||||
'''Run the doctests'''
|
||||
import doctest
|
||||
doctest.testmod()
|
||||
|
||||
if __name__ == "__main__":
|
||||
_test()
|
41
setup.py
Executable file
41
setup.py
Executable file
@@ -0,0 +1,41 @@
|
||||
#! /usr/bin/env python
|
||||
'''
|
||||
This file is part of RTSLib Community Edition.
|
||||
Copyright (c) 2011 by RisingTide Systems LLC
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, version 3 (AGPLv3).
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
'''
|
||||
|
||||
import re
|
||||
from distutils.core import setup
|
||||
import rtslib
|
||||
|
||||
PKG = rtslib
|
||||
VERSION = str(PKG.__version__)
|
||||
(AUTHOR, EMAIL) = re.match('^(.*?)\s*<(.*)>$', PKG.__author__).groups()
|
||||
URL = PKG.__url__
|
||||
LICENSE = PKG.__license__
|
||||
SCRIPTS = []
|
||||
DESCRIPTION = PKG.__description__
|
||||
|
||||
setup(
|
||||
name=PKG.__name__,
|
||||
description=DESCRIPTION,
|
||||
version=VERSION,
|
||||
author=AUTHOR,
|
||||
author_email=EMAIL,
|
||||
license=LICENSE,
|
||||
url=URL,
|
||||
scripts=SCRIPTS,
|
||||
packages=[PKG.__name__],
|
||||
package_data = {'':[]})
|
137
specs/README
Normal file
137
specs/README
Normal file
@@ -0,0 +1,137 @@
|
||||
# This file is part of RTSLib Community Edition.
|
||||
# Copyright (c) 2011 by RisingTide Systems LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, version 3 (AGPLv3).
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
This directory (normally /var/lib/target) contains the spec files for
|
||||
RisingTide Systems's LIO SCSI target subsystem fabric modules.
|
||||
|
||||
Each spec file should be named MODULE.spec, where MODULE is the name the fabric
|
||||
module is to be referred as. It contains a series of KEY = VALUE pairs, one per
|
||||
line.
|
||||
|
||||
KEY is an alphanumeric (no spaces) string.
|
||||
VALUE can be anything. Quotes can be used for strings, but are not mandatory.
|
||||
Lists of VALUEs are comma-separated.
|
||||
|
||||
Syntax
|
||||
------
|
||||
|
||||
* Strings
|
||||
String values can either be enclosed in double quotes or not.
|
||||
Those examples are equivalent:
|
||||
kernel_module = "my_module"
|
||||
kernel_module = my_module
|
||||
|
||||
* Lists
|
||||
Lists are comma-separated lists of values. If you want to use a comma in a
|
||||
string, use double quotes. Example:
|
||||
my_string = value1, value2, "value3, with comma", value4
|
||||
|
||||
* Comments
|
||||
All lines beginning with a pound sign (#) will be ignored.
|
||||
Empty lines will be ignored too.
|
||||
|
||||
Available keys
|
||||
--------------
|
||||
|
||||
* features
|
||||
Lists the target fabric available features. Default value:
|
||||
discovery_auth, acls, acls_auth, nps
|
||||
exemple: features = discovery_auth, acls, acls_auth
|
||||
|
||||
Detail of features:
|
||||
|
||||
* tpgts
|
||||
The target fabric module is using iSCSI-style target portal group tags.
|
||||
|
||||
* discovery_auth
|
||||
The target fabric module supports a fabric-wide authentication for
|
||||
discovery.
|
||||
|
||||
* acls
|
||||
The target's TPGTs do support explicit initiator ACLs.
|
||||
|
||||
* acls_auth
|
||||
The target's TPGT's ACLs do support per-ACL initiator authentication.
|
||||
|
||||
* nps
|
||||
The TPGTs do support iSCSI-like IPv4/IPv6 network portals, using IP:PORT
|
||||
group names.
|
||||
|
||||
* nexus
|
||||
The TPGTs do have a 'nexus' attribute that contains the local initiator
|
||||
serial unit. This attribute must be set before being able to create any
|
||||
LUNs.
|
||||
|
||||
* wwn_type
|
||||
Sets the type of WWN expected by the target fabric. Defaults to 'free'.
|
||||
Example: wwn_type = iqn
|
||||
Current valid types are:
|
||||
|
||||
* free
|
||||
Freeform WWN.
|
||||
|
||||
* iqn
|
||||
The fabric module targets are using iSCSI-type IQNs.
|
||||
|
||||
* naa
|
||||
NAA SAS address type WWN.
|
||||
|
||||
* unit_serial
|
||||
Disk-type unit serial.
|
||||
|
||||
* wwn_from_files
|
||||
In some cases, and independently from the wwn type, the target WWNs must be
|
||||
picked from a list of existing ones, the most obvious case being hardware-set
|
||||
WWNs. Only the WWNs both matching the wwn_type (after filtering if set, see
|
||||
below) and fetched from the specified files will be allowed for targets. The
|
||||
value of this key is a list (one or more, comma-separated) of UNIX style
|
||||
pathname patterns: * and ? wildcards can be used, and character ranges
|
||||
expressed with [] will be correctly expanded. Each file is assumed to contain
|
||||
one or more WWNs, and line ends, spaces, tabs and null (\0) will be considered
|
||||
as separators chars.
|
||||
Example: wwn_from_files = /sys/class/fc_host/host[0-9]/port_name
|
||||
|
||||
* wwn_from_files_filter
|
||||
Empty by default, this one allows specifying a shell command to which each WWN
|
||||
from files will be fed, and the output of the filter will be used as the final
|
||||
WWN to use. Examples:
|
||||
wwn_from_files_filter = "sed -e s/0x// -e 's/../&:/g' -e s/:$//"
|
||||
wwn_from_files_filter = "sed -e s/0x// -e 's/../&:/g' -e s/:$// | tr [a-z] [A-Z]"
|
||||
The first example transforms strings like '0x21000024ff314c48' into
|
||||
'21:00:00:24:ff:31:4c:48', the second one also shifts lower cases into upper
|
||||
case, demonstrating that you can pipe as many commands you want into another.
|
||||
|
||||
* wwn_from_cmds
|
||||
Same as wwn_from_files, but instead of taking a list of file patterns, takes a
|
||||
list of shell commands. Each commands output will be considered as a list of
|
||||
WWNs to be used, separated ny line ends, spaces, tabs and null (\0)
|
||||
chararcters.
|
||||
|
||||
* wwn_from_cmds_filter
|
||||
Same as wwn_from_files_filter, but filters/transforms the WWNs gotten from the
|
||||
results of the wwn_from_cmds shell commands.
|
||||
|
||||
* kernel_module
|
||||
Sets the name of the kernel module implementing the fabric modules. If not
|
||||
specified, it will be assumed to be MODULE_target_mod, where MODNAME is the
|
||||
name of the fabric module, as used to name the spec file. Note that you must
|
||||
not specify any .ko or such extension here.
|
||||
Example: kernel_module = my_module
|
||||
|
||||
* configfs_group
|
||||
Sets the name of the configfs group used by the fabric module. Defaults to the
|
||||
name of the module as used to name the spec file.
|
||||
Example: configfs_group = iscsi
|
||||
|
29
specs/example.spec.txt
Normal file
29
specs/example.spec.txt
Normal file
@@ -0,0 +1,29 @@
|
||||
# Example LIO target fabric module.
|
||||
#
|
||||
# This file is part of RTSLib Community Edition.
|
||||
# Copyright (c) 2011 by RisingTide Systems LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, version 3 (AGPLv3).
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# The example fabric module uses the default feature set.
|
||||
# features = discovery_auth, acls, acls_auth, nps
|
||||
|
||||
# This module uses anything as WWNs.
|
||||
wwn_type = free
|
||||
|
||||
# Convoluted kernel module name. Default would be example_target_mod
|
||||
kernel_module = my_complex_kernel_module_name
|
||||
|
||||
# The configfs group name. Defauklt would be "example"
|
||||
configfs_group = "example_group"
|
||||
|
32
specs/ib_srpt.spec
Normal file
32
specs/ib_srpt.spec
Normal file
@@ -0,0 +1,32 @@
|
||||
# The ib_srpt fabric module specfile.
|
||||
#
|
||||
# This file is part of RTSLib Community Edition.
|
||||
# Copyright (c) 2011 by RisingTide Systems LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, version 3 (AGPLv3).
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# The fabric module feature set
|
||||
features = acls
|
||||
|
||||
# Non-standard module naming scheme
|
||||
kernel_module = ib_srpt
|
||||
|
||||
# The module uses hardware addresses from there
|
||||
wwn_from_files = /sys/class/infiniband/*/ports/*/gids/0
|
||||
# Transform 'fe80:0000:0000:0000:0002:1903:000e:8acd' WWN notation to
|
||||
# '0x00000000000000000002c903000e8acd'
|
||||
wwn_from_files_filter = "sed -e s/fe80/0x0000/ -e 's/\://g'"
|
||||
|
||||
# The configfs group
|
||||
configfs_group = srpt
|
||||
|
29
specs/iscsi.spec
Normal file
29
specs/iscsi.spec
Normal file
@@ -0,0 +1,29 @@
|
||||
# The iscsi fabric module specfile.
|
||||
#
|
||||
# This file is part of RTSLib Community Edition.
|
||||
# Copyright (c) 2011 by RisingTide Systems LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, version 3 (AGPLv3).
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# The iscsi fabric module features set.
|
||||
features = discovery_auth, acls, acls_auth, nps, tpgts
|
||||
|
||||
# Obviously, this module uses IQN strings as WWNs.
|
||||
wwn_type = iqn
|
||||
|
||||
# This is default too
|
||||
# kernel_module = iscsi_target_mod
|
||||
|
||||
# The configfs group name, default too
|
||||
# configfs_group = iscsi
|
||||
|
28
specs/loopback.spec
Normal file
28
specs/loopback.spec
Normal file
@@ -0,0 +1,28 @@
|
||||
# The tcm_loop fabric module specfile.
|
||||
#
|
||||
# This file is part of RTSLib Community Edition.
|
||||
# Copyright (c) 2011 by RisingTide Systems LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, version 3 (AGPLv3).
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# The fabric module feature set
|
||||
features = nexus
|
||||
|
||||
# Use naa WWNs.
|
||||
wwn_type = naa
|
||||
|
||||
# Non-standard module naming scheme
|
||||
kernel_module = tcm_loop
|
||||
|
||||
# The configfs group
|
||||
configfs_group = loopback
|
28
specs/qla2xxx.spec
Normal file
28
specs/qla2xxx.spec
Normal file
@@ -0,0 +1,28 @@
|
||||
# The qla2xxx fabric module specfile.
|
||||
#
|
||||
# This file is part of RTSLib Community Edition.
|
||||
# Copyright (c) 2011 by RisingTide Systems LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, version 3 (AGPLv3).
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# The qla2xxx fabric module feature set
|
||||
features = acls
|
||||
|
||||
# Non-standard module naming scheme
|
||||
kernel_module = tcm_qla2xxx
|
||||
|
||||
# The module uses hardware addresses from there
|
||||
wwn_from_files = /sys/class/fc_host/host*/port_name
|
||||
|
||||
# Transform '0x1234567812345678' WWN notation to '12:34:56:78:12:34:56:78'
|
||||
wwn_from_files_filter = "sed -e s/0x// -e 's/../&:/g' -e s/:$//"
|
30
specs/tcm_fc.spec
Normal file
30
specs/tcm_fc.spec
Normal file
@@ -0,0 +1,30 @@
|
||||
# The tcm_fc fabric module specfile.
|
||||
#
|
||||
# This file is part of RTSLib Community Edition.
|
||||
# Copyright (c) 2011 by RisingTide Systems LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, version 3 (AGPLv3).
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# The fabric module feature set
|
||||
features = acls
|
||||
|
||||
# Non-standard module naming scheme
|
||||
kernel_module = tcm_fc
|
||||
|
||||
# The module uses hardware addresses from there
|
||||
wwn_from_files = /sys/class/fc_host/host*/port_name
|
||||
# Transform '0x1234567812345678' WWN notation to '12:34:56:78:12:34:56:78'
|
||||
wwn_from_files_filter = "sed -e s/0x// -e 's/../&:/g' -e s/:$//"
|
||||
|
||||
# The configfs group
|
||||
configfs_group = fc
|
Reference in New Issue
Block a user