diff --git a/.gitignore b/.gitignore deleted file mode 100644 index b0aa351c..00000000 --- a/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -.tox/ -cloudinit.egg-info/ -*.pyc -doc/build -doc/source/api/ -ChangeLog -AUTHORS -cover/ diff --git a/HACKING.rst b/HACKING.rst deleted file mode 100644 index def7bc22..00000000 --- a/HACKING.rst +++ /dev/null @@ -1,47 +0,0 @@ -===================== -Hacking on cloud-init -===================== - -To get changes into cloud-init, the process to follow is: - -* Fork from github, create a branch and make your changes - - - ``git clone https://github.com/openstack/cloud-init`` - - ``cd cloud-init`` - - ``echo hack`` - -* Check test and code formatting / lint and address any issues: - - - ``tox`` - -* Commit / ammend your changes (before review, make good commit messages with - one line summary followed by empty line followed by expanded comments). - - - ``git commit`` - -* Push to http://review.openstack.org: - - - ``git-review`` - -* Before your changes can be accepted, you must sign the `Canonical - Contributors License Agreement`_. Use 'Scott Moser' as the 'Project - contact'. To check to see if you've done this before, look for your - name in the `Canonical Contributor Agreement Team`_ on Launchpad. - -Then be patient and wait (or ping someone on cloud-init team). - -* `Core reviewers/maintainers`_ - -Remember the more you are involved in the project the more beneficial it is -for everyone involved (including yourself). - -**Contacting us:** - -Feel free to ping the folks listed above and/or join ``#cloud-init`` on -`freenode`_ (`IRC`_) if you have any questions. - -.. _Core reviewers/maintainers: https://review.openstack.org/#/admin/groups/665,members -.. _IRC: irc://chat.freenode.net/cloud-init -.. _freenode: http://freenode.net/ -.. _Canonical Contributors License Agreement: http://www.ubuntu.com/legal/contributors -.. _Canonical Contributor Agreement Team: https://launchpad.net/~contributor-agreement-canonical/+members#active diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 8a23b4d1..00000000 --- a/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -Copyright 2015 Canonical Ltd. - -This program is free software: you can redistribute it and/or modify it under -the terms of the GNU General Public License version 3, as published by the -Free Software Foundation. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, -SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public License along with -this program. If not, see - -Alternatively, this program may be used under the terms of the Apache License, -Version 2.0, in which case the provisions of that license are applicable -instead of those above. If you wish to allow use of your version of this -program under the terms of the Apache License, Version 2.0 only, indicate -your decision by deleting the provisions above and replace them with the notice -and other provisions required by the Apache License, Version 2.0. If you do not -delete the provisions above, a recipient may use your version of this file -under the terms of either the GPLv3 or the Apache License, Version 2.0. diff --git a/LICENSE-Apache2.0 b/LICENSE-Apache2.0 deleted file mode 100644 index d6456956..00000000 --- a/LICENSE-Apache2.0 +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/LICENSE-GPLv3 b/LICENSE-GPLv3 deleted file mode 100644 index 94a9ed02..00000000 --- a/LICENSE-GPLv3 +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is 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. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - 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. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - 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 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. Use with the GNU Affero General Public License. - - 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 Affero 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 special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU 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 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 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 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. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - 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 GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 23800499..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,8 +0,0 @@ -include AUTHORS -include ChangeLog -include README.rst -exclude .gitignore -exclude .gitreview - -global-exclude *.pyc - diff --git a/README b/README new file mode 100644 index 00000000..eb99bcd9 --- /dev/null +++ b/README @@ -0,0 +1,9 @@ +This project is no longer maintained. + +The contents of this repository are still available in the Git +source code management system. To see the contents of this +repository before it reached its end of life, please check out the +previous commit with "git checkout HEAD^1". + +For any further questions, please email +openstack-discuss@lists.openstack.org. diff --git a/README.rst b/README.rst deleted file mode 100644 index 59e6f766..00000000 --- a/README.rst +++ /dev/null @@ -1,50 +0,0 @@ -Cloud-init -========== - -*Cloud-init initializes systems for cloud environments.* - -Join us -------- - -- http://launchpad.net/cloud-init - - -Bugs ----- -Bug reports should be opened at - https://bugs.launchpad.net/cloud-init/+filebug - -On Ubuntu Systems, you can file bugs with: - -:: - - $ ubuntu-bug cloud-init - -Testing and requirements ------------------------- - -Requirements -~~~~~~~~~~~~ - -TBD - -Tox.ini -~~~~~~~ - -Our ``tox.ini`` file describes several test environments that allow to test -cloud-init with different python versions and sets of requirements installed. -Please refer to the `tox`_ documentation to understand how to make these test -environments work for you. - -Developer documentation ------------------------ - -We also have sphinx documentation in ``docs/source``. - -*To build it, run:* - -:: - - $ python setup.py build_sphinx - -.. _tox: http://tox.testrun.org/ diff --git a/cloudinit/__init__.py b/cloudinit/__init__.py deleted file mode 100644 index 20b32a23..00000000 --- a/cloudinit/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab diff --git a/cloudinit/config/__init__.py b/cloudinit/config/__init__.py deleted file mode 100644 index 20b32a23..00000000 --- a/cloudinit/config/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab diff --git a/cloudinit/exceptions.py b/cloudinit/exceptions.py deleted file mode 100644 index 677cd236..00000000 --- a/cloudinit/exceptions.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - - -class CloudInitError(Exception): - pass diff --git a/cloudinit/logging.py b/cloudinit/logging.py deleted file mode 100644 index a1e52840..00000000 --- a/cloudinit/logging.py +++ /dev/null @@ -1,113 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -from __future__ import absolute_import - -import logging -import sys - -_BASE = __name__.split(".", 1)[0] - -# Add a BLATHER level, this matches the multiprocessing utils.py module (and -# kazoo and others) that declares a similar level, this level is for -# information that is even lower level than regular DEBUG and gives out so -# much runtime information that it is only useful by low-level/certain users... -BLATHER = 5 - -# Copy over *select* attributes to make it easy to use this module. -CRITICAL = logging.CRITICAL -DEBUG = logging.DEBUG -ERROR = logging.ERROR -FATAL = logging.FATAL -INFO = logging.INFO -NOTSET = logging.NOTSET -WARN = logging.WARN -WARNING = logging.WARNING - - -class _BlatherLoggerAdapter(logging.LoggerAdapter): - - def blather(self, msg, *args, **kwargs): - """Delegate a blather call to the underlying logger.""" - self.log(BLATHER, msg, *args, **kwargs) - - def warn(self, msg, *args, **kwargs): - """Delegate a warning call to the underlying logger.""" - self.warning(msg, *args, **kwargs) - - -# TODO(harlowja): we should remove when we no longer have to support 2.6... -if sys.version_info[0:2] == (2, 6): # pragma: nocover - from logutils.dictconfig import dictConfig - - class _FixedBlatherLoggerAdapter(_BlatherLoggerAdapter): - """Ensures isEnabledFor() exists on adapters that are created.""" - - def isEnabledFor(self, level): - return self.logger.isEnabledFor(level) - - _BlatherLoggerAdapter = _FixedBlatherLoggerAdapter - - # Taken from python2.7 (same in python3.4)... - class _NullHandler(logging.Handler): - """This handler does nothing. - - It's intended to be used to avoid the - "No handlers could be found for logger XXX" one-off warning. This is - important for library code, which may contain code to log events. If a - user of the library does not configure logging, the one-off warning - might be produced; to avoid this, the library developer simply needs - to instantiate a _NullHandler and add it to the top-level logger of the - library module or package. - """ - - def handle(self, record): - """Stub.""" - - def emit(self, record): - """Stub.""" - - def createLock(self): - self.lock = None - -else: - from logging.config import dictConfig - _NullHandler = logging.NullHandler - - -def getLogger(name=_BASE, extra=None): - logger = logging.getLogger(name) - if not logger.handlers: - logger.addHandler(_NullHandler()) - return _BlatherLoggerAdapter(logger, extra=extra) - - -def configure_logging(log_to_console=False): - logging_config = { - 'version': 1, - 'disable_existing_loggers': False, - 'formatters': { - 'standard': { - 'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s', - }, - }, - 'handlers': { - 'console': { - 'level': 'INFO', - 'class': 'logging.StreamHandler', - 'formatter': 'standard', - }, - }, - 'loggers': { - '': { - 'handlers': [], - 'level': 'DEBUG', - 'propagate': True, - }, - }, - } - if log_to_console: - logging_config['loggers']['']['handlers'].append('console') - dictConfig(logging_config) diff --git a/cloudinit/osys/__init__.py b/cloudinit/osys/__init__.py deleted file mode 100644 index 20b32a23..00000000 --- a/cloudinit/osys/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab diff --git a/cloudinit/osys/base.py b/cloudinit/osys/base.py deleted file mode 100644 index 0a468d89..00000000 --- a/cloudinit/osys/base.py +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright (C) 2015 Canonical Ltd. -# Copyright 2015 Cloudbase Solutions Srl -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import abc -import importlib -import platform - -import six - - -__all__ = ( - 'get_osutils', - 'OSUtils', -) - - -def get_osutils(): - """Obtain the OS utils object for the underlying platform.""" - name, _, _ = platform.linux_distribution() - if not name: - name = platform.system() - - name = name.lower() - location = "cloudinit.osys.{0}.base".format(name) - module = importlib.import_module(location) - return module.OSUtils - - -@six.add_metaclass(abc.ABCMeta) -class OSUtils(object): - """Base class for an OS utils namespace. - - This base class provides a couple of hooks which needs to be - implemented by subclasses, for each particular OS and distro. - """ - - name = None - - @abc.abstractproperty - def network(self): - """Get the network object for the underlying platform.""" - - @abc.abstractproperty - def filesystem(self): - """Get the filesystem object for the underlying platform.""" - - @abc.abstractproperty - def users(self): - """Get the users object for the underlying platform.""" - - @abc.abstractproperty - def general(self): - """Get the general object for the underlying platform.""" - - @abc.abstractproperty - def user_class(self): - """Get the user class specific to this operating system.""" - - @abc.abstractproperty - def route_class(self): - """Get the route class specific to this operating system.""" - - @abc.abstractproperty - def interface_class(self): - """Get the interface class specific to this operating system.""" diff --git a/cloudinit/osys/general.py b/cloudinit/osys/general.py deleted file mode 100644 index 8553ccb8..00000000 --- a/cloudinit/osys/general.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (C) 2015 Canonical Ltd. -# Copyright 2015 Cloudbase Solutions Srl -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import abc - -import six - - -@six.add_metaclass(abc.ABCMeta) -class General(object): - """Base class for the general namespace. - - This class should contain common functions between all OSes, - which can't be grouped in a domain-specific namespace. - """ - - @abc.abstractmethod - def set_timezone(self, timezone): - """Change the timezone for the underlying platform. - - The `timezone` parameter should be a TZID timezone format, - e.g. 'Africa/Mogadishu' - """ - - @abc.abstractmethod - def set_locale(self, locale): - """Change the locale for the underlying platform.""" - - @abc.abstractmethod - def reboot(self): - pass diff --git a/cloudinit/osys/network.py b/cloudinit/osys/network.py deleted file mode 100644 index ac357fef..00000000 --- a/cloudinit/osys/network.py +++ /dev/null @@ -1,156 +0,0 @@ -# Copyright (C) 2015 Canonical Ltd. -# Copyright 2015 Cloudbase Solutions Srl -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import abc - -import six - -from cloudinit import util - - -__all__ = ( - 'Network', - 'Route', - 'Interface', -) - - -@six.add_metaclass(abc.ABCMeta) -class Network(object): - """Base network class for network related utilities.""" - - @abc.abstractmethod - def routes(self): - """Get the list of the available routes.""" - - @abc.abstractmethod - def default_gateway(self): - """Get the default gateway, as a route object.""" - - @abc.abstractmethod - def interfaces(self): - """Get the list of the available interfaces.""" - - @abc.abstractmethod - def hosts(self): - """Get the list of the available hosts.""" - - @abc.abstractmethod - def set_hostname(self, hostname): - """Change the host name of the instance.""" - - @abc.abstractmethod - def set_static_network_config(self, adapter_name, address, netmask, - broadcast, gateway, dnsnameservers): - """Configure a new static network.""" - - -@six.add_metaclass(abc.ABCMeta) -class Route(object): - """Base class for routes.""" - - def __init__(self, destination, gateway, netmask, - interface, metric, - flags=None, refs=None, use=None, expire=None): - self.destination = destination - self.gateway = gateway - self.netmask = netmask - self.interface = interface - self.metric = metric - self.flags = flags - self.refs = refs - self.use = use - self.expire = expire - - def __repr__(self): - return ("Route(destination={!r}, gateway={!r}, netmask={!r})" - .format(self.destination, self.gateway, self.netmask)) - - @abc.abstractproperty - def is_static(self): - """Check if this route is static.""" - - @util.abstractclassmethod - def add(cls, route): - """Add a new route in the underlying OS. - - The `route` parameter should be an instance of :class:`Route`. - """ - - @util.abstractclassmethod - def delete(cls, route): - """Delete a route from the underlying OS. - - The `route` parameter should be an instance of :class:`Route`. - """ - - -@six.add_metaclass(abc.ABCMeta) -class Interface(object): - """Base class reprensenting an interface. - - It provides both attributes for retrieving interface information, - as well as methods for modifying the state of a route, such - as activating or deactivating it. - """ - - def __init__(self, name, mac, index=None, mtu=None, - dhcp_server=None, dhcp_enabled=None): - self._mtu = mtu - - self.name = name - self.index = index - self.mac = mac - self.dhcp_server = dhcp_server - self.dhcp_enabled = dhcp_enabled - - def __eq__(self, other): - return (self.mac == other.mac and - self.name == other.name and - self.index == other.index) - - @abc.abstractmethod - def _change_mtu(self, value): - """Change the mtu for the underlying interface.""" - - @util.abstractclassmethod - def from_name(cls, name): - """Get an instance of :class:`Interface` from an interface name. - - E.g. this should retrieve the 'eth0' interface:: - - >>> Interface.from_name('eth0') - """ - - @abc.abstractmethod - def up(self): - """Activate the current interface.""" - - @abc.abstractmethod - def down(self): - """Deactivate the current interface.""" - - @abc.abstractmethod - def is_up(self): - """Check if this interface is activated.""" - - @property - def mtu(self): - return self._mtu - - @mtu.setter - def mtu(self, value): - self._change_mtu(value) - self._mtu = value diff --git a/cloudinit/osys/users.py b/cloudinit/osys/users.py deleted file mode 100644 index 24d90ab7..00000000 --- a/cloudinit/osys/users.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (C) 2015 Canonical Ltd. -# Copyright 2015 Cloudbase Solutions Srl -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import abc - -import six - -from cloudinit import util - - -@six.add_metaclass(abc.ABCMeta) -class Users(object): - """Base class for user related operations.""" - - @abc.abstractmethod - def groups(self): - """Get a list of the groups available in the system.""" - - @abc.abstractmethod - def users(self): - """Get a list of the users available in the system.""" - - -@six.add_metaclass(abc.ABCMeta) -class Group(object): - """Base class for user groups.""" - - @util.abstractclassmethod - def create(cls, group_name): - """Create a new group with the given name.""" - - @abc.abstractmethod - def add(self, member): - """Add a new member to this group.""" - - -@six.add_metaclass(abc.ABCMeta) -class User(object): - """Base class for an user.""" - - @classmethod - def create(cls, username, password, **kwargs): - """Create a new user.""" - - @abc.abstractmethod - def home(self): - """Get the user's home directory.""" - - @abc.abstractmethod - def ssh_keys(self): - """Get the ssh keys for this user.""" - - @abc.abstractmethod - def change_password(self, password): - """Change the password for this user.""" diff --git a/cloudinit/osys/windows/__init__.py b/cloudinit/osys/windows/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/cloudinit/osys/windows/base.py b/cloudinit/osys/windows/base.py deleted file mode 100644 index 65ea3ace..00000000 --- a/cloudinit/osys/windows/base.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -from cloudinit.osys import base -from cloudinit.osys.windows import general as general_module -from cloudinit.osys.windows import network as network_module - - -__all__ = ('OSUtils', ) - - -class OSUtils(base.OSUtils): - """The OS utils namespace for the Windows platform.""" - - name = "windows" - - network = network_module.Network() - general = general_module.General() - route_class = network_module.Route - - # These aren't yet implemented, use `None` for them - # so that we could instantiate the class. - filesystem = user_class = users = None - interface_class = None diff --git a/cloudinit/osys/windows/general.py b/cloudinit/osys/windows/general.py deleted file mode 100644 index db92049c..00000000 --- a/cloudinit/osys/windows/general.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -"""General utilities for Windows platform.""" - -import ctypes - -from cloudinit import exceptions -from cloudinit.osys import general -from cloudinit.osys.windows.util import kernel32 - - -class General(general.General): - """General utilities namespace for Windows.""" - - @staticmethod - def check_os_version(major, minor, build=0): - """Check if this OS version is equal or higher than (major, minor)""" - - version_info = kernel32.Win32_OSVERSIONINFOEX_W() - version_info.dwOSVersionInfoSize = ctypes.sizeof( - kernel32.Win32_OSVERSIONINFOEX_W) - - version_info.dwMajorVersion = major - version_info.dwMinorVersion = minor - version_info.dwBuildNumber = build - - mask = 0 - for type_mask in [kernel32.VER_MAJORVERSION, - kernel32.VER_MINORVERSION, - kernel32.VER_BUILDNUMBER]: - mask = kernel32.VerSetConditionMask(mask, type_mask, - kernel32.VER_GREATER_EQUAL) - - type_mask = (kernel32.VER_MAJORVERSION | - kernel32.VER_MINORVERSION | - kernel32.VER_BUILDNUMBER) - ret_val = kernel32.VerifyVersionInfoW(ctypes.byref(version_info), - type_mask, mask) - if ret_val: - return True - else: - err = kernel32.GetLastError() - if err == kernel32.ERROR_OLD_WIN_VERSION: - return False - else: - raise exceptions.CloudInitError( - "VerifyVersionInfo failed with error: %s" % err) - - def reboot(self): - raise NotImplementedError - - def set_locale(self, locale): - raise NotImplementedError - - def set_timezone(self, timezone): - raise NotImplementedError diff --git a/cloudinit/osys/windows/network.py b/cloudinit/osys/windows/network.py deleted file mode 100644 index 43ac1f25..00000000 --- a/cloudinit/osys/windows/network.py +++ /dev/null @@ -1,209 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -"""Network utilities for Windows.""" - -import contextlib -import ctypes -from ctypes import wintypes -import logging -import subprocess - -from six.moves import urllib_parse - -from cloudinit import exceptions -from cloudinit.osys import base -from cloudinit.osys import network -from cloudinit.osys.windows.util import iphlpapi -from cloudinit.osys.windows.util import kernel32 -from cloudinit.osys.windows.util import ws2_32 -from cloudinit import url_helper - - -MIB_IPPROTO_NETMGMT = 3 -_FW_IP_PROTOCOL_TCP = 6 -_FW_IP_PROTOCOL_UDP = 17 -_FW_SCOPE_ALL = 0 -_PROTOCOL_TCP = "TCP" -_PROTOCOL_UDP = "UDP" -_ERROR_FILE_NOT_FOUND = 2 -_ComputerNamePhysicalDnsHostname = 5 -_MAX_URL_CHECK_RETRIES = 3 -LOG = logging.getLogger(__name__) - - -def _heap_alloc(heap, size): - table_mem = kernel32.HeapAlloc(heap, 0, ctypes.c_size_t(size.value)) - if not table_mem: - raise exceptions.CloudInitError( - 'Unable to allocate memory for the IP forward table') - return table_mem - - -def _check_url(url, retries_count=_MAX_URL_CHECK_RETRIES): - LOG.debug("Testing url: %s", url) - try: - url_helper.read_url(url, retries=retries_count) - return True - except url_helper.UrlError: - return False - - -class Network(network.Network): - """Network namespace object tailored for the Windows platform.""" - - @staticmethod - @contextlib.contextmanager - def _get_forward_table(): - heap = kernel32.GetProcessHeap() - forward_table_size = ctypes.sizeof(iphlpapi.Win32_MIB_IPFORWARDTABLE) - size = wintypes.ULONG(forward_table_size) - table_mem = _heap_alloc(heap, size) - - p_forward_table = ctypes.cast( - table_mem, ctypes.POINTER(iphlpapi.Win32_MIB_IPFORWARDTABLE)) - - try: - err = iphlpapi.GetIpForwardTable(p_forward_table, - ctypes.byref(size), 0) - if err == iphlpapi.ERROR_INSUFFICIENT_BUFFER: - kernel32.HeapFree(heap, 0, p_forward_table) - table_mem = _heap_alloc(heap, size) - p_forward_table = ctypes.cast( - table_mem, - ctypes.POINTER(iphlpapi.Win32_MIB_IPFORWARDTABLE)) - err = iphlpapi.GetIpForwardTable(p_forward_table, - ctypes.byref(size), 0) - - if err and err != kernel32.ERROR_NO_DATA: - raise exceptions.CloudInitError( - 'Unable to get IP forward table. Error: %s' % err) - - yield p_forward_table - finally: - kernel32.HeapFree(heap, 0, p_forward_table) - - def routes(self): - """Get a collection of the available routes.""" - routing_table = [] - with self._get_forward_table() as p_forward_table: - forward_table = p_forward_table.contents - table = ctypes.cast( - ctypes.addressof(forward_table.table), - ctypes.POINTER(iphlpapi.Win32_MIB_IPFORWARDROW * - forward_table.dwNumEntries)).contents - - for row in table: - destination = ws2_32.Ws2_32.inet_ntoa( - row.dwForwardDest).decode() - netmask = ws2_32.Ws2_32.inet_ntoa( - row.dwForwardMask).decode() - gateway = ws2_32.Ws2_32.inet_ntoa( - row.dwForwardNextHop).decode() - index = row.dwForwardIfIndex - flags = row.dwForwardProto - metric = row.dwForwardMetric1 - route = Route(destination=destination, - gateway=gateway, - netmask=netmask, - interface=index, - metric=metric, - flags=flags) - routing_table.append(route) - - return routing_table - - def default_gateway(self): - """Get the default gateway. - - This will actually return a :class:`Route` instance. The gateway - can be accessed with the :attr:`gateway` attribute. - """ - return next((r for r in self.routes() if r.destination == '0.0.0.0'), - None) - - def set_metadata_ip_route(self, metadata_url): - """Set a network route if the given metadata url can't be accessed. - - This is a workaround for - https://bugs.launchpad.net/quantum/+bug/1174657. - """ - osutils = base.get_osutils() - - if osutils.general.check_os_version(6, 0): - # 169.254.x.x addresses are not getting routed starting from - # Windows Vista / 2008 - metadata_netloc = urllib_parse.urlparse(metadata_url).netloc - metadata_host = metadata_netloc.split(':')[0] - - if not metadata_host.startswith("169.254."): - return - - routes = self.routes() - exists_route = any(route.destination == metadata_host - for route in routes) - if not exists_route and not _check_url(metadata_url): - default_gateway = self.default_gateway() - if default_gateway: - try: - LOG.debug('Setting gateway for host: %s', - metadata_host) - route = Route( - destination=metadata_host, - netmask="255.255.255.255", - gateway=default_gateway.gateway, - interface=None, metric=None) - Route.add(route) - except Exception as ex: - # Ignore it - LOG.exception(ex) - - # These are not required by the Windows version for now, - # but we provide them as noop version. - def hosts(self): - """Grab the content of the hosts file.""" - raise NotImplementedError - - def interfaces(self): - raise NotImplementedError - - def set_hostname(self, hostname): - raise NotImplementedError - - def set_static_network_config(self, adapter_name, address, netmask, - broadcast, gateway, dnsnameservers): - raise NotImplementedError - - -class Route(network.Route): - """Windows route class.""" - - @property - def is_static(self): - return self.flags == MIB_IPPROTO_NETMGMT - - @classmethod - def add(cls, route): - """Add a new route in the underlying OS. - - The function should expect an instance of :class:`Route`. - """ - args = ['ROUTE', 'ADD', - route.destination, - 'MASK', route.netmask, route.gateway] - popen = subprocess.Popen(args, shell=False, - stderr=subprocess.PIPE) - _, stderr = popen.communicate() - if popen.returncode or stderr: - # Cannot use the return value to determine the outcome - raise exceptions.CloudInitError('Unable to add route: %s' % stderr) - - @classmethod - def delete(cls, _): - """Delete a route from the underlying OS. - - This function should expect an instance of :class:`Route`. - """ - raise NotImplementedError diff --git a/cloudinit/osys/windows/util/__init__.py b/cloudinit/osys/windows/util/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/cloudinit/osys/windows/util/iphlpapi.py b/cloudinit/osys/windows/util/iphlpapi.py deleted file mode 100644 index e1301860..00000000 --- a/cloudinit/osys/windows/util/iphlpapi.py +++ /dev/null @@ -1,210 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -import ctypes - -from ctypes import windll -from ctypes import wintypes - -from cloudinit.osys.windows.util import kernel32 -from cloudinit.osys.windows.util import ws2_32 - -ERROR_INSUFFICIENT_BUFFER = 122 - -MAX_ADAPTER_NAME_LENGTH = 256 -MAX_ADAPTER_DESCRIPTION_LENGTH = 128 -MAX_ADAPTER_ADDRESS_LENGTH = 8 - -# Do not return IPv6 anycast addresses. -GAA_FLAG_SKIP_ANYCAST = 2 -GAA_FLAG_SKIP_MULTICAST = 4 - -IP_ADAPTER_DHCP_ENABLED = 4 -IP_ADAPTER_IPV4_ENABLED = 0x80 -IP_ADAPTER_IPV6_ENABLED = 0x0100 - -MAX_DHCPV6_DUID_LENGTH = 130 - -IF_TYPE_ETHERNET_CSMACD = 6 -IF_TYPE_SOFTWARE_LOOPBACK = 24 -IF_TYPE_IEEE80211 = 71 -IF_TYPE_TUNNEL = 131 - -IP_ADAPTER_ADDRESSES_SIZE_2003 = 144 - - -class SOCKET_ADDRESS(ctypes.Structure): - _fields_ = [ - ('lpSockaddr', ctypes.POINTER(ws2_32.SOCKADDR)), - ('iSockaddrLength', wintypes.INT), - ] - - -class IP_ADAPTER_ADDRESSES_Struct1(ctypes.Structure): - _fields_ = [ - ('Length', wintypes.ULONG), - ('IfIndex', wintypes.DWORD), - ] - - -class IP_ADAPTER_ADDRESSES_Union1(ctypes.Union): - _fields_ = [ - ('Alignment', wintypes.ULARGE_INTEGER), - ('Struct1', IP_ADAPTER_ADDRESSES_Struct1), - ] - - -class IP_ADAPTER_UNICAST_ADDRESS(ctypes.Structure): - _fields_ = [ - ('Union1', IP_ADAPTER_ADDRESSES_Union1), - ('Next', wintypes.LPVOID), - ('Address', SOCKET_ADDRESS), - ('PrefixOrigin', wintypes.DWORD), - ('SuffixOrigin', wintypes.DWORD), - ('DadState', wintypes.DWORD), - ('ValidLifetime', wintypes.ULONG), - ('PreferredLifetime', wintypes.ULONG), - ('LeaseLifetime', wintypes.ULONG), - ] - - -class IP_ADAPTER_DNS_SERVER_ADDRESS_Struct1(ctypes.Structure): - _fields_ = [ - ('Length', wintypes.ULONG), - ('Reserved', wintypes.DWORD), - ] - - -class IP_ADAPTER_DNS_SERVER_ADDRESS_Union1(ctypes.Union): - _fields_ = [ - ('Alignment', wintypes.ULARGE_INTEGER), - ('Struct1', IP_ADAPTER_DNS_SERVER_ADDRESS_Struct1), - ] - - -class IP_ADAPTER_DNS_SERVER_ADDRESS(ctypes.Structure): - _fields_ = [ - ('Union1', IP_ADAPTER_DNS_SERVER_ADDRESS_Union1), - ('Next', wintypes.LPVOID), - ('Address', SOCKET_ADDRESS), - ] - - -class IP_ADAPTER_PREFIX_Struct1(ctypes.Structure): - _fields_ = [ - ('Length', wintypes.ULONG), - ('Flags', wintypes.DWORD), - ] - - -class IP_ADAPTER_PREFIX_Union1(ctypes.Union): - _fields_ = [ - ('Alignment', wintypes.ULARGE_INTEGER), - ('Struct1', IP_ADAPTER_PREFIX_Struct1), - ] - - -class IP_ADAPTER_PREFIX(ctypes.Structure): - _fields_ = [ - ('Union1', IP_ADAPTER_PREFIX_Union1), - ('Next', wintypes.LPVOID), - ('Address', SOCKET_ADDRESS), - ('PrefixLength', wintypes.ULONG), - ] - - -class NET_LUID_LH(ctypes.Union): - _fields_ = [ - ('Value', wintypes.ULARGE_INTEGER), - ('Info', wintypes.ULARGE_INTEGER), - ] - - -class IP_ADAPTER_ADDRESSES(ctypes.Structure): - _fields_ = [ - ('Union1', IP_ADAPTER_ADDRESSES_Union1), - ('Next', wintypes.LPVOID), - ('AdapterName', ctypes.c_char_p), - ('FirstUnicastAddress', - ctypes.POINTER(IP_ADAPTER_UNICAST_ADDRESS)), - ('FirstAnycastAddress', - ctypes.POINTER(IP_ADAPTER_DNS_SERVER_ADDRESS)), - ('FirstMulticastAddress', - ctypes.POINTER(IP_ADAPTER_DNS_SERVER_ADDRESS)), - ('FirstDnsServerAddress', - ctypes.POINTER(IP_ADAPTER_DNS_SERVER_ADDRESS)), - ('DnsSuffix', wintypes.LPWSTR), - ('Description', wintypes.LPWSTR), - ('FriendlyName', wintypes.LPWSTR), - ('PhysicalAddress', ctypes.c_ubyte * MAX_ADAPTER_ADDRESS_LENGTH), - ('PhysicalAddressLength', wintypes.DWORD), - ('Flags', wintypes.DWORD), - ('Mtu', wintypes.DWORD), - ('IfType', wintypes.DWORD), - ('OperStatus', wintypes.DWORD), - ('Ipv6IfIndex', wintypes.DWORD), - ('ZoneIndices', wintypes.DWORD * 16), - ('FirstPrefix', ctypes.POINTER(IP_ADAPTER_PREFIX)), - # kernel >= 6.0 - ('TransmitLinkSpeed', wintypes.ULARGE_INTEGER), - ('ReceiveLinkSpeed', wintypes.ULARGE_INTEGER), - ('FirstWinsServerAddress', - ctypes.POINTER(IP_ADAPTER_DNS_SERVER_ADDRESS)), - ('FirstGatewayAddress', - ctypes.POINTER(IP_ADAPTER_DNS_SERVER_ADDRESS)), - ('Ipv4Metric', wintypes.ULONG), - ('Ipv6Metric', wintypes.ULONG), - ('Luid', NET_LUID_LH), - ('Dhcpv4Server', SOCKET_ADDRESS), - ('CompartmentId', wintypes.DWORD), - ('NetworkGuid', kernel32.GUID), - ('ConnectionType', wintypes.DWORD), - ('TunnelType', wintypes.DWORD), - ('Dhcpv6Server', SOCKET_ADDRESS), - ('Dhcpv6ClientDuid', ctypes.c_ubyte * MAX_DHCPV6_DUID_LENGTH), - ('Dhcpv6ClientDuidLength', wintypes.ULONG), - ('Dhcpv6Iaid', wintypes.ULONG), - ] - - -class Win32_MIB_IPFORWARDROW(ctypes.Structure): - _fields_ = [ - ('dwForwardDest', wintypes.DWORD), - ('dwForwardMask', wintypes.DWORD), - ('dwForwardPolicy', wintypes.DWORD), - ('dwForwardNextHop', wintypes.DWORD), - ('dwForwardIfIndex', wintypes.DWORD), - ('dwForwardType', wintypes.DWORD), - ('dwForwardProto', wintypes.DWORD), - ('dwForwardAge', wintypes.DWORD), - ('dwForwardNextHopAS', wintypes.DWORD), - ('dwForwardMetric1', wintypes.DWORD), - ('dwForwardMetric2', wintypes.DWORD), - ('dwForwardMetric3', wintypes.DWORD), - ('dwForwardMetric4', wintypes.DWORD), - ('dwForwardMetric5', wintypes.DWORD) - ] - - -class Win32_MIB_IPFORWARDTABLE(ctypes.Structure): - _fields_ = [ - ('dwNumEntries', wintypes.DWORD), - ('table', Win32_MIB_IPFORWARDROW * 1) - ] - - -GetAdaptersAddresses = windll.Iphlpapi.GetAdaptersAddresses -GetAdaptersAddresses.argtypes = [ - wintypes.ULONG, wintypes.ULONG, wintypes.LPVOID, - ctypes.POINTER(IP_ADAPTER_ADDRESSES), - ctypes.POINTER(wintypes.ULONG)] -GetAdaptersAddresses.restype = wintypes.ULONG - -GetIpForwardTable = windll.Iphlpapi.GetIpForwardTable -GetIpForwardTable.argtypes = [ - ctypes.POINTER(Win32_MIB_IPFORWARDTABLE), - ctypes.POINTER(wintypes.ULONG), - wintypes.BOOL] -GetIpForwardTable.restype = wintypes.DWORD diff --git a/cloudinit/osys/windows/util/kernel32.py b/cloudinit/osys/windows/util/kernel32.py deleted file mode 100644 index c03b9e87..00000000 --- a/cloudinit/osys/windows/util/kernel32.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -import ctypes -from ctypes import windll -from ctypes import wintypes - -ERROR_BUFFER_OVERFLOW = 111 -ERROR_NO_DATA = 232 - - -class GUID(ctypes.Structure): - _fields_ = [ - ("data1", wintypes.DWORD), - ("data2", wintypes.WORD), - ("data3", wintypes.WORD), - ("data4", wintypes.BYTE * 8)] - - def __init__(self, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8): - self.data1 = l - self.data2 = w1 - self.data3 = w2 - self.data4[0] = b1 - self.data4[1] = b2 - self.data4[2] = b3 - self.data4[3] = b4 - self.data4[4] = b5 - self.data4[5] = b6 - self.data4[6] = b7 - self.data4[7] = b8 - - -class Win32_OSVERSIONINFOEX_W(ctypes.Structure): - _fields_ = [ - ('dwOSVersionInfoSize', wintypes.DWORD), - ('dwMajorVersion', wintypes.DWORD), - ('dwMinorVersion', wintypes.DWORD), - ('dwBuildNumber', wintypes.DWORD), - ('dwPlatformId', wintypes.DWORD), - ('szCSDVersion', wintypes.WCHAR * 128), - ('wServicePackMajor', wintypes.DWORD), - ('wServicePackMinor', wintypes.DWORD), - ('wSuiteMask', wintypes.DWORD), - ('wProductType', wintypes.BYTE), - ('wReserved', wintypes.BYTE) - ] - - -GetLastError = windll.kernel32.GetLastError - -GetProcessHeap = windll.kernel32.GetProcessHeap -GetProcessHeap.argtypes = [] -GetProcessHeap.restype = wintypes.HANDLE - -HeapAlloc = windll.kernel32.HeapAlloc -# Note: wintypes.ULONG must be replaced with a 64 bit variable on x64 -HeapAlloc.argtypes = [wintypes.HANDLE, wintypes.DWORD, wintypes.ULONG] -HeapAlloc.restype = wintypes.LPVOID - -HeapFree = windll.kernel32.HeapFree -HeapFree.argtypes = [wintypes.HANDLE, wintypes.DWORD, wintypes.LPVOID] -HeapFree.restype = wintypes.BOOL - -SetComputerNameExW = windll.kernel32.SetComputerNameExW - -VerifyVersionInfoW = windll.kernel32.VerifyVersionInfoW -VerSetConditionMask = windll.kernel32.VerSetConditionMask - -VerifyVersionInfoW.argtypes = [ - ctypes.POINTER(Win32_OSVERSIONINFOEX_W), - wintypes.DWORD, wintypes.ULARGE_INTEGER] -VerifyVersionInfoW.restype = wintypes.BOOL - -VerSetConditionMask.argtypes = [wintypes.ULARGE_INTEGER, - wintypes.DWORD, - wintypes.BYTE] -VerSetConditionMask.restype = wintypes.ULARGE_INTEGER - -ERROR_OLD_WIN_VERSION = 1150 -VER_MAJORVERSION = 1 -VER_MINORVERSION = 2 -VER_BUILDNUMBER = 4 -VER_GREATER_EQUAL = 3 diff --git a/cloudinit/osys/windows/util/ws2_32.py b/cloudinit/osys/windows/util/ws2_32.py deleted file mode 100644 index 890e1cd6..00000000 --- a/cloudinit/osys/windows/util/ws2_32.py +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -import ctypes -from ctypes import windll -from ctypes import wintypes - -AF_UNSPEC = 0 -AF_INET = 2 -AF_INET6 = 23 - -VERSION_2_2 = (2 << 8) + 2 - - -class SOCKADDR(ctypes.Structure): - _fields_ = [ - ('sa_family', wintypes.USHORT), - ('sa_data', ctypes.c_char * 14), - ] - - -class WSADATA(ctypes.Structure): - _fields_ = [ - ('opaque_data', wintypes.BYTE * 400), - ] - - -WSAGetLastError = windll.Ws2_32.WSAGetLastError -WSAGetLastError.argtypes = [] -WSAGetLastError.restype = wintypes.INT - -WSAStartup = windll.Ws2_32.WSAStartup -WSAStartup.argtypes = [wintypes.WORD, ctypes.POINTER(WSADATA)] -WSAStartup.restype = wintypes.INT - -WSACleanup = windll.Ws2_32.WSACleanup -WSACleanup.argtypes = [] -WSACleanup.restype = wintypes.INT - -WSAAddressToStringW = windll.Ws2_32.WSAAddressToStringW -WSAAddressToStringW.argtypes = [ - ctypes.POINTER(SOCKADDR), wintypes.DWORD, wintypes.LPVOID, - wintypes.LPWSTR, ctypes.POINTER(wintypes.DWORD)] -WSAAddressToStringW.restype = wintypes.INT - -Ws2_32 = windll.Ws2_32 -Ws2_32.inet_ntoa.restype = ctypes.c_char_p - - -def init_wsa(version=VERSION_2_2): - wsadata = WSADATA() - WSAStartup(version, ctypes.byref(wsadata)) diff --git a/cloudinit/plugin_finder.py b/cloudinit/plugin_finder.py deleted file mode 100644 index 862da66a..00000000 --- a/cloudinit/plugin_finder.py +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -"""Various base classes and implementations for finding *plugins*.""" - -import abc -import pkgutil - -import six - -from cloudinit import logging - - -LOG = logging.getLogger(__name__) - - -@six.add_metaclass(abc.ABCMeta) -class BaseModuleIterator(object): - """Base class for describing a *module iterator* - - A module iterator is a class that's capable of listing - modules or packages from a specific location, which are - already loaded. - """ - - def __init__(self, search_paths): - self._search_paths = search_paths - - @abc.abstractmethod - def list_modules(self): - """List all the modules that this finder knows about.""" - - -class PkgutilModuleIterator(BaseModuleIterator): - """A class based on the *pkgutil* module for discovering modules.""" - - @staticmethod - def _find_module(finder, module): - """Delegate to the *finder* for finding the given module.""" - return finder.find_module(module).load_module(module) - - def list_modules(self): - """List all modules that this class knows about.""" - for finder, name, _ in pkgutil.walk_packages(self._search_paths): - try: - module = self._find_module(finder, name) - except ImportError: - LOG.debug('Could not import the module %r using the ' - 'search path %r', name, finder.path) - continue - - yield module diff --git a/cloudinit/registry.py b/cloudinit/registry.py deleted file mode 100644 index 04368ddf..00000000 --- a/cloudinit/registry.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab -import copy - - -class DictRegistry(object): - """A simple registry for a mapping of objects.""" - - def __init__(self): - self.reset() - - def reset(self): - self._items = {} - - def register_item(self, key, item): - """Add item to the registry.""" - if key in self._items: - raise ValueError( - 'Item already registered with key {0}'.format(key)) - self._items[key] = item - - def unregister_item(self, key, force=True): - """Remove item from the registry.""" - if key in self._items: - del self._items[key] - elif not force: - raise KeyError("%s: key not present to unregister" % key) - - @property - def registered_items(self): - """All the items that have been registered. - - This cannot be used to modify the contents of the registry. - """ - return copy.copy(self._items) diff --git a/cloudinit/reporting/__init__.py b/cloudinit/reporting/__init__.py deleted file mode 100644 index d0bc14e3..00000000 --- a/cloudinit/reporting/__init__.py +++ /dev/null @@ -1,238 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab -""" -cloud-init reporting framework - -The reporting framework is intended to allow all parts of cloud-init to -report events in a structured manner. -""" - -from cloudinit.registry import DictRegistry -from cloudinit.reporting.handlers import available_handlers - - -FINISH_EVENT_TYPE = 'finish' -START_EVENT_TYPE = 'start' - -DEFAULT_CONFIG = { - 'logging': {'type': 'log'}, -} - - -class _nameset(set): - def __getattr__(self, name): - if name in self: - return name - raise AttributeError("%s not a valid value" % name) - - -status = _nameset(("SUCCESS", "WARN", "FAIL")) - - -class ReportingEvent(object): - """Encapsulation of event formatting.""" - - def __init__(self, event_type, name, description): - self.event_type = event_type - self.name = name - self.description = description - - def as_string(self): - """The event represented as a string.""" - return '{0}: {1}: {2}'.format( - self.event_type, self.name, self.description) - - def as_dict(self): - """The event represented as a dictionary.""" - return {'name': self.name, 'description': self.description, - 'event_type': self.event_type} - - -class FinishReportingEvent(ReportingEvent): - - def __init__(self, name, description, result=status.SUCCESS): - super(FinishReportingEvent, self).__init__( - FINISH_EVENT_TYPE, name, description) - self.result = result - if result not in status: - raise ValueError("Invalid result: %s" % result) - - def as_string(self): - return '{0}: {1}: {2}: {3}'.format( - self.event_type, self.name, self.result, self.description) - - def as_dict(self): - """The event represented as json friendly.""" - data = super(FinishReportingEvent, self).as_dict() - data['result'] = self.result - return data - - -def update_configuration(config): - """Update the instanciated_handler_registry. - - :param config: - The dictionary containing changes to apply. If a key is given - with a False-ish value, the registered handler matching that name - will be unregistered. - """ - for handler_name, handler_config in config.items(): - if not handler_config: - instantiated_handler_registry.unregister_item( - handler_name, force=True) - continue - handler_config = handler_config.copy() - cls = available_handlers.registered_items[handler_config.pop('type')] - instance = cls(**handler_config) - instantiated_handler_registry.register_item(handler_name, instance) - - -def report_event(event): - """Report an event to all registered event handlers. - - This should generally be called via one of the other functions in - the reporting module. - - :param event_type: - The type of the event; this should be a constant from the - reporting module. - """ - for _, handler in instantiated_handler_registry.registered_items.items(): - handler.publish_event(event) - - -def report_finish_event(event_name, event_description, - result=status.SUCCESS): - """Report a "finish" event. - - See :py:func:`.report_event` for parameter details. - """ - event = FinishReportingEvent(event_name, event_description, result) - return report_event(event) - - -def report_start_event(event_name, event_description): - """Report a "start" event. - - :param event_name: - The name of the event; this should be a topic which events would - share (e.g. it will be the same for start and finish events). - - :param event_description: - A human-readable description of the event that has occurred. - """ - event = ReportingEvent(START_EVENT_TYPE, event_name, event_description) - return report_event(event) - - -class ReportEventStack(object): - """Context Manager for using :py:func:`report_event` - - This enables calling :py:func:`report_start_event` and - :py:func:`report_finish_event` through a context manager. - - :param name: - the name of the event - - :param description: - the event's description, passed on to :py:func:`report_start_event` - - :param message: - the description to use for the finish event. defaults to - :param:description. - - :param parent: - :type parent: :py:class:ReportEventStack or None - The parent of this event. The parent is populated with - results of all its children. The name used in reporting - is / - - :param reporting_enabled: - Indicates if reporting events should be generated. - If not provided, defaults to the parent's value, or True if no parent - is provided. - - :param result_on_exception: - The result value to set if an exception is caught. default - value is FAIL. - """ - def __init__(self, name, description, message=None, parent=None, - reporting_enabled=None, result_on_exception=status.FAIL): - self.parent = parent - self.name = name - self.description = description - self.message = message - self.result_on_exception = result_on_exception - self.result = status.SUCCESS - - # use parents reporting value if not provided - if reporting_enabled is None: - if parent: - reporting_enabled = parent.reporting_enabled - else: - reporting_enabled = True - self.reporting_enabled = reporting_enabled - - if parent: - self.fullname = '/'.join((parent.fullname, name,)) - else: - self.fullname = self.name - self.children = {} - - def __repr__(self): - return ("ReportEventStack(%s, %s, reporting_enabled=%s)" % - (self.name, self.description, self.reporting_enabled)) - - def __enter__(self): - self.result = status.SUCCESS - if self.reporting_enabled: - report_start_event(self.fullname, self.description) - if self.parent: - self.parent.children[self.name] = (None, None) - return self - - def _childrens_finish_info(self): - for cand_result in (status.FAIL, status.WARN): - for name, (value, msg) in self.children.items(): - if value == cand_result: - return (value, self.message) - return (self.result, self.message) - - @property - def result(self): - return self._result - - @result.setter - def result(self, value): - if value not in status: - raise ValueError("'%s' not a valid result" % value) - self._result = value - - @property - def message(self): - if self._message is not None: - return self._message - return self.description - - @message.setter - def message(self, value): - self._message = value - - def _finish_info(self, exc): - # return tuple of description, and value - if exc: - return (self.result_on_exception, self.message) - return self._childrens_finish_info() - - def __exit__(self, exc_type, exc_value, traceback): - (result, msg) = self._finish_info(exc_value) - if self.parent: - self.parent.children[self.name] = (result, msg) - if self.reporting_enabled: - report_finish_event(self.fullname, msg, result) - - -instantiated_handler_registry = DictRegistry() -update_configuration(DEFAULT_CONFIG) diff --git a/cloudinit/reporting/handlers.py b/cloudinit/reporting/handlers.py deleted file mode 100644 index 24734b4e..00000000 --- a/cloudinit/reporting/handlers.py +++ /dev/null @@ -1,33 +0,0 @@ -import abc -import logging - -import six - -from cloudinit.registry import DictRegistry - - -@six.add_metaclass(abc.ABCMeta) -class ReportingHandler(object): - """Base class for report handlers. - - Implement :meth:`~publish_event` for controlling what - the handler does with an event. - """ - - @abc.abstractmethod - def publish_event(self, event): - """Publish an event to the ``INFO`` log level.""" - - -class LogHandler(ReportingHandler): - """Publishes events to the cloud-init log at the ``INFO`` log level.""" - - def publish_event(self, event): - """Publish an event to the ``INFO`` log level.""" - logger = logging.getLogger( - '.'.join(['cloudinit', 'reporting', event.event_type, event.name])) - logger.info(event.as_string()) - - -available_handlers = DictRegistry() -available_handlers.register_item('log', LogHandler) diff --git a/cloudinit/safeyaml.py b/cloudinit/safeyaml.py deleted file mode 100644 index a8f9d1ba..00000000 --- a/cloudinit/safeyaml.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -import yaml as _yaml - -from cloudinit import util - - -YAMLError = _yaml.YAMLError - - -def load(path): - """Load yaml string from a path and return the data represented. - - Exception will be raised if types other than the following are found: - dict, int, float, string, list, unicode - """ - return loads(util.load_file(path)) - - -def loads(blob): - """Load yaml string and return the data represented. - - Exception will be raised if types other than the following are found: - dict, int, float, string, list, unicode - """ - return _yaml.safe_load(blob) - - -def dumps(obj): - """Dumps an object back into a yaml string.""" - formatted = _yaml.safe_dump(obj, - line_break="\n", - indent=4, - explicit_start=True, - explicit_end=True, - default_flow_style=False) - return formatted diff --git a/cloudinit/shell.py b/cloudinit/shell.py deleted file mode 100644 index c19e27f9..00000000 --- a/cloudinit/shell.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -import argparse -import sys - -from cloudinit import logging -from cloudinit.version import version_string - - -def populate_parser(parser, common, subcommands): - """Populate an ArgumentParser with data rather than code - - This replaces boilerplate code with boilerplate data when populating a - :py:class:`argparse.ArgumentParser` - - :param parser: - the :py:mod:`argparse.ArgumentParser` to populate. - - :param common: - a :py:func:`list` of tuples. Each tuple is args and kwargs that are - passed onto :py:func:`argparse.ArgumentParser.add_argument` - - :param subcommands: - a :py:func:dict of subcommands to add. - The key is added as the subcommand name. - 'func' is called to implement the subcommand. - 'help' is set up as the subcommands help message - entries in 'opts' are passed onto - :py:func:`argparse.ArgumentParser.add_argument` - """ - for (args, kwargs) in common: - parser.add_argument(*args, **kwargs) - - subparsers = parser.add_subparsers() - for subcmd in sorted(subcommands): - val = subcommands[subcmd] - sparser = subparsers.add_parser(subcmd, help=val['help']) - sparser.set_defaults(func=val['func'], name=subcmd) - for (args, kwargs) in val.get('opts', {}): - sparser.add_argument(*args, **kwargs) - - -def main(args=sys.argv): - parser = argparse.ArgumentParser(prog='cloud-init') - - populate_parser(parser, COMMON_ARGS, SUBCOMMANDS) - parsed = parser.parse_args(args[1:]) - - if not hasattr(parsed, 'func'): - parser.error('too few arguments') - logging.configure_logging(log_to_console=parsed.log_to_console) - parsed.func(parsed) - return 0 - - -def main_version(args): - sys.stdout.write("cloud-init {0}\n".format(version_string())) - - -def unimplemented_subcommand(args): - raise NotImplementedError( - "sub command '{0}' is not implemented".format(args.name)) - - -COMMON_ARGS = [ - (('--log-to-console',), {'action': 'store_true', 'default': False}), - (('--verbose', '-v'), {'action': 'count', 'default': 0}), -] - -SUBCOMMANDS = { - # The stages a normal boot takes - 'network': { - 'func': unimplemented_subcommand, - 'help': 'locate and apply networking configuration', - }, - 'search': { - 'func': unimplemented_subcommand, - 'help': 'search available data sources', - }, - 'config': { - 'func': unimplemented_subcommand, - 'help': 'run available config modules', - }, - 'config-final': { - 'func': unimplemented_subcommand, - 'help': 'run "final" config modules', - }, - # utility - 'version': { - 'func': main_version, - 'help': 'print cloud-init version', - }, - 'all': { - 'func': unimplemented_subcommand, - 'help': 'run all stages as if from boot', - 'opts': [ - (('--clean',), - {'help': 'clear any prior system state', - 'action': 'store_true', 'default': False})], - }, - 'clean': { - 'func': unimplemented_subcommand, - 'help': 'clear any prior system state.', - 'opts': [ - (('-F', '--full'), - {'help': 'be more complete (remove logs).', - 'default': False, 'action': 'store_true'}), - ], - }, - 'query': { - 'func': unimplemented_subcommand, - 'help': 'query system state', - 'opts': [ - (('--json',), - {'help': 'output in json format', - 'action': 'store_true', 'default': False})] - }, -} - - -if __name__ == '__main__': - sys.exit(main()) diff --git a/cloudinit/sources/__init__.py b/cloudinit/sources/__init__.py deleted file mode 100644 index 20b32a23..00000000 --- a/cloudinit/sources/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab diff --git a/cloudinit/sources/base.py b/cloudinit/sources/base.py deleted file mode 100644 index 65f58538..00000000 --- a/cloudinit/sources/base.py +++ /dev/null @@ -1,217 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -import abc -import itertools - -import six - -from cloudinit import exceptions -from cloudinit import logging -from cloudinit import sources -from cloudinit.sources import strategy - - -LOG = logging.getLogger(__name__) - - -class APIResponse(object): - """Holds API response content - - To access the content in the binary format, use the - `buffer` attribute, while the unicode content can be - accessed by calling `str` over this (or by accessing - the `decoded_buffer` property). - """ - - def __init__(self, buffer, encoding="utf-8"): - self.buffer = buffer - self.encoding = encoding - self._decoded_buffer = None - - @property - def decoded_buffer(self): - # Avoid computing this again and again (although multiple threads - # may decode it if they all get in here at the same time, but meh - # thats ok). - if self._decoded_buffer is None: - self._decoded_buffer = self.buffer.decode(self.encoding) - return self._decoded_buffer - - def __str__(self): - return self.decoded_buffer - - -class DataSourceLoader(object): - """Class for retrieving an available data source instance - - :param names: - A list of possible data source names, from which the loader - should pick. This can be used to filter the data sources - that can be found from outside of cloudinit control. - - :param module_iterator: - An instance of :class:`cloudinit.plugin_finder.BaseModuleIterator`, - which is used to find possible modules where the data sources - can be found. - - :param strategies: - An iterator of search strategy classes, where each strategy is capable - of filtering the data sources that can be used by cloudinit. - Possible strategies includes serial data source search or - parallel data source or filtering data sources according to - some criteria (only network data sources) - - """ - - def __init__(self, names, module_iterator, strategies): - self._names = names - self._module_iterator = module_iterator - self._strategies = strategies - - @staticmethod - def _implements_source_api(module): - """Check if the given module implements the data source API.""" - return hasattr(module, 'data_sources') - - def _valid_modules(self): - """Return all the modules that are *valid* - - Valid modules are those that implements a particular API - for declaring the data sources it exports. - """ - modules = self._module_iterator.list_modules() - return filter(self._implements_source_api, modules) - - def all_data_sources(self): - """Get all the data source classes that this finder knows about.""" - return itertools.chain.from_iterable( - module.data_sources() - for module in self._valid_modules()) - - def valid_data_sources(self): - """Get the data sources that are valid for this run.""" - data_sources = self.all_data_sources() - # Instantiate them before passing to the strategies. - data_sources = (data_source() for data_source in data_sources) - - for strategy_instance in self._strategies: - data_sources = strategy_instance.search_data_sources(data_sources) - return data_sources - - -@six.add_metaclass(abc.ABCMeta) -class BaseDataSource(object): - """Base class for the data sources.""" - - datasource_config = {} - - def __init__(self, config=None): - self._cache = {} - # TODO(cpopa): merge them instead. - self._config = config or self.datasource_config - - def _get_cache_data(self, path): - """Do a metadata lookup for the given *path* - - This will return the available metadata under *path*, - while caching the result, so that a next call will not do - an additional API call. - """ - if path not in self._cache: - self._cache[path] = self._get_data(path) - - return self._cache[path] - - @abc.abstractmethod - def load(self): - """Try to load this metadata service. - - This should return ``True`` if the service was loaded properly, - ``False`` otherwise. - """ - - @abc.abstractmethod - def _get_data(self, path): - """Retrieve the metadata exported under the `path` key. - - This should return an instance of :class:`APIResponse`. - """ - - @abc.abstractmethod - def version(self): - """Get the version of the current data source.""" - - def instance_id(self): - """Get this instance's id.""" - - def user_data(self): - """Get the user data available for this instance.""" - - def vendor_data(self): - """Get the vendor data available for this instance.""" - - def host_name(self): - """Get the hostname available for this instance.""" - - def public_keys(self): - """Get the public keys available for this instance.""" - - def network_config(self): - """Get the specified network config, if any.""" - - def admin_password(self): - """Get the admin password.""" - - def post_password(self, password): - """Post the password to the metadata service.""" - - def can_update_password(self): - """Check if this data source can update the admin password.""" - - def is_password_changed(self): - """Check if the data source has a new password for this instance.""" - return False - - def is_password_set(self): - """Check if the password was already posted to the metadata service.""" - - -def get_data_source(names, module_iterator, strategies=None): - """Get an instance of any data source available. - - :param names: - A list of possible data source names, from which the loader - should pick. This can be used to filter the data sources - that can be found from outside of cloudinit control. - - :param module_iterator: - A subclass of :class:`cloudinit.plugin_finder.BaseModuleIterator`, - which is used to find possible modules where the data sources - can be found. - - :param strategies: - An iterator of search strategy classes, where each strategy is capable - of filtering the data sources that can be used by cloudinit. - """ - if names: - default_strategies = [strategy.FilterNameStrategy(names)] - else: - default_strategies = [] - if strategies is None: - strategies = [] - - strategy_instances = [strategy_cls() for strategy_cls in strategies] - strategies = default_strategies + strategy_instances - - iterator = module_iterator(sources.__path__) - loader = DataSourceLoader(names, iterator, strategies) - valid_sources = loader.valid_data_sources() - - data_source = next(valid_sources, None) - if not data_source: - raise exceptions.CloudInitError('No available data source found') - - return data_source diff --git a/cloudinit/sources/openstack/__init__.py b/cloudinit/sources/openstack/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/cloudinit/sources/openstack/base.py b/cloudinit/sources/openstack/base.py deleted file mode 100644 index 01f59a38..00000000 --- a/cloudinit/sources/openstack/base.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -"""Base classes for interacting with OpenStack data sources.""" - -import abc -import json -import logging -import os - -import six - -from cloudinit.sources import base - -__all__ = ('BaseOpenStackSource', ) - -_PAYLOAD_KEY = "content_path" -_ADMIN_PASSWORD = "admin_pass" -LOG = logging.getLogger(__name__) -_OS_LATEST = 'latest' -_OS_FOLSOM = '2012-08-10' -_OS_GRIZZLY = '2013-04-04' -_OS_HAVANA = '2013-10-17' -# Keep this in chronological order. New supported versions go at the end. -_OS_VERSIONS = ( - _OS_FOLSOM, - _OS_GRIZZLY, - _OS_HAVANA, -) - - -@six.add_metaclass(abc.ABCMeta) -class BaseOpenStackSource(base.BaseDataSource): - """Base classes for interacting with an OpenStack data source. - - This is useful for both the HTTP data source, as well for - ConfigDrive. - """ - def __init__(self): - super(BaseOpenStackSource, self).__init__() - self._version = None - - @abc.abstractmethod - def _available_versions(self): - """Get the available metadata versions.""" - - @abc.abstractmethod - def _path_join(self, path, *addons): - """Join one or more components together.""" - - def version(self): - """Get the underlying data source version.""" - return self._version - - def _working_version(self): - versions = self._available_versions() - # OS_VERSIONS is stored in chronological order, so - # reverse it to check newest first. - supported = reversed(_OS_VERSIONS) - selected_version = next((version for version in supported - if version in versions), _OS_LATEST) - - LOG.debug("Selected version %r from %s", selected_version, versions) - return selected_version - - def _get_content(self, name): - path = self._path_join('openstack', 'content', name) - return self._get_cache_data(path) - - def _get_meta_data(self): - path = self._path_join('openstack', self._version, 'meta_data.json') - data = self._get_cache_data(path) - if data: - return json.loads(str(data)) - - def load(self): - self._version = self._working_version() - super(BaseOpenStackSource, self).load() - - def user_data(self): - path = self._path_join('openstack', self._version, 'user_data') - return self._get_cache_data(path).buffer - - def vendor_data(self): - path = self._path_join('openstack', self._version, 'vendor_data.json') - return self._get_cache_data(path).buffer - - def instance_id(self): - return self._get_meta_data().get('uuid') - - def host_name(self): - return self._get_meta_data().get('hostname') - - def public_keys(self): - public_keys = self._get_meta_data().get('public_keys') - if public_keys: - return list(public_keys.values()) - return [] - - def network_config(self): - network_config = self._get_meta_data().get('network_config') - if not network_config: - return None - if _PAYLOAD_KEY not in network_config: - return None - - content_path = network_config[_PAYLOAD_KEY] - content_name = os.path.basename(content_path) - return str(self._get_content(content_name)) - - def admin_password(self): - meta_data = self._get_meta_data() - meta = meta_data.get('meta', {}) - return meta.get(_ADMIN_PASSWORD) or meta_data.get(_ADMIN_PASSWORD) diff --git a/cloudinit/sources/openstack/httpopenstack.py b/cloudinit/sources/openstack/httpopenstack.py deleted file mode 100644 index 618a62df..00000000 --- a/cloudinit/sources/openstack/httpopenstack.py +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -import logging -import os -import posixpath -import re - -from cloudinit import exceptions -from cloudinit.osys import base -from cloudinit.sources import base as base_source -from cloudinit.sources.openstack import base as baseopenstack -from cloudinit import url_helper - - -LOG = logging.getLogger(__name__) -IS_WINDOWS = os.name == 'nt' -# Not necessarily the same as using datetime.strftime, -# but should be enough for our use case. -VERSION_REGEX = re.compile('^\d{4}-\d{2}-\d{2}$') - - -class HttpOpenStackSource(baseopenstack.BaseOpenStackSource): - """Class for exporting the HTTP OpenStack data source.""" - - datasource_config = { - 'max_wait': 120, - 'timeout': 10, - 'metadata_url': 'http://169.254.169.254/', - 'post_password_version': '2013-04-04', - 'retries': 3, - } - - @staticmethod - def _enable_metadata_access(metadata_url): - if IS_WINDOWS: - osutils = base.get_osutils() - osutils.network.set_metadata_ip_route(metadata_url) - - @staticmethod - def _path_join(path, *addons): - return posixpath.join(path, *addons) - - @staticmethod - def _valid_api_version(version): - if version == 'latest': - return version - return VERSION_REGEX.match(version) - - def _available_versions(self): - content = str(self._get_cache_data("openstack")) - versions = list(filter(None, content.splitlines())) - if not versions: - msg = 'No metadata versions were found.' - raise exceptions.CloudInitError(msg) - - for version in versions: - if not self._valid_api_version(version): - msg = 'Invalid API version %r' % (version,) - raise exceptions.CloudInitError(msg) - - return versions - - def _get_data(self, path): - norm_path = self._path_join(self._config['metadata_url'], path) - LOG.debug('Getting metadata from: %s', norm_path) - response = url_helper.wait_any_url([norm_path], - timeout=self._config['timeout'], - max_wait=self._config['max_wait']) - if response: - _, request = response - return base_source.APIResponse(request.contents, - encoding=request.encoding) - - msg = "Metadata for url {0} was not accessible in due time" - raise exceptions.CloudInitError(msg.format(norm_path)) - - def _post_data(self, path, data): - norm_path = self._path_join(self._config['metadata_url'], path) - LOG.debug('Posting metadata to: %s', norm_path) - url_helper.read_url(norm_path, data=data, - retries=self._config['retries'], - timeout=self._config['timeout']) - - @property - def _password_path(self): - return 'openstack/%s/password' % self._version - - def load(self): - metadata_url = self._config['metadata_url'] - self._enable_metadata_access(metadata_url) - super(HttpOpenStackSource, self).load() - - try: - self._get_meta_data() - return True - except Exception: - LOG.warning('Metadata not found at URL %r', metadata_url) - return False - - def can_update_password(self): - """Check if the password can be posted for the current data source.""" - password = map(int, self._config['post_password_version'].split("-")) - if self._version == 'latest': - current = (0, ) - else: - current = map(int, self._version.split("-")) - return tuple(current) >= tuple(password) - - @property - def is_password_set(self): - path = self._password_path - content = self._get_cache_data(path).buffer - return len(content) > 0 - - def post_password(self, password): - try: - self._post_data(self._password_path, password) - return True - except url_helper.UrlError as ex: - if ex.status_code == url_helper.CONFLICT: - # Password already set - return False - else: - raise - - -def data_sources(): - """Get the data sources exported in this module.""" - return (HttpOpenStackSource,) diff --git a/cloudinit/sources/strategy.py b/cloudinit/sources/strategy.py deleted file mode 100644 index 547f8b61..00000000 --- a/cloudinit/sources/strategy.py +++ /dev/null @@ -1,98 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -import abc - -import six - -from cloudinit import logging - - -LOG = logging.getLogger(__name__) - - -@six.add_metaclass(abc.ABCMeta) -class BaseSearchStrategy(object): - """Declare search strategies for data sources - - A *search strategy* represents a decoupled way of choosing - one or more data sources from a list of data sources. - Each strategy can be used interchangeably and they can - be composed. For instance, once can apply a filtering strategy - over a parallel search strategy, which looks for the available - data sources. - """ - - @abc.abstractmethod - def search_data_sources(self, data_sources): - """Search the possible data sources for this strategy - - The method should filter the data sources that can be - considered *valid* for the given strategy. - - :param data_sources: - An iterator of data source instances, where the lookup - will be done. - """ - - @staticmethod - def is_datasource_available(data_source): - """Check if the given *data_source* is considered *available* - - A data source is considered available if it can be loaded, - but other strategies could implement their own behaviour. - """ - try: - if data_source.load(): - return True - except Exception: - LOG.error("Failed to load data source %r", data_source) - return False - - -class FilterNameStrategy(BaseSearchStrategy): - """A strategy for filtering data sources by name - - :param names: - A list of strings, where each string is a name for a possible - data source. Only the data sources that are in this list will - be loaded and filtered. - """ - - def __init__(self, names=None): - self._names = names - super(FilterNameStrategy, self).__init__() - - def search_data_sources(self, data_sources): - return (source for source in data_sources - if source.__class__.__name__ in self._names) - - -class SerialSearchStrategy(BaseSearchStrategy): - """A strategy that chooses a data source in serial.""" - - def search_data_sources(self, data_sources): - for data_source in data_sources: - if self.is_datasource_available(data_source): - yield data_source - - -class FilterVersionStrategy(BaseSearchStrategy): - """A strategy for filtering data sources by their version - - :param versions: - A list of strings, where each strings is a possible - version that a data source can have. - """ - - def __init__(self, versions=None): - if versions is None: - versions = [] - self._versions = versions - super(FilterVersionStrategy, self).__init__() - - def search_data_sources(self, data_sources): - return (source for source in data_sources - if source.version() in self._versions) diff --git a/cloudinit/templater.py b/cloudinit/templater.py deleted file mode 100644 index b9bfe87b..00000000 --- a/cloudinit/templater.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -import collections -import os -import re - -try: - import jinja2 - from jinja2 import Template as JTemplate - JINJA_AVAILABLE = True -except (ImportError, AttributeError): - JINJA_AVAILABLE = False # noqa - -from cloudinit import logging - - -LOG = logging.getLogger(__name__) -TYPE_MATCHER = re.compile(r"##\s*template:(.*)", re.I) -BASIC_MATCHER = re.compile(r'\$\{([A-Za-z0-9_.]+)\}|\$([A-Za-z0-9_.]+)') - - -def basic_render(content, params): - """This does simple replacement of bash variable like templates. - - It identifies patterns like ${a} or $a and can also identify patterns like - ${a.b} or $a.b which will look for a key 'b' in the dictionary rooted - by key 'a'. - """ - - def replacer(match): - # Only 1 of the 2 groups will actually have a valid entry. - name = match.group(1) or match.group(2) - if name is None: - # not sure how this can possibly occur - raise RuntimeError("Match encountered but no valid group present") - path = collections.deque(name.split(".")) - selected_params = params - while len(path) > 1: - key = path.popleft() - if not isinstance(selected_params, dict): - raise TypeError( - "Can not traverse into non-dictionary '%s' of type %s " - "while looking for subkey '%s'" % - (selected_params, type(selected_params), key)) - selected_params = selected_params[key] - key = path.popleft() - if not isinstance(selected_params, dict): - raise TypeError("Can not extract key '%s' from non-dictionary" - " '%s' of type %s" - % (key, selected_params, type(selected_params))) - return str(selected_params[key]) - - return BASIC_MATCHER.sub(replacer, content) - - -def detect_template(text): - - def jinja_render(content, params): - # keep_trailing_newline is in jinja2 2.7+, not 2.6 - add = "\n" if content.endswith("\n") else "" - return JTemplate(content, - undefined=jinja2.StrictUndefined, - trim_blocks=True).render(**params) + add - - if "\n" in text: - ident, rest = text.split("\n", 1) - else: - ident = text - rest = '' - type_match = TYPE_MATCHER.match(ident) - if not type_match: - return ('basic', basic_render, text) - else: - template_type = type_match.group(1).lower().strip() - if template_type not in ('jinja', 'basic'): - raise ValueError("Unknown template rendering type '%s' requested" - % template_type) - if template_type == 'jinja' and not JINJA_AVAILABLE: - raise ValueError("Template requested jinja as renderer, but Jinja " - "is not available.") - elif template_type == 'jinja' and JINJA_AVAILABLE: - return ('jinja', jinja_render, rest) - # Only thing left over is the basic renderer (it is always available). - return ('basic', basic_render, rest) - - -def render_from_file(fn, params, encoding='utf-8'): - with open(fn, 'rb') as fh: - content = fh.read() - content = content.decode(encoding) - _, renderer, content = detect_template(content) - return renderer(content, params) - - -def render_to_file(fn, outfn, params, mode=0o644, encoding='utf-8'): - contents = render_from_file(fn, params, encoding=encoding) - with open(outfn, 'wb') as fh: - fh.write(contents.encode(encoding)) - os.chmod(outfn, mode) - - -def render_string(content, params): - _, renderer, content = detect_template(content) - return renderer(content, params) diff --git a/cloudinit/tests/__init__.py b/cloudinit/tests/__init__.py deleted file mode 100644 index 719c7b62..00000000 --- a/cloudinit/tests/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -import httpretty -import testtools - - -class TestCase(testtools.TestCase): - """Base class for all cloud-init test cases.""" - - def setUp(self): - super(TestCase, self).setUp() - # Do not allow any unknown network connections to get triggered... - httpretty.HTTPretty.allow_net_connect = False - - def tearDown(self): - super(TestCase, self).tearDown() - # Ok allow it again.... - httpretty.HTTPretty.allow_net_connect = True diff --git a/cloudinit/tests/osys/__init__.py b/cloudinit/tests/osys/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/cloudinit/tests/osys/test_base.py b/cloudinit/tests/osys/test_base.py deleted file mode 100644 index 7129571f..00000000 --- a/cloudinit/tests/osys/test_base.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (C) 2015 Canonical Ltd. -# Copyright 2015 Cloudbase Solutions Srl -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from cloudinit.osys import base -from cloudinit.tests import TestCase -from cloudinit.tests.util import mock - - -class TestOSUtils(TestCase): - - @mock.patch('importlib.import_module') - @mock.patch('platform.linux_distribution') - @mock.patch('platform.system') - def _test_getosutils(self, mock_system, - mock_linux_distribution, mock_import_module, - linux=False): - if linux: - os_name = 'Linux' - mock_linux_distribution.return_value = (os_name, None, None) - else: - os_name = 'Windows' - mock_system.return_value = os_name - mock_linux_distribution.return_value = (None, None, None) - - module = base.get_osutils() - - mock_import_module.assert_called_once_with( - "cloudinit.osys.{0}.base".format(os_name.lower())) - self.assertEqual(mock_import_module.return_value.OSUtils, - module) - if linux: - mock_linux_distribution.assert_called_once_with() - self.assertFalse(mock_system.called) - else: - mock_linux_distribution.assert_called_once_with() - mock_system.assert_called_once_with() - - def test_getosutils(self): - self._test_getosutils(linux=True) - self._test_getosutils(linux=False) diff --git a/cloudinit/tests/osys/windows/__init__.py b/cloudinit/tests/osys/windows/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/cloudinit/tests/osys/windows/test_general.py b/cloudinit/tests/osys/windows/test_general.py deleted file mode 100644 index 1b2e4556..00000000 --- a/cloudinit/tests/osys/windows/test_general.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -import importlib - -from cloudinit import exceptions -from cloudinit.tests import TestCase -from cloudinit.tests.util import mock - - -class TestWindowsGeneral(TestCase): - - def setUp(self): - super(TestWindowsGeneral, self).setUp() - self._ctypes_mock = mock.Mock() - self._util_mock = mock.MagicMock() - self._module_patcher = mock.patch.dict( - 'sys.modules', - {'ctypes': self._ctypes_mock, - 'cloudinit.osys.windows.util': self._util_mock}) - - self._module_patcher.start() - self._general_module = importlib.import_module( - "cloudinit.osys.windows.general") - self._kernel32 = self._general_module.kernel32 - self._general = self._general_module.General() - - def tearDown(self): - super(TestWindowsGeneral, self).tearDown() - self._module_patcher.stop() - - def _test_check_os_version(self, ret_value, error_value=None): - verset_return = 2 - self._kernel32.VerSetConditionMask.return_value = ( - verset_return) - self._kernel32.VerifyVersionInfoW.return_value = ret_value - self._kernel32.GetLastError.return_value = error_value - old_version = self._kernel32.ERROR_OLD_WIN_VERSION - - if error_value and error_value is not old_version: - self.assertRaises(exceptions.CloudInitError, - self._general.check_os_version, 3, 1, 2) - self._kernel32.GetLastError.assert_called_once_with() - - else: - response = self._general.check_os_version(3, 1, 2) - self._ctypes_mock.sizeof.assert_called_once_with( - self._kernel32.Win32_OSVERSIONINFOEX_W) - self.assertEqual( - 3, self._kernel32.VerSetConditionMask.call_count) - - mask = (self._kernel32.VER_MAJORVERSION | - self._kernel32.VER_MINORVERSION | - self._kernel32.VER_BUILDNUMBER) - self._kernel32.VerifyVersionInfoW.assert_called_with( - self._ctypes_mock.byref.return_value, mask, verset_return) - - if error_value is old_version: - self._kernel32.GetLastError.assert_called_with() - self.assertFalse(response) - else: - self.assertTrue(response) - - def test_check_os_version(self): - m = mock.MagicMock() - self._test_check_os_version(ret_value=m) - - def test_check_os_version_expect_false(self): - self._test_check_os_version( - ret_value=None, error_value=self._kernel32.ERROR_OLD_WIN_VERSION) diff --git a/cloudinit/tests/osys/windows/test_network.py b/cloudinit/tests/osys/windows/test_network.py deleted file mode 100644 index 5fa2581b..00000000 --- a/cloudinit/tests/osys/windows/test_network.py +++ /dev/null @@ -1,361 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -import importlib -import subprocess - -from cloudinit import exceptions -from cloudinit import tests -from cloudinit.tests.util import LogSnatcher -from cloudinit.tests.util import mock - - -class TestNetworkWindows(tests.TestCase): - - def setUp(self): - super(TestNetworkWindows, self).setUp() - - self._ctypes_mock = mock.MagicMock() - self._winreg_mock = mock.Mock() - self._win32com_mock = mock.Mock() - self._wmi_mock = mock.Mock() - - self._module_patcher = mock.patch.dict( - 'sys.modules', - {'ctypes': self._ctypes_mock, - 'win32com': self._win32com_mock, - 'wmi': self._wmi_mock, - 'six.moves.winreg': self._winreg_mock}) - - self._module_patcher.start() - self._iphlpapi = mock.Mock() - self._kernel32 = mock.Mock() - self._ws2_32 = mock.Mock() - - self._network_module = importlib.import_module( - 'cloudinit.osys.windows.network') - self._network_module.iphlpapi = self._iphlpapi - self._network_module.kernel32 = self._kernel32 - self._network_module.ws2_32 = self._ws2_32 - - self._network = self._network_module.Network() - - def tearDown(self): - super(TestNetworkWindows, self).tearDown() - - self._module_patcher.stop() - - def _test__heap_alloc(self, fail): - mock_heap = mock.Mock() - mock_size = mock.Mock() - - if fail: - self._kernel32.HeapAlloc.return_value = None - - e = self.assertRaises(exceptions.CloudInitError, - self._network_module._heap_alloc, - mock_heap, mock_size) - - self.assertEqual('Unable to allocate memory for the IP ' - 'forward table', str(e)) - else: - result = self._network_module._heap_alloc(mock_heap, mock_size) - self.assertEqual(self._kernel32.HeapAlloc.return_value, result) - - self._kernel32.HeapAlloc.assert_called_once_with( - mock_heap, 0, self._ctypes_mock.c_size_t(mock_size.value)) - - def test__heap_alloc_error(self): - self._test__heap_alloc(fail=True) - - def test__heap_alloc_no_error(self): - self._test__heap_alloc(fail=False) - - def _check_raises_forward(self): - with self._network._get_forward_table(): - pass - - def test__get_forward_table_no_memory(self): - self._network_module._heap_alloc = mock.Mock() - error_msg = 'Unable to allocate memory for the IP forward table' - exc = exceptions.CloudInitError(error_msg) - self._network_module._heap_alloc.side_effect = exc - - e = self.assertRaises(exceptions.CloudInitError, - self._check_raises_forward) - - self.assertEqual(error_msg, str(e)) - self._network_module._heap_alloc.assert_called_once_with( - self._kernel32.GetProcessHeap.return_value, - self._ctypes_mock.wintypes.ULONG.return_value) - - def test__get_forward_table_insufficient_buffer_no_memory(self): - self._kernel32.HeapAlloc.side_effect = (mock.sentinel.table_mem, None) - self._iphlpapi.GetIpForwardTable.return_value = ( - self._iphlpapi.ERROR_INSUFFICIENT_BUFFER) - - self.assertRaises(exceptions.CloudInitError, - self._check_raises_forward) - - table = self._ctypes_mock.cast.return_value - self._iphlpapi.GetIpForwardTable.assert_called_once_with( - table, - self._ctypes_mock.byref.return_value, 0) - heap_calls = [ - mock.call(self._kernel32.GetProcessHeap.return_value, 0, table), - mock.call(self._kernel32.GetProcessHeap.return_value, 0, table) - ] - self.assertEqual(heap_calls, self._kernel32.HeapFree.mock_calls) - - def _test__get_forward_table(self, reallocation=False, - insufficient_buffer=False, - fail=False): - if fail: - e = self.assertRaises(exceptions.CloudInitError, - self._check_raises_forward) - msg = ('Unable to get IP forward table. Error: %s' - % mock.sentinel.error) - self.assertEqual(msg, str(e)) - else: - with self._network._get_forward_table() as table: - pass - pointer = self._ctypes_mock.POINTER( - self._iphlpapi.Win32_MIB_IPFORWARDTABLE) - expected_forward_table = self._ctypes_mock.cast( - self._kernel32.HeapAlloc.return_value, pointer) - self.assertEqual(expected_forward_table, table) - - heap_calls = [ - mock.call(self._kernel32.GetProcessHeap.return_value, 0, - self._ctypes_mock.cast.return_value) - ] - forward_calls = [ - mock.call(self._ctypes_mock.cast.return_value, - self._ctypes_mock.byref.return_value, 0), - ] - if insufficient_buffer: - # We expect two calls for GetIpForwardTable - forward_calls.append(forward_calls[0]) - if reallocation: - heap_calls.append(heap_calls[0]) - self.assertEqual(heap_calls, self._kernel32.HeapFree.mock_calls) - self.assertEqual(forward_calls, - self._iphlpapi.GetIpForwardTable.mock_calls) - - def test__get_forward_table_sufficient_buffer(self): - self._iphlpapi.GetIpForwardTable.return_value = None - self._test__get_forward_table() - - def test__get_forward_table_insufficient_buffer_reallocate(self): - self._kernel32.HeapAlloc.side_effect = ( - mock.sentinel.table_mem, mock.sentinel.table_mem) - self._iphlpapi.GetIpForwardTable.side_effect = ( - self._iphlpapi.ERROR_INSUFFICIENT_BUFFER, None) - - self._test__get_forward_table(reallocation=True, - insufficient_buffer=True) - - def test__get_forward_table_insufficient_buffer_other_error(self): - self._kernel32.HeapAlloc.side_effect = ( - mock.sentinel.table_mem, mock.sentinel.table_mem) - self._iphlpapi.GetIpForwardTable.side_effect = ( - self._iphlpapi.ERROR_INSUFFICIENT_BUFFER, mock.sentinel.error) - - self._test__get_forward_table(reallocation=True, - insufficient_buffer=True, - fail=True) - - @mock.patch('cloudinit.osys.windows.network.Network.routes') - def test_default_gateway_no_gateway(self, mock_routes): - mock_routes.return_value = iter((mock.Mock(), mock.Mock())) - - self.assertIsNone(self._network.default_gateway()) - - mock_routes.assert_called_once_with() - - @mock.patch('cloudinit.osys.windows.network.Network.routes') - def test_default_gateway(self, mock_routes): - default_gateway = mock.Mock() - default_gateway.destination = '0.0.0.0' - mock_routes.return_value = iter((mock.Mock(), default_gateway)) - - gateway = self._network.default_gateway() - - self.assertEqual(default_gateway, gateway) - - def test_route_is_static(self): - bad_route = self._network_module.Route( - destination=None, netmask=None, - gateway=None, interface=None, metric=None, - flags=404) - good_route = self._network_module.Route( - destination=None, netmask=None, - gateway=None, interface=None, metric=None, - flags=self._network_module.MIB_IPPROTO_NETMGMT) - - self.assertTrue(good_route.is_static) - self.assertFalse(bad_route.is_static) - - @mock.patch('subprocess.Popen') - def _test_route_add(self, mock_popen, err): - mock_route = mock.Mock() - mock_route.destination = mock.sentinel.destination - mock_route.netmask = mock.sentinel.netmask - mock_route.gateway = mock.sentinel.gateway - args = ['ROUTE', 'ADD', mock.sentinel.destination, - 'MASK', mock.sentinel.netmask, - mock.sentinel.gateway] - mock_popen.return_value.returncode = err - mock_popen.return_value.communicate.return_value = (None, err) - - if err: - e = self.assertRaises(exceptions.CloudInitError, - self._network_module.Route.add, - mock_route) - msg = "Unable to add route: %s" % err - self.assertEqual(msg, str(e)) - - else: - self._network_module.Route.add(mock_route) - mock_popen.assert_called_once_with(args, shell=False, - stderr=subprocess.PIPE) - - def test_route_add_fails(self): - self._test_route_add(err=1) - - def test_route_add_works(self): - self._test_route_add(err=0) - - @mock.patch('cloudinit.osys.windows.network.Network._get_forward_table') - def test_routes(self, mock_forward_table): - def _same(arg): - return arg._mock_name.encode() - - route = mock.MagicMock() - mock_cast_result = mock.Mock() - mock_cast_result.contents = [route] - self._ctypes_mock.cast.return_value = mock_cast_result - self._network_module.ws2_32.Ws2_32.inet_ntoa.side_effect = _same - route.dwForwardIfIndex = 'dwForwardIfIndex' - route.dwForwardProto = 'dwForwardProto' - route.dwForwardMetric1 = 'dwForwardMetric1' - routes = self._network.routes() - - mock_forward_table.assert_called_once_with() - enter = mock_forward_table.return_value.__enter__ - enter.assert_called_once_with() - exit_ = mock_forward_table.return_value.__exit__ - exit_.assert_called_once_with(None, None, None) - self.assertEqual(1, len(routes)) - given_route = routes[0] - self.assertEqual('dwForwardDest', given_route.destination) - self.assertEqual('dwForwardNextHop', given_route.gateway) - self.assertEqual('dwForwardMask', given_route.netmask) - self.assertEqual('dwForwardIfIndex', given_route.interface) - self.assertEqual('dwForwardMetric1', given_route.metric) - self.assertEqual('dwForwardProto', given_route.flags) - - @mock.patch('cloudinit.osys.base.get_osutils') - @mock.patch('cloudinit.osys.windows.network.Network.routes') - def test_set_metadata_ip_route_not_called(self, mock_routes, - mock_osutils): - general = mock_osutils.return_value.general - general.check_os_version.return_value = False - - self._network.set_metadata_ip_route(mock.sentinel.url) - - self.assertFalse(mock_routes.called) - general.check_os_version.assert_called_once_with(6, 0) - - @mock.patch('cloudinit.osys.base.get_osutils') - @mock.patch('cloudinit.osys.windows.network.Network.routes') - def test_set_metadata_ip_route_not_invalid_url(self, mock_routes, - mock_osutils): - general = mock_osutils.return_value.general - general.check_os_version.return_value = True - - self._network.set_metadata_ip_route("http://169.253.169.253") - - self.assertFalse(mock_routes.called) - general.check_os_version.assert_called_once_with(6, 0) - - @mock.patch('cloudinit.osys.base.get_osutils') - @mock.patch('cloudinit.osys.windows.network.Network.routes') - @mock.patch('cloudinit.osys.windows.network.Network.default_gateway') - def test_set_metadata_ip_route_route_already_exists( - self, mock_default_gateway, mock_routes, mock_osutils): - - mock_route = mock.Mock() - mock_route.destination = "169.254.169.254" - mock_routes.return_value = (mock_route, ) - - self._network.set_metadata_ip_route("http://169.254.169.254") - - self.assertTrue(mock_routes.called) - self.assertFalse(mock_default_gateway.called) - - @mock.patch('cloudinit.osys.base.get_osutils') - @mock.patch('cloudinit.osys.windows.network._check_url') - @mock.patch('cloudinit.osys.windows.network.Network.routes') - @mock.patch('cloudinit.osys.windows.network.Network.default_gateway') - def test_set_metadata_ip_route_route_missing_url_accessible( - self, mock_default_gateway, mock_routes, - mock_check_url, mock_osutils): - - mock_routes.return_value = () - mock_check_url.return_value = True - - self._network.set_metadata_ip_route("http://169.254.169.254") - - self.assertTrue(mock_routes.called) - self.assertFalse(mock_default_gateway.called) - self.assertTrue(mock_osutils.called) - - @mock.patch('cloudinit.osys.base.get_osutils') - @mock.patch('cloudinit.osys.windows.network._check_url') - @mock.patch('cloudinit.osys.windows.network.Network.routes') - @mock.patch('cloudinit.osys.windows.network.Network.default_gateway') - @mock.patch('cloudinit.osys.windows.network.Route') - def test_set_metadata_ip_route_no_default_gateway( - self, mock_Route, mock_default_gateway, - mock_routes, mock_check_url, mock_osutils): - - mock_routes.return_value = () - mock_check_url.return_value = False - mock_default_gateway.return_value = None - - self._network.set_metadata_ip_route("http://169.254.169.254") - - self.assertTrue(mock_osutils.called) - self.assertTrue(mock_routes.called) - self.assertTrue(mock_default_gateway.called) - self.assertFalse(mock_Route.called) - - @mock.patch('cloudinit.osys.base.get_osutils') - @mock.patch('cloudinit.osys.windows.network._check_url') - @mock.patch('cloudinit.osys.windows.network.Network.routes') - @mock.patch('cloudinit.osys.windows.network.Network.default_gateway') - @mock.patch('cloudinit.osys.windows.network.Route') - def test_set_metadata_ip_route( - self, mock_Route, mock_default_gateway, - mock_routes, mock_check_url, mock_osutils): - - mock_routes.return_value = () - mock_check_url.return_value = False - - with LogSnatcher('cloudinit.osys.windows.network') as snatcher: - self._network.set_metadata_ip_route("http://169.254.169.254") - - expected = ['Setting gateway for host: 169.254.169.254'] - self.assertEqual(expected, snatcher.output) - self.assertTrue(mock_routes.called) - self.assertTrue(mock_default_gateway.called) - mock_Route.assert_called_once_with( - destination="169.254.169.254", - netmask="255.255.255.255", - gateway=mock_default_gateway.return_value.gateway, - interface=None, metric=None) - mock_Route.add.assert_called_once_with(mock_Route.return_value) - self.assertTrue(mock_osutils.called) diff --git a/cloudinit/tests/sources/__init__.py b/cloudinit/tests/sources/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/cloudinit/tests/sources/openstack/__init__.py b/cloudinit/tests/sources/openstack/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/cloudinit/tests/sources/openstack/test_base.py b/cloudinit/tests/sources/openstack/test_base.py deleted file mode 100644 index d5ac11fb..00000000 --- a/cloudinit/tests/sources/openstack/test_base.py +++ /dev/null @@ -1,176 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -from cloudinit.sources import base as base_source -from cloudinit.sources.openstack import base -from cloudinit import tests -from cloudinit.tests.util import LogSnatcher -from cloudinit.tests.util import mock - - -class TestBaseOpenStackSource(tests.TestCase): - - @mock.patch('cloudinit.sources.openstack.base.BaseOpenStackSource.' - '__abstractmethods__', new=()) - def setUp(self): - self._source = base.BaseOpenStackSource() - super(TestBaseOpenStackSource, self).setUp() - - @mock.patch('cloudinit.sources.openstack.base.BaseOpenStackSource.' - '_available_versions') - def _test_working_version(self, mock_available_versions, - versions, expected_version): - - mock_available_versions.return_value = versions - - with LogSnatcher('cloudinit.sources.openstack.base') as snatcher: - version = self._source._working_version() - - msg = "Selected version '{0}' from {1}" - expected_logging = [msg.format(expected_version, versions)] - self.assertEqual(expected_logging, snatcher.output) - self.assertEqual(expected_version, version) - - def test_working_version_latest(self): - self._test_working_version(versions=(), expected_version='latest') - - def test_working_version_other_version(self): - versions = ( - base._OS_FOLSOM, - base._OS_GRIZZLY, - base._OS_HAVANA, - ) - self._test_working_version(versions=versions, - expected_version=base._OS_HAVANA) - - @mock.patch('cloudinit.sources.openstack.base.BaseOpenStackSource.' - '_get_meta_data') - def test_metadata_capabilities(self, mock_get_meta_data): - mock_get_meta_data.return_value = { - 'uuid': mock.sentinel.id, - 'hostname': mock.sentinel.hostname, - 'public_keys': {'key-one': 'key-one', 'key-two': 'key-two'}, - } - - instance_id = self._source.instance_id() - hostname = self._source.host_name() - public_keys = self._source.public_keys() - - self.assertEqual(mock.sentinel.id, instance_id) - self.assertEqual(mock.sentinel.hostname, hostname) - self.assertEqual(["key-one", "key-two"], sorted(public_keys)) - - @mock.patch('cloudinit.sources.openstack.base.BaseOpenStackSource.' - '_get_meta_data') - def test_no_public_keys(self, mock_get_meta_data): - mock_get_meta_data.return_value = {'public_keys': []} - public_keys = self._source.public_keys() - self.assertEqual([], public_keys) - - @mock.patch('cloudinit.sources.openstack.base.BaseOpenStackSource.' - '_get_meta_data') - def test_admin_password(self, mock_get_meta_data): - mock_get_meta_data.return_value = { - 'meta': {base._ADMIN_PASSWORD: mock.sentinel.password} - } - password = self._source.admin_password() - self.assertEqual(mock.sentinel.password, password) - - @mock.patch('cloudinit.sources.openstack.base.BaseOpenStackSource.' - '_path_join') - @mock.patch('cloudinit.sources.openstack.base.BaseOpenStackSource.' - '_get_cache_data') - def test_get_content(self, mock_get_cache_data, mock_path_join): - result = self._source._get_content(mock.sentinel.name) - - mock_path_join.assert_called_once_with( - 'openstack', 'content', mock.sentinel.name) - mock_get_cache_data.assert_called_once_with( - mock_path_join.return_value) - self.assertEqual(mock_get_cache_data.return_value, result) - - @mock.patch('cloudinit.sources.openstack.base.BaseOpenStackSource.' - '_path_join') - @mock.patch('cloudinit.sources.openstack.base.BaseOpenStackSource.' - '_get_cache_data') - def test_user_data(self, mock_get_cache_data, mock_path_join): - result = self._source.user_data() - - mock_path_join.assert_called_once_with( - 'openstack', self._source._version, 'user_data') - mock_get_cache_data.assert_called_once_with( - mock_path_join.return_value) - self.assertEqual(mock_get_cache_data.return_value.buffer, result) - - @mock.patch('cloudinit.sources.openstack.base.BaseOpenStackSource.' - '_path_join') - @mock.patch('cloudinit.sources.openstack.base.BaseOpenStackSource.' - '_get_cache_data') - def test_get_metadata(self, mock_get_cache_data, mock_path_join): - mock_get_cache_data.return_value = base_source.APIResponse(b"{}") - - result = self._source._get_meta_data() - - mock_path_join.assert_called_once_with( - 'openstack', self._source._version, 'meta_data.json') - mock_get_cache_data.assert_called_once_with( - mock_path_join.return_value) - self.assertEqual({}, result) - - @mock.patch('cloudinit.sources.openstack.base.BaseOpenStackSource.' - '_path_join') - @mock.patch('cloudinit.sources.openstack.base.BaseOpenStackSource.' - '_get_cache_data') - def test_vendor_data(self, mock_get_cache_data, mock_path_join): - result = self._source.vendor_data() - - mock_path_join.assert_called_once_with( - 'openstack', self._source._version, 'vendor_data.json') - mock_get_cache_data.assert_called_once_with( - mock_path_join.return_value) - self.assertEqual(mock_get_cache_data.return_value.buffer, result) - - @mock.patch('cloudinit.sources.openstack.base.BaseOpenStackSource.' - '_working_version') - def test_load(self, mock_working_version): - self._source.load() - - self.assertTrue(mock_working_version.called) - self.assertEqual(mock_working_version.return_value, - self._source._version) - - @mock.patch('cloudinit.sources.openstack.base.BaseOpenStackSource.' - '_get_meta_data') - def test_network_config_no_config(self, mock_get_metadata): - mock_get_metadata.return_value = {} - - self.assertIsNone(self._source.network_config()) - - mock_get_metadata.return_value = {1: 2} - - self.assertIsNone(self._source.network_config()) - - @mock.patch('cloudinit.sources.openstack.base.BaseOpenStackSource.' - '_get_meta_data') - @mock.patch('cloudinit.sources.openstack.base.BaseOpenStackSource.' - '_get_content') - def test_network_config(self, mock_get_content, mock_get_metadata): - mock_get_metadata.return_value = { - "network_config": {base._PAYLOAD_KEY: "content_path"} - } - - result = self._source.network_config() - - mock_get_content.assert_called_once_with("content_path") - self.assertEqual(str(mock_get_content.return_value), result) - - @mock.patch('cloudinit.sources.openstack.base.BaseOpenStackSource.' - '_get_data') - def test_get_cache_data(self, mock_get_data): - mock_get_data.return_value = b'test' - result = self._source._get_cache_data(mock.sentinel.path) - - mock_get_data.assert_called_once_with(mock.sentinel.path) - self.assertEqual(b'test', result) diff --git a/cloudinit/tests/sources/openstack/test_httpopenstack.py b/cloudinit/tests/sources/openstack/test_httpopenstack.py deleted file mode 100644 index 8ec8aa0a..00000000 --- a/cloudinit/tests/sources/openstack/test_httpopenstack.py +++ /dev/null @@ -1,251 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -import textwrap - -from six.moves import http_client - -from cloudinit import exceptions -from cloudinit.sources import base -from cloudinit.sources.openstack import httpopenstack -from cloudinit import tests -from cloudinit.tests.util import LogSnatcher -from cloudinit.tests.util import mock -from cloudinit import url_helper - - -class TestHttpOpenStackSource(tests.TestCase): - - def setUp(self): - self._source = httpopenstack.HttpOpenStackSource() - super(TestHttpOpenStackSource, self).setUp() - - @mock.patch.object(httpopenstack, 'IS_WINDOWS', new=False) - @mock.patch('cloudinit.osys.windows.network.Network.' - 'set_metadata_ip_route') - def test__enable_metadata_access_not_nt(self, mock_set_metadata_ip_route): - self._source._enable_metadata_access(mock.sentinel.metadata_url) - - self.assertFalse(mock_set_metadata_ip_route.called) - - @mock.patch.object(httpopenstack, 'IS_WINDOWS', new=True) - @mock.patch('cloudinit.osys.base.get_osutils') - def test__enable_metadata_access_nt(self, mock_get_osutils): - - self._source._enable_metadata_access(mock.sentinel.metadata_url) - - mock_get_osutils.assert_called_once_with() - osutils = mock_get_osutils.return_value - osutils.network.set_metadata_ip_route.assert_called_once_with( - mock.sentinel.metadata_url) - - def test__path_join(self): - calls = [ - (('path', 'a', 'b'), 'path/a/b'), - (('path', ), 'path'), - (('path/', 'b/'), 'path/b/'), - ] - for arguments, expected in calls: - path = self._source._path_join(*arguments) - self.assertEqual(expected, path) - - @mock.patch('cloudinit.sources.openstack.httpopenstack.' - 'HttpOpenStackSource._get_cache_data') - def test__available_versions(self, mock_get_cache_data): - mock_get_cache_data.return_value = textwrap.dedent(""" - 2013-02-02 - 2014-04-04 - - 2015-05-05 - - latest""") - versions = self._source._available_versions() - expected = ['2013-02-02', '2014-04-04', '2015-05-05', 'latest'] - mock_get_cache_data.assert_called_once_with("openstack") - self.assertEqual(expected, versions) - - @mock.patch('cloudinit.sources.openstack.httpopenstack.' - 'HttpOpenStackSource._get_cache_data') - def _test__available_versions_invalid_versions( - self, version, mock_get_cache_data): - - mock_get_cache_data.return_value = version - - exc = self.assertRaises(exceptions.CloudInitError, - self._source._available_versions) - expected = 'Invalid API version %r' % (version,) - self.assertEqual(expected, str(exc)) - - def test__available_versions_invalid_versions(self): - versions = ['2013-no-worky', '2012', '2012-02', - 'lates', '20004-111-222', '2004-11-11111', - ' 2004-11-20'] - for version in versions: - self._test__available_versions_invalid_versions(version) - - @mock.patch('cloudinit.sources.openstack.httpopenstack.' - 'HttpOpenStackSource._get_cache_data') - def test__available_versions_no_version_found(self, mock_get_cache_data): - mock_get_cache_data.return_value = '' - - exc = self.assertRaises(exceptions.CloudInitError, - self._source._available_versions) - self.assertEqual('No metadata versions were found.', str(exc)) - - @mock.patch('cloudinit.sources.openstack.httpopenstack.' - 'HttpOpenStackSource._get_cache_data') - def _test_is_password_set(self, mock_get_cache_data, data, expected): - mock_get_cache_data.return_value = data - - result = self._source.is_password_set - self.assertEqual(expected, result) - mock_get_cache_data.assert_called_once_with( - self._source._password_path) - - def test_is_password_set(self): - empty_data = base.APIResponse(b"") - non_empty_data = base.APIResponse(b"password") - self._test_is_password_set(data=empty_data, expected=False) - self._test_is_password_set(data=non_empty_data, expected=True) - - def _test_can_update_password(self, version, expected): - with mock.patch.object(self._source, '_version', new=version): - self.assertEqual(self._source.can_update_password(), expected) - - def test_can_update_password(self): - self._test_can_update_password('2012-08-10', expected=False) - self._test_can_update_password('2012-11-10', expected=False) - self._test_can_update_password('2013-04-04', expected=True) - self._test_can_update_password('2014-04-04', expected=True) - self._test_can_update_password('latest', expected=False) - - @mock.patch('cloudinit.sources.openstack.httpopenstack.' - 'HttpOpenStackSource._path_join') - @mock.patch('cloudinit.url_helper.read_url') - def test__post_data(self, mock_read_url, mock_path_join): - with LogSnatcher('cloudinit.sources.openstack.' - 'httpopenstack') as snatcher: - self._source._post_data(mock.sentinel.path, - mock.sentinel.data) - - expected_logging = [ - 'Posting metadata to: %s' % mock_path_join.return_value - ] - self.assertEqual(expected_logging, snatcher.output) - mock_path_join.assert_called_once_with( - self._source._config['metadata_url'], mock.sentinel.path) - mock_read_url.assert_called_once_with( - mock_path_join.return_value, data=mock.sentinel.data, - retries=self._source._config['retries'], - timeout=self._source._config['timeout']) - - @mock.patch('cloudinit.sources.openstack.httpopenstack.' - 'HttpOpenStackSource._post_data') - def test_post_password(self, mock_post_data): - self.assertTrue(self._source.post_password(mock.sentinel.password)) - mock_post_data.assert_called_once_with( - self._source._password_path, mock.sentinel.password) - - @mock.patch('cloudinit.sources.openstack.httpopenstack.' - 'HttpOpenStackSource._post_data') - def test_post_password_already_posted(self, mock_post_data): - exc = url_helper.UrlError(None) - exc.status_code = http_client.CONFLICT - mock_post_data.side_effect = exc - - self.assertFalse(self._source.post_password(mock.sentinel.password)) - mock_post_data.assert_called_once_with( - self._source._password_path, mock.sentinel.password) - - @mock.patch('cloudinit.sources.openstack.httpopenstack.' - 'HttpOpenStackSource._post_data') - def test_post_password_other_error(self, mock_post_data): - exc = url_helper.UrlError(None) - exc.status_code = http_client.NOT_FOUND - mock_post_data.side_effect = exc - - self.assertRaises(url_helper.UrlError, - self._source.post_password, - mock.sentinel.password) - mock_post_data.assert_called_once_with( - self._source._password_path, mock.sentinel.password) - - @mock.patch('cloudinit.sources.openstack.base.' - 'BaseOpenStackSource.load') - @mock.patch('cloudinit.sources.openstack.httpopenstack.' - 'HttpOpenStackSource._get_meta_data') - @mock.patch('cloudinit.sources.openstack.httpopenstack.' - 'HttpOpenStackSource._enable_metadata_access') - def _test_load(self, mock_enable_metadata_access, - mock_get_metadata, mock_load, expected, - expected_logging, metadata_side_effect=None): - - mock_get_metadata.side_effect = metadata_side_effect - with LogSnatcher('cloudinit.sources.openstack.' - 'httpopenstack') as snatcher: - response = self._source.load() - - self.assertEqual(expected, response) - mock_enable_metadata_access.assert_called_once_with( - self._source._config['metadata_url']) - mock_load.assert_called_once_with() - mock_get_metadata.assert_called_once_with() - self.assertEqual(expected_logging, snatcher.output) - - def test_load_works(self): - self._test_load(expected=True, expected_logging=[]) - - def test_load_fails(self): - expected_logging = [ - 'Metadata not found at URL %r' - % self._source._config['metadata_url'] - ] - self._test_load(expected=False, - expected_logging=expected_logging, - metadata_side_effect=ValueError) - - @mock.patch('cloudinit.sources.openstack.httpopenstack.' - 'HttpOpenStackSource._path_join') - @mock.patch('cloudinit.url_helper.wait_any_url') - def test__get_data_inaccessible_metadata(self, mock_wait_any_url, - mock_path_join): - - mock_wait_any_url.return_value = None - mock_path_join.return_value = mock.sentinel.path_join - msg = "Metadata for url {0} was not accessible in due time" - expected = msg.format(mock.sentinel.path_join) - expected_logging = [ - 'Getting metadata from: %s' % mock.sentinel.path_join - ] - with LogSnatcher('cloudinit.sources.openstack.' - 'httpopenstack') as snatcher: - exc = self.assertRaises(exceptions.CloudInitError, - self._source._get_data, 'test') - - self.assertEqual(expected, str(exc)) - self.assertEqual(expected_logging, snatcher.output) - - @mock.patch('cloudinit.sources.openstack.httpopenstack.' - 'HttpOpenStackSource._path_join') - @mock.patch('cloudinit.url_helper.wait_any_url') - def test__get_data(self, mock_wait_any_url, mock_path_join): - mock_response = mock.Mock() - response = b"test" - mock_response.contents = response - mock_response.encoding = 'utf-8' - - mock_wait_any_url.return_value = (None, mock_response) - mock_path_join.return_value = mock.sentinel.path_join - expected_logging = [ - 'Getting metadata from: %s' % mock.sentinel.path_join - ] - with LogSnatcher('cloudinit.sources.openstack.' - 'httpopenstack') as snatcher: - result = self._source._get_data('test') - - self.assertEqual(expected_logging, snatcher.output) - self.assertIsInstance(result, base.APIResponse) - self.assertEqual('test', str(result)) - self.assertEqual(b'test', result.buffer) diff --git a/cloudinit/tests/sources/test_base.py b/cloudinit/tests/sources/test_base.py deleted file mode 100644 index a39aa729..00000000 --- a/cloudinit/tests/sources/test_base.py +++ /dev/null @@ -1,115 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -import functools -import string -import types - -from cloudinit import exceptions -from cloudinit import plugin_finder -from cloudinit.sources import base -from cloudinit.sources import strategy -from cloudinit import tests - - -class TestDataSourceDiscovery(tests.TestCase): - - def setUp(self): - super(TestDataSourceDiscovery, self).setUp() - self._modules = None - - @property - def modules(self): - if self._modules: - return self._modules - - class Module(types.ModuleType): - def data_sources(self): - return (self, ) - - def __call__(self): - return self - - @property - def __class__(self): - return self - - modules = self._modules = list(map(Module, string.ascii_letters)) - return modules - - @property - def module_iterator(self): - modules = self.modules - - class ModuleIterator(plugin_finder.BaseModuleIterator): - def list_modules(self): - return modules + [None, "", 42] - - return ModuleIterator(None) - - def test_loader_api(self): - # Test that the API of DataSourceLoader is sane - loader = base.DataSourceLoader( - names=[], module_iterator=self.module_iterator, - strategies=[]) - - all_data_sources = list(loader.all_data_sources()) - valid_data_sources = list(loader.valid_data_sources()) - - self.assertEqual(all_data_sources, self.modules) - self.assertEqual(valid_data_sources, self.modules) - - def test_loader_strategies(self): - class OrdStrategy(strategy.BaseSearchStrategy): - def search_data_sources(self, data_sources): - return filter(lambda source: ord(source.__name__) < 100, - data_sources) - - class NameStrategy(strategy.BaseSearchStrategy): - def search_data_sources(self, data_sources): - return (source for source in data_sources - if source.__name__ in ('a', 'b', 'c')) - - loader = base.DataSourceLoader( - names=[], module_iterator=self.module_iterator, - strategies=(OrdStrategy(), NameStrategy(), )) - valid_data_sources = list(loader.valid_data_sources()) - - self.assertEqual(len(valid_data_sources), 3) - self.assertEqual([source.__name__ for source in valid_data_sources], - ['a', 'b', 'c']) - - def test_get_data_source_filtered_by_name(self): - source = base.get_data_source( - names=['a', 'c'], - module_iterator=self.module_iterator.__class__) - self.assertEqual(source.__name__, 'a') - - def test_get_data_source_multiple_strategies(self): - class ReversedStrategy(strategy.BaseSearchStrategy): - def search_data_sources(self, data_sources): - return reversed(list(data_sources)) - - source = base.get_data_source( - names=['a', 'b', 'c'], - module_iterator=self.module_iterator.__class__, - strategies=(ReversedStrategy, )) - - self.assertEqual(source.__name__, 'c') - - def test_get_data_source_no_data_source(self): - get_data_source = functools.partial( - base.get_data_source, - names=['totallymissing'], - module_iterator=self.module_iterator.__class__) - - exc = self.assertRaises(exceptions.CloudInitError, - get_data_source) - self.assertEqual(str(exc), 'No available data source found') - - def test_get_data_source_no_name_filtering(self): - source = base.get_data_source( - names=[], module_iterator=self.module_iterator.__class__) - self.assertEqual(source.__name__, 'a') diff --git a/cloudinit/tests/sources/test_strategy.py b/cloudinit/tests/sources/test_strategy.py deleted file mode 100644 index 9d84a0bc..00000000 --- a/cloudinit/tests/sources/test_strategy.py +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -from cloudinit.sources import strategy -from cloudinit import tests -from cloudinit.tests.util import mock - - -class TestStrategy(tests.TestCase): - - def test_custom_strategy(self): - class CustomStrategy(strategy.BaseSearchStrategy): - - def search_data_sources(self, data_sources): - # Return them in reverse order - return list(reversed(data_sources)) - - data_sources = [mock.sentinel.first, mock.sentinel.second] - instance = CustomStrategy() - sources = instance.search_data_sources(data_sources) - - self.assertEqual(sources, [mock.sentinel.second, mock.sentinel.first]) - - def test_is_datasource_available(self): - class CustomStrategy(strategy.BaseSearchStrategy): - def search_data_sources(self, _): - pass - - instance = CustomStrategy() - good_source = mock.Mock() - good_source.load.return_value = True - bad_source = mock.Mock() - bad_source.load.return_value = False - - self.assertTrue(instance.is_datasource_available(good_source)) - self.assertFalse(instance.is_datasource_available(bad_source)) - - def test_filter_name_strategy(self): - names = ['first', 'second', 'third'] - full_names = names + ['fourth', 'fifth'] - sources = [type(name, (object, ), {})() for name in full_names] - instance = strategy.FilterNameStrategy(names) - - sources = list(instance.search_data_sources(sources)) - - self.assertEqual(len(sources), 3) - self.assertEqual([source.__class__.__name__ for source in sources], - names) - - def test_serial_search_strategy(self): - def is_available(self, data_source): - return data_source in available_sources - - sources = [mock.sentinel.first, mock.sentinel.second, - mock.sentinel.third, mock.sentinel.fourth] - available_sources = [mock.sentinel.second, mock.sentinel.fourth] - - with mock.patch('cloudinit.sources.strategy.BaseSearchStrategy.' - 'is_datasource_available', new=is_available): - instance = strategy.SerialSearchStrategy() - valid_sources = list(instance.search_data_sources(sources)) - - self.assertEqual(available_sources, valid_sources) - - def test_filter_version_strategy(self): - class SourceV1(object): - def version(self): - return 'first' - - class SourceV2(SourceV1): - def version(self): - return 'second' - - class SourceV3(object): - def version(self): - return 'third' - - sources = [SourceV1(), SourceV2(), SourceV3()] - instance = strategy.FilterVersionStrategy(['third', 'first']) - - filtered_sources = sorted( - source.version() - for source in instance.search_data_sources(sources)) - - self.assertEqual(len(filtered_sources), 2) - self.assertEqual(filtered_sources, ['first', 'third']) - - def test_filter_version_strategy_no_versions_given(self): - class SourceV1(object): - def version(self): - return 'first' - - sources = [SourceV1()] - instance = strategy.FilterVersionStrategy() - - filtered_sources = list(instance.search_data_sources(sources)) - - self.assertEqual(len(filtered_sources), 0) diff --git a/cloudinit/tests/test_plugin_finder.py b/cloudinit/tests/test_plugin_finder.py deleted file mode 100644 index 2cb244d8..00000000 --- a/cloudinit/tests/test_plugin_finder.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -import contextlib -import os -import shutil -import tempfile - -from cloudinit import plugin_finder -from cloudinit.tests import TestCase -from cloudinit.tests import util - - -class TestPkgutilModuleIterator(TestCase): - - @staticmethod - @contextlib.contextmanager - def _create_tmpdir(): - tmpdir = tempfile.mkdtemp() - try: - yield tmpdir - finally: - shutil.rmtree(tmpdir) - - @contextlib.contextmanager - def _create_package(self): - with self._create_tmpdir() as tmpdir: - path = os.path.join(tmpdir, 'good.py') - with open(path, 'w') as stream: - stream.write('name = 42') - - # Make sure this fails. - bad = os.path.join(tmpdir, 'bad.py') - with open(bad, 'w') as stream: - stream.write('import missingmodule') - - yield tmpdir - - def test_pkgutil_module_iterator(self): - logging_format = ("Could not import the module 'bad' " - "using the search path %r") - - with util.LogSnatcher('cloudinit.plugin_finder') as snatcher: - with self._create_package() as tmpdir: - expected_logging = logging_format % tmpdir - iterator = plugin_finder.PkgutilModuleIterator([tmpdir]) - modules = list(iterator.list_modules()) - - self.assertEqual(len(modules), 1) - module = modules[0] - self.assertEqual(module.name, 42) - self.assertEqual(len(snatcher.output), 1) - self.assertEqual(snatcher.output[0], expected_logging) diff --git a/cloudinit/tests/test_registry.py b/cloudinit/tests/test_registry.py deleted file mode 100644 index fdfbd8eb..00000000 --- a/cloudinit/tests/test_registry.py +++ /dev/null @@ -1,28 +0,0 @@ -from cloudinit.registry import DictRegistry -from cloudinit.tests import TestCase -from cloudinit.tests.util import mock - - -class TestDictRegistry(TestCase): - - def test_added_item_included_in_output(self): - registry = DictRegistry() - item_key, item_to_register = 'test_key', mock.Mock() - registry.register_item(item_key, item_to_register) - self.assertEqual({item_key: item_to_register}, - registry.registered_items) - - def test_registry_starts_out_empty(self): - self.assertEqual({}, DictRegistry().registered_items) - - def test_modifying_registered_items_isnt_exposed_to_other_callers(self): - registry = DictRegistry() - registry.registered_items['test_item'] = mock.Mock() - self.assertEqual({}, registry.registered_items) - - def test_keys_cannot_be_replaced(self): - registry = DictRegistry() - item_key = 'test_key' - registry.register_item(item_key, mock.Mock()) - self.assertRaises(ValueError, - registry.register_item, item_key, mock.Mock()) diff --git a/cloudinit/tests/test_reporting.py b/cloudinit/tests/test_reporting.py deleted file mode 100644 index 922f5959..00000000 --- a/cloudinit/tests/test_reporting.py +++ /dev/null @@ -1,360 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -from cloudinit import reporting -from cloudinit.reporting import handlers -from cloudinit.tests import TestCase -from cloudinit.tests.util import mock - - -def _fake_registry(): - return mock.Mock(registered_items={'a': mock.MagicMock(), - 'b': mock.MagicMock()}) - - -class TestReportStartEvent(TestCase): - - @mock.patch('cloudinit.reporting.instantiated_handler_registry', - new_callable=_fake_registry) - def test_report_start_event_passes_something_with_as_string_to_handlers( - self, instantiated_handler_registry): - event_name, event_description = 'my_test_event', 'my description' - reporting.report_start_event(event_name, event_description) - expected_string_representation = ': '.join( - ['start', event_name, event_description]) - for _, handler in ( - instantiated_handler_registry.registered_items.items()): - self.assertEqual(1, handler.publish_event.call_count) - event = handler.publish_event.call_args[0][0] - self.assertEqual(expected_string_representation, event.as_string()) - - -class TestReportFinishEvent(TestCase): - - def _report_finish_event(self, result=reporting.status.SUCCESS): - event_name, event_description = 'my_test_event', 'my description' - reporting.report_finish_event( - event_name, event_description, result=result) - return event_name, event_description - - def assertHandlersPassedObjectWithAsString( - self, handlers, expected_as_string): - for _, handler in handlers.items(): - self.assertEqual(1, handler.publish_event.call_count) - event = handler.publish_event.call_args[0][0] - self.assertEqual(expected_as_string, event.as_string()) - - @mock.patch('cloudinit.reporting.instantiated_handler_registry', - new_callable=_fake_registry) - def test_report_finish_event_passes_something_with_as_string_to_handlers( - self, instantiated_handler_registry): - event_name, event_description = self._report_finish_event() - expected_string_representation = ': '.join( - ['finish', event_name, reporting.status.SUCCESS, - event_description]) - self.assertHandlersPassedObjectWithAsString( - instantiated_handler_registry.registered_items, - expected_string_representation) - - @mock.patch('cloudinit.reporting.instantiated_handler_registry', - new_callable=_fake_registry) - def test_reporting_successful_finish_has_sensible_string_repr( - self, instantiated_handler_registry): - event_name, event_description = self._report_finish_event( - result=reporting.status.SUCCESS) - expected_string_representation = ': '.join( - ['finish', event_name, reporting.status.SUCCESS, - event_description]) - self.assertHandlersPassedObjectWithAsString( - instantiated_handler_registry.registered_items, - expected_string_representation) - - @mock.patch('cloudinit.reporting.instantiated_handler_registry', - new_callable=_fake_registry) - def test_reporting_unsuccessful_finish_has_sensible_string_repr( - self, instantiated_handler_registry): - event_name, event_description = self._report_finish_event( - result=reporting.status.FAIL) - expected_string_representation = ': '.join( - ['finish', event_name, reporting.status.FAIL, event_description]) - self.assertHandlersPassedObjectWithAsString( - instantiated_handler_registry.registered_items, - expected_string_representation) - - def test_invalid_result_raises_attribute_error(self): - self.assertRaises(ValueError, self._report_finish_event, ("BOGUS",)) - - -class TestReportingEvent(TestCase): - - def test_as_string(self): - event_type, name, description = 'test_type', 'test_name', 'test_desc' - event = reporting.ReportingEvent(event_type, name, description) - expected_string_representation = ': '.join( - [event_type, name, description]) - self.assertEqual(expected_string_representation, event.as_string()) - - def test_as_dict(self): - event_type, name, desc = 'test_type', 'test_name', 'test_desc' - event = reporting.ReportingEvent(event_type, name, desc) - self.assertEqual( - {'event_type': event_type, 'name': name, 'description': desc}, - event.as_dict()) - - -class TestFinishReportingEvent(TestCase): - def test_as_has_result(self): - result = reporting.status.SUCCESS - name, desc = 'test_name', 'test_desc' - event = reporting.FinishReportingEvent(name, desc, result) - ret = event.as_dict() - self.assertTrue('result' in ret) - self.assertEqual(ret['result'], result) - - -class TestBaseReportingHandler(TestCase): - - def test_base_reporting_handler_is_abstract(self): - exc = self.assertRaises(TypeError, handlers.ReportingHandler) - self.assertIn("publish_event", str(exc)) - self.assertIn("abstract", str(exc)) - - -class TestLogHandler(TestCase): - - @mock.patch.object(reporting.handlers.logging, 'getLogger') - def test_appropriate_logger_used(self, getLogger): - event_type, event_name = 'test_type', 'test_name' - event = reporting.ReportingEvent(event_type, event_name, 'description') - reporting.handlers.LogHandler().publish_event(event) - self.assertEqual( - [mock.call( - 'cloudinit.reporting.{0}.{1}'.format(event_type, event_name))], - getLogger.call_args_list) - - @mock.patch.object(reporting.handlers.logging, 'getLogger') - def test_single_log_message_at_info_published(self, getLogger): - event = reporting.ReportingEvent('type', 'name', 'description') - reporting.handlers.LogHandler().publish_event(event) - self.assertEqual(1, getLogger.return_value.info.call_count) - - @mock.patch.object(reporting.handlers.logging, 'getLogger') - def test_log_message_uses_event_as_string(self, getLogger): - event = reporting.ReportingEvent('type', 'name', 'description') - reporting.handlers.LogHandler().publish_event(event) - self.assertIn(event.as_string(), - getLogger.return_value.info.call_args[0][0]) - - -class TestDefaultRegisteredHandler(TestCase): - - def test_log_handler_registered_by_default(self): - registered_items = ( - reporting.instantiated_handler_registry.registered_items) - for _, item in registered_items.items(): - if isinstance(item, reporting.handlers.LogHandler): - break - else: - self.fail('No reporting LogHandler registered by default.') - - -class TestReportingConfiguration(TestCase): - - @mock.patch.object(reporting, 'instantiated_handler_registry') - def test_empty_configuration_doesnt_add_handlers( - self, instantiated_handler_registry): - reporting.update_configuration({}) - self.assertEqual( - 0, instantiated_handler_registry.register_item.call_count) - - @mock.patch.object( - reporting, 'instantiated_handler_registry', reporting.DictRegistry()) - @mock.patch.object(reporting, 'available_handlers') - def test_looks_up_handler_by_type_and_adds_it(self, available_handlers): - handler_type_name = 'test_handler' - handler_cls = mock.Mock() - available_handlers.registered_items = {handler_type_name: handler_cls} - handler_name = 'my_test_handler' - reporting.update_configuration( - {handler_name: {'type': handler_type_name}}) - self.assertEqual( - {handler_name: handler_cls.return_value}, - reporting.instantiated_handler_registry.registered_items) - - @mock.patch.object( - reporting, 'instantiated_handler_registry', reporting.DictRegistry()) - @mock.patch.object(reporting, 'available_handlers') - def test_uses_non_type_parts_of_config_dict_as_kwargs( - self, available_handlers): - handler_type_name = 'test_handler' - handler_cls = mock.Mock() - available_handlers.registered_items = {handler_type_name: handler_cls} - extra_kwargs = {'foo': 'bar', 'bar': 'baz'} - handler_config = extra_kwargs.copy() - handler_config.update({'type': handler_type_name}) - handler_name = 'my_test_handler' - reporting.update_configuration({handler_name: handler_config}) - self.assertEqual( - handler_cls.return_value, - reporting.instantiated_handler_registry.registered_items[ - handler_name]) - self.assertEqual([mock.call(**extra_kwargs)], - handler_cls.call_args_list) - - @mock.patch.object( - reporting, 'instantiated_handler_registry', reporting.DictRegistry()) - @mock.patch.object(reporting, 'available_handlers') - def test_handler_config_not_modified(self, available_handlers): - handler_type_name = 'test_handler' - handler_cls = mock.Mock() - available_handlers.registered_items = {handler_type_name: handler_cls} - handler_config = {'type': handler_type_name, 'foo': 'bar'} - expected_handler_config = handler_config.copy() - reporting.update_configuration({'my_test_handler': handler_config}) - self.assertEqual(expected_handler_config, handler_config) - - @mock.patch.object( - reporting, 'instantiated_handler_registry', reporting.DictRegistry()) - @mock.patch.object(reporting, 'available_handlers') - def test_handlers_removed_if_falseish_specified(self, available_handlers): - handler_type_name = 'test_handler' - handler_cls = mock.Mock() - available_handlers.registered_items = {handler_type_name: handler_cls} - handler_name = 'my_test_handler' - reporting.update_configuration( - {handler_name: {'type': handler_type_name}}) - self.assertEqual( - 1, len(reporting.instantiated_handler_registry.registered_items)) - reporting.update_configuration({handler_name: None}) - self.assertEqual( - 0, len(reporting.instantiated_handler_registry.registered_items)) - - -class TestReportingEventStack(TestCase): - @mock.patch('cloudinit.reporting.report_finish_event') - @mock.patch('cloudinit.reporting.report_start_event') - def test_start_and_finish_success(self, report_start, report_finish): - with reporting.ReportEventStack(name="myname", description="mydesc"): - pass - self.assertEqual( - [mock.call('myname', 'mydesc')], report_start.call_args_list) - self.assertEqual( - [mock.call('myname', 'mydesc', reporting.status.SUCCESS)], - report_finish.call_args_list) - - @mock.patch('cloudinit.reporting.report_finish_event') - @mock.patch('cloudinit.reporting.report_start_event') - def test_finish_exception_defaults_fail(self, report_start, report_finish): - name = "myname" - desc = "mydesc" - try: - with reporting.ReportEventStack(name, description=desc): - raise ValueError("This didnt work") - except ValueError: - pass - self.assertEqual([mock.call(name, desc)], report_start.call_args_list) - self.assertEqual( - [mock.call(name, desc, reporting.status.FAIL)], - report_finish.call_args_list) - - @mock.patch('cloudinit.reporting.report_finish_event') - @mock.patch('cloudinit.reporting.report_start_event') - def test_result_on_exception_used(self, report_start, report_finish): - name = "myname" - desc = "mydesc" - try: - with reporting.ReportEventStack( - name, desc, result_on_exception=reporting.status.WARN): - raise ValueError("This didnt work") - except ValueError: - pass - self.assertEqual([mock.call(name, desc)], report_start.call_args_list) - self.assertEqual( - [mock.call(name, desc, reporting.status.WARN)], - report_finish.call_args_list) - - @mock.patch('cloudinit.reporting.report_start_event') - def test_child_fullname_respects_parent(self, report_start): - parent_name = "topname" - c1_name = "c1name" - c2_name = "c2name" - c2_expected_fullname = '/'.join([parent_name, c1_name, c2_name]) - c1_expected_fullname = '/'.join([parent_name, c1_name]) - - parent = reporting.ReportEventStack(parent_name, "topdesc") - c1 = reporting.ReportEventStack(c1_name, "c1desc", parent=parent) - c2 = reporting.ReportEventStack(c2_name, "c2desc", parent=c1) - with c1: - report_start.assert_called_with(c1_expected_fullname, "c1desc") - with c2: - report_start.assert_called_with(c2_expected_fullname, "c2desc") - - @mock.patch('cloudinit.reporting.report_finish_event') - @mock.patch('cloudinit.reporting.report_start_event') - def test_child_result_bubbles_up(self, report_start, report_finish): - parent = reporting.ReportEventStack("topname", "topdesc") - child = reporting.ReportEventStack("c_name", "c_desc", parent=parent) - with parent: - with child: - child.result = reporting.status.WARN - - report_finish.assert_called_with( - "topname", "topdesc", reporting.status.WARN) - - @mock.patch('cloudinit.reporting.report_finish_event') - def test_message_used_in_finish(self, report_finish): - with reporting.ReportEventStack("myname", "mydesc", - message="mymessage"): - pass - self.assertEqual( - [mock.call("myname", "mymessage", reporting.status.SUCCESS)], - report_finish.call_args_list) - - @mock.patch('cloudinit.reporting.report_finish_event') - def test_message_updatable(self, report_finish): - with reporting.ReportEventStack("myname", "mydesc") as c: - c.message = "all good" - self.assertEqual( - [mock.call("myname", "all good", reporting.status.SUCCESS)], - report_finish.call_args_list) - - @mock.patch('cloudinit.reporting.report_start_event') - @mock.patch('cloudinit.reporting.report_finish_event') - def test_reporting_disabled_does_not_report_events( - self, report_start, report_finish): - with reporting.ReportEventStack("a", "b", reporting_enabled=False): - pass - self.assertEqual(report_start.call_count, 0) - self.assertEqual(report_finish.call_count, 0) - - @mock.patch('cloudinit.reporting.report_start_event') - @mock.patch('cloudinit.reporting.report_finish_event') - def test_reporting_child_default_to_parent( - self, report_start, report_finish): - parent = reporting.ReportEventStack( - "pname", "pdesc", reporting_enabled=False) - child = reporting.ReportEventStack("cname", "cdesc", parent=parent) - with parent: - with child: - pass - pass - self.assertEqual(report_start.call_count, 0) - self.assertEqual(report_finish.call_count, 0) - - def test_reporting_event_has_sane_repr(self): - myrep = reporting.ReportEventStack("fooname", "foodesc", - reporting_enabled=True).__repr__() - self.assertIn("fooname", myrep) - self.assertIn("foodesc", myrep) - self.assertIn("True", myrep) - - def test_set_invalid_result_raises_value_error(self): - f = reporting.ReportEventStack("myname", "mydesc") - self.assertRaises(ValueError, setattr, f, "result", "BOGUS") - - -class TestStatusAccess(TestCase): - def test_invalid_status_access_raises_value_error(self): - self.assertRaises(AttributeError, getattr, reporting.status, "BOGUS") diff --git a/cloudinit/tests/test_safeyaml.py b/cloudinit/tests/test_safeyaml.py deleted file mode 100644 index da90d53f..00000000 --- a/cloudinit/tests/test_safeyaml.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -from cloudinit import safeyaml as yaml -from cloudinit.tests import TestCase - -import tempfile - - -class TestSafeYaml(TestCase): - def test_simple(self): - blob = '\nk1: one\nk2: two' - expected = {'k1': "one", 'k2': "two"} - self.assertEqual(yaml.loads(blob), expected) - - def test_bogus_raises_exception(self): - badyaml = "1\n 2:" - self.assertRaises(yaml.YAMLError, yaml.loads, badyaml) - - def test_unsafe_types(self): - # should not load complex types - unsafe_yaml = "!!python/object:__builtin__.object {}" - self.assertRaises(yaml.YAMLError, yaml.loads, unsafe_yaml) - - def test_python_unicode_not_allowed(self): - # python/unicode is not allowed - # in the past this type was allowed, but not now, so explicit test. - blob = "{k1: !!python/unicode 'my unicode', k2: my string}" - self.assertRaises(yaml.YAMLError, yaml.loads, blob) - - def test_dumps_returns_string(self): - self.assertTrue( - isinstance(yaml.dumps(867 - 5309), (str,))) - - def test_dumps_is_loadable(self): - mydata = {'a': 'hey', 'b': ['bee', 'Bea']} - self.assertEqual(yaml.loads(yaml.dumps(mydata)), mydata) - - def test_load(self): - valid_yaml = "foo: bar" - expected = {'foo': 'bar'} - with tempfile.NamedTemporaryFile(mode='w', delete=False) as tmpf: - tmpf.write(valid_yaml) - tmpf.close() - self.assertEqual(yaml.load(tmpf.name), expected) diff --git a/cloudinit/tests/test_shell.py b/cloudinit/tests/test_shell.py deleted file mode 100644 index 2b115862..00000000 --- a/cloudinit/tests/test_shell.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -import six - -import cloudinit.shell as shell -from cloudinit.tests import TestCase -from cloudinit.tests.util import mock - - -class TestMain(TestCase): - - def test_help_exits_success(self): - with mock.patch('cloudinit.shell.sys.stdout'): - exc = self.assertRaises( - SystemExit, shell.main, args=['cloud-init', '--help']) - self.assertEqual(exc.code, 0) - - def test_invalid_arguments_exit_fail(self): - # silence writes that get to stderr - with mock.patch('cloudinit.shell.sys.stderr'): - exc = self.assertRaises( - SystemExit, shell.main, args=['cloud-init', 'bogus_argument']) - self.assertNotEqual(exc.code, 0) - - @mock.patch('cloudinit.shell.sys.stdout') - def test_version_shows_cloud_init(self, mock_out_write): - shell.main(args=['cloud-init', 'version']) - write_arg = mock_out_write.write.call_args[0][0] - self.assertTrue(write_arg.startswith('cloud-init')) - - @mock.patch('cloudinit.shell.sys.stderr', new_callable=six.StringIO) - def test_no_arguments_shows_usage(self, stderr): - self.assertRaises(SystemExit, shell.main, args=['cloud-init']) - self.assertIn('usage: cloud-init', stderr.getvalue()) - - @mock.patch('cloudinit.shell.sys.stderr', mock.MagicMock()) - def test_no_arguments_exits_2(self): - exc = self.assertRaises(SystemExit, shell.main, args=['cloud-init']) - self.assertEqual(2, exc.code) - - @mock.patch('cloudinit.shell.sys.stderr', new_callable=six.StringIO) - def test_no_arguments_shows_error_message(self, stderr): - self.assertRaises(SystemExit, shell.main, args=['cloud-init']) - self.assertIn('cloud-init: error: too few arguments', - stderr.getvalue()) - - -class TestLoggingConfiguration(TestCase): - - @mock.patch('cloudinit.shell.sys.stderr', new_callable=six.StringIO) - def test_log_to_console(self, stderr): - shell.main(args=['cloud-init', '--log-to-console', 'version']) - shell.logging.getLogger().info('test log message') - self.assertIn('test log message', stderr.getvalue()) - - @mock.patch('cloudinit.shell.sys.stderr', new_callable=six.StringIO) - def test_log_to_console_not_default(self, stderr): - shell.main(args=['cloud-init', 'version']) - shell.logging.getLogger().info('test log message') - self.assertNotIn('test log message', stderr.getvalue()) diff --git a/cloudinit/tests/test_templating.py b/cloudinit/tests/test_templating.py deleted file mode 100644 index e4c8ce76..00000000 --- a/cloudinit/tests/test_templating.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -import fixtures -import mock -import os -import textwrap - -from cloudinit import templater -from cloudinit.tests import TestCase - - -class TestTemplates(TestCase): - jinja_tmpl = '\n'.join(( - "## template:jinja", - "{{a}},{{b}}", - "" - )) - jinja_params = {'a': '1', 'b': '2'} - jinja_expected = '1,2\n' - - def test_render_basic(self): - in_data = textwrap.dedent(""" - ${b} - - c = d - """) - in_data = in_data.strip() - expected_data = textwrap.dedent(""" - 2 - - c = d - """) - out_data = templater.basic_render(in_data, {'b': 2}) - self.assertEqual(expected_data.strip(), out_data) - - def test_render_jinja(self): - c = templater.render_string(self.jinja_tmpl, self.jinja_params) - self.assertEqual(self.jinja_expected, c) - - def test_render_jinja_crlf(self): - blob = '\r\n'.join(( - "## template:jinja", - "{{a}},{{b}}")) - c = templater.render_string(blob, {"a": 1, "b": 2}) - self.assertEqual("1,2", c) - - def test_render_default(self): - blob = '''$a,$b''' - c = templater.render_string(blob, {"a": 1, "b": 2}) - self.assertEqual("1,2", c) - - def test_render_explict_default(self): - blob = '\n'.join(('## template: basic', '$a,$b',)) - c = templater.render_string(blob, {"a": 1, "b": 2}) - self.assertEqual("1,2", c) - - def test_render_basic_deeper(self): - hn = 'myfoohost.yahoo.com' - expected_data = "h=%s\nc=d\n" % hn - in_data = "h=$hostname.canonical_name\nc=d\n" - params = { - "hostname": { - "canonical_name": hn, - }, - } - out_data = templater.render_string(in_data, params) - self.assertEqual(expected_data, out_data) - - def test_render_basic_no_parens(self): - hn = "myfoohost" - in_data = "h=$hostname\nc=d\n" - expected_data = "h=%s\nc=d\n" % hn - out_data = templater.basic_render(in_data, {'hostname': hn}) - self.assertEqual(expected_data, out_data) - - def test_render_basic_parens(self): - hn = "myfoohost" - in_data = "h = ${hostname}\nc=d\n" - expected_data = "h = %s\nc=d\n" % hn - out_data = templater.basic_render(in_data, {'hostname': hn}) - self.assertEqual(expected_data, out_data) - - def test_render_basic2(self): - mirror = "mymirror" - codename = "zany" - in_data = "deb $mirror $codename-updates main contrib non-free" - ex_data = "deb %s %s-updates main contrib non-free" % (mirror, - codename) - - out_data = templater.basic_render( - in_data, {'mirror': mirror, 'codename': codename}) - self.assertEqual(ex_data, out_data) - - def test_render_basic_exception_1(self): - in_data = "h=${foo.bar}" - self.assertRaises( - TypeError, templater.basic_render, in_data, {'foo': [1, 2]}) - - def test_unknown_renderer_raises_exception(self): - blob = '\n'.join(( - "## template:bigfastcat", - "Hellow $name" - "")) - self.assertRaises( - ValueError, templater.render_string, blob, {'name': 'foo'}) - - @mock.patch.object(templater, 'JINJA_AVAILABLE', False) - def test_jinja_without_jinja_raises_exception(self): - blob = '\n'.join(( - "## template:jinja", - "Hellow {{name}}" - "")) - templater.JINJA_AVAILABLE = False - self.assertRaises( - ValueError, templater.render_string, blob, {'name': 'foo'}) - - def test_render_from_file(self): - td = self.useFixture(fixtures.TempDir()).path - fname = os.path.join(td, "myfile") - with open(fname, "w") as fp: - fp.write(self.jinja_tmpl) - rendered = templater.render_from_file(fname, self.jinja_params) - self.assertEqual(rendered, self.jinja_expected) - - def test_render_to_file(self): - td = self.useFixture(fixtures.TempDir()).path - src = os.path.join(td, "src") - target = os.path.join(td, "target") - with open(src, "w") as fp: - fp.write(self.jinja_tmpl) - templater.render_to_file(src, target, self.jinja_params) - with open(target, "r") as fp: - rendered = fp.read() - self.assertEqual(rendered, self.jinja_expected) diff --git a/cloudinit/tests/test_url_helper.py b/cloudinit/tests/test_url_helper.py deleted file mode 100644 index bd1b9236..00000000 --- a/cloudinit/tests/test_url_helper.py +++ /dev/null @@ -1,138 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -import httpretty - -from cloudinit.tests import TestCase -from cloudinit.tests.util import mock -from cloudinit import url_helper - - -class TimeJumpSideEffect(object): - - def __init__(self, first_time, remaining_time): - def generator(): - yield first_time - while True: - yield remaining_time - - self.time = generator() - - def __call__(self): - return next(self.time) - - -class UrlHelperWaitForUrlsTest(TestCase): - - @httpretty.activate - def test_url_wait_for(self): - urls_actions = [ - ("http://www.yahoo.com", (False, False, True)), - ("http://www.google.com", (False, False, False)), - ] - urls = [] - for (url, actions) in urls_actions: - urls.append(url) - for worked in actions: - if worked: - httpretty.register_uri(httpretty.GET, - url, body=b'it worked!') - else: - httpretty.register_uri(httpretty.GET, - url, body=b'no worky', - status=400) - - url, response = url_helper.wait_any_url(urls) - self.assertEqual("http://www.yahoo.com", url) - self.assertIsInstance(response, url_helper.RequestsResponse) - self.assertEqual(response.contents, b'it worked!') - - @httpretty.activate - @mock.patch.object( - url_helper, 'now', mock.Mock(side_effect=TimeJumpSideEffect(0, 100))) - def test_url_wait_for_no_work(self): - - def request_callback(request, uri, headers): - return (400, headers, b"no worky") - - urls = [ - "http://www.yahoo.com", - "http://www.google.com", - ] - for url in urls: - httpretty.register_uri(httpretty.GET, - url, body=request_callback) - - self.assertIsNone(url_helper.wait_any_url(urls, max_wait=1)) - - -class UrlHelperFetchTest(TestCase): - - @httpretty.activate - def test_url_fetch(self): - httpretty.register_uri(httpretty.GET, - "http://www.yahoo.com", - body=b'it worked!') - - resp = url_helper.read_url("http://www.yahoo.com") - self.assertEqual(b"it worked!", resp.contents) - self.assertEqual(url_helper.OK, resp.status_code) - - @httpretty.activate - def test_no_protocol_url(self): - body = b'it worked!' - no_proto = 'www.yahoo.com' - httpretty.register_uri(httpretty.GET, "http://" + no_proto, body=body) - resp = url_helper.read_url(no_proto) - self.assertTrue(resp.url.startswith("http://")) - - @httpretty.activate - def test_response_has_url(self): - body = b'it worked!' - url = 'http://www.yahoo.com/' - httpretty.register_uri(httpretty.GET, url, body=body) - resp = url_helper.read_url(url) - self.assertEqual(resp.url, url) - self.assertEqual(body, resp.contents) - - @httpretty.activate - def test_retry_url_fetch(self): - httpretty.register_uri(httpretty.GET, - "http://www.yahoo.com", - responses=[ - httpretty.Response(body=b"no worky", - status=400), - httpretty.Response(body=b"it worked!", - status=200), - ]) - - resp = url_helper.read_url("http://www.yahoo.com", retries=2) - self.assertEqual(b"it worked!", resp.contents) - self.assertEqual(url_helper.OK, resp.status_code) - - @httpretty.activate - def test_failed_url_fetch(self): - httpretty.register_uri(httpretty.GET, - "http://www.yahoo.com", - body=b'no worky', status=400) - self.assertRaises(url_helper.UrlError, - url_helper.read_url, "http://www.yahoo.com") - - @httpretty.activate - def test_failed_retry_url_fetch(self): - httpretty.register_uri(httpretty.GET, - "http://www.yahoo.com", - responses=[ - httpretty.Response(body=b"no worky", - status=400), - httpretty.Response(body=b"no worky", - status=400), - httpretty.Response(body=b"no worky", - status=400), - ]) - - self.assertRaises(url_helper.UrlError, - url_helper.read_url, "http://www.yahoo.com", - retries=2) diff --git a/cloudinit/tests/util.py b/cloudinit/tests/util.py deleted file mode 100644 index b1331527..00000000 --- a/cloudinit/tests/util.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -import logging -import sys - -try: - from unittest import mock -except ImportError: - import mock # noqa - - -_IS_PY26 = sys.version_info[0:2] == (2, 6) - - -# This is similar with unittest.TestCase.assertLogs from Python 3.4. -class SnatchHandler(logging.Handler): - - if _IS_PY26: - # Old style junk is required on 2.6... - def __init__(self, *args, **kwargs): - logging.Handler.__init__(self, *args, **kwargs) - self.output = [] - else: - def __init__(self, *args, **kwargs): - super(SnatchHandler, self).__init__(*args, **kwargs) - self.output = [] - - def emit(self, record): - msg = self.format(record) - self.output.append(msg) - - -class LogSnatcher(object): - """A context manager to capture emitted logged messages. - - The class can be used as following:: - - with LogSnatcher('plugins.windows.createuser') as snatcher: - LOG.info("doing stuff") - LOG.info("doing stuff %s", 1) - LOG.warn("doing other stuff") - ... - self.assertEqual(snatcher.output, - ['INFO:unknown:doing stuff', - 'INFO:unknown:doing stuff 1', - 'WARN:unknown:doing other stuff']) - """ - - @property - def output(self): - """Get the output of this Snatcher. - - The output is a list of log messages, already formatted. - """ - return self._snatch_handler.output - - def __init__(self, logger_name): - self._logger_name = logger_name - self._snatch_handler = SnatchHandler() - self._logger = logging.getLogger(self._logger_name) - self._previous_level = self._logger.getEffectiveLevel() - - def __enter__(self): - self._logger.setLevel(logging.DEBUG) - self._logger.handlers.append(self._snatch_handler) - return self - - def __exit__(self, *args): - self._logger.handlers.remove(self._snatch_handler) - self._logger.setLevel(self._previous_level) diff --git a/cloudinit/url_helper.py b/cloudinit/url_helper.py deleted file mode 100644 index effeb683..00000000 --- a/cloudinit/url_helper.py +++ /dev/null @@ -1,307 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -import time - -try: - from time import monotonic as now -except ImportError: # pragma: nocover - from time import time as now - -import requests -from requests import adapters -from requests import exceptions -from requests import structures - -# Arg, why does requests vendorize urllib3.... -from requests.packages.urllib3 import util as urllib3_util - -from six.moves.urllib.parse import quote as urlquote # noqa -from six.moves.urllib.parse import urlparse # noqa -from six.moves.urllib.parse import urlunparse # noqa - -from six.moves.http_client import BAD_REQUEST as _BAD_REQUEST -from six.moves.http_client import CONFLICT # noqa -from six.moves.http_client import MULTIPLE_CHOICES as _MULTIPLE_CHOICES -from six.moves.http_client import OK - -from cloudinit import logging -from cloudinit import version - - -SSL_ENABLED = True -try: - import ssl as _ssl # noqa -except ImportError: - SSL_ENABLED = False - - -LOG = logging.getLogger(__name__) - - -def _get_base_url(url): - parsed_url = list(urlparse(url, scheme='http')) - parsed_url[2] = parsed_url[3] = parsed_url[4] = parsed_url[5] = '' - return urlunparse(parsed_url) - - -def _clean_url(url): - parsed_url = list(urlparse(url, scheme='http')) - if not parsed_url[1] and parsed_url[2]: - # Swap these since this seems to be a common - # occurrence when given urls like 'www.google.com' - parsed_url[1] = parsed_url[2] - parsed_url[2] = '' - return urlunparse(parsed_url) - - -class _Retry(urllib3_util.Retry): - def is_forced_retry(self, method, status_code): - # Allow >= 400 to be tried... - return status_code >= _BAD_REQUEST - - def sleep(self): - # The base class doesn't have a way to log what we are doing, - # so replace it with one that does... - backoff = self.get_backoff_time() - if backoff <= 0: - return - else: - LOG.debug("Please wait %s seconds while we wait to try again", - backoff) - time.sleep(backoff) - - -class RequestsResponse(object): - """A wrapper for requests responses (that provides common functions). - - This exists so that things like StringResponse or FileResponse can - also exist, but with different sources of their response (aka not - just from the requests library). - """ - - def __init__(self, response): - self._response = response - - @property - def contents(self): - return self._response.content - - @property - def url(self): - return self._response.url - - def ok(self, redirects_ok=False): - upper = _MULTIPLE_CHOICES - if redirects_ok: - upper = _BAD_REQUEST - return self.status_code >= OK and self.status_code < upper - - @property - def headers(self): - return self._response.headers - - @property - def status_code(self): - return self._response.status_code - - def __str__(self): - return self._response.text - - -class UrlError(IOError): - def __init__(self, cause, code=None, headers=None): - super(UrlError, self).__init__(str(cause)) - self.cause = cause - self.status_code = code - self.headers = headers or {} - - -def _get_ssl_args(url, ssl_details): - ssl_args = {} - scheme = urlparse(url).scheme - if scheme == 'https' and ssl_details: - if not SSL_ENABLED: - LOG.warn("SSL is not supported, " - "cert. verification can not occur!") - else: - if 'ca_certs' in ssl_details and ssl_details['ca_certs']: - ssl_args['verify'] = ssl_details['ca_certs'] - else: - ssl_args['verify'] = True - if 'cert_file' in ssl_details and 'key_file' in ssl_details: - ssl_args['cert'] = [ssl_details['cert_file'], - ssl_details['key_file']] - elif 'cert_file' in ssl_details: - ssl_args['cert'] = str(ssl_details['cert_file']) - return ssl_args - - -def read_url(url, data=None, timeout=None, retries=0, - headers=None, ssl_details=None, - check_status=True, allow_redirects=True): - """Fetch a url (or post to one) with the given options. - - :param url: url to fetch - :param data: - any data to POST (this switches the request method to POST - instead of GET) - :param timeout: the timeout (in seconds) to wait for a response - :param headers: any headers to provide (and send along) in the request - :param ssl_details: - a dictionary containing any ssl settings, cert_file, ca_certs - and verify are valid entries (and they are only used when the - url provided is https) - :param check_status: - checks that the response status is OK after fetching (this - ensures a exception is raised on non-OK status codes) - :param allow_redirects: enables redirects (or disables them) - :param retries: - maximum number of retries to attempt when fetching the url and - the fetch fails - """ - url = _clean_url(url) - request_args = { - 'url': url, - } - request_args.update(_get_ssl_args(url, ssl_details)) - request_args['allow_redirects'] = allow_redirects - request_args['method'] = 'GET' - if timeout is not None: - request_args['timeout'] = max(float(timeout), 0) - if data: - request_args['method'] = 'POST' - request_args['data'] = data - if not headers: - headers = structures.CaseInsensitiveDict() - else: - headers = structures.CaseInsensitiveDict(headers) - if 'User-Agent' not in headers: - headers['User-Agent'] = 'Cloud-Init/%s' % (version.version_string()) - request_args['headers'] = headers - session = requests.Session() - if retries: - retry = _Retry(total=max(int(retries), 0), - raise_on_redirect=not allow_redirects) - session.mount(_get_base_url(url), - adapters.HTTPAdapter(max_retries=retry)) - try: - with session: - response = session.request(**request_args) - if check_status: - response.raise_for_status() - except exceptions.RequestException as e: - if e.response is not None: - raise UrlError(e, code=e.response.status_code, - headers=e.response.headers) - else: - raise UrlError(e) - else: - LOG.debug("Read from %s (%s, %sb)", url, response.status_code, - len(response.content)) - return RequestsResponse(response) - - -def wait_any_url(urls, max_wait=None, timeout=None, - status_cb=None, sleep_time=1, - exception_cb=None): - """Wait for one of many urls to respond correctly. - - :param urls: a list of urls to try - :param max_wait: roughly the maximum time to wait before giving up - :param timeout: the timeout provided to ``read_url`` - :param status_cb: - call method with string message when a url is not available - :param exception_cb: - call method with 2 arguments 'msg' (per status_cb) and - 'exception', the exception that occurred. - :param sleep_time: how long to sleep before trying each url again - - The idea of this routine is to wait for the EC2 metdata service to - come up. On both Eucalyptus and EC2 we have seen the case where - the instance hit the MD before the MD service was up. EC2 seems - to have permenantely fixed this, though. - - In openstack, the metadata service might be painfully slow, and - unable to avoid hitting a timeout of even up to 10 seconds or more - (LP: #894279) for a simple GET. - - Offset those needs with the need to not hang forever (and block boot) - on a system where cloud-init is configured to look for EC2 Metadata - service but is not going to find one. It is possible that the instance - data host (169.254.169.254) may be firewalled off Entirely for a sytem, - meaning that the connection will block forever unless a timeout is set. - - This will return a tuple of the first url which succeeded and the - response object. - """ - start_time = now() - - def log_status_cb(msg, exc=None): - LOG.debug(msg) - - if not status_cb: - status_cb = log_status_cb - - def timeup(max_wait, start_time): - current_time = now() - return ((max_wait <= 0 or max_wait is None) or - (current_time - start_time > max_wait)) - - loop_n = 0 - while True: - # This makes a backoff with the following graph: - # - # https://www.desmos.com/calculator/c8pwjy6wmt - sleep_time = int(loop_n / 5) + 1 - for url in urls: - current_time = now() - if loop_n != 0: - if timeup(max_wait, start_time): - break - if (timeout and - (current_time + timeout > (start_time + max_wait))): - # shorten timeout to not run way over max_time - timeout = int((start_time + max_wait) - current_time) - reason = "" - url_exc = None - try: - response = read_url(url, timeout=timeout, check_status=False) - if not response.contents: - reason = "empty response [%s]" % (response.code) - url_exc = UrlError(ValueError(reason), code=response.code, - headers=response.headers) - elif not response.ok(): - reason = "bad status code [%s]" % (response.code) - url_exc = UrlError(ValueError(reason), code=response.code, - headers=response.headers) - else: - return url, response - except UrlError as e: - reason = "request error [%s]" % e - url_exc = e - except Exception as e: - reason = "unexpected error [%s]" % e - url_exc = e - - current_time = now() - time_taken = int(current_time - start_time) - status_msg = "Calling '%s' failed [%s/%ss]: %s" % (url, - time_taken, - max_wait, - reason) - status_cb(status_msg) - if exception_cb: - exception_cb(msg=status_msg, exception=url_exc) - - if timeup(max_wait, start_time): - break - - loop_n = loop_n + 1 - LOG.debug("Please wait %s seconds while we wait to try again", - sleep_time) - time.sleep(sleep_time) - - return None diff --git a/cloudinit/util.py b/cloudinit/util.py deleted file mode 100644 index 368ebeb6..00000000 --- a/cloudinit/util.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -from cloudinit import logging - -LOG = logging.getLogger(__name__) - - -def load_file(path, encoding='utf8'): - LOG.blather("Loading file from path '%s' (%s)", path, encoding) - with open(path, 'rb') as fh: - return fh.read().decode(encoding) - - -class abstractclassmethod(classmethod): - """A backport for abc.abstractclassmethod from Python 3.""" - - __isabstractmethod__ = True - - def __init__(self, func): - func.__isabstractmethod__ = True - super(abstractclassmethod, self).__init__(func) diff --git a/cloudinit/version.py b/cloudinit/version.py deleted file mode 100644 index a153e67e..00000000 --- a/cloudinit/version.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab - -import pkg_resources - -try: - from pbr import version as pbr_version - _version_info = pbr_version.VersionInfo('cloudinit') - version_string = _version_info.version_string -except ImportError: # pragma: nocover - _version_info = pkg_resources.get_distribution('cloudinit') - version_string = lambda: _version_info.version diff --git a/doc/source/api.rst b/doc/source/api.rst deleted file mode 100644 index 85ff488c..00000000 --- a/doc/source/api.rst +++ /dev/null @@ -1,7 +0,0 @@ -cloud-init Python API Documentation -=================================== - -.. toctree:: - :maxdepth: 2 - - api/autoindex diff --git a/doc/source/api/README b/doc/source/api/README deleted file mode 100644 index 643cd1e0..00000000 --- a/doc/source/api/README +++ /dev/null @@ -1,4 +0,0 @@ -Don't put files in here, it's intended only for auto-generated output! - -Specifically, files which aren't known to git will be cleaned out of -this directory before the docs are generated by tox. diff --git a/doc/source/conf.py b/doc/source/conf.py deleted file mode 100644 index 7d2cc54d..00000000 --- a/doc/source/conf.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright 2015 Canonical Ltd. -# This file is part of cloud-init. See LICENCE file for license information. -# -# vi: ts=4 expandtab -extensions = ['sphinx.ext.autodoc'] diff --git a/doc/source/contents.rst b/doc/source/contents.rst deleted file mode 100644 index c0615986..00000000 --- a/doc/source/contents.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. _index: - -===================== -Documentation -===================== - -.. rubric:: Everything about cloud-init. - - -Summary ------------------ - -`Cloud-init`_ is the *defacto* multi-distribution package that handles -early initialization of a cloud instance. - -.. toctree:: - :maxdepth: 2 - - api - - -.. _Cloud-init: https://launchpad.net/cloud-init diff --git a/inits/systemd/cloud-config.service b/inits/systemd/cloud-config.service deleted file mode 100644 index 41a86147..00000000 --- a/inits/systemd/cloud-config.service +++ /dev/null @@ -1,17 +0,0 @@ -[Unit] -Description=Apply the settings specified in cloud-config -After=network.target syslog.target cloud-config.target -Requires=cloud-config.target -Wants=network.target - -[Service] -Type=oneshot -ExecStart=/usr/bin/cloud-init modules --mode=config -RemainAfterExit=yes -TimeoutSec=0 - -# Output needs to appear in instance console output -StandardOutput=journal+console - -[Install] -WantedBy=multi-user.target diff --git a/inits/systemd/cloud-config.target b/inits/systemd/cloud-config.target deleted file mode 100644 index 28f5bcf1..00000000 --- a/inits/systemd/cloud-config.target +++ /dev/null @@ -1,10 +0,0 @@ -# cloud-init normally emits a "cloud-config" upstart event to inform third -# parties that cloud-config is available, which does us no good when we're -# using systemd. cloud-config.target serves as this synchronization point -# instead. Services that would "start on cloud-config" with upstart can -# instead use "After=cloud-config.target" and "Wants=cloud-config.target" -# as appropriate. - -[Unit] -Description=Cloud-config availability -Requires=cloud-init-local.service cloud-init.service diff --git a/inits/systemd/cloud-final.service b/inits/systemd/cloud-final.service deleted file mode 100644 index ef0f52b9..00000000 --- a/inits/systemd/cloud-final.service +++ /dev/null @@ -1,17 +0,0 @@ -[Unit] -Description=Execute cloud user/final scripts -After=network.target syslog.target cloud-config.service rc-local.service -Requires=cloud-config.target -Wants=network.target - -[Service] -Type=oneshot -ExecStart=/usr/bin/cloud-init modules --mode=final -RemainAfterExit=yes -TimeoutSec=0 - -# Output needs to appear in instance console output -StandardOutput=journal+console - -[Install] -WantedBy=multi-user.target diff --git a/inits/systemd/cloud-init-local.service b/inits/systemd/cloud-init-local.service deleted file mode 100644 index a31985c6..00000000 --- a/inits/systemd/cloud-init-local.service +++ /dev/null @@ -1,16 +0,0 @@ -[Unit] -Description=Initial cloud-init job (pre-networking) -Wants=local-fs.target -After=local-fs.target - -[Service] -Type=oneshot -ExecStart=/usr/bin/cloud-init init --local -RemainAfterExit=yes -TimeoutSec=0 - -# Output needs to appear in instance console output -StandardOutput=journal+console - -[Install] -WantedBy=multi-user.target diff --git a/inits/systemd/cloud-init.service b/inits/systemd/cloud-init.service deleted file mode 100644 index 6b0c7229..00000000 --- a/inits/systemd/cloud-init.service +++ /dev/null @@ -1,18 +0,0 @@ -[Unit] -Description=Initial cloud-init job (metadata service crawler) -After=local-fs.target network.target cloud-init-local.service -Before=sshd.service sshd-keygen.service -Requires=network.target -Wants=local-fs.target cloud-init-local.service sshd.service sshd-keygen.service - -[Service] -Type=oneshot -ExecStart=/usr/bin/cloud-init init -RemainAfterExit=yes -TimeoutSec=0 - -# Output needs to appear in instance console output -StandardOutput=journal+console - -[Install] -WantedBy=multi-user.target diff --git a/inits/sysvinit/debian/cloud-config b/inits/sysvinit/debian/cloud-config deleted file mode 100644 index 53322748..00000000 --- a/inits/sysvinit/debian/cloud-config +++ /dev/null @@ -1,64 +0,0 @@ -#! /bin/sh -### BEGIN INIT INFO -# Provides: cloud-config -# Required-Start: cloud-init cloud-init-local -# Required-Stop: -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Cloud init modules --mode config -# Description: Cloud configuration initialization -### END INIT INFO - -# Authors: Julien Danjou -# Juerg Haefliger -# Thomas Goirand - -PATH=/sbin:/usr/sbin:/bin:/usr/bin -DESC="Cloud service" -NAME=cloud-init -DAEMON=/usr/bin/$NAME -DAEMON_ARGS="modules --mode config" -SCRIPTNAME=/etc/init.d/$NAME - -# Exit if the package is not installed -[ -x "$DAEMON" ] || exit 0 - -# Read configuration variable file if it is present -[ -r /etc/default/$NAME ] && . /etc/default/$NAME - -# Define LSB log_* functions. -# Depend on lsb-base (>= 3.2-14) to ensure that this file is present -# and status_of_proc is working. -. /lib/lsb/init-functions - -if init_is_upstart; then - case "$1" in - stop) - exit 0 - ;; - *) - exit 1 - ;; - esac -fi - -case "$1" in -start) - log_daemon_msg "Starting $DESC" "$NAME" - $DAEMON ${DAEMON_ARGS} - case "$?" in - 0|1) log_end_msg 0 ;; - 2) log_end_msg 1 ;; - esac -;; -stop|restart|force-reload) - echo "Error: argument '$1' not supported" >&2 - exit 3 -;; -*) - echo "Usage: $SCRIPTNAME {start}" >&2 - exit 3 -;; -esac - -: diff --git a/inits/sysvinit/debian/cloud-final b/inits/sysvinit/debian/cloud-final deleted file mode 100644 index 55afc8b0..00000000 --- a/inits/sysvinit/debian/cloud-final +++ /dev/null @@ -1,66 +0,0 @@ -#! /bin/sh -### BEGIN INIT INFO -# Provides: cloud-final -# Required-Start: $all cloud-config -# Required-Stop: -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Cloud init modules final jobs -# Description: This runs the cloud configuration initialization "final" jobs -# and can be seen as the traditional "rc.local" time for the cloud. -# It runs after all cloud-config jobs are run -### END INIT INFO - -# Authors: Julien Danjou -# Juerg Haefliger -# Thomas Goirand - -PATH=/sbin:/usr/sbin:/bin:/usr/bin -DESC="Cloud service" -NAME=cloud-init -DAEMON=/usr/bin/$NAME -DAEMON_ARGS="modules --mode final" -SCRIPTNAME=/etc/init.d/$NAME - -# Exit if the package is not installed -[ -x "$DAEMON" ] || exit 0 - -# Read configuration variable file if it is present -[ -r /etc/default/$NAME ] && . /etc/default/$NAME - -# Define LSB log_* functions. -# Depend on lsb-base (>= 3.2-14) to ensure that this file is present -# and status_of_proc is working. -. /lib/lsb/init-functions - -if init_is_upstart; then - case "$1" in - stop) - exit 0 - ;; - *) - exit 1 - ;; - esac -fi - -case "$1" in -start) - log_daemon_msg "Starting $DESC" "$NAME" - $DAEMON ${DAEMON_ARGS} - case "$?" in - 0|1) log_end_msg 0 ;; - 2) log_end_msg 1 ;; - esac -;; -stop|restart|force-reload) - echo "Error: argument '$1' not supported" >&2 - exit 3 -;; -*) - echo "Usage: $SCRIPTNAME {start}" >&2 - exit 3 -;; -esac - -: diff --git a/inits/sysvinit/debian/cloud-init b/inits/sysvinit/debian/cloud-init deleted file mode 100755 index 48fa0423..00000000 --- a/inits/sysvinit/debian/cloud-init +++ /dev/null @@ -1,64 +0,0 @@ -#! /bin/sh -### BEGIN INIT INFO -# Provides: cloud-init -# Required-Start: $local_fs $remote_fs $syslog $network cloud-init-local -# Required-Stop: $remote_fs -# X-Start-Before: sshd -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Cloud init -# Description: Cloud configuration initialization -### END INIT INFO - -# Authors: Julien Danjou -# Thomas Goirand - -PATH=/sbin:/usr/sbin:/bin:/usr/bin -DESC="Cloud service" -NAME=cloud-init -DAEMON=/usr/bin/$NAME -DAEMON_ARGS="init" -SCRIPTNAME=/etc/init.d/$NAME - -# Exit if the package is not installed -[ -x "$DAEMON" ] || exit 0 - -# Read configuration variable file if it is present -[ -r /etc/default/$NAME ] && . /etc/default/$NAME - -# Define LSB log_* functions. -# Depend on lsb-base (>= 3.2-14) to ensure that this file is present -# and status_of_proc is working. -. /lib/lsb/init-functions - -if init_is_upstart; then - case "$1" in - stop) - exit 0 - ;; - *) - exit 1 - ;; - esac -fi - -case "$1" in - start) - log_daemon_msg "Starting $DESC" "$NAME" - $DAEMON ${DAEMON_ARGS} - case "$?" in - 0|1) log_end_msg 0 ;; - 2) log_end_msg 1 ;; - esac - ;; - stop|restart|force-reload) - echo "Error: argument '$1' not supported" >&2 - exit 3 - ;; - *) - echo "Usage: $SCRIPTNAME {start}" >&2 - exit 3 - ;; -esac - -: diff --git a/inits/sysvinit/debian/cloud-init-local b/inits/sysvinit/debian/cloud-init-local deleted file mode 100644 index 802ee8e9..00000000 --- a/inits/sysvinit/debian/cloud-init-local +++ /dev/null @@ -1,63 +0,0 @@ -#! /bin/sh -### BEGIN INIT INFO -# Provides: cloud-init-local -# Required-Start: $local_fs $remote_fs -# Required-Stop: -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Cloud init local -# Description: Cloud configuration initialization -### END INIT INFO - -# Authors: Julien Danjou -# Juerg Haefliger - -PATH=/sbin:/usr/sbin:/bin:/usr/bin -DESC="Cloud service" -NAME=cloud-init -DAEMON=/usr/bin/$NAME -DAEMON_ARGS="init --local" -SCRIPTNAME=/etc/init.d/$NAME - -# Exit if the package is not installed -[ -x "$DAEMON" ] || exit 0 - -# Read configuration variable file if it is present -[ -r /etc/default/$NAME ] && . /etc/default/$NAME - -# Define LSB log_* functions. -# Depend on lsb-base (>= 3.2-14) to ensure that this file is present -# and status_of_proc is working. -. /lib/lsb/init-functions - -if init_is_upstart; then - case "$1" in - stop) - exit 0 - ;; - *) - exit 1 - ;; - esac -fi - -case "$1" in -start) - log_daemon_msg "Starting $DESC" "$NAME" - $DAEMON ${DAEMON_ARGS} - case "$?" in - 0|1) log_end_msg 0 ;; - 2) log_end_msg 1 ;; - esac -;; -stop|restart|force-reload) - echo "Error: argument '$1' not supported" >&2 - exit 3 -;; -*) - echo "Usage: $SCRIPTNAME {start}" >&2 - exit 3 -;; -esac - -: diff --git a/inits/sysvinit/freebsd/cloudconfig b/inits/sysvinit/freebsd/cloudconfig deleted file mode 100755 index 01bc061e..00000000 --- a/inits/sysvinit/freebsd/cloudconfig +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh - -# PROVIDE: cloudconfig -# REQUIRE: cloudinit cloudinitlocal -# BEFORE: cloudfinal - -. /etc/rc.subr - -PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" -export CLOUD_CFG=/usr/local/etc/cloud/cloud.cfg - -name="cloudconfig" -command="/usr/local/bin/cloud-init" -start_cmd="cloudconfig_start" -stop_cmd=":" -rcvar="cloudinit_enable" -start_precmd="cloudinit_override" -start_cmd="cloudconfig_start" - -cloudinit_override() -{ - # If there exist sysconfig/defaults variable override files use it... - if [ -f /etc/defaults/cloud-init ]; then - . /etc/defaults/cloud-init - fi -} - -cloudconfig_start() -{ - echo "${command} starting" - ${command} modules --mode config -} - -load_rc_config $name -run_rc_command "$1" diff --git a/inits/sysvinit/freebsd/cloudfinal b/inits/sysvinit/freebsd/cloudfinal deleted file mode 100755 index 1b487aa0..00000000 --- a/inits/sysvinit/freebsd/cloudfinal +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh - -# PROVIDE: cloudfinal -# REQUIRE: LOGIN cloudinit cloudconfig cloudinitlocal -# REQUIRE: cron mail sshd swaplate - -. /etc/rc.subr - -PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" -export CLOUD_CFG=/usr/local/etc/cloud/cloud.cfg - -name="cloudfinal" -command="/usr/local/bin/cloud-init" -start_cmd="cloudfinal_start" -stop_cmd=":" -rcvar="cloudinit_enable" -start_precmd="cloudinit_override" -start_cmd="cloudfinal_start" - -cloudinit_override() -{ - # If there exist sysconfig/defaults variable override files use it... - if [ -f /etc/defaults/cloud-init ]; then - . /etc/defaults/cloud-init - fi -} - -cloudfinal_start() -{ - echo -n "${command} starting" - ${command} modules --mode final -} - -load_rc_config $name -run_rc_command "$1" diff --git a/inits/sysvinit/freebsd/cloudinit b/inits/sysvinit/freebsd/cloudinit deleted file mode 100755 index 862eeab4..00000000 --- a/inits/sysvinit/freebsd/cloudinit +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh - -# PROVIDE: cloudinit -# REQUIRE: FILESYSTEMS NETWORKING cloudinitlocal -# BEFORE: cloudconfig cloudfinal - -. /etc/rc.subr - -PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" -export CLOUD_CFG=/usr/local/etc/cloud/cloud.cfg - -name="cloudinit" -command="/usr/local/bin/cloud-init" -start_cmd="cloudinit_start" -stop_cmd=":" -rcvar="cloudinit_enable" -start_precmd="cloudinit_override" -start_cmd="cloudinit_start" - -cloudinit_override() -{ - # If there exist sysconfig/defaults variable override files use it... - if [ -f /etc/defaults/cloud-init ]; then - . /etc/defaults/cloud-init - fi -} - -cloudinit_start() -{ - echo -n "${command} starting" - ${command} init -} - -load_rc_config $name -run_rc_command "$1" diff --git a/inits/sysvinit/freebsd/cloudinitlocal b/inits/sysvinit/freebsd/cloudinitlocal deleted file mode 100755 index fb342a0f..00000000 --- a/inits/sysvinit/freebsd/cloudinitlocal +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh - -# PROVIDE: cloudinitlocal -# REQUIRE: mountcritlocal -# BEFORE: NETWORKING FILESYSTEMS cloudinit cloudconfig cloudfinal - -. /etc/rc.subr - -PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" -export CLOUD_CFG=/usr/local/etc/cloud/cloud.cfg - -name="cloudinitlocal" -command="/usr/local/bin/cloud-init" -start_cmd="cloudlocal_start" -stop_cmd=":" -rcvar="cloudinit_enable" -start_precmd="cloudinit_override" -start_cmd="cloudlocal_start" - -cloudinit_override() -{ - # If there exist sysconfig/defaults variable override files use it... - if [ -f /etc/defaults/cloud-init ]; then - . /etc/defaults/cloud-init - fi -} - -cloudlocal_start() -{ - echo -n "${command} starting" - ${command} init --local -} - -load_rc_config $name -run_rc_command "$1" diff --git a/inits/sysvinit/gentoo/cloud-config b/inits/sysvinit/gentoo/cloud-config deleted file mode 100644 index b0fa786d..00000000 --- a/inits/sysvinit/gentoo/cloud-config +++ /dev/null @@ -1,13 +0,0 @@ -#!/sbin/runscript - -depend() { - after cloud-init-local - after cloud-init - before cloud-final - provide cloud-config -} - -start() { - cloud-init modules --mode config - eend 0 -} diff --git a/inits/sysvinit/gentoo/cloud-final b/inits/sysvinit/gentoo/cloud-final deleted file mode 100644 index b457a354..00000000 --- a/inits/sysvinit/gentoo/cloud-final +++ /dev/null @@ -1,11 +0,0 @@ -#!/sbin/runscript - -depend() { - after cloud-config - provide cloud-final -} - -start() { - cloud-init modules --mode final - eend 0 -} diff --git a/inits/sysvinit/gentoo/cloud-init b/inits/sysvinit/gentoo/cloud-init deleted file mode 100644 index 9ab64ad8..00000000 --- a/inits/sysvinit/gentoo/cloud-init +++ /dev/null @@ -1,12 +0,0 @@ -#!/sbin/runscript -# add depends for network, dns, fs etc -depend() { - after cloud-init-local - before cloud-config - provide cloud-init -} - -start() { - cloud-init init - eend 0 -} diff --git a/inits/sysvinit/gentoo/cloud-init-local b/inits/sysvinit/gentoo/cloud-init-local deleted file mode 100644 index 9d47263e..00000000 --- a/inits/sysvinit/gentoo/cloud-init-local +++ /dev/null @@ -1,13 +0,0 @@ -#!/sbin/runscript - -depend() { - after localmount - after netmount - before cloud-init - provide cloud-init-local -} - -start() { - cloud-init init --local - eend 0 -} diff --git a/inits/sysvinit/redhat/cloud-config b/inits/sysvinit/redhat/cloud-config deleted file mode 100755 index e98c3376..00000000 --- a/inits/sysvinit/redhat/cloud-config +++ /dev/null @@ -1,105 +0,0 @@ -#!/bin/sh -# Copyright 2012 Yahoo! Inc. -# This file is part of cloud-init. See LICENCE file for license information. -# -# See: http://wiki.debian.org/LSBInitScripts -# See: http://tiny.cc/czvbgw -# See: http://www.novell.com/coolsolutions/feature/15380.html -# Also based on dhcpd in RHEL (for comparison) - -### BEGIN INIT INFO -# Provides: cloud-config -# Required-Start: cloud-init cloud-init-local -# Should-Start: $time -# Required-Stop: -# Should-Stop: -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: The config cloud-init job -# Description: Start cloud-init and runs the config phase -# and any associated config modules as desired. -### END INIT INFO - -# Return values acc. to LSB for all commands but status: -# 0 - success -# 1 - generic or unspecified error -# 2 - invalid or excess argument(s) -# 3 - unimplemented feature (e.g. "reload") -# 4 - user had insufficient privileges -# 5 - program is not installed -# 6 - program is not configured -# 7 - program is not running -# 8--199 - reserved (8--99 LSB, 100--149 distrib, 150--199 appl) -# -# Note that starting an already running service, stopping -# or restarting a not-running service as well as the restart -# with force-reload (in case signaling is not supported) are -# considered a success. - -RETVAL=0 - -prog="cloud-init" -cloud_init="/usr/bin/cloud-init" -conf="/etc/cloud/cloud.cfg" - -# If there exist sysconfig/default variable override files use it... -[ -f /etc/sysconfig/cloud-init ] && . /etc/sysconfig/cloud-init -[ -f /etc/default/cloud-init ] && . /etc/default/cloud-init - -start() { - [ -x $cloud_init ] || return 5 - [ -f $conf ] || return 6 - - echo -n $"Starting $prog: " - $cloud_init $CLOUDINITARGS modules --mode config - RETVAL=$? - return $RETVAL -} - -stop() { - echo -n $"Shutting down $prog: " - # No-op - RETVAL=7 - return $RETVAL -} - -case "$1" in - start) - start - RETVAL=$? - ;; - stop) - stop - RETVAL=$? - ;; - restart|try-restart|condrestart) - ## Stop the service and regardless of whether it was - ## running or not, start it again. - # - ## Note: try-restart is now part of LSB (as of 1.9). - ## RH has a similar command named condrestart. - start - RETVAL=$? - ;; - reload|force-reload) - # It does not support reload - RETVAL=3 - ;; - status) - echo -n $"Checking for service $prog:" - # Return value is slightly different for the status command: - # 0 - service up and running - # 1 - service dead, but /var/run/ pid file exists - # 2 - service dead, but /var/lock/ lock file exists - # 3 - service not running (unused) - # 4 - service status unknown :-( - # 5--199 reserved (5--99 LSB, 100--149 distro, 150--199 appl.) - RETVAL=3 - ;; - *) - echo "Usage: $0 {start|stop|status|try-restart|condrestart|restart|force-reload|reload}" - RETVAL=3 - ;; -esac - -exit $RETVAL diff --git a/inits/sysvinit/redhat/cloud-final b/inits/sysvinit/redhat/cloud-final deleted file mode 100755 index 5c17fddc..00000000 --- a/inits/sysvinit/redhat/cloud-final +++ /dev/null @@ -1,105 +0,0 @@ -#!/bin/sh -# Copyright 2012 Yahoo! Inc. -# This file is part of cloud-init. See LICENCE file for license information. - -# See: http://wiki.debian.org/LSBInitScripts -# See: http://tiny.cc/czvbgw -# See: http://www.novell.com/coolsolutions/feature/15380.html -# Also based on dhcpd in RHEL (for comparison) - -### BEGIN INIT INFO -# Provides: cloud-final -# Required-Start: $all cloud-config -# Should-Start: $time -# Required-Stop: -# Should-Stop: -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: The final cloud-init job -# Description: Start cloud-init and runs the final phase -# and any associated final modules as desired. -### END INIT INFO - -# Return values acc. to LSB for all commands but status: -# 0 - success -# 1 - generic or unspecified error -# 2 - invalid or excess argument(s) -# 3 - unimplemented feature (e.g. "reload") -# 4 - user had insufficient privileges -# 5 - program is not installed -# 6 - program is not configured -# 7 - program is not running -# 8--199 - reserved (8--99 LSB, 100--149 distrib, 150--199 appl) -# -# Note that starting an already running service, stopping -# or restarting a not-running service as well as the restart -# with force-reload (in case signaling is not supported) are -# considered a success. - -RETVAL=0 - -prog="cloud-init" -cloud_init="/usr/bin/cloud-init" -conf="/etc/cloud/cloud.cfg" - -# If there exist sysconfig/default variable override files use it... -[ -f /etc/sysconfig/cloud-init ] && . /etc/sysconfig/cloud-init -[ -f /etc/default/cloud-init ] && . /etc/default/cloud-init - -start() { - [ -x $cloud_init ] || return 5 - [ -f $conf ] || return 6 - - echo -n $"Starting $prog: " - $cloud_init $CLOUDINITARGS modules --mode final - RETVAL=$? - return $RETVAL -} - -stop() { - echo -n $"Shutting down $prog: " - # No-op - RETVAL=7 - return $RETVAL -} - -case "$1" in - start) - start - RETVAL=$? - ;; - stop) - stop - RETVAL=$? - ;; - restart|try-restart|condrestart) - ## Stop the service and regardless of whether it was - ## running or not, start it again. - # - ## Note: try-restart is now part of LSB (as of 1.9). - ## RH has a similar command named condrestart. - start - RETVAL=$? - ;; - reload|force-reload) - # It does not support reload - RETVAL=3 - ;; - status) - echo -n $"Checking for service $prog:" - # Return value is slightly different for the status command: - # 0 - service up and running - # 1 - service dead, but /var/run/ pid file exists - # 2 - service dead, but /var/lock/ lock file exists - # 3 - service not running (unused) - # 4 - service status unknown :-( - # 5--199 reserved (5--99 LSB, 100--149 distro, 150--199 appl.) - RETVAL=3 - ;; - *) - echo "Usage: $0 {start|stop|status|try-restart|condrestart|restart|force-reload|reload}" - RETVAL=3 - ;; -esac - -exit $RETVAL diff --git a/inits/sysvinit/redhat/cloud-init b/inits/sysvinit/redhat/cloud-init deleted file mode 100755 index af8f25f6..00000000 --- a/inits/sysvinit/redhat/cloud-init +++ /dev/null @@ -1,105 +0,0 @@ -#!/bin/sh -# Copyright 2012 Yahoo! Inc. -# This file is part of cloud-init. See LICENCE file for license information. - -# See: http://wiki.debian.org/LSBInitScripts -# See: http://tiny.cc/czvbgw -# See: http://www.novell.com/coolsolutions/feature/15380.html -# Also based on dhcpd in RHEL (for comparison) - -### BEGIN INIT INFO -# Provides: cloud-init -# Required-Start: $local_fs $network $named $remote_fs cloud-init-local -# Should-Start: $time -# Required-Stop: -# Should-Stop: -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: The initial cloud-init job (net and fs contingent) -# Description: Start cloud-init and runs the initialization phase -# and any associated initial modules as desired. -### END INIT INFO - -# Return values acc. to LSB for all commands but status: -# 0 - success -# 1 - generic or unspecified error -# 2 - invalid or excess argument(s) -# 3 - unimplemented feature (e.g. "reload") -# 4 - user had insufficient privileges -# 5 - program is not installed -# 6 - program is not configured -# 7 - program is not running -# 8--199 - reserved (8--99 LSB, 100--149 distrib, 150--199 appl) -# -# Note that starting an already running service, stopping -# or restarting a not-running service as well as the restart -# with force-reload (in case signaling is not supported) are -# considered a success. - -RETVAL=0 - -prog="cloud-init" -cloud_init="/usr/bin/cloud-init" -conf="/etc/cloud/cloud.cfg" - -# If there exist sysconfig/default variable override files use it... -[ -f /etc/sysconfig/cloud-init ] && . /etc/sysconfig/cloud-init -[ -f /etc/default/cloud-init ] && . /etc/default/cloud-init - -start() { - [ -x $cloud_init ] || return 5 - [ -f $conf ] || return 6 - - echo -n $"Starting $prog: " - $cloud_init $CLOUDINITARGS init - RETVAL=$? - return $RETVAL -} - -stop() { - echo -n $"Shutting down $prog: " - # No-op - RETVAL=7 - return $RETVAL -} - -case "$1" in - start) - start - RETVAL=$? - ;; - stop) - stop - RETVAL=$? - ;; - restart|try-restart|condrestart) - ## Stop the service and regardless of whether it was - ## running or not, start it again. - # - ## Note: try-restart is now part of LSB (as of 1.9). - ## RH has a similar command named condrestart. - start - RETVAL=$? - ;; - reload|force-reload) - # It does not support reload - RETVAL=3 - ;; - status) - echo -n $"Checking for service $prog:" - # Return value is slightly different for the status command: - # 0 - service up and running - # 1 - service dead, but /var/run/ pid file exists - # 2 - service dead, but /var/lock/ lock file exists - # 3 - service not running (unused) - # 4 - service status unknown :-( - # 5--199 reserved (5--99 LSB, 100--149 distro, 150--199 appl.) - RETVAL=3 - ;; - *) - echo "Usage: $0 {start|stop|status|try-restart|condrestart|restart|force-reload|reload}" - RETVAL=3 - ;; -esac - -exit $RETVAL diff --git a/inits/sysvinit/redhat/cloud-init-local b/inits/sysvinit/redhat/cloud-init-local deleted file mode 100755 index 7d45e6fb..00000000 --- a/inits/sysvinit/redhat/cloud-init-local +++ /dev/null @@ -1,105 +0,0 @@ -#!/bin/sh -# Copyright 2012 Yahoo! Inc. -# This file is part of cloud-init. See LICENCE file for license information. - -# See: http://wiki.debian.org/LSBInitScripts -# See: http://tiny.cc/czvbgw -# See: http://www.novell.com/coolsolutions/feature/15380.html -# Also based on dhcpd in RHEL (for comparison) - -### BEGIN INIT INFO -# Provides: cloud-init-local -# Required-Start: $local_fs $remote_fs -# Should-Start: $time -# Required-Stop: -# Should-Stop: -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: The initial cloud-init job (local fs contingent) -# Description: Start cloud-init and runs the initialization phases -# and any associated initial modules as desired. -### END INIT INFO - -# Return values acc. to LSB for all commands but status: -# 0 - success -# 1 - generic or unspecified error -# 2 - invalid or excess argument(s) -# 3 - unimplemented feature (e.g. "reload") -# 4 - user had insufficient privileges -# 5 - program is not installed -# 6 - program is not configured -# 7 - program is not running -# 8--199 - reserved (8--99 LSB, 100--149 distrib, 150--199 appl) -# -# Note that starting an already running service, stopping -# or restarting a not-running service as well as the restart -# with force-reload (in case signaling is not supported) are -# considered a success. - -RETVAL=0 - -prog="cloud-init" -cloud_init="/usr/bin/cloud-init" -conf="/etc/cloud/cloud.cfg" - -# If there exist sysconfig/default variable override files use it... -[ -f /etc/sysconfig/cloud-init ] && . /etc/sysconfig/cloud-init -[ -f /etc/default/cloud-init ] && . /etc/default/cloud-init - -start() { - [ -x $cloud_init ] || return 5 - [ -f $conf ] || return 6 - - echo -n $"Starting $prog: " - $cloud_init $CLOUDINITARGS init --local - RETVAL=$? - return $RETVAL -} - -stop() { - echo -n $"Shutting down $prog: " - # No-op - RETVAL=7 - return $RETVAL -} - -case "$1" in - start) - start - RETVAL=$? - ;; - stop) - stop - RETVAL=$? - ;; - restart|try-restart|condrestart) - ## Stop the service and regardless of whether it was - ## running or not, start it again. - # - ## Note: try-restart is now part of LSB (as of 1.9). - ## RH has a similar command named condrestart. - start - RETVAL=$? - ;; - reload|force-reload) - # It does not support reload - RETVAL=3 - ;; - status) - echo -n $"Checking for service $prog:" - # Return value is slightly different for the status command: - # 0 - service up and running - # 1 - service dead, but /var/run/ pid file exists - # 2 - service dead, but /var/lock/ lock file exists - # 3 - service not running (unused) - # 4 - service status unknown :-( - # 5--199 reserved (5--99 LSB, 100--149 distro, 150--199 appl.) - RETVAL=3 - ;; - *) - echo "Usage: $0 {start|stop|status|try-restart|condrestart|restart|force-reload|reload}" - RETVAL=3 - ;; -esac - -exit $RETVAL diff --git a/inits/upstart/cloud-config.conf b/inits/upstart/cloud-config.conf deleted file mode 100644 index 2c3ef67b..00000000 --- a/inits/upstart/cloud-config.conf +++ /dev/null @@ -1,9 +0,0 @@ -# cloud-config - Handle applying the settings specified in cloud-config -description "Handle applying cloud-config" -emits cloud-config - -start on (filesystem and started rsyslog) -console output -task - -exec cloud-init modules --mode=config diff --git a/inits/upstart/cloud-final.conf b/inits/upstart/cloud-final.conf deleted file mode 100644 index 72ae5052..00000000 --- a/inits/upstart/cloud-final.conf +++ /dev/null @@ -1,10 +0,0 @@ -# cloud-final.conf - run "final" jobs -# this runs around traditional "rc.local" time. -# and after all cloud-config jobs are run -description "execute cloud user/final scripts" - -start on (stopped rc RUNLEVEL=[2345] and stopped cloud-config) -console output -task - -exec cloud-init modules --mode=final diff --git a/inits/upstart/cloud-init-blocknet.conf b/inits/upstart/cloud-init-blocknet.conf deleted file mode 100644 index be09e7d8..00000000 --- a/inits/upstart/cloud-init-blocknet.conf +++ /dev/null @@ -1,83 +0,0 @@ -# cloud-init-blocknet -# the purpose of this job is -# * to block networking from coming up until cloud-init-nonet has run -# * timeout if they all do not come up in a reasonable amount of time -description "block networking until cloud-init-local" -start on (starting network-interface - or starting network-manager - or starting networking) -stop on stopped cloud-init-local - -instance $JOB${INTERFACE:+/}${INTERFACE:-} -export INTERFACE -task - -script - set +e # you cannot trap TERM reliably with 'set -e' - SLEEP_CHILD="" - - static_network_up() { - local emitted="/run/network/static-network-up-emitted" - # /run/network/static-network-up-emitted is written by - # upstart (via /etc/network/if-up.d/upstart). its presense would - # indicate that static-network-up has already fired. - [ -e "$emitted" -o -e "/var/$emitted" ] - } - msg() { - local uptime="" idle="" msg="" - if [ -r /proc/uptime ]; then - read uptime idle < /proc/uptime - fi - msg="${UPSTART_INSTANCE}${uptime:+[${uptime}]}: $*" - echo "$msg" - } - - handle_sigterm() { - # if we received sigterm and static networking is up then it probably - # came from upstart as a result of 'stop on static-network-up' - msg "got sigterm" - if [ -n "$SLEEP_CHILD" ]; then - if ! kill $SLEEP_CHILD 2>/dev/null; then - [ ! -d "/proc/$SLEEP_CHILD" ] || - msg "hm.. failed to kill sleep pid $SLEEP_CHILD" - fi - fi - msg "stopped" - exit 0 - } - - dowait() { - msg "blocking $1 seconds" - # all this 'exec -a' does is get me a nicely named process in 'ps' - # ie, 'sleep-block-network-interface.eth1' - if [ -x /bin/bash ]; then - bash -c 'exec -a sleep-block-$1 sleep $2' -- "$UPSTART_INSTANCE" "$1" & - else - sleep "$1" & - fi - SLEEP_CHILD=$! - msg "sleepchild=$SLEEP_CHILD" - wait $SLEEP_CHILD - SLEEP_CHILD="" - } - - trap handle_sigterm TERM - - if [ -n "$INTERFACE" -a "${INTERFACE#lo}" != "${INTERFACE}" ]; then - msg "ignoring interface ${INTERFACE}"; - exit 0; - fi - - # static_network_up already occurred - static_network_up && { msg "static_network_up already"; exit 0; } - - # local-finished cloud-init-local success or failure - lfin="/run/cloud-init/local-finished" - disable="/etc/cloud/no-blocknet" - [ -f "$lfin" ] && { msg "$lfin found"; exit 0; } - [ -f "$disable" ] && { msg "$disable found"; exit 0; } - - dowait 120 - msg "gave up waiting for $lfin" - exit 1 -end script diff --git a/inits/upstart/cloud-init-container.conf b/inits/upstart/cloud-init-container.conf deleted file mode 100644 index 6bdbe77e..00000000 --- a/inits/upstart/cloud-init-container.conf +++ /dev/null @@ -1,57 +0,0 @@ -# in a lxc container, events for network interfaces do not -# get created or may be missed. This helps cloud-init-nonet along -# by emitting those events if they have not been emitted. - -start on container -stop on static-network-up -task - -emits net-device-added - -console output - -script - # if we are inside a container, then we may have to emit the ifup - # events for 'auto' network devices. - set -f - - # from /etc/network/if-up.d/upstart - MARK_DEV_PREFIX="/run/network/ifup." - MARK_STATIC_NETWORK_EMITTED="/run/network/static-network-up-emitted" - # if the all static network interfaces are already up, nothing to do - [ -f "$MARK_STATIC_NETWORK_EMITTED" ] && exit 0 - - # ifquery will exit failure if there is no /run/network directory. - # normally that would get created by one of network-interface.conf - # or networking.conf. But, it is possible that we're running - # before either of those have. - mkdir -p /run/network - - # get list of all 'auto' interfaces. if there are none, nothing to do. - auto_list=$(ifquery --list --allow auto 2>/dev/null) || : - [ -z "$auto_list" ] && exit 0 - set -- ${auto_list} - [ "$*" = "lo" ] && exit 0 - - # we only want to emit for interfaces that do not exist, so filter - # out anything that does not exist. - for iface in "$@"; do - [ "$iface" = "lo" ] && continue - # skip interfaces that are already up - [ -f "${MARK_DEV_PREFIX}${iface}" ] && continue - - if [ -d /sys/net ]; then - # if /sys is mounted, and there is no /sys/net/iface, then no device - [ -e "/sys/net/$iface" ] && continue - else - # sys wasn't mounted, so just check via 'ifconfig' - ifconfig "$iface" >/dev/null 2>&1 || continue - fi - initctl emit --no-wait net-device-added "INTERFACE=$iface" && - emitted="$emitted $iface" || - echo "warn: ${UPSTART_JOB} failed to emit net-device-added INTERFACE=$iface" - done - - [ -z "${emitted# }" ] || - echo "${UPSTART_JOB}: emitted ifup for ${emitted# }" -end script diff --git a/inits/upstart/cloud-init-local.conf b/inits/upstart/cloud-init-local.conf deleted file mode 100644 index 5def043d..00000000 --- a/inits/upstart/cloud-init-local.conf +++ /dev/null @@ -1,16 +0,0 @@ -# cloud-init - the initial cloud-init job -# crawls metadata service, emits cloud-config -start on mounted MOUNTPOINT=/ and mounted MOUNTPOINT=/run - -task - -console output - -script - lfin=/run/cloud-init/local-finished - ret=0 - cloud-init init --local || ret=$? - [ -r /proc/uptime ] && read up idle < /proc/uptime || up="N/A" - echo "$ret up $up" > "$lfin" - exit $ret -end script diff --git a/inits/upstart/cloud-init-nonet.conf b/inits/upstart/cloud-init-nonet.conf deleted file mode 100644 index 6abf6573..00000000 --- a/inits/upstart/cloud-init-nonet.conf +++ /dev/null @@ -1,66 +0,0 @@ -# cloud-init-no-net -# the purpose of this job is -# * to block running of cloud-init until all network interfaces -# configured in /etc/network/interfaces are up -# * timeout if they all do not come up in a reasonable amount of time -start on mounted MOUNTPOINT=/ and stopped cloud-init-local -stop on static-network-up -task - -console output - -script - set +e # you cannot trap TERM reliably with 'set -e' - SLEEP_CHILD="" - - static_network_up() { - local emitted="/run/network/static-network-up-emitted" - # /run/network/static-network-up-emitted is written by - # upstart (via /etc/network/if-up.d/upstart). its presense would - # indicate that static-network-up has already fired. - [ -e "$emitted" -o -e "/var/$emitted" ] - } - msg() { - local uptime="" idle="" - if [ -r /proc/uptime ]; then - read uptime idle < /proc/uptime - fi - echo "$UPSTART_JOB${uptime:+[${uptime}]}:" "$1" - } - - handle_sigterm() { - # if we received sigterm and static networking is up then it probably - # came from upstart as a result of 'stop on static-network-up' - if [ -n "$SLEEP_CHILD" ]; then - if ! kill $SLEEP_CHILD 2>/dev/null; then - [ ! -d "/proc/$SLEEP_CHILD" ] || - msg "hm.. failed to kill sleep pid $SLEEP_CHILD" - fi - fi - if static_network_up; then - msg "static networking is now up" - exit 0 - fi - msg "recieved SIGTERM, networking not up" - exit 2 - } - - dowait() { - [ $# -eq 2 ] || msg "waiting $1 seconds for network device" - sleep "$1" & - SLEEP_CHILD=$! - wait $SLEEP_CHILD - SLEEP_CHILD="" - } - - trap handle_sigterm TERM - - # static_network_up already occurred - static_network_up && exit 0 - - dowait 5 silent - dowait 10 - dowait 115 - msg "gave up waiting for a network device." - : > /var/lib/cloud/data/no-net -end script diff --git a/inits/upstart/cloud-init.conf b/inits/upstart/cloud-init.conf deleted file mode 100644 index 41ddd284..00000000 --- a/inits/upstart/cloud-init.conf +++ /dev/null @@ -1,9 +0,0 @@ -# cloud-init - the initial cloud-init job -# crawls metadata service, emits cloud-config -start on mounted MOUNTPOINT=/ and stopped cloud-init-nonet - -task - -console output - -exec /usr/bin/cloud-init init diff --git a/inits/upstart/cloud-log-shutdown.conf b/inits/upstart/cloud-log-shutdown.conf deleted file mode 100644 index 278b9c06..00000000 --- a/inits/upstart/cloud-log-shutdown.conf +++ /dev/null @@ -1,19 +0,0 @@ -# log shutdowns and reboots to the console (/dev/console) -# this is useful for correlating logs -start on runlevel PREVLEVEL=2 - -task -console output - -script - # runlevel(7) says INIT_HALT will be set to HALT or POWEROFF - date=$(date --utc) - case "$RUNLEVEL:$INIT_HALT" in - 6:*) mode="reboot";; - 0:HALT) mode="halt";; - 0:POWEROFF) mode="poweroff";; - 0:*) mode="shutdown-unknown";; - esac - { read seconds idle < /proc/uptime; } 2>/dev/null || : - echo "$date: shutting down for $mode${seconds:+ [up ${seconds%.*}s]}." -end script diff --git a/packages/bddeb b/packages/bddeb deleted file mode 100755 index 9d264f92..00000000 --- a/packages/bddeb +++ /dev/null @@ -1,210 +0,0 @@ -#!/usr/bin/python - -import os -import shutil -import sys - - -def find_root(): - # expected path is in /packages/ - top_dir = os.environ.get("CLOUD_INIT_TOP_D", None) - if top_dir is None: - top_dir = os.path.dirname( - os.path.dirname(os.path.abspath(sys.argv[0]))) - if os.path.isfile(os.path.join(top_dir, 'setup.py')): - return os.path.abspath(top_dir) - raise OSError(("Unable to determine where your cloud-init topdir is." - " set CLOUD_INIT_TOP_D?")) - -# Use the util functions from cloudinit -sys.path.insert(0, find_root()) - -from cloudinit import templater -from cloudinit import util - -import argparse - -# Package names that will showup in requires to what we can actually -# use in our debian 'control' file, this is a translation of the 'requires' -# file pypi package name to a debian/ubuntu package name. -PKG_MP = { - 'argparse': 'python-argparse', - 'cheetah': 'python-cheetah', - 'configobj': 'python-configobj', - 'jinja2': 'python-jinja2', - 'jsonpatch': 'python-jsonpatch | python-json-patch', - 'oauth': 'python-oauth', - 'prettytable': 'python-prettytable', - 'pyserial': 'python-serial', - 'pyyaml': 'python-yaml', - 'requests': 'python-requests', -} -DEBUILD_ARGS = ["-S", "-d"] - - -def write_debian_folder(root, version, revno, append_requires=[]): - deb_dir = util.abs_join(root, 'debian') - os.makedirs(deb_dir) - - # Fill in the change log template - templater.render_to_file(util.abs_join(find_root(), - 'packages', 'debian', 'changelog.in'), - util.abs_join(deb_dir, 'changelog'), - params={ - 'version': version, - 'revision': revno, - }) - - # Write out the control file template - cmd = [util.abs_join(find_root(), 'tools', 'read-dependencies')] - (stdout, _stderr) = util.subp(cmd) - pkgs = [p.lower().strip() for p in stdout.splitlines()] - - # Map to known packages - requires = append_requires - for p in pkgs: - tgt_pkg = PKG_MP.get(p) - if not tgt_pkg: - raise RuntimeError(("Do not know how to translate pypi dependency" - " %r to a known package") % (p)) - else: - requires.append(tgt_pkg) - - templater.render_to_file(util.abs_join(find_root(), - 'packages', 'debian', 'control.in'), - util.abs_join(deb_dir, 'control'), - params={'requires': requires}) - - # Just copy the following directly - for base_fn in ['dirs', 'copyright', 'compat', 'rules']: - shutil.copy(util.abs_join(find_root(), - 'packages', 'debian', base_fn), - util.abs_join(deb_dir, base_fn)) - - -def main(): - - parser = argparse.ArgumentParser() - parser.add_argument("-v", "--verbose", dest="verbose", - help=("run verbosely" - " (default: %(default)s)"), - default=False, - action='store_true') - parser.add_argument("--no-cloud-utils", dest="no_cloud_utils", - help=("don't depend on cloud-utils package" - " (default: %(default)s)"), - default=False, - action='store_true') - - parser.add_argument("--init-system", dest="init_system", - help=("build deb with INIT_SYSTEM=xxx" - " (default: %(default)s"), - default=os.environ.get("INIT_SYSTEM", - "upstart,systemd")) - - - for ent in DEBUILD_ARGS: - parser.add_argument(ent, dest="debuild_args", action='append_const', - const=ent, help=("pass through '%s' to debuild" % ent), - default=[]) - - parser.add_argument("--sign", default=False, action='store_true', - help="sign result. do not pass -us -uc to debuild") - - args = parser.parse_args() - - if not args.sign: - args.debuild_args.extend(['-us', '-uc']) - - os.environ['INIT_SYSTEM'] = args.init_system - - capture = True - if args.verbose: - capture = False - - with util.tempdir() as tdir: - - cmd = [util.abs_join(find_root(), 'tools', 'read-version')] - (sysout, _stderr) = util.subp(cmd) - version = sysout.strip() - - cmd = ['bzr', 'revno'] - (sysout, _stderr) = util.subp(cmd) - revno = sysout.strip() - - # This is really only a temporary archive - # since we will extract it then add in the debian - # folder, then re-archive it for debian happiness - print("Creating a temporary tarball using the 'make-tarball' helper") - cmd = [util.abs_join(find_root(), 'tools', 'make-tarball')] - (sysout, _stderr) = util.subp(cmd) - arch_fn = sysout.strip() - tmp_arch_fn = util.abs_join(tdir, os.path.basename(arch_fn)) - shutil.move(arch_fn, tmp_arch_fn) - - print("Extracting temporary tarball %r" % (tmp_arch_fn)) - cmd = ['tar', '-xvzf', tmp_arch_fn, '-C', tdir] - util.subp(cmd, capture=capture) - extracted_name = tmp_arch_fn[:-len('.tar.gz')] - os.remove(tmp_arch_fn) - - xdir = util.abs_join(tdir, 'cloud-init') - shutil.move(extracted_name, xdir) - - print("Creating a debian/ folder in %r" % (xdir)) - if not args.no_cloud_utils: - append_requires=['cloud-utils | cloud-guest-utils'] - else: - append_requires=[] - write_debian_folder(xdir, version, revno, append_requires) - - # The naming here seems to follow some debian standard - # so it will whine if it is changed... - tar_fn = "cloud-init_%s~bzr%s.orig.tar.gz" % (version, revno) - print("Archiving the adjusted source into %r" % - (util.abs_join(tdir, tar_fn))) - cmd = ['tar', '-czvf', - util.abs_join(tdir, tar_fn), - '-C', xdir] - cmd.extend(os.listdir(xdir)) - util.subp(cmd, capture=capture) - - # Copy it locally for reference - shutil.copy(util.abs_join(tdir, tar_fn), - util.abs_join(os.getcwd(), tar_fn)) - print("Copied that archive to %r for local usage (if desired)." % - (util.abs_join(os.getcwd(), tar_fn))) - - print("Running 'debuild %s' in %r" % (' '.join(args.debuild_args), - xdir)) - with util.chdir(xdir): - cmd = ['debuild', '--preserve-envvar', 'INIT_SYSTEM'] - if args.debuild_args: - cmd.extend(args.debuild_args) - util.subp(cmd, capture=capture) - - link_fn = os.path.join(os.getcwd(), 'cloud-init_all.deb') - link_dsc = os.path.join(os.getcwd(), 'cloud-init.dsc') - for base_fn in os.listdir(os.path.join(tdir)): - full_fn = os.path.join(tdir, base_fn) - if not os.path.isfile(full_fn): - continue - shutil.move(full_fn, base_fn) - print("Wrote %r" % (base_fn)) - if base_fn.endswith('_all.deb'): - # Add in the local link - util.del_file(link_fn) - os.symlink(base_fn, link_fn) - print("Linked %r to %r" % (base_fn, - os.path.basename(link_fn))) - if base_fn.endswith('.dsc'): - util.del_file(link_dsc) - os.symlink(base_fn, link_dsc) - print("Linked %r to %r" % (base_fn, - os.path.basename(link_dsc))) - - return 0 - - -if __name__ == '__main__': - sys.exit(main()) diff --git a/packages/brpm b/packages/brpm deleted file mode 100755 index 9657b1dd..00000000 --- a/packages/brpm +++ /dev/null @@ -1,275 +0,0 @@ -#!/usr/bin/python - -import argparse -import contextlib -import glob -import os -import shutil -import subprocess -import sys -import tempfile -import re - -from datetime import datetime - - -def find_root(): - # expected path is in /packages/ - top_dir = os.environ.get("CLOUD_INIT_TOP_D", None) - if top_dir is None: - top_dir = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0]))) - if os.path.isfile(os.path.join(top_dir, 'setup.py')): - return os.path.abspath(top_dir) - raise OSError(("Unable to determine where your cloud-init topdir is." - " set CLOUD_INIT_TOP_D?")) - - -# Use the util functions from cloudinit -sys.path.insert(0, find_root()) - -from cloudinit import templater -from cloudinit import util - -# Mapping of expected packages to there full name... -# this is a translation of the 'requires' -# file pypi package name to a redhat/fedora package name. -PKG_MP = { - 'redhat': { - 'argparse': 'python-argparse', - 'cheetah': 'python-cheetah', - 'jinja2': 'python-jinja2', - 'configobj': 'python-configobj', - 'jsonpatch': 'python-jsonpatch', - 'oauth': 'python-oauth', - 'prettytable': 'python-prettytable', - 'pyserial': 'pyserial', - 'pyyaml': 'PyYAML', - 'requests': 'python-requests', - }, - 'suse': { - 'argparse': 'python-argparse', - 'cheetah': 'python-cheetah', - 'configobj': 'python-configobj', - 'jsonpatch': 'python-jsonpatch', - 'oauth': 'python-oauth', - 'prettytable': 'python-prettytable', - 'pyserial': 'python-pyserial', - 'pyyaml': 'python-yaml', - 'requests': 'python-requests', - } -} - -# Subdirectories of the ~/rpmbuild dir -RPM_BUILD_SUBDIRS = ['BUILD', 'RPMS', 'SOURCES', 'SPECS', 'SRPMS'] - - -def get_log_header(version): - # Try to find the version in the tags output - cmd = ['bzr', 'tags'] - (stdout, _stderr) = util.subp(cmd) - a_rev = None - for t in stdout.splitlines(): - ver, rev = t.split(None) - if ver == version: - a_rev = rev - break - if not a_rev: - return None - - # Extract who made that tag as the header - cmd = ['bzr', 'log', '-r%s' % (a_rev), '--timezone=utc'] - (stdout, _stderr) = util.subp(cmd) - kvs = { - 'comment': version, - } - - for line in stdout.splitlines(): - if line.startswith('committer:'): - kvs['who'] = line[len('committer:'):].strip() - if line.startswith('timestamp:'): - ts = line[len('timestamp:'):] - ts = ts.strip() - # http://bugs.python.org/issue6641 - ts = ts.replace("+0000", '').strip() - ds = datetime.strptime(ts, '%a %Y-%m-%d %H:%M:%S') - kvs['ds'] = ds - - return format_change_line(**kvs) - - -def format_change_line(ds, who, comment=None): - # Rpmbuild seems to be pretty strict about the date format - d = ds.strftime("%a %b %d %Y") - d += " - %s" % (who) - if comment: - d += " - %s" % (comment) - return "* %s" % (d) - - -def generate_spec_contents(args, tmpl_fn, top_dir, arc_fn): - - # Figure out the version and revno - cmd = [util.abs_join(find_root(), 'tools', 'read-version')] - (stdout, _stderr) = util.subp(cmd) - version = stdout.strip() - - cmd = ['bzr', 'revno'] - (stdout, _stderr) = util.subp(cmd) - revno = stdout.strip() - - # Tmpl params - subs = {} - subs['version'] = version - subs['revno'] = revno - subs['release'] = "bzr%s" % (revno) - if args.sub_release is not None: - subs['subrelease'] = "." + str(args.sub_release) - else: - subs['subrelease'] = '' - subs['archive_name'] = arc_fn - - cmd = [util.abs_join(find_root(), 'tools', 'read-dependencies')] - (stdout, _stderr) = util.subp(cmd) - pkgs = [p.lower().strip() for p in stdout.splitlines()] - - # Map to known packages - requires = [] - for p in pkgs: - tgt_pkg = PKG_MP[args.distro].get(p) - if not tgt_pkg: - raise RuntimeError(("Do not know how to translate pypi dependency" - " %r to a known package") % (p)) - else: - requires.append(tgt_pkg) - subs['requires'] = requires - - # Format a nice changelog (as best as we can) - changelog = util.load_file(util.abs_join(find_root(), 'ChangeLog')) - changelog_lines = [] - missing_versions = 0 - for line in changelog.splitlines(): - if not line.strip(): - continue - if re.match(r"^\s*[\d][.][\d][.][\d]:\s*", line): - line = line.strip(":") - header = get_log_header(line) - if not header: - missing_versions += 1 - if missing_versions == 1: - # Must be using a new 'dev'/'trunk' release - changelog_lines.append(format_change_line(datetime.now(), - '??')) - else: - sys.stderr.write(("Changelog version line %s does not " - "have a corresponding tag!\n") % (line)) - else: - changelog_lines.append(header) - else: - changelog_lines.append(line) - subs['changelog'] = "\n".join(changelog_lines) - - if args.boot == 'sysvinit': - subs['sysvinit'] = True - else: - subs['sysvinit'] = False - - if args.boot == 'systemd': - subs['systemd'] = True - else: - subs['systemd'] = False - - subs['defines'] = ["_topdir %s" % (top_dir)] - subs['init_sys'] = args.boot - subs['patches'] = [os.path.basename(p) for p in args.patches] - return templater.render_from_file(tmpl_fn, params=subs) - - -def main(): - - parser = argparse.ArgumentParser() - parser.add_argument("-d", "--distro", dest="distro", - help="select distro (default: %(default)s)", - metavar="DISTRO", default='redhat', - choices=('redhat', 'suse')) - parser.add_argument("-b", "--boot", dest="boot", - help="select boot type (default: %(default)s)", - metavar="TYPE", default='sysvinit', - choices=('sysvinit', 'systemd')) - parser.add_argument("-v", "--verbose", dest="verbose", - help=("run verbosely" - " (default: %(default)s)"), - default=False, - action='store_true') - parser.add_argument('-s', "--sub-release", dest="sub_release", - metavar="RELEASE", - help=("a 'internal' release number to concat" - " with the bzr version number to form" - " the final version number"), - type=int, - default=None) - parser.add_argument("-p", "--patch", dest="patches", - help=("include the following patch when building"), - default=[], - action='append') - args = parser.parse_args() - capture = True - if args.verbose: - capture = False - - # Clean out the root dir and make sure the dirs we want are in place - root_dir = os.path.expanduser("~/rpmbuild") - if os.path.isdir(root_dir): - shutil.rmtree(root_dir) - - arc_dir = util.abs_join(root_dir, 'SOURCES') - build_dirs = [root_dir, arc_dir] - for dname in RPM_BUILD_SUBDIRS: - build_dirs.append(util.abs_join(root_dir, dname)) - build_dirs.sort() - util.ensure_dirs(build_dirs) - - # Archive the code - cmd = [util.abs_join(find_root(), 'tools', 'make-tarball')] - (stdout, _stderr) = util.subp(cmd) - archive_fn = stdout.strip() - real_archive_fn = os.path.join(arc_dir, os.path.basename(archive_fn)) - shutil.move(archive_fn, real_archive_fn) - print("Archived the code in %r" % (real_archive_fn)) - - # Form the spec file to be used - tmpl_fn = util.abs_join(find_root(), 'packages', - args.distro, 'cloud-init.spec.in') - contents = generate_spec_contents(args, tmpl_fn, root_dir, - os.path.basename(archive_fn)) - spec_fn = util.abs_join(root_dir, 'cloud-init.spec') - util.write_file(spec_fn, contents) - print("Created spec file at %r" % (spec_fn)) - print(contents) - for p in args.patches: - util.copy(p, util.abs_join(arc_dir, os.path.basename(p))) - - # Now build it! - print("Running 'rpmbuild' in %r" % (root_dir)) - cmd = ['rpmbuild', '-ba', spec_fn] - util.subp(cmd, capture=capture) - - # Copy the items built to our local dir - globs = [] - globs.extend(glob.glob("%s/*.rpm" % - (util.abs_join(root_dir, 'RPMS', 'noarch')))) - globs.extend(glob.glob("%s/*.rpm" % - (util.abs_join(root_dir, 'RPMS', 'x86_64')))) - globs.extend(glob.glob("%s/*.rpm" % - (util.abs_join(root_dir, 'RPMS')))) - globs.extend(glob.glob("%s/*.rpm" % - (util.abs_join(root_dir, 'SRPMS')))) - for rpm_fn in globs: - tgt_fn = util.abs_join(os.getcwd(), os.path.basename(rpm_fn)) - shutil.move(rpm_fn, tgt_fn) - print("Wrote out %s package %r" % (args.distro, tgt_fn)) - - return 0 - - -if __name__ == '__main__': - sys.exit(main()) diff --git a/packages/debian/changelog.in b/packages/debian/changelog.in deleted file mode 100644 index e3e94f54..00000000 --- a/packages/debian/changelog.in +++ /dev/null @@ -1,6 +0,0 @@ -## This is a cheetah template -cloud-init (${version}~bzr${revision}-1) UNRELEASED; urgency=low - - * build - - -- Scott Moser Fri, 16 Dec 2011 11:50:25 -0500 diff --git a/packages/debian/compat b/packages/debian/compat deleted file mode 100644 index ec635144..00000000 --- a/packages/debian/compat +++ /dev/null @@ -1 +0,0 @@ -9 diff --git a/packages/debian/control.in b/packages/debian/control.in deleted file mode 100644 index 9207e5f4..00000000 --- a/packages/debian/control.in +++ /dev/null @@ -1,36 +0,0 @@ -## This is a cheetah template -Source: cloud-init -Section: admin -Priority: optional -Maintainer: Scott Moser -Build-Depends: debhelper (>= 9), - dh-python, - dh-systemd, - python (>= 2.6.6-3~), - python-nose, - pyflakes, - python-setuptools, - python-selinux, - python-cheetah, - python-mocker, - python-httpretty, -#for $r in $requires - ${r}, -#end for -XS-Python-Version: all -Standards-Version: 3.9.3 - -Package: cloud-init -Architecture: all -Depends: procps, - python, -#for $r in $requires - ${r}, -#end for - python-software-properties | software-properties-common, - \${misc:Depends}, -Recommends: sudo -XB-Python-Version: \${python:Versions} -Description: Init scripts for cloud instances - Cloud instances need special scripts to run during initialisation - to retrieve and install ssh keys and to let the user run various scripts. diff --git a/packages/debian/copyright b/packages/debian/copyright deleted file mode 100644 index 5f698957..00000000 --- a/packages/debian/copyright +++ /dev/null @@ -1,41 +0,0 @@ -Format-Specification: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?op=file&rev=135 -Name: cloud-init -Maintainer: Scott Moser -Source: https://launchpad.net/cloud-init - -Upstream Author: Scott Moser - Soren Hansen - Chuck Short - -Copyright: 2010, Canonical Ltd. -License: GPL-3 or Apache-2.0 -License: GPL-3 - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License version 3, as - published by the Free Software Foundation. - . - 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 General Public License for more details. - . - You should have received a copy of the GNU General Public License - along with this program. If not, see . - . - The complete text of the GPL version 3 can be seen in - /usr/share/common-licenses/GPL-3. -License: Apache-2 - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - . - http://www.apache.org/licenses/LICENSE-2.0 - . - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - . - On Debian-based systems the full text of the Apache version 2.0 license - can be found in `/usr/share/common-licenses/Apache-2.0'. diff --git a/packages/debian/dirs b/packages/debian/dirs deleted file mode 100644 index f3de468d..00000000 --- a/packages/debian/dirs +++ /dev/null @@ -1,5 +0,0 @@ -var/lib/cloud -usr/bin -etc/init -usr/share/doc/cloud -etc/cloud diff --git a/packages/debian/rules b/packages/debian/rules deleted file mode 100755 index 9e0c5ddb..00000000 --- a/packages/debian/rules +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/make -f - -INIT_SYSTEM ?= upstart,systemd -export PYBUILD_INSTALL_ARGS=--init-system=$(INIT_SYSTEM) - -%: - dh $@ --with python2,systemd --buildsystem pybuild - -override_dh_install: - dh_install - install -d debian/cloud-init/etc/rsyslog.d - cp tools/21-cloudinit.conf debian/cloud-init/etc/rsyslog.d/21-cloudinit.conf - -override_dh_auto_test: - # Becuase setup tools didn't copy data... - cp -r tests/data .pybuild/pythonX.Y_2.7/build/tests - http_proxy= dh_auto_test -- --test-nose diff --git a/packages/debian/watch b/packages/debian/watch deleted file mode 100644 index 0f7a600b..00000000 --- a/packages/debian/watch +++ /dev/null @@ -1,2 +0,0 @@ -version=3 -https://launchpad.net/cloud-init/+download .*/\+download/cloud-init-(.+)\.tar.gz diff --git a/packages/redhat/cloud-init.spec.in b/packages/redhat/cloud-init.spec.in deleted file mode 100644 index 1a7001fd..00000000 --- a/packages/redhat/cloud-init.spec.in +++ /dev/null @@ -1,200 +0,0 @@ -## This is a cheetah template -%{!?python_sitelib: %global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} - -# See: http://www.zarb.org/~jasonc/macros.php -# Or: http://fedoraproject.org/wiki/Packaging:ScriptletSnippets -# Or: http://www.rpm.org/max-rpm/ch-rpm-inside.html - -#for $d in $defines -%define ${d} -#end for - -Name: cloud-init -Version: ${version} -Release: ${release}${subrelease}%{?dist} -Summary: Cloud instance init scripts - -Group: System Environment/Base -License: dual license GPLv3 or Apache 2.0 -URL: http://launchpad.net/cloud-init - -Source0: ${archive_name} -BuildArch: noarch -BuildRoot: %{_tmppath} - -BuildRequires: python-devel -BuildRequires: python-setuptools -BuildRequires: python-cheetah - -# System util packages needed -Requires: shadow-utils -Requires: rsyslog -Requires: iproute -Requires: e2fsprogs -Requires: net-tools -Requires: procps -Requires: shadow-utils -Requires: sudo >= 1.7.2p2-3 - -# Install pypi 'dynamic' requirements -#for $r in $requires -Requires: ${r} -#end for - -# Custom patches -#set $size = 0 -#for $p in $patches -Patch${size}: $p -#set $size += 1 -#end for - -#if $sysvinit -Requires(post): chkconfig -Requires(postun): initscripts -Requires(preun): chkconfig -Requires(preun): initscripts -#end if - -#if $systemd -BuildRequires: systemd-units -Requires(post): systemd-units -Requires(postun): systemd-units -Requires(preun): systemd-units -#end if - -%description -Cloud-init is a set of init scripts for cloud instances. Cloud instances -need special scripts to run during initialization to retrieve and install -ssh keys and to let the user run various scripts. - -%prep -%setup -q -n %{name}-%{version}~${release} - -# Custom patches activation -#set $size = 0 -#for $p in $patches -%patch${size} -p1 -#set $size += 1 -#end for - -%build -%{__python} setup.py build - -%install - -%{__python} setup.py install -O1 \ - --skip-build --root \$RPM_BUILD_ROOT \ - --init-system=${init_sys} - -# Note that /etc/rsyslog.d didn't exist by default until F15. -# el6 request: https://bugzilla.redhat.com/show_bug.cgi?id=740420 -mkdir -p \$RPM_BUILD_ROOT/%{_sysconfdir}/rsyslog.d -cp -p tools/21-cloudinit.conf \ - \$RPM_BUILD_ROOT/%{_sysconfdir}/rsyslog.d/21-cloudinit.conf - -# Remove the tests -rm -rf \$RPM_BUILD_ROOT%{python_sitelib}/tests - -# Required dirs... -mkdir -p \$RPM_BUILD_ROOT/%{_sharedstatedir}/cloud -mkdir -p \$RPM_BUILD_ROOT/%{_libexecdir}/%{name} - -#if $systemd -mkdir -p \$RPM_BUILD_ROOT/%{_unitdir} -cp -p systemd/* \$RPM_BUILD_ROOT/%{_unitdir} -#end if - -%clean -rm -rf \$RPM_BUILD_ROOT - -%post - -#if $systemd -if [ \$1 -eq 1 ] -then - /bin/systemctl enable cloud-config.service >/dev/null 2>&1 || : - /bin/systemctl enable cloud-final.service >/dev/null 2>&1 || : - /bin/systemctl enable cloud-init.service >/dev/null 2>&1 || : - /bin/systemctl enable cloud-init-local.service >/dev/null 2>&1 || : -fi -#end if - -#if $sysvinit -/sbin/chkconfig --add %{_initrddir}/cloud-init-local -/sbin/chkconfig --add %{_initrddir}/cloud-init -/sbin/chkconfig --add %{_initrddir}/cloud-config -/sbin/chkconfig --add %{_initrddir}/cloud-final -#end if - -%preun - -#if $sysvinit -if [ \$1 -eq 0 ] -then - /sbin/service cloud-init stop >/dev/null 2>&1 || : - /sbin/chkconfig --del cloud-init || : - /sbin/service cloud-init-local stop >/dev/null 2>&1 || : - /sbin/chkconfig --del cloud-init-local || : - /sbin/service cloud-config stop >/dev/null 2>&1 || : - /sbin/chkconfig --del cloud-config || : - /sbin/service cloud-final stop >/dev/null 2>&1 || : - /sbin/chkconfig --del cloud-final || : -fi -#end if - -#if $systemd -if [ \$1 -eq 0 ] -then - /bin/systemctl --no-reload disable cloud-config.service >/dev/null 2>&1 || : - /bin/systemctl --no-reload disable cloud-final.service >/dev/null 2>&1 || : - /bin/systemctl --no-reload disable cloud-init.service >/dev/null 2>&1 || : - /bin/systemctl --no-reload disable cloud-init-local.service >/dev/null 2>&1 || : -fi -#end if - -%postun - -#if $systemd -/bin/systemctl daemon-reload >/dev/null 2>&1 || : -#end if - -%files - -#if $sysvinit -%attr(0755, root, root) %{_initddir}/cloud-config -%attr(0755, root, root) %{_initddir}/cloud-final -%attr(0755, root, root) %{_initddir}/cloud-init-local -%attr(0755, root, root) %{_initddir}/cloud-init -#end if - -#if $systemd -%{_unitdir}/cloud-* -#end if - -# Program binaries -%{_bindir}/cloud-init* -%{_libexecdir}/%{name}/uncloud-init -%{_libexecdir}/%{name}/write-ssh-key-fingerprints - -# Docs -%doc LICENSE ChangeLog TODO.rst requirements.txt -%doc %{_defaultdocdir}/cloud-init/* - -# Configs -%config(noreplace) %{_sysconfdir}/cloud/cloud.cfg -%dir %{_sysconfdir}/cloud/cloud.cfg.d -%config(noreplace) %{_sysconfdir}/cloud/cloud.cfg.d/*.cfg -%config(noreplace) %{_sysconfdir}/cloud/cloud.cfg.d/README -%dir %{_sysconfdir}/cloud/templates -%config(noreplace) %{_sysconfdir}/cloud/templates/* -%config(noreplace) %{_sysconfdir}/rsyslog.d/21-cloudinit.conf - -%{_libexecdir}/%{name} -%dir %{_sharedstatedir}/cloud - -# Python code is here... -%{python_sitelib}/* - -%changelog - -${changelog} diff --git a/packages/suse/cloud-init.spec.in b/packages/suse/cloud-init.spec.in deleted file mode 100644 index 6d0e9c54..00000000 --- a/packages/suse/cloud-init.spec.in +++ /dev/null @@ -1,163 +0,0 @@ -## This is a cheetah template - -# See: http://www.zarb.org/~jasonc/macros.php -# Or: http://fedoraproject.org/wiki/Packaging:ScriptletSnippets -# Or: http://www.rpm.org/max-rpm/ch-rpm-inside.html - -#for $d in $defines -%define ${d} -#end for - -Name: cloud-init -Version: ${version} -Release: ${release}${subrelease}%{?dist} -Summary: Cloud instance init scripts - -Group: System/Management -License: dual license GPLv3 or Apache 2.0 -URL: http://launchpad.net/cloud-init - -Source0: ${archive_name} -BuildRoot: %{_tmppath}/%{name}-%{version}-build - -%if 0%{?suse_version} && 0%{?suse_version} <= 1110 -%{!?python_sitelib: %global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} -%else -BuildArch: noarch -%endif - -BuildRequires: fdupes -BuildRequires: filesystem -BuildRequires: python-devel -BuildRequires: python-setuptools -BuildRequires: python-cheetah - -%if 0%{?suse_version} && 0%{?suse_version} <= 1210 - %define initsys sysvinit -%else - %define initsys systemd -%endif - -# System util packages needed -Requires: iproute2 -Requires: e2fsprogs -Requires: net-tools -Requires: procps -Requires: sudo - -# Install pypi 'dynamic' requirements -#for $r in $requires -Requires: ${r} -#end for - -# Custom patches -#set $size = 0 -#for $p in $patches -Patch${size}: $p -#set $size += 1 -#end for - -%description -Cloud-init is a set of init scripts for cloud instances. Cloud instances -need special scripts to run during initialization to retrieve and install -ssh keys and to let the user run various scripts. - -%prep -%setup -q -n %{name}-%{version}~${release} - -# Custom patches activation -#set $size = 0 -#for $p in $patches -%patch${size} -p1 -#set $size += 1 -#end for - -%build -%{__python} setup.py build - -%install -%{__python} setup.py install \ - --skip-build --root=%{buildroot} --prefix=%{_prefix} \ - --record-rpm=INSTALLED_FILES --install-lib=%{python_sitelib} \ - --init-system=%{initsys} - -# Remove non-SUSE templates -rm %{buildroot}/%{_sysconfdir}/cloud/templates/*.debian.* -rm %{buildroot}/%{_sysconfdir}/cloud/templates/*.redhat.* -rm %{buildroot}/%{_sysconfdir}/cloud/templates/*.ubuntu.* - -# Remove cloud-init tests -rm -r %{buildroot}/%{python_sitelib}/tests - -# Move sysvinit scripts to the correct place and create symbolic links -%if %{initsys} == sysvinit - mkdir -p %{buildroot}/%{_initddir} - mv %{buildroot}%{_sysconfdir}/rc.d/init.d/* %{buildroot}%{_initddir}/ - rmdir %{buildroot}%{_sysconfdir}/rc.d/init.d - rmdir %{buildroot}%{_sysconfdir}/rc.d - - mkdir -p %{buildroot}/%{_sbindir} - pushd %{buildroot}/%{_initddir} - for file in * ; do - ln -s %{_initddir}/\${file} %{buildroot}/%{_sbindir}/rc\${file} - done - popd -%endif - -# Move documentation -mkdir -p %{buildroot}/%{_defaultdocdir} -mv %{buildroot}/usr/share/doc/cloud-init %{buildroot}/%{_defaultdocdir} -for doc in TODO LICENSE ChangeLog requirements.txt; do - cp \${doc} %{buildroot}/%{_defaultdocdir}/cloud-init -done - -# Remove duplicate files -%if 0%{?suse_version} - %fdupes %{buildroot}/%{python_sitelib} -%endif - -mkdir -p %{buildroot}/var/lib/cloud - -%postun -%insserv_cleanup - -%files - -# Sysvinit scripts -%if %{initsys} == sysvinit - %attr(0755, root, root) %{_initddir}/cloud-config - %attr(0755, root, root) %{_initddir}/cloud-final - %attr(0755, root, root) %{_initddir}/cloud-init-local - %attr(0755, root, root) %{_initddir}/cloud-init - - %{_sbindir}/rccloud-* -%endif - -# Program binaries -%{_bindir}/cloud-init* - -# There doesn't seem to be an agreed upon place for these -# although it appears the standard says /usr/lib but rpmbuild -# will try /usr/lib64 ?? -/usr/lib/%{name}/uncloud-init -/usr/lib/%{name}/write-ssh-key-fingerprints - -# Docs -%doc %{_defaultdocdir}/cloud-init/* - -# Configs -%config(noreplace) %{_sysconfdir}/cloud/cloud.cfg -%dir %{_sysconfdir}/cloud/cloud.cfg.d -%config(noreplace) %{_sysconfdir}/cloud/cloud.cfg.d/*.cfg -%config(noreplace) %{_sysconfdir}/cloud/cloud.cfg.d/README -%dir %{_sysconfdir}/cloud/templates -%config(noreplace) %{_sysconfdir}/cloud/templates/* - -# Python code is here... -%{python_sitelib}/* - -/var/lib/cloud - -%changelog - -${changelog} diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 0219db9f..00000000 --- a/requirements.txt +++ /dev/null @@ -1,13 +0,0 @@ -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. - -# See: https://bugs.launchpad.net/pbr/+bug/1384919 for why this is here... -pbr>=0.11,<2.0 - -six>=1.7.0 -pyyaml -jsonpatch -requests>=1.0 -requests-oauthlib -jinja2 diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index c48cd538..00000000 --- a/setup.cfg +++ /dev/null @@ -1,42 +0,0 @@ -[metadata] -name = cloudinit -version = 1.9.0 -summary = cloud initialisation magic -description-file = - README.rst -author = Scott Moser -author-email = scott.moser@canonical.com -home-page = http://launchpad.net/cloud-init/ -classifier = - Environment :: Console - Environment :: Cloud - Intended Audience :: Information Technology - Intended Audience :: System Administrators - License :: OSI Approved :: dual GPLv3 or Apache Software License - Operating System :: OS Independent - Programming Language :: Python - Programming Language :: Python :: 2.7 - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.3 - -[files] -packages = - cloudinit - -[global] -setup-hooks = - pbr.hooks.setup_hook - -[entry_points] -console_scripts = - cloud-init = cloudinit.shell:main - -[build_sphinx] -source-dir = doc/source -build-dir = doc/build - -[pbr] -autodoc_index_modules = 1 -autodoc_exclude_modules = - cloudinit.osys.windows.* -warnerrors = 1 diff --git a/setup.py b/setup.py deleted file mode 100755 index e14585b0..00000000 --- a/setup.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python - -import setuptools - -# In python < 2.7.4, a lazy loading of package `pbr` will break -# setuptools if some other modules registered functions in `atexit`. -# solution from: http://bugs.python.org/issue15881#msg170215 -try: - import multiprocessing # noqa -except ImportError: - pass - -setuptools.setup( - setup_requires=['pbr'], - pbr=True) diff --git a/specs/distros.rst b/specs/distros.rst deleted file mode 100644 index 05c55351..00000000 --- a/specs/distros.rst +++ /dev/null @@ -1,504 +0,0 @@ -API design for the new distros namespace -======================================== - -API convention --------------- - -Before dwelving into the details of the proposed API, some conventions -should be established, so that the API could be pythonic, easy to -comprehend and extend. We have the following examples of how an object -should look, depending on its state and behaviour: - - - Use ``.attribute`` if the attribute is not changeable - throughout the life of the object. - For instance, the name of a device. - - - Use ``.method()`` for obtaining a variant attribute, which can be - different throughout the execution of the object and not modifiable - through our object. This is the case for ``device.size()``, we can't - set a new size and it can vary throughout the life of the device. - - - For attributes which are modifiable by us and which aren't changing - throughout the life of the object, we could use a property-based approach. - - >>> device.mtu - 1500 - # actually changing the value, not the cached value. - >>> device.mtu = 1400 - 1400 - - - -Proposed distro hierarchy -========================= - -Both frameworks have a concept of Distro, each different in its way: - - - cloudinit has a ``distros`` location. There is a ``Distro`` base class, - with abstract methods implemented by particular distros. - - Problems: - - * not DRY: many implementations have duplicate code with the base class - * not properly encapsulated: distro-specific code executed outside the - ``distros`` namespace. - * lots of utilities, with low coherence between them. - - - cloudbaseinit has a ``osutils`` location. There is a ``BaseOSUtils`` - base class, with a WindowsUtils implementation. - - Problems: - - * it's a pure utilities class, leading to low coherence - between functions. - * it is not a namespace of OS specific functionality. - For this, there is also ``utils.windows``. - -As seen, both projects lack a namespaced location for all the OS related code. - -The following architecture proposal tries to solve this issue, by having one -namespace for both general utilies related to a distro, as well as distro -specific code, which doesn't have a counterpart on other distros. - -It can have the following advantages: - - * one common place for all distro interaction, with standardized - API for each subnamespace and increased coherence. - - * avoids leaky abstractions. Distro specific code goes into ``distros`` - namespace. - - * eases testability, it is easy to provide a mock with autospeccing - for a namespace class, such as Route. - - * Pythonic API, easy to understand and use. - - -The distros location is proposed, with the following structure and attributes: - - - The base class for a distro is found in ``distros.base``. - - - There are specific submodules for interaction with the OS, - such as network, users. The submodules are part of distros namespaces, - e.g. ``distros.windows`` should contain the modules ``network``, - ``users`` etc. - - - More modules can be added, if we identify a group of interactions that can - be categorized in one. - - - There should be ``general`` module, which contains general utilities that can't be moved - in another module. - - - Each submodule has its own abstract base class, which must be implemented - by each distro. Code reuse between distros is recommended. - - - Each submodule can expose additional behaviour that might not exist in - the base class, if that behaviour does not make sense or if there is no - equivalent on other platforms. But don't expose leaky abstraction, this - specific code must be written in an abstract way, so that possible alternatives - can be found for other distros in the mean time. This means that no ctypes - interaction with the Windows should be exposed, - but an object with a guaranteed interface. - - - cloudinit/distros/__init__.py - base.py - - freebsd/ - __init__.py - network.py - users.py - general.py - filesystem.py - windows/ - __init__.py - network.py - users.py - general.py - ubuntu/ - __init__.py - network.py - .... - - ->>> from cloudinit.distros.base import get_distro ->>> distro = get_distro() ->>> distro.network # the actual object, not the submodule - ->>> distro.users - ->>> distro.general - - - -As an implementation detail, obtaining the distro object for the underlying -distro can use a combination of `platform.system`_ and `platform.linux_distribution`_. - - -In the following, I'll try to emphasize some possible APIs for each namespace. - - -Network module --------------- - - The abstract class can look like this: - - class NetworkBase(ABCMeta): - - def routes(self): - """Get the available routes, this can be the output of - `netstat` on Posix and ``GetIpForwardTable`` on Windows. - Each route should be an object encapsulating the inner workings - of each variant. - - :meth:`routes` returns an object with behaviour similar to that - of a sequence (it could be implemented using collections.Sequence - or something similar, as long as it guarantees an interface). - See the description of :class:`Route` for the API of the route object. - - The following behaviour should be supported by the object returned by - :meth:`routes`. - - def __iter__(self): - """Support iteration.""" - - def __contains__(self, item): - """Support containment.""" - - def __getitem__(self, item): - """Support element access""" - - Some API usages: - - >>> routes = network.routes() - >>> route_object in routes - True - >>> '192.168.70.14' in routes - False - >>> route = Route.from_route_entry( - "0.0.0.0 192.168.60.2 " - "0.0.0.0 UG 0 0 " - "0 eth0") - >>> route.delete() - """ - - def default_gateway(self): - """Get the default gateway. - - Can be implemented in the terms of :meth:`routes`. - """ - - def interfaces(self): - """Get the network interfaces - - This can be implemented in the same vein as :meth:`routes`, e.g. - ``sequence(Interface(...), Interface(...), ...)`` - """ - - def firewall_rules(self): - """Get a wrapper over the existing firewall rules. - - Since this seems to be only used in Windows, it can be provided - only in the Windows utils. - The same behaviour as for :meth:`routes` can be used, that is: - - >>> rules = distro.network.firewall_rules() - # Creating a new rule. - >>> rule = distro.network.FirewallRule(name=..., port=..., protocol=...) - # Deleting a rule - >>> rule.delete() - >>> rule in rules - >>> for rule in rules: print(rules) - >>> rule = rules[0] - >>> rule.name, rule.port, rule.protocol, rule.allow - - This gets rid of ``cloudbaseinit.osutils.firewall_add_rule`` and - ``cloudbaseinit.osutils.firewall_remove_rule``. - """ - - def set_static_network_config(self, adapter_name, address, netmask, - broadcast, gateway, dnsnameservers): - """Configure a new static network. - - The :meth:``cloudinit.distros.Distro.apply_network`` should be - removed in the favour of this method, - which will be called by each network plugin. - The method can be a template method, providing - hooks for setting static DNS servers, setting static gateways or - setting static IP addresses, which will be implemented by specific - implementations of Distros. - """ - - def hosts(self): - """Get the content of /etc/hosts file in a more OO approach. - - - >>> hosts = distro.network.hosts() - # Add a new entry in the hosts file, as well - # in the object container itself - >>> hosts.add(ipaddress, hostname, alias) - # Delete an entry from the hosts file and from - # the object container itself - >>> hosts.delete(ipaddress, hostname, alias) - - This gets rid of ``cloudinit.distros.Distro.update_etc_hosts`` - and can provide support for adding a new hostname for Windows, as well. - """ - - class Route(object): - """ - Encapsulate behaviour and state of a route. - Something similar to Posix can be adopted, with the following API: - - route.destination - route.gateway - route.flags - route.refs - route.use - route.netif -> instance of :class:`Interface` object - route.expire - route.static -> 'S' in self.flags - route.usable -> 'U' in self.flag - - This can use a namedtuple as a base, but this should - be considered an implementation detail by the users - of this class. - """ - - @classmethod - def from_route_item(self, item): - """ - Build a Route from a routing entry, either from - the output of `netstat` or what will be used on Posix or - from `GetIpForwardTable`. - """ - - class Interface(object): - """Encapsulation for the state and behaviour of an interface. - - This method gets rid of ``cloudbaseinit.osutils.get_network_adapters`` - and with the following behaviour - it gets rid of ``cloudinit.distros._bring_up_interface``: - - >>> interfaces = distro.network.interfaces() - >>> interface = interfaces[0] - >>> interface.up() - >>> interface.down() - >>> interface.is_up() - # Change mtu for this interface - >>> interface.mtu = 1400 - # Get interface mtu - >>> interface.mtu - 1400 - - If we have only the name of an interface, we should be able to - obtain a :class:`Interface` instance from it. - - >>> interface = distro.network.Interface.from_name('eth0') - >>> interface = distro.network.Interface.from_mac( u'00:50:56:C0:00:01') - - Each Distro specific implementation of :class:`Interface` should - be exported in the `network` module as the `Interface` attribute, - so that the underlying OS is completely hidden from an API point-of-view. - """ - - # alternative constructors - - @classmethod - def from_name(cls, name): - # return a new Interface - - @classmethod - def from_mac(self, mac): - # return a new Interface - - # Actual methods for behaviour - - def up(self): - """Activate the interface.""" - - def down(self): - """Deactivate the interface.""" - - def is_up(self): - """Check if the interface is activated.""" - - # Other getters and setter for what can be changed for an - # interface, such as the mtu. - - @property - def mtu(self): - pass - - @mtu.setter - def mtu(self, value): - pass - - # Other read only attributes, such as ``.name``, ``.mac`` etc. - - .. note:: - - TODO: finish this section with APis for set_hostname, _read_hostname, update_hostname - - -Users module ------------- - -The base class for this namespace can look like this - - - class UserBase(ABCMeta): - - def groups(self): - """Get all the user groups from the instance. - - Similar with network.routes() et al, that is - - >>> groups = distro.users.groups() - sequence(Group(...), Group(....), ...) - # create a new group - >>> group = distro.users.Group.create(name) - # Add new members to a group - >>> group.add(member) - # Remove a group - >>> group.delete() - # Iterate groups - >>> list(groups) - - This gets rid of ``cloudinit.distros.Distro.create_group``, - which creates a group and adds members to it as well and it get rids of - ``cloudbaseinit.osutils.add_user_to_local``. - """ - - def users(self): - """Get all the users from the instance. - - Using the same idion as for :meth:`routes` and :meth:`groups`. - - >>> users = distro.users.users() - # containment (cloudbaseinit.osutils.user_exists) - >>> user in users - # Iteration - >>> for i in user: print(user) - """ - - class User: - """ Abstracts away user interaction. - - # Creating a new user. - >>> User.create(username=..., password=..., ...) - # get the home dir of an user - >>> user.home() - # Get the password (?) - >>> user.password - # Set the password - >>> user.password = .... - # Get an instance of an User from a name - >>> user = distros.users.User.from_name('user') - # Disable login password - >>> user.disable_login_password() - # Get ssh keys - >>> keys = user.ssh_keys() - - Posix specific implementations might provide some method - to operate with '/etc/sudoers' file. - """ - -.. note:: - - TODO: what is cloudinit.distros.get_default_user? - -Packaging module ----------------- - -This object is a thin layer over Distro specific packaging utilities, -used in cloudinit through ``distro.Distro.package_command``. -Instead of passing strings with arguments, as it currently does, -we could have a more OO approach: - - >>> distro.packaging.install(...) - - # cloudinit provides a ``package_command`` and an ``update_package_sources`` method, - # which is: - # self._runner.run("update-sources", self.package_command, - # ["update"], freq=PER_INSTANCE) - # distro.packaging.update() can be a noop operation if it was already called - >>> distro.packaging.update(...) - - On Windows side, this can be implemented with OneGet. - - -Filesystem module ------------------ - -Layer over filesystem interaction specific for each OS. -Most of the uses encountered are related to the concept of devices and partitions. - - -class FilesystemBase(ABC): - - def devices(self): - """Get a list of devices for this instance. - - As usual, this is abstracted through a container - DevicesContainer([Device(...), Device(...), Device(...)]) - - Where the container has the following API: - - >>> devices = distro.filesystem.devices() - >>> devices.name, devices.type, devices.label - >>> devices.size() - # TODO: geometry on Windows? Define the concept better. - >>> devices.layout() - >>> device in devices - >>> for device in devices: print(device) - >>> devices.partitions() - [DevicePartition('sda1'), DevicePartition('sda2'), ...] - # TODO: FreeBSD has slices, which translates to partitions on - # Windows and partitions of slices, how - # does this translate with the current arch? - - - Each DevicePartition shares a couple of methods / attributes with the Device, - such as ``name``, ``type``, ``label``, ``size``. They have extra methods: - - >>> partition.resize() - >>> partition.recover() - >>> partition.mount() - >>> with partition.mount(): # This can be noop on Windows. - .... - - Obtaining either a device or a partition from a string, should be done - in the following way: - - >>> device = Device.from_name('sda') - >>> partition = DevicePartition.from_name('sda', 1) - >>> partition = DevicePartition.from_name('sda1') - """ - -General module --------------- - -Here we could have other general OS utilities: terminate, apply_locale, -set_timezone, execute_process etc. If some utilities can be grouped -after some time into a more specialized namespace, then they can be moved. - - -Drawbacks -========= - -The only reasonable drawbacks that this proposal can have are: - - * moving all the parts from both projects will take a while. - Since we started from the beginning knowing that cloudinit - and cloudbaseinit codebases aren't compatible enough for a - clean merge, this drawback might not be that huge. It's a pain - that we must deal with as soon as possible. - - * the new API could be a source of unexpected bugs, but we should - target a high testing coverage in order to alleviate this. - - - - .. _platform.system: https://docs.python.org/2/library/platform.html#platform.system - .. _platform.linux_distribution: https://docs.python.org/2/library/platform.html#platform.linux_distribution diff --git a/test-requirements.txt b/test-requirements.txt deleted file mode 100644 index a27acde2..00000000 --- a/test-requirements.txt +++ /dev/null @@ -1,18 +0,0 @@ -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. - -httpretty>=0.7.1,!=0.8.11,!=0.8.12 -mock -nose -testtools -fixtures - -# For doc building -sphinx>=1.2.0,<1.3 -oslosphinx - -# For style checking -hacking<0.11,>=0.10.0 -doc8 -coverage diff --git a/tools/noproxy b/tools/noproxy deleted file mode 100644 index d371e63c..00000000 --- a/tools/noproxy +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python -# -# clean http_proxy variables from environment as they make httpretty -# fail, but may be in the environment for reasons such as pip install -import os -import sys - -for k in ('http_proxy', 'https_proxy', 'HTTP_PROXY', 'HTTPS_PROXY'): - if k in os.environ: - del os.environ[k] - -os.execvpe(sys.argv[1], sys.argv[1:], os.environ) diff --git a/tools/tox-venv b/tools/tox-venv deleted file mode 100755 index 32a16851..00000000 --- a/tools/tox-venv +++ /dev/null @@ -1,82 +0,0 @@ -#!/bin/sh -# vi: ts=4 expandtab - -error() { echo "$@" 1>&2; } -fail() { [ $# -eq 0 ] || error "$@"; exit 1; } -list_environments() { - local tox_d="$1" tox_ini="$2" prefix=" " - envs=$(tox -c "$tox_ini" --listenvs) || return 1 - ( - cd "$tox_d" && - for d in ${envs}; do - [ -f "$d/bin/activate" ] && s="*" || s="" - echo "${prefix}$d$s"; - done - ) -} - -Usage() { - local tox_d="$1" tox_ini="$2" - cat <&2; exit 1; } -[ "$1" = "-h" -o "$1" = "--help" ] && { Usage "$tox_d" "$tox_ini"; exit 0; } - -[ -d "$tox_d" ] || fail "$tox_d: not a dir. maybe run 'tox'?" -[ -f "$tox_ini" ] || fail "$tox_ini: did not find tox.ini" - -if [ "$1" = "-l" -o "$1" = "--list" ]; then - list_environments "$tox_d" "$tox_ini" - exit -fi - -nocreate="false" -if [ "$1" = "--no-create" ]; then - nocreate="true" - shift -fi - -env="$1" -shift -activate="$tox_d/$env/bin/activate" - -if [ ! -f "$activate" ]; then - if $nocreate; then - fail "tox env '$env' did not exist, and no-create specified" - elif list_environments "$tox_d" "$tox_ini" | grep -q " $env$"; then - error "attempting to create $env:" - error " tox -c $tox_ini --recreate --notest -e $env" - tox -c "$tox_ini" --recreate --notest -e "$env" || - fail "failed creation of env $env" - else - error "$env: not a valid tox environment?" - error "found tox_d=$tox_d" - error "try one of:" - list_environments "$tox_d" "$tox_ini" 1>&2 - fail - fi -fi -. "$activate" - -[ "$#" -gt 0 ] || set -- ${SHELL:-/bin/bash} -debian_chroot="tox:$env" exec "$@" diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 88a25889..00000000 --- a/tox.ini +++ /dev/null @@ -1,48 +0,0 @@ -[tox] -minversion = 1.6 -skipsdist = True -envlist = py34, py27, docs, pep8, py34-coverage, py27-coverage - -[testenv] -usedevelop = True -# LC_ALL see https://github.com/gabrielfalcao/HTTPretty/issues/223 -setenv = VIRTUAL_ENV={envdir} - LC_ALL = en_US.utf-8 -deps = -r{toxinidir}/test-requirements.txt - -r{toxinidir}/requirements.txt -commands = {envpython} {toxinidir}/tools/noproxy nosetests {posargs} - -# tox uses '--pre' by default to pip install. We don't want that, and -# 'pip_pre=False' isn't available until tox version 1.9. -install_command = pip install {opts} {packages} - -[testenv:py26] -deps = {[testenv]deps} - importlib - logutils - -[testenv:py27-coverage] -commands = {envpython} {toxinidir}/tools/noproxy nosetests --with-coverage --cover-erase --cover-package=cloudinit --cover-min-percentage=90 --cover-html {posargs} - -[testenv:py34-coverage] -commands = {envpython} {toxinidir}/tools/noproxy nosetests --with-coverage --cover-erase --cover-package=cloudinit --cover-min-percentage=90 --cover-html {posargs} - -[testenv:pep8] -commands = flake8 {posargs} - -[testenv:docs] -whitelist_externals = git -commands = - git clean -fx doc/source/api - python setup.py build_sphinx - doc8 doc/source - -[testenv:venv] -commands = {posargs} - -[flake8] -builtins = _ -exclude = .venv,.git,.tox,dist,doc,*lib/python*,*egg,build,.ropeproject - -# TODO(harlowja): fix these up... -ignore = H102,H104,H105