Retire Murano: remove repo content
Murano project is retiring - https://review.opendev.org/c/openstack/governance/+/919358 this commit remove the content of this project repo Depends-On: https://review.opendev.org/c/openstack/project-config/+/919359/ Change-Id: If7df8b0a227fb76e49637fcc3b26eb05fbb5e453
This commit is contained in:
parent
9ae3969bbd
commit
92660e2cdd
|
@ -1,50 +0,0 @@
|
|||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
x64/
|
||||
build/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
|
||||
|
||||
#IntelJ Idea
|
||||
.idea/
|
||||
|
||||
#Build results
|
||||
build/
|
||||
dist/
|
||||
*.egg-info/
|
||||
*.egg
|
||||
.tox
|
||||
AUTHORS
|
||||
ChangeLog
|
||||
|
||||
#Python
|
||||
*.pyc
|
||||
|
||||
#Translation build
|
||||
*.mo
|
||||
|
||||
#Autogenerated sample config file
|
||||
etc/muranoagent/muranoagent.conf.sample
|
||||
.stestr/
|
||||
|
||||
#swap file
|
||||
*.swp
|
||||
|
||||
# Files created by releasenotes build
|
||||
releasenotes/build
|
||||
|
||||
# Coverage
|
||||
.coverage
|
||||
cover/
|
|
@ -1,4 +0,0 @@
|
|||
[DEFAULT]
|
||||
test_path=./muranoagent/tests/unit
|
||||
top_dir=./
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
- project:
|
||||
templates:
|
||||
- check-requirements
|
||||
- openstack-python3-jobs
|
||||
- release-notes-jobs-python3
|
|
@ -1,19 +0,0 @@
|
|||
The source repository for this project can be found at:
|
||||
|
||||
https://opendev.org/openstack/murano-agent
|
||||
|
||||
Pull requests submitted through GitHub are not monitored.
|
||||
|
||||
To start contributing to OpenStack, follow the steps in the contribution guide
|
||||
to set up and use Gerrit:
|
||||
|
||||
https://docs.openstack.org/contributors/code-and-documentation/quick-start.html
|
||||
|
||||
Bugs should be filed on Launchpad:
|
||||
|
||||
https://bugs.launchpad.net/murano
|
||||
|
||||
For more specific information about contributing to this repository, see the
|
||||
murano-agent contributor guide:
|
||||
|
||||
https://docs.openstack.org/murano/latest/contributor/contributing.html
|
|
@ -1,4 +0,0 @@
|
|||
Style Commandments
|
||||
==================
|
||||
|
||||
Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/
|
176
LICENSE
176
LICENSE
|
@ -1,176 +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.
|
||||
|
62
README.rst
62
README.rst
|
@ -1,56 +1,10 @@
|
|||
========================
|
||||
Team and repository tags
|
||||
========================
|
||||
This project is no longer maintained.
|
||||
|
||||
.. image:: https://governance.openstack.org/tc/badges/murano-agent.svg
|
||||
:target: https://governance.openstack.org/tc/reference/tags/index.html
|
||||
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".
|
||||
|
||||
.. Change things from this point on
|
||||
|
||||
Murano Agent
|
||||
============
|
||||
|
||||
Murano Agent is a VM-side guest agent that accepts commands from Murano engine
|
||||
and executes them.
|
||||
|
||||
Image building using DiskImage-Builder
|
||||
--------------------------------------
|
||||
|
||||
Folder, named *contrib/elements* contains
|
||||
`diskimage-builder <https://opendev.org/openstack/diskimage-builder>`_
|
||||
elements to build an image which contains the Murano Agent required to use Murano.
|
||||
|
||||
Ubuntu based image containing the agent can be built and uploaded
|
||||
to Glance with the following commands:
|
||||
|
||||
::
|
||||
|
||||
$ git clone https://opendev.org/openstack/diskimage-builder.git
|
||||
$ git clone https://opendev.org/openstack/murano-agent.git
|
||||
$ export ELEMENTS_PATH=murano-agent/contrib/elements
|
||||
$ export DIB_CLOUD_INIT_DATASOURCES=OpenStack
|
||||
$ diskimage-builder/bin/disk-image-create vm ubuntu \
|
||||
murano-agent -o ubuntu-murano-agent.qcow2
|
||||
$ openstack image create ubuntu-murano --disk-format qcow2
|
||||
--container-format bare --file ubuntu-murano-agent.qcow2 \
|
||||
--property murano_image_info='{"title": "Ubuntu for Murano", "type": "linux"}'
|
||||
|
||||
Project Resources
|
||||
-----------------
|
||||
|
||||
Project status, bugs, and blueprints are tracked on Launchpad:
|
||||
|
||||
https://launchpad.net/murano
|
||||
|
||||
Developer documentation can be found here:
|
||||
|
||||
https://docs.openstack.org/murano/latest/
|
||||
|
||||
Additional resources are linked from the project wiki page:
|
||||
|
||||
https://wiki.openstack.org/wiki/Murano
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Apache License Version 2.0 http://www.apache.org/licenses/LICENSE-2.0
|
||||
For any further questions, please email
|
||||
openstack-discuss@lists.openstack.org or join #openstack-dev on
|
||||
OFTC.
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
# Makefile for murano agent
|
||||
|
||||
CFLAGS= -Wall
|
||||
|
||||
default_target: all
|
||||
|
||||
|
||||
all: agent-binary producer-binary
|
||||
|
||||
|
||||
# NOTE: Use this if RabbitMQ needs to be linked in as a dynamic library
|
||||
#agent-binary: murano-agent.o lcfg_static.o utils.o
|
||||
# gcc -o murano-agent murano-agent.o lcfg_static.o utils.o -lrabbitmq -lrt
|
||||
|
||||
agent-binary: murano-agent.o lcfg_static.o utils.o librabbitmq.a
|
||||
gcc $(CFLAGS) -o murano-agent murano-agent.o lcfg_static.o utils.o -L. -lrabbitmq -lrt
|
||||
|
||||
# NOTE: Use this if RabbitMQ needs to be linked in as a dynamic library
|
||||
#producer-binary: producer.o utils.o
|
||||
# gcc -o producer producer.o utils.o -lrabbitmq -lrt
|
||||
|
||||
producer-binary: producer.o utils.o librabbitmq.a
|
||||
gcc $(CFLAGS) -o producer producer.o utils.o -L. -lrabbitmq -lrt
|
||||
|
||||
murano-agent.o: murano-agent.c
|
||||
gcc $(CFLAGS) -c -I. -fPIC murano-agent.c
|
||||
|
||||
producer.o: producer.c
|
||||
gcc $(CFLAGS) -c -I. -fPIC producer.c
|
||||
|
||||
lcfg_static.o: lcfg_static.c
|
||||
gcc $(CFLAGS) -c -fPIC lcfg_static.c
|
||||
|
||||
utils.o: utils.c
|
||||
gcc $(CFLAGS) -c -I. -fPIC utils.c
|
||||
|
||||
clean:
|
||||
rm -f *.o murano-agent producer
|
|
@ -1,64 +0,0 @@
|
|||
This is a demo implementation of Murano agent.
|
||||
|
||||
=== The scenario ===
|
||||
|
||||
Effectively the agent only connects to an AMQP queue
|
||||
using properties from the configuration file, consumes
|
||||
all the messages from the queue, logs them and puts
|
||||
result messages into a result queue. Result messages
|
||||
are just stubs signaling success back to the calling
|
||||
application.
|
||||
|
||||
=== Building binaries ===
|
||||
|
||||
Build process has been tested on Ubuntu 13.04 only for
|
||||
x86_64 architecture. Note that the distribution contains
|
||||
RabbitMQ client pre-built as a static library. It gets
|
||||
linked with demo agent object files statically. It was
|
||||
done to eliminate the need to build and install RabbitMQ
|
||||
client library separately in case of using Ubuntu 64 bit.
|
||||
|
||||
In order to build demo agent for embedded operating systems
|
||||
based on BusyBox technology (such as Cirros) refer to
|
||||
Buildroot toolkit (http://buildroot.uclibc.org/).
|
||||
|
||||
To build agent binary:
|
||||
make agent-binary
|
||||
|
||||
To build test producer binary:
|
||||
make prodcer-binary
|
||||
|
||||
To build both binaries:
|
||||
make
|
||||
|
||||
To clean up the source directory:
|
||||
make clean
|
||||
|
||||
=== Usage ===
|
||||
|
||||
To run agent:
|
||||
./murano-agent <config_file> <log_file>
|
||||
|
||||
<config_file>:
|
||||
Refer to murano-agent.conf.example file to find out
|
||||
what configuration properties are possible.
|
||||
|
||||
<log_file>:
|
||||
Path to log file which agent writes the messages into.
|
||||
|
||||
To run test message producer:
|
||||
./producer <host> <port> <message_rate> <message_count>
|
||||
|
||||
<host>:
|
||||
RabbitMQ server host for the producer to connect to.
|
||||
|
||||
<port>:
|
||||
RabbitMQ server port for the producer to connect to.
|
||||
|
||||
<message_rate>:
|
||||
Message rate at which the producer sends messages
|
||||
(delay in seconds between messages).
|
||||
|
||||
<message_count>:
|
||||
A number of messages the producer should send.
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -1,630 +0,0 @@
|
|||
/* Generated code. Do not edit. Edit and re-run codegen.py instead.
|
||||
*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MIT
|
||||
*
|
||||
* Portions created by Alan Antonuk are Copyright (c) 2012-2013
|
||||
* Alan Antonuk. All Rights Reserved.
|
||||
*
|
||||
* Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
|
||||
* VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
* ***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#ifndef AMQP_FRAMING_H
|
||||
#define AMQP_FRAMING_H
|
||||
|
||||
#include <amqp.h>
|
||||
|
||||
AMQP_BEGIN_DECLS
|
||||
|
||||
#define AMQP_PROTOCOL_VERSION_MAJOR 0
|
||||
#define AMQP_PROTOCOL_VERSION_MINOR 9
|
||||
#define AMQP_PROTOCOL_VERSION_REVISION 1
|
||||
#define AMQP_PROTOCOL_PORT 5672
|
||||
#define AMQP_FRAME_METHOD 1
|
||||
#define AMQP_FRAME_HEADER 2
|
||||
#define AMQP_FRAME_BODY 3
|
||||
#define AMQP_FRAME_HEARTBEAT 8
|
||||
#define AMQP_FRAME_MIN_SIZE 4096
|
||||
#define AMQP_FRAME_END 206
|
||||
#define AMQP_REPLY_SUCCESS 200
|
||||
#define AMQP_CONTENT_TOO_LARGE 311
|
||||
#define AMQP_NO_ROUTE 312
|
||||
#define AMQP_NO_CONSUMERS 313
|
||||
#define AMQP_ACCESS_REFUSED 403
|
||||
#define AMQP_NOT_FOUND 404
|
||||
#define AMQP_RESOURCE_LOCKED 405
|
||||
#define AMQP_PRECONDITION_FAILED 406
|
||||
#define AMQP_CONNECTION_FORCED 320
|
||||
#define AMQP_INVALID_PATH 402
|
||||
#define AMQP_FRAME_ERROR 501
|
||||
#define AMQP_SYNTAX_ERROR 502
|
||||
#define AMQP_COMMAND_INVALID 503
|
||||
#define AMQP_CHANNEL_ERROR 504
|
||||
#define AMQP_UNEXPECTED_FRAME 505
|
||||
#define AMQP_RESOURCE_ERROR 506
|
||||
#define AMQP_NOT_ALLOWED 530
|
||||
#define AMQP_NOT_IMPLEMENTED 540
|
||||
#define AMQP_INTERNAL_ERROR 541
|
||||
|
||||
/* Function prototypes. */
|
||||
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
char const *
|
||||
AMQP_CALL amqp_constant_name(int constantNumber);
|
||||
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
amqp_boolean_t
|
||||
AMQP_CALL amqp_constant_is_hard_error(int constantNumber);
|
||||
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
char const *
|
||||
AMQP_CALL amqp_method_name(amqp_method_number_t methodNumber);
|
||||
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
amqp_boolean_t
|
||||
AMQP_CALL amqp_method_has_content(amqp_method_number_t methodNumber);
|
||||
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
int
|
||||
AMQP_CALL amqp_decode_method(amqp_method_number_t methodNumber,
|
||||
amqp_pool_t *pool,
|
||||
amqp_bytes_t encoded,
|
||||
void **decoded);
|
||||
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
int
|
||||
AMQP_CALL amqp_decode_properties(uint16_t class_id,
|
||||
amqp_pool_t *pool,
|
||||
amqp_bytes_t encoded,
|
||||
void **decoded);
|
||||
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
int
|
||||
AMQP_CALL amqp_encode_method(amqp_method_number_t methodNumber,
|
||||
void *decoded,
|
||||
amqp_bytes_t encoded);
|
||||
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
int
|
||||
AMQP_CALL amqp_encode_properties(uint16_t class_id,
|
||||
void *decoded,
|
||||
amqp_bytes_t encoded);
|
||||
|
||||
/* Method field records. */
|
||||
|
||||
#define AMQP_CONNECTION_START_METHOD ((amqp_method_number_t) 0x000A000A) /* 10, 10; 655370 */
|
||||
typedef struct amqp_connection_start_t_ {
|
||||
uint8_t version_major;
|
||||
uint8_t version_minor;
|
||||
amqp_table_t server_properties;
|
||||
amqp_bytes_t mechanisms;
|
||||
amqp_bytes_t locales;
|
||||
} amqp_connection_start_t;
|
||||
|
||||
#define AMQP_CONNECTION_START_OK_METHOD ((amqp_method_number_t) 0x000A000B) /* 10, 11; 655371 */
|
||||
typedef struct amqp_connection_start_ok_t_ {
|
||||
amqp_table_t client_properties;
|
||||
amqp_bytes_t mechanism;
|
||||
amqp_bytes_t response;
|
||||
amqp_bytes_t locale;
|
||||
} amqp_connection_start_ok_t;
|
||||
|
||||
#define AMQP_CONNECTION_SECURE_METHOD ((amqp_method_number_t) 0x000A0014) /* 10, 20; 655380 */
|
||||
typedef struct amqp_connection_secure_t_ {
|
||||
amqp_bytes_t challenge;
|
||||
} amqp_connection_secure_t;
|
||||
|
||||
#define AMQP_CONNECTION_SECURE_OK_METHOD ((amqp_method_number_t) 0x000A0015) /* 10, 21; 655381 */
|
||||
typedef struct amqp_connection_secure_ok_t_ {
|
||||
amqp_bytes_t response;
|
||||
} amqp_connection_secure_ok_t;
|
||||
|
||||
#define AMQP_CONNECTION_TUNE_METHOD ((amqp_method_number_t) 0x000A001E) /* 10, 30; 655390 */
|
||||
typedef struct amqp_connection_tune_t_ {
|
||||
uint16_t channel_max;
|
||||
uint32_t frame_max;
|
||||
uint16_t heartbeat;
|
||||
} amqp_connection_tune_t;
|
||||
|
||||
#define AMQP_CONNECTION_TUNE_OK_METHOD ((amqp_method_number_t) 0x000A001F) /* 10, 31; 655391 */
|
||||
typedef struct amqp_connection_tune_ok_t_ {
|
||||
uint16_t channel_max;
|
||||
uint32_t frame_max;
|
||||
uint16_t heartbeat;
|
||||
} amqp_connection_tune_ok_t;
|
||||
|
||||
#define AMQP_CONNECTION_OPEN_METHOD ((amqp_method_number_t) 0x000A0028) /* 10, 40; 655400 */
|
||||
typedef struct amqp_connection_open_t_ {
|
||||
amqp_bytes_t virtual_host;
|
||||
amqp_bytes_t capabilities;
|
||||
amqp_boolean_t insist;
|
||||
} amqp_connection_open_t;
|
||||
|
||||
#define AMQP_CONNECTION_OPEN_OK_METHOD ((amqp_method_number_t) 0x000A0029) /* 10, 41; 655401 */
|
||||
typedef struct amqp_connection_open_ok_t_ {
|
||||
amqp_bytes_t known_hosts;
|
||||
} amqp_connection_open_ok_t;
|
||||
|
||||
#define AMQP_CONNECTION_CLOSE_METHOD ((amqp_method_number_t) 0x000A0032) /* 10, 50; 655410 */
|
||||
typedef struct amqp_connection_close_t_ {
|
||||
uint16_t reply_code;
|
||||
amqp_bytes_t reply_text;
|
||||
uint16_t class_id;
|
||||
uint16_t method_id;
|
||||
} amqp_connection_close_t;
|
||||
|
||||
#define AMQP_CONNECTION_CLOSE_OK_METHOD ((amqp_method_number_t) 0x000A0033) /* 10, 51; 655411 */
|
||||
typedef struct amqp_connection_close_ok_t_ {
|
||||
char dummy; /* Dummy field to avoid empty struct */
|
||||
} amqp_connection_close_ok_t;
|
||||
|
||||
#define AMQP_CHANNEL_OPEN_METHOD ((amqp_method_number_t) 0x0014000A) /* 20, 10; 1310730 */
|
||||
typedef struct amqp_channel_open_t_ {
|
||||
amqp_bytes_t out_of_band;
|
||||
} amqp_channel_open_t;
|
||||
|
||||
#define AMQP_CHANNEL_OPEN_OK_METHOD ((amqp_method_number_t) 0x0014000B) /* 20, 11; 1310731 */
|
||||
typedef struct amqp_channel_open_ok_t_ {
|
||||
amqp_bytes_t channel_id;
|
||||
} amqp_channel_open_ok_t;
|
||||
|
||||
#define AMQP_CHANNEL_FLOW_METHOD ((amqp_method_number_t) 0x00140014) /* 20, 20; 1310740 */
|
||||
typedef struct amqp_channel_flow_t_ {
|
||||
amqp_boolean_t active;
|
||||
} amqp_channel_flow_t;
|
||||
|
||||
#define AMQP_CHANNEL_FLOW_OK_METHOD ((amqp_method_number_t) 0x00140015) /* 20, 21; 1310741 */
|
||||
typedef struct amqp_channel_flow_ok_t_ {
|
||||
amqp_boolean_t active;
|
||||
} amqp_channel_flow_ok_t;
|
||||
|
||||
#define AMQP_CHANNEL_CLOSE_METHOD ((amqp_method_number_t) 0x00140028) /* 20, 40; 1310760 */
|
||||
typedef struct amqp_channel_close_t_ {
|
||||
uint16_t reply_code;
|
||||
amqp_bytes_t reply_text;
|
||||
uint16_t class_id;
|
||||
uint16_t method_id;
|
||||
} amqp_channel_close_t;
|
||||
|
||||
#define AMQP_CHANNEL_CLOSE_OK_METHOD ((amqp_method_number_t) 0x00140029) /* 20, 41; 1310761 */
|
||||
typedef struct amqp_channel_close_ok_t_ {
|
||||
char dummy; /* Dummy field to avoid empty struct */
|
||||
} amqp_channel_close_ok_t;
|
||||
|
||||
#define AMQP_ACCESS_REQUEST_METHOD ((amqp_method_number_t) 0x001E000A) /* 30, 10; 1966090 */
|
||||
typedef struct amqp_access_request_t_ {
|
||||
amqp_bytes_t realm;
|
||||
amqp_boolean_t exclusive;
|
||||
amqp_boolean_t passive;
|
||||
amqp_boolean_t active;
|
||||
amqp_boolean_t write;
|
||||
amqp_boolean_t read;
|
||||
} amqp_access_request_t;
|
||||
|
||||
#define AMQP_ACCESS_REQUEST_OK_METHOD ((amqp_method_number_t) 0x001E000B) /* 30, 11; 1966091 */
|
||||
typedef struct amqp_access_request_ok_t_ {
|
||||
uint16_t ticket;
|
||||
} amqp_access_request_ok_t;
|
||||
|
||||
#define AMQP_EXCHANGE_DECLARE_METHOD ((amqp_method_number_t) 0x0028000A) /* 40, 10; 2621450 */
|
||||
typedef struct amqp_exchange_declare_t_ {
|
||||
uint16_t ticket;
|
||||
amqp_bytes_t exchange;
|
||||
amqp_bytes_t type;
|
||||
amqp_boolean_t passive;
|
||||
amqp_boolean_t durable;
|
||||
amqp_boolean_t auto_delete;
|
||||
amqp_boolean_t internal;
|
||||
amqp_boolean_t nowait;
|
||||
amqp_table_t arguments;
|
||||
} amqp_exchange_declare_t;
|
||||
|
||||
#define AMQP_EXCHANGE_DECLARE_OK_METHOD ((amqp_method_number_t) 0x0028000B) /* 40, 11; 2621451 */
|
||||
typedef struct amqp_exchange_declare_ok_t_ {
|
||||
char dummy; /* Dummy field to avoid empty struct */
|
||||
} amqp_exchange_declare_ok_t;
|
||||
|
||||
#define AMQP_EXCHANGE_DELETE_METHOD ((amqp_method_number_t) 0x00280014) /* 40, 20; 2621460 */
|
||||
typedef struct amqp_exchange_delete_t_ {
|
||||
uint16_t ticket;
|
||||
amqp_bytes_t exchange;
|
||||
amqp_boolean_t if_unused;
|
||||
amqp_boolean_t nowait;
|
||||
} amqp_exchange_delete_t;
|
||||
|
||||
#define AMQP_EXCHANGE_DELETE_OK_METHOD ((amqp_method_number_t) 0x00280015) /* 40, 21; 2621461 */
|
||||
typedef struct amqp_exchange_delete_ok_t_ {
|
||||
char dummy; /* Dummy field to avoid empty struct */
|
||||
} amqp_exchange_delete_ok_t;
|
||||
|
||||
#define AMQP_EXCHANGE_BIND_METHOD ((amqp_method_number_t) 0x0028001E) /* 40, 30; 2621470 */
|
||||
typedef struct amqp_exchange_bind_t_ {
|
||||
uint16_t ticket;
|
||||
amqp_bytes_t destination;
|
||||
amqp_bytes_t source;
|
||||
amqp_bytes_t routing_key;
|
||||
amqp_boolean_t nowait;
|
||||
amqp_table_t arguments;
|
||||
} amqp_exchange_bind_t;
|
||||
|
||||
#define AMQP_EXCHANGE_BIND_OK_METHOD ((amqp_method_number_t) 0x0028001F) /* 40, 31; 2621471 */
|
||||
typedef struct amqp_exchange_bind_ok_t_ {
|
||||
char dummy; /* Dummy field to avoid empty struct */
|
||||
} amqp_exchange_bind_ok_t;
|
||||
|
||||
#define AMQP_EXCHANGE_UNBIND_METHOD ((amqp_method_number_t) 0x00280028) /* 40, 40; 2621480 */
|
||||
typedef struct amqp_exchange_unbind_t_ {
|
||||
uint16_t ticket;
|
||||
amqp_bytes_t destination;
|
||||
amqp_bytes_t source;
|
||||
amqp_bytes_t routing_key;
|
||||
amqp_boolean_t nowait;
|
||||
amqp_table_t arguments;
|
||||
} amqp_exchange_unbind_t;
|
||||
|
||||
#define AMQP_EXCHANGE_UNBIND_OK_METHOD ((amqp_method_number_t) 0x00280033) /* 40, 51; 2621491 */
|
||||
typedef struct amqp_exchange_unbind_ok_t_ {
|
||||
char dummy; /* Dummy field to avoid empty struct */
|
||||
} amqp_exchange_unbind_ok_t;
|
||||
|
||||
#define AMQP_QUEUE_DECLARE_METHOD ((amqp_method_number_t) 0x0032000A) /* 50, 10; 3276810 */
|
||||
typedef struct amqp_queue_declare_t_ {
|
||||
uint16_t ticket;
|
||||
amqp_bytes_t queue;
|
||||
amqp_boolean_t passive;
|
||||
amqp_boolean_t durable;
|
||||
amqp_boolean_t exclusive;
|
||||
amqp_boolean_t auto_delete;
|
||||
amqp_boolean_t nowait;
|
||||
amqp_table_t arguments;
|
||||
} amqp_queue_declare_t;
|
||||
|
||||
#define AMQP_QUEUE_DECLARE_OK_METHOD ((amqp_method_number_t) 0x0032000B) /* 50, 11; 3276811 */
|
||||
typedef struct amqp_queue_declare_ok_t_ {
|
||||
amqp_bytes_t queue;
|
||||
uint32_t message_count;
|
||||
uint32_t consumer_count;
|
||||
} amqp_queue_declare_ok_t;
|
||||
|
||||
#define AMQP_QUEUE_BIND_METHOD ((amqp_method_number_t) 0x00320014) /* 50, 20; 3276820 */
|
||||
typedef struct amqp_queue_bind_t_ {
|
||||
uint16_t ticket;
|
||||
amqp_bytes_t queue;
|
||||
amqp_bytes_t exchange;
|
||||
amqp_bytes_t routing_key;
|
||||
amqp_boolean_t nowait;
|
||||
amqp_table_t arguments;
|
||||
} amqp_queue_bind_t;
|
||||
|
||||
#define AMQP_QUEUE_BIND_OK_METHOD ((amqp_method_number_t) 0x00320015) /* 50, 21; 3276821 */
|
||||
typedef struct amqp_queue_bind_ok_t_ {
|
||||
char dummy; /* Dummy field to avoid empty struct */
|
||||
} amqp_queue_bind_ok_t;
|
||||
|
||||
#define AMQP_QUEUE_PURGE_METHOD ((amqp_method_number_t) 0x0032001E) /* 50, 30; 3276830 */
|
||||
typedef struct amqp_queue_purge_t_ {
|
||||
uint16_t ticket;
|
||||
amqp_bytes_t queue;
|
||||
amqp_boolean_t nowait;
|
||||
} amqp_queue_purge_t;
|
||||
|
||||
#define AMQP_QUEUE_PURGE_OK_METHOD ((amqp_method_number_t) 0x0032001F) /* 50, 31; 3276831 */
|
||||
typedef struct amqp_queue_purge_ok_t_ {
|
||||
uint32_t message_count;
|
||||
} amqp_queue_purge_ok_t;
|
||||
|
||||
#define AMQP_QUEUE_DELETE_METHOD ((amqp_method_number_t) 0x00320028) /* 50, 40; 3276840 */
|
||||
typedef struct amqp_queue_delete_t_ {
|
||||
uint16_t ticket;
|
||||
amqp_bytes_t queue;
|
||||
amqp_boolean_t if_unused;
|
||||
amqp_boolean_t if_empty;
|
||||
amqp_boolean_t nowait;
|
||||
} amqp_queue_delete_t;
|
||||
|
||||
#define AMQP_QUEUE_DELETE_OK_METHOD ((amqp_method_number_t) 0x00320029) /* 50, 41; 3276841 */
|
||||
typedef struct amqp_queue_delete_ok_t_ {
|
||||
uint32_t message_count;
|
||||
} amqp_queue_delete_ok_t;
|
||||
|
||||
#define AMQP_QUEUE_UNBIND_METHOD ((amqp_method_number_t) 0x00320032) /* 50, 50; 3276850 */
|
||||
typedef struct amqp_queue_unbind_t_ {
|
||||
uint16_t ticket;
|
||||
amqp_bytes_t queue;
|
||||
amqp_bytes_t exchange;
|
||||
amqp_bytes_t routing_key;
|
||||
amqp_table_t arguments;
|
||||
} amqp_queue_unbind_t;
|
||||
|
||||
#define AMQP_QUEUE_UNBIND_OK_METHOD ((amqp_method_number_t) 0x00320033) /* 50, 51; 3276851 */
|
||||
typedef struct amqp_queue_unbind_ok_t_ {
|
||||
char dummy; /* Dummy field to avoid empty struct */
|
||||
} amqp_queue_unbind_ok_t;
|
||||
|
||||
#define AMQP_BASIC_QOS_METHOD ((amqp_method_number_t) 0x003C000A) /* 60, 10; 3932170 */
|
||||
typedef struct amqp_basic_qos_t_ {
|
||||
uint32_t prefetch_size;
|
||||
uint16_t prefetch_count;
|
||||
amqp_boolean_t global;
|
||||
} amqp_basic_qos_t;
|
||||
|
||||
#define AMQP_BASIC_QOS_OK_METHOD ((amqp_method_number_t) 0x003C000B) /* 60, 11; 3932171 */
|
||||
typedef struct amqp_basic_qos_ok_t_ {
|
||||
char dummy; /* Dummy field to avoid empty struct */
|
||||
} amqp_basic_qos_ok_t;
|
||||
|
||||
#define AMQP_BASIC_CONSUME_METHOD ((amqp_method_number_t) 0x003C0014) /* 60, 20; 3932180 */
|
||||
typedef struct amqp_basic_consume_t_ {
|
||||
uint16_t ticket;
|
||||
amqp_bytes_t queue;
|
||||
amqp_bytes_t consumer_tag;
|
||||
amqp_boolean_t no_local;
|
||||
amqp_boolean_t no_ack;
|
||||
amqp_boolean_t exclusive;
|
||||
amqp_boolean_t nowait;
|
||||
amqp_table_t arguments;
|
||||
} amqp_basic_consume_t;
|
||||
|
||||
#define AMQP_BASIC_CONSUME_OK_METHOD ((amqp_method_number_t) 0x003C0015) /* 60, 21; 3932181 */
|
||||
typedef struct amqp_basic_consume_ok_t_ {
|
||||
amqp_bytes_t consumer_tag;
|
||||
} amqp_basic_consume_ok_t;
|
||||
|
||||
#define AMQP_BASIC_CANCEL_METHOD ((amqp_method_number_t) 0x003C001E) /* 60, 30; 3932190 */
|
||||
typedef struct amqp_basic_cancel_t_ {
|
||||
amqp_bytes_t consumer_tag;
|
||||
amqp_boolean_t nowait;
|
||||
} amqp_basic_cancel_t;
|
||||
|
||||
#define AMQP_BASIC_CANCEL_OK_METHOD ((amqp_method_number_t) 0x003C001F) /* 60, 31; 3932191 */
|
||||
typedef struct amqp_basic_cancel_ok_t_ {
|
||||
amqp_bytes_t consumer_tag;
|
||||
} amqp_basic_cancel_ok_t;
|
||||
|
||||
#define AMQP_BASIC_PUBLISH_METHOD ((amqp_method_number_t) 0x003C0028) /* 60, 40; 3932200 */
|
||||
typedef struct amqp_basic_publish_t_ {
|
||||
uint16_t ticket;
|
||||
amqp_bytes_t exchange;
|
||||
amqp_bytes_t routing_key;
|
||||
amqp_boolean_t mandatory;
|
||||
amqp_boolean_t immediate;
|
||||
} amqp_basic_publish_t;
|
||||
|
||||
#define AMQP_BASIC_RETURN_METHOD ((amqp_method_number_t) 0x003C0032) /* 60, 50; 3932210 */
|
||||
typedef struct amqp_basic_return_t_ {
|
||||
uint16_t reply_code;
|
||||
amqp_bytes_t reply_text;
|
||||
amqp_bytes_t exchange;
|
||||
amqp_bytes_t routing_key;
|
||||
} amqp_basic_return_t;
|
||||
|
||||
#define AMQP_BASIC_DELIVER_METHOD ((amqp_method_number_t) 0x003C003C) /* 60, 60; 3932220 */
|
||||
typedef struct amqp_basic_deliver_t_ {
|
||||
amqp_bytes_t consumer_tag;
|
||||
uint64_t delivery_tag;
|
||||
amqp_boolean_t redelivered;
|
||||
amqp_bytes_t exchange;
|
||||
amqp_bytes_t routing_key;
|
||||
} amqp_basic_deliver_t;
|
||||
|
||||
#define AMQP_BASIC_GET_METHOD ((amqp_method_number_t) 0x003C0046) /* 60, 70; 3932230 */
|
||||
typedef struct amqp_basic_get_t_ {
|
||||
uint16_t ticket;
|
||||
amqp_bytes_t queue;
|
||||
amqp_boolean_t no_ack;
|
||||
} amqp_basic_get_t;
|
||||
|
||||
#define AMQP_BASIC_GET_OK_METHOD ((amqp_method_number_t) 0x003C0047) /* 60, 71; 3932231 */
|
||||
typedef struct amqp_basic_get_ok_t_ {
|
||||
uint64_t delivery_tag;
|
||||
amqp_boolean_t redelivered;
|
||||
amqp_bytes_t exchange;
|
||||
amqp_bytes_t routing_key;
|
||||
uint32_t message_count;
|
||||
} amqp_basic_get_ok_t;
|
||||
|
||||
#define AMQP_BASIC_GET_EMPTY_METHOD ((amqp_method_number_t) 0x003C0048) /* 60, 72; 3932232 */
|
||||
typedef struct amqp_basic_get_empty_t_ {
|
||||
amqp_bytes_t cluster_id;
|
||||
} amqp_basic_get_empty_t;
|
||||
|
||||
#define AMQP_BASIC_ACK_METHOD ((amqp_method_number_t) 0x003C0050) /* 60, 80; 3932240 */
|
||||
typedef struct amqp_basic_ack_t_ {
|
||||
uint64_t delivery_tag;
|
||||
amqp_boolean_t multiple;
|
||||
} amqp_basic_ack_t;
|
||||
|
||||
#define AMQP_BASIC_REJECT_METHOD ((amqp_method_number_t) 0x003C005A) /* 60, 90; 3932250 */
|
||||
typedef struct amqp_basic_reject_t_ {
|
||||
uint64_t delivery_tag;
|
||||
amqp_boolean_t requeue;
|
||||
} amqp_basic_reject_t;
|
||||
|
||||
#define AMQP_BASIC_RECOVER_ASYNC_METHOD ((amqp_method_number_t) 0x003C0064) /* 60, 100; 3932260 */
|
||||
typedef struct amqp_basic_recover_async_t_ {
|
||||
amqp_boolean_t requeue;
|
||||
} amqp_basic_recover_async_t;
|
||||
|
||||
#define AMQP_BASIC_RECOVER_METHOD ((amqp_method_number_t) 0x003C006E) /* 60, 110; 3932270 */
|
||||
typedef struct amqp_basic_recover_t_ {
|
||||
amqp_boolean_t requeue;
|
||||
} amqp_basic_recover_t;
|
||||
|
||||
#define AMQP_BASIC_RECOVER_OK_METHOD ((amqp_method_number_t) 0x003C006F) /* 60, 111; 3932271 */
|
||||
typedef struct amqp_basic_recover_ok_t_ {
|
||||
char dummy; /* Dummy field to avoid empty struct */
|
||||
} amqp_basic_recover_ok_t;
|
||||
|
||||
#define AMQP_BASIC_NACK_METHOD ((amqp_method_number_t) 0x003C0078) /* 60, 120; 3932280 */
|
||||
typedef struct amqp_basic_nack_t_ {
|
||||
uint64_t delivery_tag;
|
||||
amqp_boolean_t multiple;
|
||||
amqp_boolean_t requeue;
|
||||
} amqp_basic_nack_t;
|
||||
|
||||
#define AMQP_TX_SELECT_METHOD ((amqp_method_number_t) 0x005A000A) /* 90, 10; 5898250 */
|
||||
typedef struct amqp_tx_select_t_ {
|
||||
char dummy; /* Dummy field to avoid empty struct */
|
||||
} amqp_tx_select_t;
|
||||
|
||||
#define AMQP_TX_SELECT_OK_METHOD ((amqp_method_number_t) 0x005A000B) /* 90, 11; 5898251 */
|
||||
typedef struct amqp_tx_select_ok_t_ {
|
||||
char dummy; /* Dummy field to avoid empty struct */
|
||||
} amqp_tx_select_ok_t;
|
||||
|
||||
#define AMQP_TX_COMMIT_METHOD ((amqp_method_number_t) 0x005A0014) /* 90, 20; 5898260 */
|
||||
typedef struct amqp_tx_commit_t_ {
|
||||
char dummy; /* Dummy field to avoid empty struct */
|
||||
} amqp_tx_commit_t;
|
||||
|
||||
#define AMQP_TX_COMMIT_OK_METHOD ((amqp_method_number_t) 0x005A0015) /* 90, 21; 5898261 */
|
||||
typedef struct amqp_tx_commit_ok_t_ {
|
||||
char dummy; /* Dummy field to avoid empty struct */
|
||||
} amqp_tx_commit_ok_t;
|
||||
|
||||
#define AMQP_TX_ROLLBACK_METHOD ((amqp_method_number_t) 0x005A001E) /* 90, 30; 5898270 */
|
||||
typedef struct amqp_tx_rollback_t_ {
|
||||
char dummy; /* Dummy field to avoid empty struct */
|
||||
} amqp_tx_rollback_t;
|
||||
|
||||
#define AMQP_TX_ROLLBACK_OK_METHOD ((amqp_method_number_t) 0x005A001F) /* 90, 31; 5898271 */
|
||||
typedef struct amqp_tx_rollback_ok_t_ {
|
||||
char dummy; /* Dummy field to avoid empty struct */
|
||||
} amqp_tx_rollback_ok_t;
|
||||
|
||||
#define AMQP_CONFIRM_SELECT_METHOD ((amqp_method_number_t) 0x0055000A) /* 85, 10; 5570570 */
|
||||
typedef struct amqp_confirm_select_t_ {
|
||||
amqp_boolean_t nowait;
|
||||
} amqp_confirm_select_t;
|
||||
|
||||
#define AMQP_CONFIRM_SELECT_OK_METHOD ((amqp_method_number_t) 0x0055000B) /* 85, 11; 5570571 */
|
||||
typedef struct amqp_confirm_select_ok_t_ {
|
||||
char dummy; /* Dummy field to avoid empty struct */
|
||||
} amqp_confirm_select_ok_t;
|
||||
|
||||
/* Class property records. */
|
||||
#define AMQP_CONNECTION_CLASS (0x000A) /* 10 */
|
||||
typedef struct amqp_connection_properties_t_ {
|
||||
amqp_flags_t _flags;
|
||||
char dummy; /* Dummy field to avoid empty struct */
|
||||
} amqp_connection_properties_t;
|
||||
|
||||
#define AMQP_CHANNEL_CLASS (0x0014) /* 20 */
|
||||
typedef struct amqp_channel_properties_t_ {
|
||||
amqp_flags_t _flags;
|
||||
char dummy; /* Dummy field to avoid empty struct */
|
||||
} amqp_channel_properties_t;
|
||||
|
||||
#define AMQP_ACCESS_CLASS (0x001E) /* 30 */
|
||||
typedef struct amqp_access_properties_t_ {
|
||||
amqp_flags_t _flags;
|
||||
char dummy; /* Dummy field to avoid empty struct */
|
||||
} amqp_access_properties_t;
|
||||
|
||||
#define AMQP_EXCHANGE_CLASS (0x0028) /* 40 */
|
||||
typedef struct amqp_exchange_properties_t_ {
|
||||
amqp_flags_t _flags;
|
||||
char dummy; /* Dummy field to avoid empty struct */
|
||||
} amqp_exchange_properties_t;
|
||||
|
||||
#define AMQP_QUEUE_CLASS (0x0032) /* 50 */
|
||||
typedef struct amqp_queue_properties_t_ {
|
||||
amqp_flags_t _flags;
|
||||
char dummy; /* Dummy field to avoid empty struct */
|
||||
} amqp_queue_properties_t;
|
||||
|
||||
#define AMQP_BASIC_CLASS (0x003C) /* 60 */
|
||||
#define AMQP_BASIC_CONTENT_TYPE_FLAG (1 << 15)
|
||||
#define AMQP_BASIC_CONTENT_ENCODING_FLAG (1 << 14)
|
||||
#define AMQP_BASIC_HEADERS_FLAG (1 << 13)
|
||||
#define AMQP_BASIC_DELIVERY_MODE_FLAG (1 << 12)
|
||||
#define AMQP_BASIC_PRIORITY_FLAG (1 << 11)
|
||||
#define AMQP_BASIC_CORRELATION_ID_FLAG (1 << 10)
|
||||
#define AMQP_BASIC_REPLY_TO_FLAG (1 << 9)
|
||||
#define AMQP_BASIC_EXPIRATION_FLAG (1 << 8)
|
||||
#define AMQP_BASIC_MESSAGE_ID_FLAG (1 << 7)
|
||||
#define AMQP_BASIC_TIMESTAMP_FLAG (1 << 6)
|
||||
#define AMQP_BASIC_TYPE_FLAG (1 << 5)
|
||||
#define AMQP_BASIC_USER_ID_FLAG (1 << 4)
|
||||
#define AMQP_BASIC_APP_ID_FLAG (1 << 3)
|
||||
#define AMQP_BASIC_CLUSTER_ID_FLAG (1 << 2)
|
||||
typedef struct amqp_basic_properties_t_ {
|
||||
amqp_flags_t _flags;
|
||||
amqp_bytes_t content_type;
|
||||
amqp_bytes_t content_encoding;
|
||||
amqp_table_t headers;
|
||||
uint8_t delivery_mode;
|
||||
uint8_t priority;
|
||||
amqp_bytes_t correlation_id;
|
||||
amqp_bytes_t reply_to;
|
||||
amqp_bytes_t expiration;
|
||||
amqp_bytes_t message_id;
|
||||
uint64_t timestamp;
|
||||
amqp_bytes_t type;
|
||||
amqp_bytes_t user_id;
|
||||
amqp_bytes_t app_id;
|
||||
amqp_bytes_t cluster_id;
|
||||
} amqp_basic_properties_t;
|
||||
|
||||
#define AMQP_TX_CLASS (0x005A) /* 90 */
|
||||
typedef struct amqp_tx_properties_t_ {
|
||||
amqp_flags_t _flags;
|
||||
char dummy; /* Dummy field to avoid empty struct */
|
||||
} amqp_tx_properties_t;
|
||||
|
||||
#define AMQP_CONFIRM_CLASS (0x0055) /* 85 */
|
||||
typedef struct amqp_confirm_properties_t_ {
|
||||
amqp_flags_t _flags;
|
||||
char dummy; /* Dummy field to avoid empty struct */
|
||||
} amqp_confirm_properties_t;
|
||||
|
||||
/* API functions for methods */
|
||||
|
||||
AMQP_PUBLIC_FUNCTION amqp_channel_open_ok_t * AMQP_CALL amqp_channel_open(amqp_connection_state_t state, amqp_channel_t channel);
|
||||
AMQP_PUBLIC_FUNCTION amqp_channel_flow_ok_t * AMQP_CALL amqp_channel_flow(amqp_connection_state_t state, amqp_channel_t channel, amqp_boolean_t active);
|
||||
AMQP_PUBLIC_FUNCTION amqp_exchange_declare_ok_t * AMQP_CALL amqp_exchange_declare(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t exchange, amqp_bytes_t type, amqp_boolean_t passive, amqp_boolean_t durable, amqp_table_t arguments);
|
||||
AMQP_PUBLIC_FUNCTION amqp_exchange_delete_ok_t * AMQP_CALL amqp_exchange_delete(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t exchange, amqp_boolean_t if_unused);
|
||||
AMQP_PUBLIC_FUNCTION amqp_exchange_bind_ok_t * AMQP_CALL amqp_exchange_bind(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t destination, amqp_bytes_t source, amqp_bytes_t routing_key, amqp_table_t arguments);
|
||||
AMQP_PUBLIC_FUNCTION amqp_exchange_unbind_ok_t * AMQP_CALL amqp_exchange_unbind(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t destination, amqp_bytes_t source, amqp_bytes_t routing_key, amqp_table_t arguments);
|
||||
AMQP_PUBLIC_FUNCTION amqp_queue_declare_ok_t * AMQP_CALL amqp_queue_declare(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, amqp_boolean_t passive, amqp_boolean_t durable, amqp_boolean_t exclusive, amqp_boolean_t auto_delete, amqp_table_t arguments);
|
||||
AMQP_PUBLIC_FUNCTION amqp_queue_bind_ok_t * AMQP_CALL amqp_queue_bind(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, amqp_bytes_t exchange, amqp_bytes_t routing_key, amqp_table_t arguments);
|
||||
AMQP_PUBLIC_FUNCTION amqp_queue_purge_ok_t * AMQP_CALL amqp_queue_purge(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue);
|
||||
AMQP_PUBLIC_FUNCTION amqp_queue_delete_ok_t * AMQP_CALL amqp_queue_delete(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, amqp_boolean_t if_unused, amqp_boolean_t if_empty);
|
||||
AMQP_PUBLIC_FUNCTION amqp_queue_unbind_ok_t * AMQP_CALL amqp_queue_unbind(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, amqp_bytes_t exchange, amqp_bytes_t routing_key, amqp_table_t arguments);
|
||||
AMQP_PUBLIC_FUNCTION amqp_basic_qos_ok_t * AMQP_CALL amqp_basic_qos(amqp_connection_state_t state, amqp_channel_t channel, uint32_t prefetch_size, uint16_t prefetch_count, amqp_boolean_t global);
|
||||
AMQP_PUBLIC_FUNCTION amqp_basic_consume_ok_t * AMQP_CALL amqp_basic_consume(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, amqp_bytes_t consumer_tag, amqp_boolean_t no_local, amqp_boolean_t no_ack, amqp_boolean_t exclusive, amqp_table_t arguments);
|
||||
AMQP_PUBLIC_FUNCTION amqp_basic_cancel_ok_t * AMQP_CALL amqp_basic_cancel(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t consumer_tag);
|
||||
AMQP_PUBLIC_FUNCTION amqp_basic_recover_ok_t * AMQP_CALL amqp_basic_recover(amqp_connection_state_t state, amqp_channel_t channel, amqp_boolean_t requeue);
|
||||
AMQP_PUBLIC_FUNCTION amqp_tx_select_ok_t * AMQP_CALL amqp_tx_select(amqp_connection_state_t state, amqp_channel_t channel);
|
||||
AMQP_PUBLIC_FUNCTION amqp_tx_commit_ok_t * AMQP_CALL amqp_tx_commit(amqp_connection_state_t state, amqp_channel_t channel);
|
||||
AMQP_PUBLIC_FUNCTION amqp_tx_rollback_ok_t * AMQP_CALL amqp_tx_rollback(amqp_connection_state_t state, amqp_channel_t channel);
|
||||
AMQP_PUBLIC_FUNCTION amqp_confirm_select_ok_t * AMQP_CALL amqp_confirm_select(amqp_connection_state_t state, amqp_channel_t channel);
|
||||
|
||||
AMQP_END_DECLS
|
||||
|
||||
#endif /* AMQP_FRAMING_H */
|
|
@ -1,161 +0,0 @@
|
|||
/* vim:set ft=c ts=2 sw=2 sts=2 et cindent: */
|
||||
/** \file */
|
||||
/*
|
||||
* Copyright 2012-2013 Michael Steinert
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef AMQP_SSL_H
|
||||
#define AMQP_SSL_H
|
||||
|
||||
#include <amqp.h>
|
||||
|
||||
AMQP_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* Create a new SSL/TLS socket object.
|
||||
*
|
||||
* The returned socket object is owned by the \ref amqp_connection_state_t object
|
||||
* and will be destroyed when the state object is destroyed or a new socket
|
||||
* object is created.
|
||||
*
|
||||
* If the socket object creation fails, the \ref amqp_connection_state_t object
|
||||
* will not be changed.
|
||||
*
|
||||
* The object returned by this function can be retrieved from the
|
||||
* amqp_connection_state_t object later using the amqp_get_socket() function.
|
||||
*
|
||||
* Calling this function may result in the underlying SSL library being initialized.
|
||||
* \sa amqp_set_initialize_ssl_library()
|
||||
*
|
||||
* \param [in,out] state The connection object that owns the SSL/TLS socket
|
||||
* \return A new socket object or NULL if an error occurred.
|
||||
*
|
||||
* \since v0.4.0
|
||||
*/
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
amqp_socket_t *
|
||||
AMQP_CALL
|
||||
amqp_ssl_socket_new(amqp_connection_state_t state);
|
||||
|
||||
/**
|
||||
* Set the CA certificate.
|
||||
*
|
||||
* \param [in,out] self An SSL/TLS socket object.
|
||||
* \param [in] cacert Path to the CA cert file in PEM format.
|
||||
*
|
||||
* \return \ref AMQP_STATUS_OK on success an enum
|
||||
*
|
||||
* \since v0.4.0
|
||||
*/
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
int
|
||||
AMQP_CALL
|
||||
amqp_ssl_socket_set_cacert(amqp_socket_t *self,
|
||||
const char *cacert);
|
||||
|
||||
/**
|
||||
* Set the client key.
|
||||
*
|
||||
* \param [in,out] self An SSL/TLS socket object.
|
||||
* \param [in] cert Path to the client certificate in PEM foramt.
|
||||
* \param [in] key Path to the client key in PEM format.
|
||||
*
|
||||
* \return Zero if successful, -1 otherwise.
|
||||
*
|
||||
* \since v0.4.0
|
||||
*/
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
int
|
||||
AMQP_CALL
|
||||
amqp_ssl_socket_set_key(amqp_socket_t *self,
|
||||
const char *cert,
|
||||
const char *key);
|
||||
|
||||
/**
|
||||
* Set the client key from a buffer.
|
||||
*
|
||||
* \param [in,out] self An SSL/TLS socket object.
|
||||
* \param [in] cert Path to the client certificate in PEM foramt.
|
||||
* \param [in] key A buffer containing client key in PEM format.
|
||||
* \param [in] n The length of the buffer.
|
||||
*
|
||||
* \return Zero if successful, -1 otherwise.
|
||||
*
|
||||
* \since v0.4.0
|
||||
*/
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
int
|
||||
AMQP_CALL
|
||||
amqp_ssl_socket_set_key_buffer(amqp_socket_t *self,
|
||||
const char *cert,
|
||||
const void *key,
|
||||
size_t n);
|
||||
|
||||
/**
|
||||
* Enable or disable peer verification.
|
||||
*
|
||||
* If peer verification is enabled then the common name in the server
|
||||
* certificate must match the server name. Peer verification is enabled by
|
||||
* default.
|
||||
*
|
||||
* \param [in,out] self An SSL/TLS socket object.
|
||||
* \param [in] verify Enable or disable peer verification.
|
||||
*
|
||||
* \since v0.4.0
|
||||
*/
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
void
|
||||
AMQP_CALL
|
||||
amqp_ssl_socket_set_verify(amqp_socket_t *self,
|
||||
amqp_boolean_t verify);
|
||||
|
||||
/**
|
||||
* Sets whether rabbitmq-c initializes the underlying SSL library.
|
||||
*
|
||||
* For SSL libraries that require a one-time initialization across
|
||||
* a whole program (e.g., OpenSSL) this sets whether or not rabbitmq-c
|
||||
* will initialize the SSL library when the first call to
|
||||
* amqp_open_socket() is made. You should call this function with
|
||||
* do_init = 0 if the underlying SSL library is initialized somewhere else
|
||||
* the program.
|
||||
*
|
||||
* Failing to initialize or double initialization of the SSL library will
|
||||
* result in undefined behavior
|
||||
*
|
||||
* By default rabbitmq-c will initialize the underlying SSL library
|
||||
*
|
||||
* NOTE: calling this function after the first socket has been opened with
|
||||
* amqp_open_socket() will not have any effect.
|
||||
*
|
||||
* \param [in] do_initalize If 0 rabbitmq-c will not initialize the SSL
|
||||
* library, otherwise rabbitmq-c will initialize the
|
||||
* SSL library
|
||||
*
|
||||
* \since v0.4.0
|
||||
*/
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
void
|
||||
AMQP_CALL
|
||||
amqp_set_initialize_ssl_library(amqp_boolean_t do_initialize);
|
||||
|
||||
AMQP_END_DECLS
|
||||
|
||||
#endif /* AMQP_SSL_H */
|
|
@ -1,69 +0,0 @@
|
|||
/* vim:set ft=c ts=2 sw=2 sts=2 et cindent: */
|
||||
/** \file */
|
||||
/*
|
||||
* Copyright 2012-2013 Michael Steinert
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A TCP socket connection.
|
||||
*/
|
||||
|
||||
#ifndef AMQP_TCP_SOCKET_H
|
||||
#define AMQP_TCP_SOCKET_H
|
||||
|
||||
#include <amqp.h>
|
||||
|
||||
AMQP_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* Create a new TCP socket.
|
||||
*
|
||||
* Call amqp_socket_close() to release socket resources.
|
||||
*
|
||||
* \return A new socket object or NULL if an error occurred.
|
||||
*
|
||||
* \since v0.4.0
|
||||
*/
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
amqp_socket_t *
|
||||
AMQP_CALL
|
||||
amqp_tcp_socket_new(amqp_connection_state_t state);
|
||||
|
||||
/**
|
||||
* Assign an open file descriptor to a socket object.
|
||||
*
|
||||
* This function must not be used in conjunction with amqp_socket_open(), i.e.
|
||||
* the socket connection should already be open(2) when this function is
|
||||
* called.
|
||||
*
|
||||
* \param [in,out] self A TCP socket object.
|
||||
* \param [in] sockfd An open socket descriptor.
|
||||
*
|
||||
* \since v0.4.0
|
||||
*/
|
||||
AMQP_PUBLIC_FUNCTION
|
||||
void
|
||||
AMQP_CALL
|
||||
amqp_tcp_socket_set_sockfd(amqp_socket_t *base, int sockfd);
|
||||
|
||||
AMQP_END_DECLS
|
||||
|
||||
#endif /* AMQP_TCP_SOCKET_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -1,146 +0,0 @@
|
|||
/* This file is an autogenerated single-file version of liblcfg.
|
||||
* It is recommended that you update this file on a regular
|
||||
* basis from the original liblcfg distribution package.
|
||||
*
|
||||
* The most recent version of liblcfg is available at
|
||||
* <http://liblcfg.carnivore.it>
|
||||
*/
|
||||
/*
|
||||
Copyright (c) 2012, Paul Baecher
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the <organization> nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <THE COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef LCFG_H
|
||||
#define LCFG_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
struct lcfg;
|
||||
|
||||
enum lcfg_status { lcfg_status_ok, lcfg_status_error };
|
||||
|
||||
typedef enum lcfg_status (*lcfg_visitor_function)(const char *key, void *data, size_t size, void *user_data);
|
||||
|
||||
|
||||
/* open a new config file */
|
||||
struct lcfg * lcfg_new(const char *filename);
|
||||
|
||||
/* parse config into memory */
|
||||
enum lcfg_status lcfg_parse(struct lcfg *);
|
||||
|
||||
/* visit all configuration elements */
|
||||
enum lcfg_status lcfg_accept(struct lcfg *, lcfg_visitor_function, void *);
|
||||
|
||||
/* access a value by path */
|
||||
enum lcfg_status lcfg_value_get(struct lcfg *, const char *, void **, size_t *);
|
||||
|
||||
/* return the last error message */
|
||||
const char * lcfg_error_get(struct lcfg *);
|
||||
|
||||
/* set error */
|
||||
void lcfg_error_set(struct lcfg *, const char *fmt, ...);
|
||||
|
||||
/* destroy lcfg context */
|
||||
void lcfg_delete(struct lcfg *);
|
||||
|
||||
|
||||
#endif
|
||||
/*
|
||||
Copyright (c) 2012, Paul Baecher
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the <organization> nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <THE COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef LCFGX_TREE_H
|
||||
#define LCFGX_TREE_H
|
||||
|
||||
|
||||
enum lcfgx_type
|
||||
{
|
||||
lcfgx_string,
|
||||
lcfgx_list,
|
||||
lcfgx_map,
|
||||
};
|
||||
|
||||
struct lcfgx_tree_node
|
||||
{
|
||||
enum lcfgx_type type;
|
||||
char *key; /* NULL for root node */
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
void *data;
|
||||
size_t len;
|
||||
} string;
|
||||
struct lcfgx_tree_node *elements; /* in case of list or map type */
|
||||
} value;
|
||||
|
||||
struct lcfgx_tree_node *next;
|
||||
};
|
||||
|
||||
struct lcfgx_tree_node *lcfgx_tree_new(struct lcfg *);
|
||||
|
||||
void lcfgx_tree_delete(struct lcfgx_tree_node *);
|
||||
void lcfgx_tree_dump(struct lcfgx_tree_node *node, int depth);
|
||||
|
||||
enum lcfgx_path_access
|
||||
{
|
||||
LCFGX_PATH_NOT_FOUND,
|
||||
LCFGX_PATH_FOUND_WRONG_TYPE_BAD,
|
||||
LCFGX_PATH_FOUND_TYPE_OK,
|
||||
};
|
||||
|
||||
extern const char *lcfgx_path_access_strings[];
|
||||
|
||||
|
||||
enum lcfgx_path_access lcfgx_get(struct lcfgx_tree_node *root, struct lcfgx_tree_node **n, const char *key, enum lcfgx_type type);
|
||||
enum lcfgx_path_access lcfgx_get_list(struct lcfgx_tree_node *root, struct lcfgx_tree_node **n, const char *key);
|
||||
enum lcfgx_path_access lcfgx_get_map(struct lcfgx_tree_node *root, struct lcfgx_tree_node **n, const char *key);
|
||||
enum lcfgx_path_access lcfgx_get_string(struct lcfgx_tree_node *root, struct lcfgx_tree_node **n, const char *key);
|
||||
|
||||
|
||||
#endif
|
||||
|
Binary file not shown.
|
@ -1,239 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <amqp_tcp_socket.h>
|
||||
#include <amqp.h>
|
||||
#include <amqp_framing.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "lcfg_static.h"
|
||||
|
||||
|
||||
static void run ( amqp_connection_state_t conn, int log_fd , const char *result_routing_key )
|
||||
{
|
||||
int received = 0;
|
||||
amqp_frame_t frame;
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
amqp_rpc_reply_t ret;
|
||||
amqp_envelope_t envelope;
|
||||
|
||||
amqp_maybe_release_buffers ( conn );
|
||||
ret = amqp_consume_message ( conn, &envelope, NULL, 0 );
|
||||
|
||||
if ( AMQP_RESPONSE_NORMAL == ret.reply_type )
|
||||
{
|
||||
int i;
|
||||
amqp_bytes_t body = envelope.message.body;
|
||||
const char *title = "A new message received:\n";
|
||||
|
||||
fprintf ( stdout, title, received );
|
||||
for ( i = 0; i < body.len; i++ )
|
||||
{
|
||||
fprintf ( stdout, "%c", * ( char* ) ( body.bytes + i ) );
|
||||
}
|
||||
puts ( "\n" );
|
||||
|
||||
write ( log_fd, ( void * ) title, strlen ( title ) );
|
||||
write ( log_fd, body.bytes, body.len );
|
||||
write ( log_fd, ( void * ) "\n\n", 2 );
|
||||
|
||||
/* Send a reply. */
|
||||
amqp_basic_properties_t props;
|
||||
props._flags = AMQP_BASIC_CONTENT_TYPE_FLAG | AMQP_BASIC_DELIVERY_MODE_FLAG | AMQP_BASIC_MESSAGE_ID_FLAG;
|
||||
|
||||
printf("message id: %s", (const char*)envelope.message.properties.message_id.bytes);
|
||||
|
||||
props.message_id = amqp_bytes_malloc_dup ( envelope.message.properties.message_id );
|
||||
props.content_type = amqp_cstring_bytes ( "text/json" );
|
||||
props.delivery_mode = 2; /* persistent delivery mode */
|
||||
|
||||
const char *result_body = "{\"IsException\": false, \"Result\": [{\"IsException\": false, \"Result\": []}]}";
|
||||
|
||||
die_on_error ( amqp_basic_publish ( conn,
|
||||
1,
|
||||
amqp_cstring_bytes ( "" ),
|
||||
amqp_cstring_bytes ( result_routing_key ),
|
||||
0,
|
||||
0,
|
||||
&props,
|
||||
amqp_cstring_bytes ( result_body ) ),
|
||||
"Publishing" );
|
||||
|
||||
amqp_destroy_envelope ( &envelope );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( AMQP_RESPONSE_LIBRARY_EXCEPTION == ret.reply_type &&
|
||||
AMQP_STATUS_UNEXPECTED_STATE == ret.library_error )
|
||||
{
|
||||
if ( AMQP_STATUS_OK != amqp_simple_wait_frame ( conn, &frame ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( AMQP_FRAME_METHOD == frame.frame_type )
|
||||
{
|
||||
switch ( frame.payload.method.id )
|
||||
{
|
||||
case AMQP_BASIC_ACK_METHOD:
|
||||
/* if we've turned publisher confirms on, and we've published a message
|
||||
* here is a message being confirmed
|
||||
*/
|
||||
|
||||
break;
|
||||
case AMQP_BASIC_RETURN_METHOD:
|
||||
/* if a published message couldn't be routed and the mandatory flag was set
|
||||
* this is what would be returned. The message then needs to be read.
|
||||
*/
|
||||
{
|
||||
amqp_message_t message;
|
||||
ret = amqp_read_message ( conn, frame.channel, &message, 0 );
|
||||
|
||||
if ( AMQP_RESPONSE_NORMAL != ret.reply_type )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
amqp_destroy_message ( &message );
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case AMQP_CHANNEL_CLOSE_METHOD:
|
||||
/* a channel.close method happens when a channel exception occurs, this
|
||||
* can happen by publishing to an exchange that doesn't exist for example
|
||||
*
|
||||
* In this case you would need to open another channel redeclare any queues
|
||||
* that were declared auto-delete, and restart any consumers that were attached
|
||||
* to the previous channel
|
||||
*/
|
||||
return;
|
||||
|
||||
case AMQP_CONNECTION_CLOSE_METHOD:
|
||||
/* a connection.close method happens when a connection exception occurs,
|
||||
* this can happen by trying to use a channel that isn't open for example.
|
||||
*
|
||||
* In this case the whole connection must be restarted.
|
||||
*/
|
||||
return;
|
||||
|
||||
default:
|
||||
fprintf ( stderr ,"An unexpected method was received %d\n", frame.payload.method.id );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
received++;
|
||||
}
|
||||
}
|
||||
|
||||
static const char* get_config_value ( struct lcfg *cfg, const char *key, int verbose )
|
||||
{
|
||||
void *data;
|
||||
size_t len;
|
||||
|
||||
if ( lcfg_value_get ( cfg, key, &data, &len ) != lcfg_status_ok )
|
||||
{
|
||||
fprintf ( stderr, "Key %s is not found in the configuration file", key );
|
||||
}
|
||||
|
||||
const char *val = ( const char * ) data;
|
||||
|
||||
if ( verbose )
|
||||
{
|
||||
fprintf ( stdout, "%s = %s\n", key, val );
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
int main ( int argc, char const *const *argv )
|
||||
{
|
||||
if ( argc != 3 )
|
||||
{
|
||||
printf ( "usage: %s CFG_FILE LOG_FILE\n", argv[0] );
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *log_filename = argv[2];
|
||||
int flags = O_CREAT | O_APPEND | O_RDWR;
|
||||
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
|
||||
int log_fd = open ( log_filename, flags, mode );
|
||||
|
||||
if ( log_fd < 0 )
|
||||
{
|
||||
fprintf ( stderr, "ERROR: Falied to open the log file '%s'\n", log_filename );
|
||||
|
||||
exit ( 1 );
|
||||
}
|
||||
|
||||
/* Read the configuration file. */
|
||||
struct lcfg *cfg = lcfg_new ( argv[1] );
|
||||
|
||||
if ( lcfg_parse ( cfg ) != lcfg_status_ok )
|
||||
{
|
||||
printf ( "lcfg error: %s\n", lcfg_error_get ( cfg ) );
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Read all the configuration parameters. */
|
||||
fprintf ( stdout, "Starting Murano agent with the following configuration:\n\n" );
|
||||
|
||||
const char *host = get_config_value ( cfg, "RABBITMQ_HOST" , 1 );
|
||||
int port = atoi ( get_config_value ( cfg, "RABBITMQ_PORT" , 1 ) );
|
||||
const char *vhost = get_config_value ( cfg, "RABBITMQ_VHOST" , 1 );
|
||||
const char *username = get_config_value ( cfg, "RABBITMQ_USERNAME" , 1 );
|
||||
const char *password = get_config_value ( cfg, "RABBITMQ_PASSWORD" , 1 );
|
||||
const char *queuename = get_config_value ( cfg, "RABBITMQ_INPUT_QUEUE" , 1 );
|
||||
const char *result_routing_key = get_config_value ( cfg, "RABBITMQ_RESULT_ROUTING_KEY", 1 );
|
||||
|
||||
amqp_connection_state_t conn = amqp_new_connection();
|
||||
amqp_socket_t *socket = NULL;
|
||||
amqp_bytes_t queuename_bytes = amqp_cstring_bytes ( queuename );
|
||||
|
||||
socket = amqp_tcp_socket_new ( conn );
|
||||
if ( !socket )
|
||||
{
|
||||
die ( "creating TCP socket" );
|
||||
}
|
||||
|
||||
if ( amqp_socket_open ( socket, host, port ) )
|
||||
{
|
||||
die ( "opening TCP socket" );
|
||||
}
|
||||
|
||||
die_on_amqp_error ( amqp_login ( conn, vhost, 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, username, password ),
|
||||
"Logging in" );
|
||||
amqp_channel_open ( conn, 1 );
|
||||
die_on_amqp_error ( amqp_get_rpc_reply ( conn ), "Opening channel" );
|
||||
|
||||
amqp_basic_consume ( conn, 1, queuename_bytes, amqp_empty_bytes, 0, 1, 0, amqp_empty_table );
|
||||
die_on_amqp_error ( amqp_get_rpc_reply ( conn ), "Consuming" );
|
||||
|
||||
puts ( "\nSuccessfully connected to Rabbit MQ server! Ready for messages..." );
|
||||
|
||||
run ( conn, log_fd , result_routing_key );
|
||||
|
||||
close ( log_fd );
|
||||
lcfg_delete ( cfg );
|
||||
|
||||
die_on_amqp_error ( amqp_channel_close ( conn, 1, AMQP_REPLY_SUCCESS ), "Closing channel" );
|
||||
die_on_amqp_error ( amqp_connection_close ( conn, AMQP_REPLY_SUCCESS ), "Closing connection" );
|
||||
die_on_error ( amqp_destroy_connection ( conn ), "Ending connection" );
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
RABBITMQ_HOST = "localhost"
|
||||
RABBITMQ_PORT = "5672"
|
||||
RABBITMQ_USERNAME = "guest"
|
||||
RABBITMQ_PASSWORD = "guest"
|
||||
RABBITMQ_VHOST = "/"
|
||||
RABBITMQ_INPUT_QUEUE = "test queue"
|
||||
RABBITMQ_RESULT_ROUTING_KEY = "result queue"
|
|
@ -1,128 +0,0 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <amqp_tcp_socket.h>
|
||||
#include <amqp.h>
|
||||
#include <amqp_framing.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#define SUMMARY_EVERY_US 1000000
|
||||
|
||||
static void send_batch ( amqp_connection_state_t conn,
|
||||
char const *queue_name,
|
||||
int rate_limit,
|
||||
int message_count )
|
||||
{
|
||||
uint64_t start_time = now_microseconds();
|
||||
int i;
|
||||
int sent = 0;
|
||||
int previous_sent = 0;
|
||||
uint64_t previous_report_time = start_time;
|
||||
uint64_t next_summary_time = start_time + SUMMARY_EVERY_US;
|
||||
|
||||
const char *message = "Hello from the producer!";
|
||||
amqp_bytes_t message_bytes;
|
||||
|
||||
message_bytes.len = strlen ( message );
|
||||
message_bytes.bytes = ( void * ) message;
|
||||
|
||||
for ( i = 0; i < message_count; i++ )
|
||||
{
|
||||
uint64_t now = now_microseconds();
|
||||
|
||||
amqp_basic_properties_t props;
|
||||
props._flags = AMQP_BASIC_CONTENT_TYPE_FLAG | AMQP_BASIC_DELIVERY_MODE_FLAG | AMQP_BASIC_MESSAGE_ID_FLAG;
|
||||
props.message_id = amqp_cstring_bytes( " msg_id " );
|
||||
props.content_type = amqp_cstring_bytes ( "text/json" );
|
||||
props.delivery_mode = 2; /* persistent delivery mode */
|
||||
|
||||
die_on_error ( amqp_basic_publish ( conn,
|
||||
1,
|
||||
amqp_cstring_bytes ( "" ),
|
||||
amqp_cstring_bytes ( queue_name ),
|
||||
0,
|
||||
0,
|
||||
&props,
|
||||
message_bytes ),
|
||||
"Publishing" );
|
||||
sent++;
|
||||
if ( now > next_summary_time )
|
||||
{
|
||||
int countOverInterval = sent - previous_sent;
|
||||
double intervalRate = countOverInterval / ( ( now - previous_report_time ) / 1000000.0 );
|
||||
printf ( "%d ms: Sent %d - %d since last report (%d Hz)\n",
|
||||
( int ) ( now - start_time ) / 1000, sent, countOverInterval, ( int ) intervalRate );
|
||||
|
||||
previous_sent = sent;
|
||||
previous_report_time = now;
|
||||
next_summary_time += SUMMARY_EVERY_US;
|
||||
}
|
||||
|
||||
while ( ( ( i * 1000000.0 ) / ( now - start_time ) ) > rate_limit )
|
||||
{
|
||||
microsleep ( 2000 );
|
||||
now = now_microseconds();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
uint64_t stop_time = now_microseconds();
|
||||
int total_delta = stop_time - start_time;
|
||||
|
||||
printf ( "PRODUCER - Message count: %d\n", message_count );
|
||||
printf ( "Total time, milliseconds: %d\n", total_delta / 1000 );
|
||||
printf ( "Overall messages-per-second: %g\n", ( message_count / ( total_delta / 1000000.0 ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
int main ( int argc, char const *const *argv )
|
||||
{
|
||||
char const *hostname;
|
||||
int port, status;
|
||||
int rate_limit;
|
||||
int message_count;
|
||||
amqp_socket_t *socket = NULL;
|
||||
amqp_connection_state_t conn;
|
||||
|
||||
if ( argc < 5 )
|
||||
{
|
||||
fprintf ( stderr, "Usage: producer host port rate_limit message_count\n" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
hostname = argv[1];
|
||||
port = atoi ( argv[2] );
|
||||
rate_limit = atoi ( argv[3] );
|
||||
message_count = atoi ( argv[4] );
|
||||
|
||||
conn = amqp_new_connection();
|
||||
|
||||
socket = amqp_tcp_socket_new ( conn );
|
||||
if ( !socket )
|
||||
{
|
||||
die ( "creating TCP socket" );
|
||||
}
|
||||
|
||||
status = amqp_socket_open ( socket, hostname, port );
|
||||
if ( status )
|
||||
{
|
||||
die ( "opening TCP socket" );
|
||||
}
|
||||
|
||||
die_on_amqp_error ( amqp_login ( conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, "guest", "guest" ),
|
||||
"Logging in" );
|
||||
amqp_channel_open ( conn, 1 );
|
||||
die_on_amqp_error ( amqp_get_rpc_reply ( conn ), "Opening channel" );
|
||||
|
||||
send_batch ( conn, "test queue", rate_limit, message_count );
|
||||
|
||||
die_on_amqp_error ( amqp_channel_close ( conn, 1, AMQP_REPLY_SUCCESS ), "Closing channel" );
|
||||
die_on_amqp_error ( amqp_connection_close ( conn, AMQP_REPLY_SUCCESS ), "Closing connection" );
|
||||
die_on_error ( amqp_destroy_connection ( conn ), "Ending connection" );
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,201 +0,0 @@
|
|||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <amqp.h>
|
||||
#include <amqp_framing.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
/* For usleep */
|
||||
/* #define _BSD_SOURCE */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
uint64_t now_microseconds ( void )
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday ( &tv, NULL );
|
||||
return ( uint64_t ) tv.tv_sec * 1000000 + ( uint64_t ) tv.tv_usec;
|
||||
}
|
||||
|
||||
void microsleep ( int usec )
|
||||
{
|
||||
usleep ( usec );
|
||||
}
|
||||
|
||||
void die ( const char *fmt, ... )
|
||||
{
|
||||
va_list ap;
|
||||
va_start ( ap, fmt );
|
||||
vfprintf ( stderr, fmt, ap );
|
||||
va_end ( ap );
|
||||
fprintf ( stderr, "\n" );
|
||||
exit ( 1 );
|
||||
}
|
||||
|
||||
void die_on_error ( int x, char const *context )
|
||||
{
|
||||
if ( x < 0 )
|
||||
{
|
||||
fprintf ( stderr, "%s: %s\n", context, amqp_error_string2 ( x ) );
|
||||
exit ( 1 );
|
||||
}
|
||||
}
|
||||
|
||||
void die_on_amqp_error ( amqp_rpc_reply_t x, char const *context )
|
||||
{
|
||||
switch ( x.reply_type )
|
||||
{
|
||||
case AMQP_RESPONSE_NORMAL:
|
||||
return;
|
||||
|
||||
case AMQP_RESPONSE_NONE:
|
||||
fprintf ( stderr, "%s: missing RPC reply type!\n", context );
|
||||
break;
|
||||
|
||||
case AMQP_RESPONSE_LIBRARY_EXCEPTION:
|
||||
fprintf ( stderr, "%s: %s\n", context, amqp_error_string2 ( x.library_error ) );
|
||||
break;
|
||||
|
||||
case AMQP_RESPONSE_SERVER_EXCEPTION:
|
||||
switch ( x.reply.id )
|
||||
{
|
||||
case AMQP_CONNECTION_CLOSE_METHOD:
|
||||
{
|
||||
amqp_connection_close_t *m = ( amqp_connection_close_t * ) x.reply.decoded;
|
||||
fprintf ( stderr, "%s: server connection error %d, message: %.*s\n",
|
||||
context,
|
||||
m->reply_code,
|
||||
( int ) m->reply_text.len, ( char * ) m->reply_text.bytes );
|
||||
break;
|
||||
}
|
||||
case AMQP_CHANNEL_CLOSE_METHOD:
|
||||
{
|
||||
amqp_channel_close_t *m = ( amqp_channel_close_t * ) x.reply.decoded;
|
||||
fprintf ( stderr, "%s: server channel error %d, message: %.*s\n",
|
||||
context,
|
||||
m->reply_code,
|
||||
( int ) m->reply_text.len, ( char * ) m->reply_text.bytes );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
fprintf ( stderr, "%s: unknown server error, method id 0x%08X\n", context, x.reply.id );
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
exit ( 1 );
|
||||
}
|
||||
|
||||
static void dump_row ( long count, int numinrow, int *chs )
|
||||
{
|
||||
int i;
|
||||
|
||||
printf ( "%08lX:", count - numinrow );
|
||||
|
||||
if ( numinrow > 0 )
|
||||
{
|
||||
for ( i = 0; i < numinrow; i++ )
|
||||
{
|
||||
if ( i == 8 )
|
||||
{
|
||||
printf ( " :" );
|
||||
}
|
||||
printf ( " %02X", chs[i] );
|
||||
}
|
||||
for ( i = numinrow; i < 16; i++ )
|
||||
{
|
||||
if ( i == 8 )
|
||||
{
|
||||
printf ( " :" );
|
||||
}
|
||||
printf ( " " );
|
||||
}
|
||||
printf ( " " );
|
||||
for ( i = 0; i < numinrow; i++ )
|
||||
{
|
||||
if ( isprint ( chs[i] ) )
|
||||
{
|
||||
printf ( "%c", chs[i] );
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ( "." );
|
||||
}
|
||||
}
|
||||
}
|
||||
printf ( "\n" );
|
||||
}
|
||||
|
||||
static int rows_eq ( int *a, int *b )
|
||||
{
|
||||
int i;
|
||||
|
||||
for ( i=0; i<16; i++ )
|
||||
if ( a[i] != b[i] )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void amqp_dump ( void const *buffer, size_t len )
|
||||
{
|
||||
unsigned char *buf = ( unsigned char * ) buffer;
|
||||
long count = 0;
|
||||
int numinrow = 0;
|
||||
int chs[16];
|
||||
int oldchs[16] = {0};
|
||||
int showed_dots = 0;
|
||||
size_t i;
|
||||
|
||||
for ( i = 0; i < len; i++ )
|
||||
{
|
||||
int ch = buf[i];
|
||||
|
||||
if ( numinrow == 16 )
|
||||
{
|
||||
int i;
|
||||
|
||||
if ( rows_eq ( oldchs, chs ) )
|
||||
{
|
||||
if ( !showed_dots )
|
||||
{
|
||||
showed_dots = 1;
|
||||
printf ( " .. .. .. .. .. .. .. .. : .. .. .. .. .. .. .. ..\n" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
showed_dots = 0;
|
||||
dump_row ( count, numinrow, chs );
|
||||
}
|
||||
|
||||
for ( i=0; i<16; i++ )
|
||||
{
|
||||
oldchs[i] = chs[i];
|
||||
}
|
||||
|
||||
numinrow = 0;
|
||||
}
|
||||
|
||||
count++;
|
||||
chs[numinrow++] = ch;
|
||||
}
|
||||
|
||||
dump_row ( count, numinrow, chs );
|
||||
|
||||
if ( numinrow != 0 )
|
||||
{
|
||||
printf ( "%08lX:\n", count );
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
#ifndef agent_utils_h
|
||||
#define agent_utils_h
|
||||
|
||||
void die(const char *fmt, ...);
|
||||
extern void die_on_error(int x, char const *context);
|
||||
extern void die_on_amqp_error(amqp_rpc_reply_t x, char const *context);
|
||||
|
||||
extern void amqp_dump(void const *buffer, size_t len);
|
||||
|
||||
extern uint64_t now_microseconds(void);
|
||||
extern void microsleep(int usec);
|
||||
|
||||
#endif
|
|
@ -1,2 +0,0 @@
|
|||
cloud-init-datasources
|
||||
murano-agent
|
|
@ -1,13 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -eu
|
||||
set -o pipefail
|
||||
|
||||
# Fix problem with default hostname
|
||||
rm -rf /etc/cloud/cloud.cfg.d/01_hostname.cfg
|
||||
|
||||
# Fix problem with locale generation
|
||||
sed -e '/# en_US.UTF-8/ s/^# //' -i /etc/locale.gen
|
||||
locale-gen
|
||||
|
||||
sed -e 's/exit 0/service murano-agent start\nexit 0/g' -i /etc/rc.local
|
|
@ -1,26 +0,0 @@
|
|||
inetutils-ping:
|
||||
net-tools:
|
||||
vim:
|
||||
isc-dhcp-client:
|
||||
git-core:
|
||||
wget:
|
||||
make:
|
||||
gcc:
|
||||
python-pip:
|
||||
dib_python_version: 2
|
||||
python3-pip:
|
||||
dib_python_version: 3
|
||||
python-dev:
|
||||
dib_python_version: 2
|
||||
python3-dev:
|
||||
dib_python_version: 3
|
||||
python-setuptools:
|
||||
dib_python_version: 2
|
||||
python3-setuptools:
|
||||
dib_python_version: 3
|
||||
python-virtualenv:
|
||||
dib_python_version: 2
|
||||
python3-virtualenv:
|
||||
dib_python_version: 3
|
||||
iptables:
|
||||
virtualenvwrapper:
|
|
@ -1,52 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -eu
|
||||
|
||||
install-packages git-core wget make gcc python-pip python-dev python-setuptools puppet
|
||||
|
||||
if test -f /etc/redhat-release
|
||||
then
|
||||
rpm -Uvh https://packages.chef.io/stable/el/7/chef-12.9.41-1.el7.x86_64.rpm
|
||||
else
|
||||
install-packages chef
|
||||
fi
|
||||
|
||||
name=murano-agent
|
||||
svc_root=${DIB_MURANO_AGENT_SVC_ROOT:-/opt/stack/$name}
|
||||
repo=${DIB_MURANO_AGENT_REPO:-https://opendev.org/openstack/murano-agent.git}
|
||||
branch=${DIB_MURANO_AGENT_BRANCH:-master}
|
||||
ref=${DIB_MURANO_AGENT_REF:-''}
|
||||
|
||||
# clone murano-agent source code into /opt/stack/murano-agent
|
||||
mkdir -p $svc_root
|
||||
git clone --depth=1 -b $branch $repo $svc_root
|
||||
if [ -n "$ref" ]; then
|
||||
pushd $svc_root
|
||||
git fetch $repo $ref && git checkout FETCH_HEAD
|
||||
popd
|
||||
fi
|
||||
|
||||
# install murano-agent to default location using pip
|
||||
# FIXME(starodubcevna): probably pip is not the best case for this. I suggest to
|
||||
# switch to package when they will be available to all supported distros.
|
||||
|
||||
pip install $svc_root
|
||||
|
||||
# setup config file at /etc/murano/agent.conf
|
||||
mkdir -p /etc/murano
|
||||
|
||||
PATH="/usr/local/bin:/usr/bin:$PATH"
|
||||
|
||||
pushd ${svc_root}
|
||||
oslo-config-generator --config-file ${svc_root}/etc/oslo-config-generator/muranoagent.conf
|
||||
popd
|
||||
|
||||
cp ${svc_root}/etc/muranoagent/muranoagent.conf.sample /etc/murano/agent.conf.sample
|
||||
|
||||
# install upstart script for murano-agent
|
||||
if [ -d "/etc/init/" ]; then
|
||||
install -D -g root -o root -m 0755 $(dirname $0)/murano-agent.conf /etc/init/
|
||||
fi
|
||||
if [ -d "/etc/systemd/system/" ]; then
|
||||
install -D -g root -o root -m 0755 $(dirname $0)/murano-agent.service /etc/systemd/system/
|
||||
fi
|
|
@ -1,14 +0,0 @@
|
|||
start on runlevel [2345]
|
||||
stop on runlevel [016]
|
||||
|
||||
respawn
|
||||
# the default post-start of 1 second sleep delays respawning enough to
|
||||
# not hit the default of 10 times in 5 seconds. Make it 2 times in 5s.
|
||||
respawn limit 2 5
|
||||
|
||||
# We're logging to syslog
|
||||
console none
|
||||
|
||||
exec start-stop-daemon --start -c root --exec /usr/local/bin/muranoagent -- --config-dir /etc/murano 2>&1 | logger -t murano-agent
|
||||
|
||||
post-start exec sleep 1
|
|
@ -1,10 +0,0 @@
|
|||
[Unit]
|
||||
Description=OpenStack Murano Agent
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/local/bin/muranoagent --config-dir /etc/murano
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -1,51 +0,0 @@
|
|||
Murano Agent Elements
|
||||
=====================
|
||||
|
||||
This folder contains necessary DIB elements to build Murano Agent image.
|
||||
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
1. Install diskimage-builder
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
sudo pip install diskimage-builder
|
||||
|
||||
2. Install qemu-uils and kpartx
|
||||
|
||||
On Ubuntu, Debian:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
sudo apt-get install qemu-utils kpartx
|
||||
|
||||
|
||||
On Centos, Fedora:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
sudo yum install qemu-utils kpartx
|
||||
|
||||
|
||||
Image building
|
||||
--------------
|
||||
|
||||
To build Ubuntu-based image
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
sudo ELEMENTS_PATH=${murano_agent_root}/contrib/elements \
|
||||
DIB_CLOUD_INIT_DATASOURCES="Ec2, ConfigDrive, OpenStack" disk-image-create \
|
||||
vm ubuntu murano-agent -o ubuntu14.04-x64-agent
|
||||
|
||||
To build Debian-based image
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
sudo ELEMENTS_PATH=${murano_agent_root}/contrib/elements DIB_RELEASE=jessie \
|
||||
DIB_CLOUD_INIT_DATASOURCES="Ec2, ConfigDrive, OpenStack" disk-image-create \
|
||||
vm debian murano-agent-debian -o debian8-x64-agent
|
||||
|
||||
Where ${murano_agent_root} is a path to murano-agent files.
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
|
||||
</startup>
|
||||
</configuration>
|
|
@ -1,60 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{501BE151-4B8C-4355-88DC-3AEF1921B2D7}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Mirantis.Murano</RootNamespace>
|
||||
<AssemblyName>ExecutionPlanGenerator</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -1,156 +0,0 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// The ASF licenses this file to you 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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Mirantis.Murano
|
||||
{
|
||||
internal class Command
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public Dictionary<string, object> Arguments { get; set; }
|
||||
}
|
||||
|
||||
internal class ExecutionPlan
|
||||
{
|
||||
public List<string> Scripts { get; set; }
|
||||
public List<Command> Commands { get; set; }
|
||||
public int RebootOnCompletion { get; set; }
|
||||
}
|
||||
|
||||
|
||||
static class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
if (args.Length < 1 || args.Length > 2)
|
||||
{
|
||||
Console.WriteLine("Usage: ExecutionPlanGenerator inputfile [outputfile]");
|
||||
return;
|
||||
}
|
||||
|
||||
var outFile = args.Length == 2 ? args[1] : null;
|
||||
|
||||
var plan = new ExecutionPlan {
|
||||
Scripts = new List<string>(),
|
||||
Commands = new List<Command>()
|
||||
};
|
||||
|
||||
|
||||
|
||||
var lines = File.ReadAllLines(args[0]);
|
||||
|
||||
|
||||
foreach (var statement in lines
|
||||
.Select(t => t.Split(new[] { ' ', '\t' }, 2))
|
||||
.Where(t => t.Length == 2)
|
||||
.Select(t => new Tuple<string, string>(t[0].Trim().ToLower(), t[1].Trim())))
|
||||
{
|
||||
switch (statement.Item1)
|
||||
{
|
||||
case "include":
|
||||
Include(statement.Item2, plan, args[0]);
|
||||
break;
|
||||
case "call":
|
||||
Call(statement.Item2, plan);
|
||||
break;
|
||||
case "reboot":
|
||||
plan.RebootOnCompletion = int.Parse(statement.Item2);
|
||||
break;
|
||||
case "out":
|
||||
if (args.Length < 2)
|
||||
{
|
||||
var path = statement.Item2;
|
||||
if (!Path.IsPathRooted(path))
|
||||
{
|
||||
path = Path.Combine(Path.GetDirectoryName(args[0]), path);
|
||||
}
|
||||
outFile = path;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var data = JsonConvert.SerializeObject(plan, Formatting.Indented);
|
||||
if (outFile == null)
|
||||
{
|
||||
Console.WriteLine(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
File.WriteAllText(outFile, data);
|
||||
}
|
||||
}
|
||||
|
||||
private static void Call(string line, ExecutionPlan plan)
|
||||
{
|
||||
var parts = line.Split(new[] { ' ', '\t'}, 2);
|
||||
var command = new Command() {
|
||||
Name = parts[0].Trim(),
|
||||
Arguments = new Dictionary<string, object>()
|
||||
};
|
||||
|
||||
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
foreach (var x in parts[1]
|
||||
.Split(',')
|
||||
.Select(t => t.Split('='))
|
||||
.Where(t => t.Length == 2)
|
||||
.Select(t => new KeyValuePair<string, string>(t[0].Trim(), t[1].Trim())))
|
||||
{
|
||||
object value = null;
|
||||
long num;
|
||||
bool boolean;
|
||||
if (x.Value.StartsWith("\""))
|
||||
{
|
||||
value = x.Value.Substring(1, x.Value.Length - 2);
|
||||
}
|
||||
else if (long.TryParse(x.Value, out num))
|
||||
{
|
||||
value = num;
|
||||
}
|
||||
else if (bool.TryParse(x.Value, out boolean))
|
||||
{
|
||||
value = boolean;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
command.Arguments.Add(x.Key, value);
|
||||
}
|
||||
}
|
||||
plan.Commands.Add(command);
|
||||
}
|
||||
|
||||
private static void Include(string file, ExecutionPlan plan, string dslPath)
|
||||
{
|
||||
var path = file;
|
||||
if (!Path.IsPathRooted(file))
|
||||
{
|
||||
path = Path.Combine(Path.GetDirectoryName(dslPath), path);
|
||||
}
|
||||
|
||||
var text = File.ReadAllText(path, Encoding.UTF8);
|
||||
plan.Scripts.Add(Convert.ToBase64String(Encoding.UTF8.GetBytes(text)));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// The ASF licenses this file to you 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.
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("ConsoleApplication1")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("ConsoleApplication1")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2013")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("9aab688a-ce5f-402e-8891-2d7b4ae85ea3")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -1,4 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net45" />
|
||||
</packages>
|
|
@ -1,34 +0,0 @@
|
|||
Murano Windows Agent
|
||||
====================
|
||||
|
||||
Murano Windows Agent is an initial version of Murano Agent.
|
||||
Currently, it's outdated and not supported.
|
||||
|
||||
The main difference with the new Python agent is support of running PowerShell.
|
||||
After this support will be added to Python Agent, Windows Agent will be dropped.
|
||||
|
||||
|
||||
How to build
|
||||
============
|
||||
|
||||
Build using Visual Studio
|
||||
-------------------------
|
||||
1. Launch Visual Studio
|
||||
#. Ensure that you have latest ``NuGet`` extension installed (shipped with recent
|
||||
versions of VS)
|
||||
#. Open ``WindowsAgent.sln``
|
||||
#. Select target configuration (Release or Debug) using drop-down found in VS
|
||||
toolbar
|
||||
#. Build the Solution (Build menu -> Build Solution).
|
||||
|
||||
|
||||
Build from command line
|
||||
-----------------------
|
||||
1. CD to where ``WindowsAgent.sln`` is located
|
||||
#. Download nuget.exe from https://dist.nuget.org/win-x86-commandline/latest/nuget.exe
|
||||
to current directory.
|
||||
#. Run ``nuget.exe restore``
|
||||
#. Build the solution using msbuild:
|
||||
``C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\MSBuild.exe /p:Configuration=Release``
|
||||
|
||||
The exact path to msbuild may differ on your system and .NET version installed.
|
|
@ -1,26 +0,0 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 2012
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsAgent", "WindowsAgent\WindowsAgent.csproj", "{F7E2A8D5-6D24-4651-A4BC-1024D59F4903}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExecutionPlanGenerator", "ExecutionPlanGenerator\ExecutionPlanGenerator.csproj", "{501BE151-4B8C-4355-88DC-3AEF1921B2D7}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{F7E2A8D5-6D24-4651-A4BC-1024D59F4903}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F7E2A8D5-6D24-4651-A4BC-1024D59F4903}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F7E2A8D5-6D24-4651-A4BC-1024D59F4903}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F7E2A8D5-6D24-4651-A4BC-1024D59F4903}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{501BE151-4B8C-4355-88DC-3AEF1921B2D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{501BE151-4B8C-4355-88DC-3AEF1921B2D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{501BE151-4B8C-4355-88DC-3AEF1921B2D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{501BE151-4B8C-4355-88DC-3AEF1921B2D7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -1,30 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<configuration>
|
||||
<configSections>
|
||||
<section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog"/>
|
||||
</configSections>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
|
||||
</startup>
|
||||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<targets>
|
||||
<target name="file" xsi:type="File" fileName="${basedir}/log.txt" layout="${date} ${level}: <${logger:shortName=true}> ${message} ${exception:format=tostring}"/>
|
||||
</targets>
|
||||
|
||||
<rules>
|
||||
<logger name="*" minlevel="Debug" writeTo="file"/>
|
||||
</rules>
|
||||
</nlog>
|
||||
<appSettings>
|
||||
<add key="rabbitmq.host" value="localhost"/>
|
||||
<add key="rabbitmq.user" value="murano"/>
|
||||
<add key="rabbitmq.password" value="murano"/>
|
||||
<add key="rabbitmq.vhost" value="/"/>
|
||||
<add key="rabbitmq.resultExchange" value=""/>
|
||||
<add key="rabbitmq.resultRoutingKey" value="dd"/>
|
||||
<add key="rabbitmq.durableMessages" value="true"/>
|
||||
|
||||
<add key="engine.key" value=""/>
|
||||
|
||||
</appSettings>
|
||||
</configuration>
|
|
@ -1,34 +0,0 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// The ASF licenses this file to you 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.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Mirantis.Murano.WindowsAgent
|
||||
{
|
||||
internal class ExecutionPlan
|
||||
{
|
||||
public class Command
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public Dictionary<string, object> Arguments { get; set; }
|
||||
}
|
||||
|
||||
public string[] Scripts { get; set; }
|
||||
public LinkedList<Command> Commands { get; set; }
|
||||
public int RebootOnCompletion { get; set; }
|
||||
|
||||
public long Stamp { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// The ASF licenses this file to you 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.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Mirantis.Murano.WindowsAgent
|
||||
{
|
||||
internal class Message : IDisposable
|
||||
{
|
||||
private readonly Action ackFunc;
|
||||
|
||||
public Message(Action ackFunc)
|
||||
{
|
||||
this.ackFunc = ackFunc;
|
||||
}
|
||||
|
||||
public Message()
|
||||
{
|
||||
}
|
||||
|
||||
public string Body { get; set; }
|
||||
public string Id { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ackFunc();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,163 +0,0 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// The ASF licenses this file to you 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.
|
||||
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.Net.Security;
|
||||
using System.Security.Authentication;
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using RabbitMQ.Client;
|
||||
|
||||
namespace Mirantis.Murano.WindowsAgent
|
||||
{
|
||||
internal class MessageSource : IDisposable
|
||||
{
|
||||
private static readonly Logger log = LogManager.GetCurrentClassLogger();
|
||||
private static readonly ConnectionFactory connectionFactory;
|
||||
private static readonly string queueName;
|
||||
private IConnection currentConnecton;
|
||||
private readonly SignatureVerifier signatureVerifier;
|
||||
|
||||
|
||||
static MessageSource()
|
||||
{
|
||||
var ssl = new SslOption {
|
||||
Enabled = bool.Parse(ConfigurationManager.AppSettings["rabbitmq.ssl"] ?? "false"),
|
||||
Version = SslProtocols.Default,
|
||||
AcceptablePolicyErrors = bool.Parse(ConfigurationManager.AppSettings["rabbitmq.allowInvalidCA"] ?? "true") ?
|
||||
SslPolicyErrors.RemoteCertificateChainErrors : SslPolicyErrors.None
|
||||
};
|
||||
|
||||
var sslServerName = ConfigurationManager.AppSettings["rabbitmq.sslServerName"] ?? "";
|
||||
ssl.ServerName = sslServerName;
|
||||
if (String.IsNullOrWhiteSpace(sslServerName))
|
||||
{
|
||||
ssl.AcceptablePolicyErrors |= SslPolicyErrors.RemoteCertificateNameMismatch;
|
||||
}
|
||||
|
||||
connectionFactory = new ConnectionFactory {
|
||||
HostName = ConfigurationManager.AppSettings["rabbitmq.host"] ?? "localhost",
|
||||
UserName = ConfigurationManager.AppSettings["rabbitmq.user"] ?? "guest",
|
||||
Password = ConfigurationManager.AppSettings["rabbitmq.password"] ??"guest",
|
||||
Protocol = Protocols.DefaultProtocol,
|
||||
VirtualHost = ConfigurationManager.AppSettings["rabbitmq.vhost"] ?? "/",
|
||||
Port = int.Parse(ConfigurationManager.AppSettings["rabbitmq.port"] ?? "5672"),
|
||||
RequestedHeartbeat = 10,
|
||||
Ssl = ssl
|
||||
};
|
||||
queueName = ConfigurationManager.AppSettings["rabbitmq.inputQueue"];
|
||||
}
|
||||
|
||||
public MessageSource()
|
||||
{
|
||||
this.signatureVerifier = new SignatureVerifier(Encoding.ASCII.GetBytes(queueName));
|
||||
}
|
||||
|
||||
public Message GetMessage()
|
||||
{
|
||||
try
|
||||
{
|
||||
IConnection connection;
|
||||
lock (this)
|
||||
{
|
||||
connection = this.currentConnecton = this.currentConnecton ?? connectionFactory.CreateConnection();
|
||||
}
|
||||
var session = connection.CreateModel();
|
||||
session.BasicQos(0, 1, false);
|
||||
var consumer = new QueueingBasicConsumer(session);
|
||||
var consumeTag = session.BasicConsume(queueName, false, consumer);
|
||||
|
||||
while (true)
|
||||
{
|
||||
var e = consumer.Queue.Dequeue();
|
||||
Action ackFunc = delegate
|
||||
{
|
||||
session.BasicAck(e.DeliveryTag, false);
|
||||
session.BasicCancel(consumeTag);
|
||||
session.Close();
|
||||
};
|
||||
|
||||
byte[] signature = null;
|
||||
if (e.BasicProperties.Headers.ContainsKey("signature"))
|
||||
{
|
||||
signature = (byte[]) e.BasicProperties.Headers["signature"];
|
||||
}
|
||||
|
||||
if (this.signatureVerifier.Verify(e.Body, signature))
|
||||
{
|
||||
return new Message(ackFunc) {
|
||||
Body = Encoding.UTF8.GetString(e.Body),
|
||||
Id = e.BasicProperties.MessageId,
|
||||
};
|
||||
}
|
||||
|
||||
log.Warn("Dropping message with invalid/missing signature");
|
||||
session.BasicReject(e.DeliveryTag, false);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (this.currentConnecton == null) return null;
|
||||
Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public void SendResult(Message message)
|
||||
{
|
||||
var exchangeName = ConfigurationManager.AppSettings["rabbitmq.resultExchange"] ?? "";
|
||||
var resultRoutingKey = ConfigurationManager.AppSettings["rabbitmq.resultRoutingKey"] ?? "-execution-results";
|
||||
bool durable = bool.Parse(ConfigurationManager.AppSettings["rabbitmq.durableMessages"] ?? "true");
|
||||
|
||||
try
|
||||
{
|
||||
IConnection connection;
|
||||
lock (this)
|
||||
{
|
||||
connection = this.currentConnecton = this.currentConnecton ?? connectionFactory.CreateConnection();
|
||||
}
|
||||
var session = connection.CreateModel();
|
||||
var basicProperties = session.CreateBasicProperties();
|
||||
basicProperties.Persistent = durable;
|
||||
basicProperties.MessageId = message.Id;
|
||||
basicProperties.ContentType = "application/json";
|
||||
session.BasicPublish(exchangeName, resultRoutingKey, basicProperties, Encoding.UTF8.GetBytes(message.Body));
|
||||
session.Close();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
try
|
||||
{
|
||||
var connection = this.currentConnecton;
|
||||
this.currentConnecton = null;
|
||||
connection.Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,303 +0,0 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// The ASF licenses this file to you 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.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Management.Automation;
|
||||
using System.Management.Automation.Runspaces;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Mirantis.Murano.WindowsAgent
|
||||
{
|
||||
internal class PlanExecutor
|
||||
{
|
||||
private static readonly Logger log = LogManager.GetCurrentClassLogger();
|
||||
private long lastStamp = -1;
|
||||
|
||||
class ExecutionResult
|
||||
{
|
||||
public bool IsException { get; set; }
|
||||
public object Result { get; set; }
|
||||
}
|
||||
|
||||
private readonly string baseDir;
|
||||
|
||||
public PlanExecutor(string baseDir)
|
||||
{
|
||||
this.baseDir = baseDir;
|
||||
}
|
||||
|
||||
public bool RebootNeeded { get; set; }
|
||||
|
||||
public void Execute(string path)
|
||||
{
|
||||
RebootNeeded = false;
|
||||
var resultPath = path + ".result";
|
||||
var tmpResultPath = resultPath + ".tmp";
|
||||
Runspace runSpace = null;
|
||||
try
|
||||
{
|
||||
var plan = JsonConvert.DeserializeObject<ExecutionPlan>(File.ReadAllText(path));
|
||||
List<ExecutionResult> currentResults;
|
||||
try
|
||||
{
|
||||
currentResults = File.Exists(tmpResultPath) ?
|
||||
JsonConvert.DeserializeObject<List<ExecutionResult>>(File.ReadAllText(tmpResultPath)) :
|
||||
new List<ExecutionResult>();
|
||||
}
|
||||
catch(Exception exception)
|
||||
{
|
||||
log.Warn(exception, "Cannot deserialize previous execution result");
|
||||
currentResults = new List<ExecutionResult>();
|
||||
}
|
||||
|
||||
var lastStamp = GetLastStamp();
|
||||
if (plan.Stamp > 0 && plan.Stamp <= lastStamp)
|
||||
{
|
||||
log.Warn("Dropping old/duplicate plan");
|
||||
return;
|
||||
}
|
||||
|
||||
runSpace = RunspaceFactory.CreateRunspace();
|
||||
runSpace.Open();
|
||||
|
||||
var runSpaceInvoker = new RunspaceInvoke(runSpace);
|
||||
runSpaceInvoker.Invoke("Set-ExecutionPolicy Unrestricted");
|
||||
if (plan.Scripts != null)
|
||||
{
|
||||
var index = 0;
|
||||
foreach (var script in plan.Scripts)
|
||||
{
|
||||
runSpaceInvoker.Invoke(Encoding.UTF8.GetString(Convert.FromBase64String(script)));
|
||||
log.Debug("Loaded script #{0}", ++index);
|
||||
}
|
||||
}
|
||||
|
||||
while (plan.Commands != null && plan.Commands.Any())
|
||||
{
|
||||
var command = plan.Commands.First();
|
||||
log.Debug("Preparing to execute command {0}", command.Name);
|
||||
|
||||
var pipeline = runSpace.CreatePipeline();
|
||||
if (command.Name != null)
|
||||
{
|
||||
var psCommand = new Command(command.Name);
|
||||
if (command.Arguments != null)
|
||||
{
|
||||
foreach (var kvp in command.Arguments)
|
||||
{
|
||||
var value = ConvertArgument(kvp.Value);
|
||||
psCommand.Parameters.Add(kvp.Key, value);
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("Executing {0} {1}", command.Name, string.Join(" ",
|
||||
(command.Arguments ?? new Dictionary<string, object>()).Select(
|
||||
t => string.Format("{0}={1}", t.Key, t.Value?.ToString() ?? "null"))));
|
||||
|
||||
pipeline.Commands.Add(psCommand);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var result = pipeline.Invoke();
|
||||
log.Debug("Command {0} executed", command.Name);
|
||||
if (result != null)
|
||||
{
|
||||
currentResults.Add(new ExecutionResult {
|
||||
IsException = false,
|
||||
Result = result.Where(obj => obj != null).Select(SerializePsObject).ToList()
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
object additionInfo = null;
|
||||
var apse = exception as ActionPreferenceStopException;
|
||||
if (apse?.ErrorRecord != null)
|
||||
{
|
||||
additionInfo = new {
|
||||
ScriptStackTrace = apse.ErrorRecord.ScriptStackTrace,
|
||||
PositionMessage = apse.ErrorRecord.InvocationInfo.PositionMessage
|
||||
};
|
||||
exception = apse.ErrorRecord.Exception;
|
||||
}
|
||||
|
||||
|
||||
log.Warn(exception, "Exception while executing command " + command.Name);
|
||||
currentResults.Add(new ExecutionResult
|
||||
{
|
||||
IsException = true,
|
||||
Result = new[] {
|
||||
exception.GetType().FullName, exception.Message, command.Name, additionInfo
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
finally
|
||||
{
|
||||
plan.Commands.RemoveFirst();
|
||||
File.WriteAllText(path, JsonConvert.SerializeObject(plan));
|
||||
File.WriteAllText(tmpResultPath, JsonConvert.SerializeObject(currentResults));
|
||||
}
|
||||
}
|
||||
runSpace.Close();
|
||||
if (plan.Stamp > 0)
|
||||
{
|
||||
SetLastStamp(plan.Stamp);
|
||||
}
|
||||
var executionResult = JsonConvert.SerializeObject(new ExecutionResult {
|
||||
IsException = false,
|
||||
Result = currentResults
|
||||
}, Formatting.Indented);
|
||||
|
||||
if (plan.RebootOnCompletion > 0)
|
||||
{
|
||||
if (plan.RebootOnCompletion == 1)
|
||||
{
|
||||
RebootNeeded = !currentResults.Any(t => t.IsException);
|
||||
}
|
||||
else
|
||||
{
|
||||
RebootNeeded = true;
|
||||
}
|
||||
}
|
||||
File.Delete(tmpResultPath);
|
||||
File.WriteAllText(resultPath, executionResult);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
log.Warn(exception, "Exception while processing execution plan");
|
||||
File.WriteAllText(resultPath, JsonConvert.SerializeObject(new ExecutionResult {
|
||||
IsException = true,
|
||||
Result = exception.Message
|
||||
}, Formatting.Indented));
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (runSpace != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
runSpace.Close();
|
||||
}
|
||||
catch
|
||||
{}
|
||||
}
|
||||
log.Debug("Finished processing of execution plan");
|
||||
}
|
||||
}
|
||||
|
||||
private static object ConvertArgument(object arg)
|
||||
{
|
||||
switch (arg)
|
||||
{
|
||||
case JArray array:
|
||||
return array.Select(ConvertArgument).ToArray();
|
||||
case JValue value:
|
||||
return value.Value;
|
||||
case JObject dict:
|
||||
var result = new Hashtable();
|
||||
foreach (var item in dict)
|
||||
{
|
||||
result.Add(item.Key, ConvertArgument(item.Value));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
private static object SerializePsObject(PSObject obj)
|
||||
{
|
||||
if (obj.BaseObject is PSCustomObject)
|
||||
{
|
||||
var result = new Dictionary<string, object>();
|
||||
foreach (var property in obj.Properties.Where(p => p.IsGettable))
|
||||
{
|
||||
try
|
||||
{
|
||||
result[property.Name] = property.Value.ToString();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (obj.BaseObject is IEnumerable<PSObject> objects)
|
||||
{
|
||||
return objects.Select(SerializePsObject).ToArray();
|
||||
}
|
||||
|
||||
return obj.BaseObject;
|
||||
}
|
||||
|
||||
private long GetLastStamp()
|
||||
{
|
||||
if (this.lastStamp >= 0)
|
||||
{
|
||||
return this.lastStamp;
|
||||
}
|
||||
|
||||
var path = Path.Combine(this.baseDir, "stamp.txt");
|
||||
if (File.Exists(path))
|
||||
{
|
||||
try
|
||||
{
|
||||
var stampData = File.ReadAllText(path);
|
||||
this.lastStamp = long.Parse(stampData);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.lastStamp = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.lastStamp = 0;
|
||||
}
|
||||
|
||||
return this.lastStamp;
|
||||
}
|
||||
|
||||
private void SetLastStamp(long value)
|
||||
{
|
||||
var path = Path.Combine(this.baseDir, "stamp.txt");
|
||||
try
|
||||
{
|
||||
File.WriteAllText(path, value.ToString());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.Error(e, "Cannot persist last stamp");
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.lastStamp = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,175 +0,0 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// The ASF licenses this file to you 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.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Management.Automation;
|
||||
using System.Security.AccessControl;
|
||||
using System.Security.Principal;
|
||||
using System.Threading;
|
||||
using NLog;
|
||||
|
||||
namespace Mirantis.Murano.WindowsAgent
|
||||
{
|
||||
[DisplayName("Murano Agent")]
|
||||
public sealed class Program : WindowsService
|
||||
{
|
||||
private static readonly Logger log = LogManager.GetCurrentClassLogger();
|
||||
private volatile bool stop;
|
||||
private Thread thread;
|
||||
private MessageSource messageSource;
|
||||
private int delayFactor = 1;
|
||||
private string plansDir;
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
Start(new Program(), args);
|
||||
}
|
||||
|
||||
protected override void OnStart(string[] args)
|
||||
{
|
||||
base.OnStart(args);
|
||||
|
||||
log.Info("Version 0.6");
|
||||
|
||||
this.messageSource = new MessageSource();
|
||||
|
||||
var basePath = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
|
||||
this.plansDir = Path.Combine(basePath, "plans");
|
||||
if (!Directory.Exists(plansDir))
|
||||
{
|
||||
Directory.CreateDirectory(plansDir);
|
||||
}
|
||||
|
||||
this.thread = new Thread(Loop);
|
||||
this.thread.Start();
|
||||
}
|
||||
|
||||
private void Loop()
|
||||
{
|
||||
const string unknownName = "unknown";
|
||||
var executor = new PlanExecutor(this.plansDir);
|
||||
while (!stop)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var file in Directory.GetFiles(this.plansDir, "*.json.result")
|
||||
.Where(file => !File.Exists(Path.Combine(this.plansDir, Path.GetFileNameWithoutExtension(file)))))
|
||||
{
|
||||
var id = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(file));
|
||||
if (id.Equals(unknownName, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
id = "";
|
||||
}
|
||||
|
||||
var result = File.ReadAllText(file);
|
||||
log.Info("Sending results for {0}", id);
|
||||
messageSource.SendResult(new Message { Body = result, Id = id });
|
||||
File.Delete(file);
|
||||
}
|
||||
|
||||
var path = Directory.EnumerateFiles(this.plansDir, "*.json").FirstOrDefault();
|
||||
if (path == null)
|
||||
{
|
||||
using (var message = messageSource.GetMessage())
|
||||
{
|
||||
if (message == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var id = message.Id;
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
id = unknownName;
|
||||
}
|
||||
|
||||
path = Path.Combine(this.plansDir, string.Format("{0}.json", id));
|
||||
File.WriteAllText(path, message.Body);
|
||||
log.Info("Received new execution plan {0}", id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var id = Path.GetFileNameWithoutExtension(path);
|
||||
log.Info("Executing exising plan {0}", id);
|
||||
}
|
||||
|
||||
executor.Execute(path);
|
||||
File.Delete(path);
|
||||
delayFactor = 1;
|
||||
|
||||
if (stop) break;
|
||||
if (executor.RebootNeeded)
|
||||
{
|
||||
Reboot();
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
WaitOnException(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Reboot()
|
||||
{
|
||||
log.Info("Going for reboot!!");
|
||||
LogManager.Flush();
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
PowerShell.Create().AddCommand("Restart-Computer").AddParameter("Force").Invoke();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
|
||||
log.Fatal(exception, "Reboot exception");
|
||||
}
|
||||
finally
|
||||
{
|
||||
log.Info("Waiting for reboot");
|
||||
for (var i = 0; i < 10 * 60 * 5 && !stop; i++)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
log.Info("Done waiting for reboot");
|
||||
}
|
||||
}
|
||||
|
||||
private void WaitOnException(Exception exception)
|
||||
{
|
||||
if (stop) return;
|
||||
log.Warn(exception, "Exception in main loop");
|
||||
var i = 0;
|
||||
while (!stop && i < 10 * (delayFactor * delayFactor))
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
i++;
|
||||
}
|
||||
delayFactor = Math.Min(delayFactor + 1, 6);
|
||||
}
|
||||
|
||||
protected override void OnStop()
|
||||
{
|
||||
stop = true;
|
||||
this.messageSource.Dispose();
|
||||
base.OnStop();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// The ASF licenses this file to you 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.
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("WindowsAgent")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("WindowsAgent")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2013")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("9591bf2c-f38b-47e0-a39d-ea9849356371")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -1,37 +0,0 @@
|
|||
{
|
||||
"Scripts":
|
||||
[
|
||||
"ZnVuY3Rpb24gdDMgeyAxMjsgcmV0dXJuICJ0ZXN0IiB9",
|
||||
"ZnVuY3Rpb24gTmV3LVBlcnNvbigpDQp7DQogIHBhcmFtICgkRmlyc3ROYW1lLCAkTGFzdE5hbWUsICRQaG9uZSkNCg0KICAkcGVyc29uID0gbmV3LW9iamVjdCBQU09iamVjdA0KDQogICRwZXJzb24gfCBhZGQtbWVtYmVyIC10eXBlIE5vdGVQcm9wZXJ0eSAtTmFtZSBGaXJzdCAtVmFsdWUgJEZpcnN0TmFtZQ0KICAkcGVyc29uIHwgYWRkLW1lbWJlciAtdHlwZSBOb3RlUHJvcGVydHkgLU5hbWUgTGFzdCAtVmFsdWUgJExhc3ROYW1lDQogICRwZXJzb24gfCBhZGQtbWVtYmVyIC10eXBlIE5vdGVQcm9wZXJ0eSAtTmFtZSBQaG9uZSAtVmFsdWUgJFBob25lDQoNCiAgcmV0dXJuICRwZXJzb24NCn0=",
|
||||
"ZnVuY3Rpb24gVGVzdFRocm93KCkNCnsNCglUaHJvdyBbc3lzdGVtLkluZGV4T3V0T2ZSYW5nZUV4Y2VwdGlvbl0gDQp9"
|
||||
],
|
||||
"Commands" :
|
||||
[
|
||||
{
|
||||
"Name": "New-Person",
|
||||
"Arguments" :
|
||||
{
|
||||
"FirstName": "MyFirstName",
|
||||
"LastName": "MyLastName",
|
||||
"Phone": "123-456"
|
||||
}
|
||||
|
||||
},
|
||||
{
|
||||
"Name": "t3",
|
||||
"Arguments" :
|
||||
{
|
||||
}
|
||||
|
||||
},
|
||||
{
|
||||
"Name": "Get-Date",
|
||||
|
||||
},
|
||||
{
|
||||
"Name": "TestThrow",
|
||||
|
||||
}
|
||||
],
|
||||
"RebootOnCompletion": 0
|
||||
}
|
|
@ -1,126 +0,0 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// The ASF licenses this file to you 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.
|
||||
|
||||
using System;
|
||||
using System.Configuration.Install;
|
||||
using System.Reflection;
|
||||
using System.ServiceProcess;
|
||||
using NLog;
|
||||
|
||||
namespace Mirantis.Murano.WindowsAgent
|
||||
{
|
||||
public class ServiceManager
|
||||
{
|
||||
private readonly string serviceName;
|
||||
|
||||
public ServiceManager(string serviceName)
|
||||
{
|
||||
this.serviceName = serviceName;
|
||||
}
|
||||
|
||||
private static readonly Logger log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public bool Restart(string[] args, TimeSpan timeout)
|
||||
{
|
||||
var service = new ServiceController(serviceName);
|
||||
try
|
||||
{
|
||||
var millisec1 = TimeSpan.FromMilliseconds(Environment.TickCount);
|
||||
|
||||
service.Stop();
|
||||
service.WaitForStatus(ServiceControllerStatus.Stopped, timeout);
|
||||
log.Info("Service is stopped");
|
||||
|
||||
// count the rest of the timeout
|
||||
var millisec2 = TimeSpan.FromMilliseconds(Environment.TickCount);
|
||||
timeout = timeout - (millisec2 - millisec1);
|
||||
|
||||
service.Start(args);
|
||||
service.WaitForStatus(ServiceControllerStatus.Running, timeout);
|
||||
log.Info("Service has started");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Error(ex, "Cannot restart service " + serviceName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Stop(TimeSpan timeout)
|
||||
{
|
||||
var service = new ServiceController(serviceName);
|
||||
try
|
||||
{
|
||||
service.Stop();
|
||||
service.WaitForStatus(ServiceControllerStatus.Stopped, timeout);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Error(ex, "Cannot stop service " + serviceName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Start(string[] args, TimeSpan timeout)
|
||||
{
|
||||
var service = new ServiceController(serviceName);
|
||||
try
|
||||
{
|
||||
service.Start(args);
|
||||
service.WaitForStatus(ServiceControllerStatus.Running, timeout);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Error(ex, "Cannot start service " + serviceName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Install()
|
||||
{
|
||||
try
|
||||
{
|
||||
ManagedInstallerClass.InstallHelper(
|
||||
new[] { Assembly.GetEntryAssembly().Location });
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
log.Error(ex, "Cannot install service " + serviceName);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Uninstall()
|
||||
{
|
||||
try
|
||||
{
|
||||
ManagedInstallerClass.InstallHelper(
|
||||
new[] { "/u", Assembly.GetEntryAssembly().Location });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Error(ex, "Cannot uninstall service " + serviceName);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// The ASF licenses this file to you 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.
|
||||
|
||||
using System.Configuration;
|
||||
using System.IO;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.OpenSsl;
|
||||
using Org.BouncyCastle.Security;
|
||||
|
||||
namespace Mirantis.Murano.WindowsAgent
|
||||
{
|
||||
internal class SignatureVerifier
|
||||
{
|
||||
private readonly ISigner signer;
|
||||
private readonly byte[] salt;
|
||||
|
||||
public SignatureVerifier(byte[] salt)
|
||||
{
|
||||
var keyStr = ConfigurationManager.AppSettings["engine.key"];
|
||||
if (string.IsNullOrEmpty(keyStr)) return;
|
||||
|
||||
var reader = new StringReader(keyStr);
|
||||
var key = (RsaKeyParameters) new PemReader(reader).ReadObject();
|
||||
this.signer = SignerUtilities.GetSigner("SHA256withRSA");
|
||||
this.signer.Init(false, key);
|
||||
this.salt = salt;
|
||||
}
|
||||
|
||||
public bool Verify(byte[] data, byte[] signature)
|
||||
{
|
||||
if (this.signer == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (signature == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this.signer.Reset();
|
||||
this.signer.BlockUpdate(this.salt, 0, this.salt.Length);
|
||||
this.signer.BlockUpdate(data, 0, data.Length);
|
||||
return this.signer.VerifySignature(signature);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{F7E2A8D5-6D24-4651-A4BC-1024D59F4903}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Mirantis.Murano.WindowsAgent</RootNamespace>
|
||||
<AssemblyName>WindowsAgent</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile>
|
||||
</TargetFrameworkProfile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<StartupObject>
|
||||
</StartupObject>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="BouncyCastle.Crypto, Version=1.8.1.0, Culture=neutral, PublicKeyToken=0e99375e54769942">
|
||||
<HintPath>..\packages\BouncyCastle.1.8.1\lib\BouncyCastle.Crypto.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.4.12\lib\net45\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RabbitMQ.Client, Version=3.6.9.0, Culture=neutral, PublicKeyToken=89e7d7c5feba84ce, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\RabbitMQ.Client.3.6.9\lib\net45\RabbitMQ.Client.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Configuration.Install" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0\System.Management.Automation.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ServiceProcess" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ExecutionPlan.cs" />
|
||||
<Compile Include="Message.cs" />
|
||||
<Compile Include="PlanExecutor.cs" />
|
||||
<Compile Include="Program.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="MessageSource.cs" />
|
||||
<Compile Include="ServiceManager.cs" />
|
||||
<Compile Include="SignatureVerifier.cs" />
|
||||
<Compile Include="WindowsService.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="WindowsServiceInstaller.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
<None Include="packages.config" />
|
||||
<None Include="SampleExecutionPlan.json" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
</PropertyGroup>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -1,107 +0,0 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// The ASF licenses this file to you 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.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.ServiceProcess;
|
||||
using NLog;
|
||||
|
||||
namespace Mirantis.Murano.WindowsAgent
|
||||
{
|
||||
public abstract class WindowsService : ServiceBase
|
||||
{
|
||||
private static readonly Logger log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
protected static void Start(WindowsService service, string[] arguments)
|
||||
{
|
||||
Directory.SetCurrentDirectory(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location));
|
||||
|
||||
if (arguments.Contains("/install", StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
new ServiceManager(service.ServiceName).Install();
|
||||
}
|
||||
else if (arguments.Contains("/uninstall", StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
new ServiceManager(service.ServiceName).Uninstall();
|
||||
}
|
||||
else if (arguments.Contains("/start", StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
new ServiceManager(service.ServiceName).Start(Environment.GetCommandLineArgs(), TimeSpan.FromMinutes(1));
|
||||
}
|
||||
else if (arguments.Contains("/stop", StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
new ServiceManager(service.ServiceName).Stop(TimeSpan.FromMinutes(1));
|
||||
}
|
||||
else if (arguments.Contains("/restart", StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
new ServiceManager(service.ServiceName).Restart(Environment.GetCommandLineArgs(), TimeSpan.FromMinutes(1));
|
||||
}
|
||||
else if (!arguments.Contains("/console", StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
Run(service);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.Title = service.ServiceName;
|
||||
service.OnStart(Environment.GetCommandLineArgs());
|
||||
service.WaitForExitSignal();
|
||||
}
|
||||
finally
|
||||
{
|
||||
service.OnStop();
|
||||
service.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected WindowsService()
|
||||
{
|
||||
var displayNameAttribute =
|
||||
this.GetType().GetCustomAttributes(typeof (DisplayNameAttribute), false).Cast<DisplayNameAttribute>().
|
||||
FirstOrDefault();
|
||||
if(displayNameAttribute != null)
|
||||
{
|
||||
ServiceName = displayNameAttribute.DisplayName;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected virtual void WaitForExitSignal()
|
||||
{
|
||||
Console.WriteLine("Press ESC to exit");
|
||||
while (Console.ReadKey(true).Key != ConsoleKey.Escape)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnStart(string[] args)
|
||||
{
|
||||
log.Info("Service {0} started", ServiceName);
|
||||
|
||||
base.OnStart(args);
|
||||
}
|
||||
|
||||
protected override void OnStop()
|
||||
{
|
||||
log.Info("Service {0} exited", ServiceName);
|
||||
base.OnStop();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// The ASF licenses this file to you 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.
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Configuration.Install;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.ServiceProcess;
|
||||
|
||||
namespace Mirantis.Murano.WindowsAgent
|
||||
{
|
||||
[RunInstaller(true)]
|
||||
public class WindowsServiceInstaller : Installer
|
||||
{
|
||||
public WindowsServiceInstaller()
|
||||
{
|
||||
var processInstaller = new ServiceProcessInstaller { Account = ServiceAccount.LocalSystem };
|
||||
foreach (var type in Assembly.GetEntryAssembly().GetExportedTypes().Where(t => t.IsSubclassOf(typeof(ServiceBase))))
|
||||
{
|
||||
var nameAttribute = type.GetCustomAttributes(typeof (DisplayNameAttribute), false)
|
||||
.Cast<DisplayNameAttribute>().FirstOrDefault();
|
||||
if(nameAttribute == null) continue;
|
||||
var serviceInstaller = new ServiceInstaller {
|
||||
StartType = ServiceStartMode.Automatic,
|
||||
ServiceName = nameAttribute.DisplayName,
|
||||
DisplayName = nameAttribute.DisplayName
|
||||
};
|
||||
var descriptionAttribute = type.GetCustomAttributes(typeof(DescriptionAttribute), false)
|
||||
.Cast<DescriptionAttribute>().FirstOrDefault();
|
||||
if(descriptionAttribute != null)
|
||||
{
|
||||
serviceInstaller.Description = descriptionAttribute.Description;
|
||||
}
|
||||
|
||||
Installers.Add(serviceInstaller);
|
||||
}
|
||||
|
||||
Installers.Add(processInstaller);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="BouncyCastle" version="1.8.1" targetFramework="net451" />
|
||||
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net45" />
|
||||
<package id="NLog" version="4.4.12" targetFramework="net45" />
|
||||
<package id="RabbitMQ.Client" version="3.6.9" targetFramework="net45" />
|
||||
</packages>
|
|
@ -1,5 +0,0 @@
|
|||
sphinx>=2.0.0,!=2.1.0 # BSD
|
||||
openstackdocstheme>=2.2.1 # Apache-2.0
|
||||
sphinxcontrib-pecanwsme>=0.8.0 # Apache-2.0
|
||||
sphinxcontrib-httpdomain>=1.3.0 # BSD
|
||||
reno>=3.1.0 # Apache-2.0
|
|
@ -1,78 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# 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 os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.abspath('../..'))
|
||||
# -- General configuration ----------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
'openstackdocstheme',
|
||||
]
|
||||
|
||||
# autodoc generation is a bit aggressive and a nuisance when doing heavy
|
||||
# text edit cycles.
|
||||
# execute "export SPHINX_DEBUG=1" in your terminal to disable
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Murano Agent'
|
||||
copyright = u'2014, OpenStack Foundation'
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
add_module_names = True
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'native'
|
||||
|
||||
# -- Options for HTML output --------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||
# html_theme_path = ["."]
|
||||
# html_theme = '_theme'
|
||||
# html_static_path = []
|
||||
|
||||
html_theme = 'openstackdocs'
|
||||
|
||||
# openstackdocstheme options
|
||||
openstackdocs_repo_name = 'openstack/murano-agent'
|
||||
openstackdocs_auto_name = False
|
||||
openstackdocs_bug_project = 'murano'
|
||||
openstackdocs_bug_tag = ''
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = '%sdoc' % project
|
||||
|
||||
# -- Options for manual page output -------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = []
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
man_show_urls = True
|
|
@ -1 +0,0 @@
|
|||
.. include:: ../../README.rst
|
|
@ -1,4 +0,0 @@
|
|||
To generate the sample muranoagent.conf file, run the following
|
||||
command from the top level of the murano-agent directory:
|
||||
|
||||
tox -egenconfig
|
|
@ -1,5 +0,0 @@
|
|||
[DEFAULT]
|
||||
output_file = etc/muranoagent/muranoagent.conf.sample
|
||||
namespace = muranoagent
|
||||
namespace = oslo.service.service
|
||||
namespace = oslo.log
|
|
@ -1,170 +0,0 @@
|
|||
# Copyright (c) 2013 Mirantis Inc.
|
||||
#
|
||||
# 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 os
|
||||
import sys
|
||||
import time
|
||||
|
||||
from oslo_log import log as logging
|
||||
from oslo_service import service
|
||||
from oslo_utils import strutils
|
||||
|
||||
from muranoagent.common import config
|
||||
from muranoagent.common import messaging
|
||||
from muranoagent import execution_plan_queue
|
||||
from muranoagent import execution_plan_runner
|
||||
from muranoagent import execution_result as ex_result
|
||||
from muranoagent import validation
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MuranoAgent(service.Service):
|
||||
def __init__(self):
|
||||
self._queue = execution_plan_queue.ExecutionPlanQueue()
|
||||
super(MuranoAgent, self).__init__()
|
||||
|
||||
@staticmethod
|
||||
def _load_package(name):
|
||||
try:
|
||||
LOG.debug('Loading plugin %s', name)
|
||||
__import__(name)
|
||||
except Exception:
|
||||
LOG.warning('Cannot load package %s', name, exc_info=True)
|
||||
pass
|
||||
|
||||
def _load(self):
|
||||
path = os.path.join(os.path.dirname(__file__), 'executors')
|
||||
sys.path.insert(1, path)
|
||||
for entry in os.listdir(path):
|
||||
package_path = os.path.join(path, entry)
|
||||
if os.path.isdir(package_path):
|
||||
MuranoAgent._load_package(entry)
|
||||
|
||||
def start(self):
|
||||
self._load()
|
||||
msg_iterator = self._wait_plan()
|
||||
while True:
|
||||
try:
|
||||
self._loop_func(msg_iterator)
|
||||
except Exception as ex:
|
||||
LOG.exception(ex)
|
||||
time.sleep(5)
|
||||
|
||||
def _loop_func(self, msg_iterator):
|
||||
result, timestamp = self._queue.get_execution_plan_result()
|
||||
if result is not None:
|
||||
if self._send_result(result):
|
||||
self._queue.remove(timestamp)
|
||||
return
|
||||
|
||||
plan = self._queue.get_execution_plan()
|
||||
if plan is not None:
|
||||
LOG.debug("Got an execution plan '{0}':".format(
|
||||
strutils.mask_password(str(plan))))
|
||||
if self._verify_plan(plan):
|
||||
self._run(plan)
|
||||
return
|
||||
|
||||
next(msg_iterator)
|
||||
|
||||
def _verify_plan(self, plan):
|
||||
try:
|
||||
validation.validate_plan(plan)
|
||||
return True
|
||||
except Exception as err:
|
||||
try:
|
||||
execution_result = ex_result.ExecutionResult.from_error(
|
||||
err, plan)
|
||||
if 'ReplyTo' in plan and CONF.enable_dynamic_result_queue:
|
||||
execution_result['ReplyTo'] = plan.ReplyTo
|
||||
|
||||
self._send_result(execution_result)
|
||||
except ValueError:
|
||||
LOG.warning('Execution result is not produced')
|
||||
finally:
|
||||
return False
|
||||
|
||||
def _run(self, plan):
|
||||
try:
|
||||
with execution_plan_runner.ExecutionPlanRunner(plan) as runner:
|
||||
result = runner.run()
|
||||
execution_result = ex_result.ExecutionResult.from_result(
|
||||
result, plan)
|
||||
self._queue.put_execution_result(execution_result, plan)
|
||||
except Exception as ex:
|
||||
LOG.exception('Error running execution plan')
|
||||
execution_result = ex_result.ExecutionResult.from_error(ex,
|
||||
plan)
|
||||
self._queue.put_execution_result(execution_result, plan)
|
||||
|
||||
def _send_result(self, result):
|
||||
with self._create_rmq_client() as mq:
|
||||
msg = messaging.Message()
|
||||
msg.body = result
|
||||
msg.id = result.get('SourceID')
|
||||
routing_key = CONF.rabbitmq.result_routing_key
|
||||
if ('ReplyTo' in result) and CONF.enable_dynamic_result_queue:
|
||||
routing_key = result.pop('ReplyTo')
|
||||
mq.send(message=msg,
|
||||
key=routing_key,
|
||||
exchange=CONF.rabbitmq.result_exchange)
|
||||
return True
|
||||
|
||||
def _create_rmq_client(self):
|
||||
rabbitmq = CONF.rabbitmq
|
||||
connection_params = {
|
||||
'login': rabbitmq.login,
|
||||
'password': rabbitmq.password,
|
||||
'host': rabbitmq.host,
|
||||
'port': rabbitmq.port,
|
||||
'virtual_host': rabbitmq.virtual_host,
|
||||
'ssl': rabbitmq.ssl,
|
||||
'ssl_version': rabbitmq.ssl_version,
|
||||
'ca_certs': rabbitmq.ca_certs.strip() or None,
|
||||
'insecure': rabbitmq.insecure
|
||||
}
|
||||
return messaging.MqClient(**connection_params)
|
||||
|
||||
def _wait_plan(self):
|
||||
delay = 5
|
||||
while True:
|
||||
try:
|
||||
with self._create_rmq_client() as mq:
|
||||
with mq.open(CONF.rabbitmq.input_queue,
|
||||
prefetch_count=1) as subscription:
|
||||
while True:
|
||||
msg = subscription.get_message(timeout=5)
|
||||
if msg is not None:
|
||||
try:
|
||||
self._queue.put_execution_plan(
|
||||
msg.body,
|
||||
msg.signature,
|
||||
msg.id,
|
||||
msg.reply_to)
|
||||
finally:
|
||||
msg.ack()
|
||||
|
||||
delay = 5
|
||||
if msg is not None:
|
||||
yield
|
||||
except KeyboardInterrupt:
|
||||
break
|
||||
except Exception:
|
||||
LOG.warning('Communication error', exc_info=True)
|
||||
time.sleep(delay)
|
||||
delay = min(delay * 1.2, 60)
|
|
@ -1,29 +0,0 @@
|
|||
# Copyright (c) 2014 Mirantis, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
class Bunch(dict):
|
||||
"""Bunch dict/object-like container.
|
||||
|
||||
Bunch container provides both dictionary-like and
|
||||
object-like attribute access.
|
||||
"""
|
||||
def __getattr__(self, item):
|
||||
return self.__getitem__(item)
|
||||
|
||||
def __setattr__(self, key, value):
|
||||
return self.__setitem__(key, value)
|
||||
|
||||
def __delattr__(self, key):
|
||||
del self[key]
|
|
@ -1,66 +0,0 @@
|
|||
# Copyright (c) 2013 Mirantis Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
# flake8: noqa: E402
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
# If ../muranoagent/__init__.py exists, add ../ to Python search path, so
|
||||
# it will override what happens to be installed in /usr/(local/)lib/python...
|
||||
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(__file__),
|
||||
os.pardir,
|
||||
os.pardir,
|
||||
os.pardir))
|
||||
if os.path.exists(os.path.join(possible_topdir,
|
||||
'muranoagent',
|
||||
'__init__.py')):
|
||||
sys.path.insert(0, possible_topdir)
|
||||
|
||||
import eventlet
|
||||
eventlet.monkey_patch()
|
||||
# Monkey patch the original current_thread to use the up-to-date _active
|
||||
# global variable. See https://bugs.launchpad.net/bugs/1863021 and
|
||||
# https://github.com/eventlet/eventlet/issues/592
|
||||
import __original_module_threading as orig_threading
|
||||
import threading # noqa
|
||||
orig_threading.current_thread.__globals__['_active'] = threading._active
|
||||
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_service import service
|
||||
|
||||
from muranoagent import app
|
||||
from muranoagent.common import config
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
config.parse_args()
|
||||
logging.setup(CONF, 'muranoagent')
|
||||
launcher = service.ServiceLauncher(CONF)
|
||||
launcher.launch_service(app.MuranoAgent())
|
||||
launcher.wait()
|
||||
except RuntimeError as e:
|
||||
sys.stderr.write("ERROR: %s\n" % e)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,97 +0,0 @@
|
|||
# Copyright 2011 OpenStack LLC.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Routines for configuring Murano-Agent
|
||||
"""
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
from muranoagent import version
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
opts = [
|
||||
cfg.StrOpt('storage',
|
||||
default='/var/murano/plans',
|
||||
help='Directory to store execution plans'),
|
||||
|
||||
cfg.StrOpt('engine_key',
|
||||
help='Public key of murano-engine')
|
||||
]
|
||||
|
||||
message_routing_opt = cfg.BoolOpt(
|
||||
'enable_dynamic_result_queue',
|
||||
help='Enable taking dynamic result queue from task field reply_to',
|
||||
default=False)
|
||||
|
||||
|
||||
rabbit_opts = [
|
||||
cfg.HostAddressOpt('host',
|
||||
help='The RabbitMQ broker address which used for '
|
||||
'communication with Murano guest agents.',
|
||||
default='localhost'),
|
||||
cfg.IntOpt('port', help='The RabbitMQ broker port.', default=5672),
|
||||
cfg.StrOpt('login',
|
||||
help='The RabbitMQ login.',
|
||||
default='guest'),
|
||||
cfg.StrOpt('password',
|
||||
help='The RabbitMQ password.',
|
||||
secret=True,
|
||||
default='guest'),
|
||||
cfg.StrOpt('virtual_host',
|
||||
help='The RabbitMQ virtual host.',
|
||||
default='/'),
|
||||
cfg.BoolOpt('ssl',
|
||||
help='Boolean flag to enable SSL communication through the '
|
||||
'RabbitMQ broker between murano-engine and guest agents.',
|
||||
default=False),
|
||||
cfg.StrOpt('ssl_version',
|
||||
default='',
|
||||
help='SSL version to use (valid only if SSL enabled). '
|
||||
'Valid values are TLSv1 and SSLv23. SSLv2, SSLv3, '
|
||||
'TLSv1_1, and TLSv1_2 may be available on some '
|
||||
'distributions.'),
|
||||
cfg.StrOpt('ca_certs',
|
||||
help='SSL cert file (valid only if SSL enabled).',
|
||||
default=''),
|
||||
cfg.BoolOpt('insecure', default=False,
|
||||
help='This option explicitly allows Murano to perform '
|
||||
'"insecure" SSL connections to RabbitMQ'),
|
||||
cfg.StrOpt('result_routing_key',
|
||||
help='This value should be obtained from API'),
|
||||
cfg.StrOpt('result_exchange',
|
||||
help='This value must be obtained from API',
|
||||
default=''),
|
||||
cfg.StrOpt('input_queue',
|
||||
help='This value must be obtained from API',
|
||||
default='')
|
||||
|
||||
]
|
||||
|
||||
CONF.register_opts(opts)
|
||||
CONF.register_cli_opt(message_routing_opt)
|
||||
CONF.register_opts(rabbit_opts, group='rabbitmq')
|
||||
logging.register_options(CONF)
|
||||
|
||||
|
||||
def parse_args(args=None, usage=None, default_config_files=None):
|
||||
version_string = version.version_info.version_string()
|
||||
CONF(args=args,
|
||||
project='muranoagent',
|
||||
version=version_string,
|
||||
usage=usage,
|
||||
default_config_files=default_config_files)
|
|
@ -1,20 +0,0 @@
|
|||
# Copyright (c) 2013 Mirantis Inc.
|
||||
#
|
||||
# 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 muranoagent.common.messaging.message import Message # noqa
|
||||
from muranoagent.common.messaging.mqclient import MqClient # noqa
|
||||
from muranoagent.common.messaging.subscription import Subscription # noqa
|
||||
|
||||
__all__ = ['Message', 'Subscription', 'MqClient']
|
|
@ -1,66 +0,0 @@
|
|||
# Copyright (c) 2013 Mirantis Inc.
|
||||
#
|
||||
# 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 oslo_log import log as logging
|
||||
|
||||
LOG = logging.getLogger("murano-common.messaging")
|
||||
|
||||
|
||||
class Message(object):
|
||||
def __init__(self, connection=None, message_handle=None):
|
||||
self._body = None
|
||||
self._connection = connection
|
||||
self._message_handle = message_handle
|
||||
if message_handle:
|
||||
self.id = message_handle.properties.get('message_id')
|
||||
self._reply_to = message_handle.properties.get('reply_to')
|
||||
self._signature = message_handle.headers.get('signature')
|
||||
else:
|
||||
self.id = None
|
||||
self._reply_to = None
|
||||
self._signature = None
|
||||
|
||||
if message_handle:
|
||||
self.body = message_handle.body
|
||||
|
||||
else:
|
||||
self.body = None
|
||||
|
||||
@property
|
||||
def body(self):
|
||||
return self._body
|
||||
|
||||
@body.setter
|
||||
def body(self, value):
|
||||
self._body = value
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._id
|
||||
|
||||
@id.setter
|
||||
def id(self, value):
|
||||
self._id = value or ''
|
||||
|
||||
@property
|
||||
def reply_to(self):
|
||||
return self._reply_to
|
||||
|
||||
def ack(self):
|
||||
self._message_handle.ack()
|
||||
|
||||
@property
|
||||
def signature(self):
|
||||
return self._signature
|
|
@ -1,151 +0,0 @@
|
|||
# Copyright (c) 2013 Mirantis Inc.
|
||||
#
|
||||
# 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 random
|
||||
import ssl as ssl_module
|
||||
|
||||
import eventlet
|
||||
import json
|
||||
import kombu
|
||||
from oslo_service import sslutils
|
||||
|
||||
from muranoagent.common.messaging import subscription
|
||||
|
||||
|
||||
class MqClient(object):
|
||||
def __init__(self, login, password, host, port, virtual_host,
|
||||
ssl=False, ssl_version=None, ca_certs=None, insecure=False):
|
||||
ssl_params = None
|
||||
|
||||
if ssl:
|
||||
cert_reqs = ssl_module.CERT_REQUIRED
|
||||
if insecure:
|
||||
if ca_certs:
|
||||
cert_reqs = ssl_module.CERT_OPTIONAL
|
||||
else:
|
||||
cert_reqs = ssl_module.CERT_NONE
|
||||
|
||||
ssl_params = {
|
||||
'ca_certs': ca_certs,
|
||||
'cert_reqs': cert_reqs
|
||||
}
|
||||
|
||||
if ssl_version:
|
||||
key = ssl_version.lower()
|
||||
try:
|
||||
ssl_params['ssl_version'] = sslutils._SSL_PROTOCOLS[key]
|
||||
except KeyError:
|
||||
raise RuntimeError("Invalid SSL version: %s" % ssl_version)
|
||||
|
||||
# Time interval after which RabbitMQ will disconnect client if no
|
||||
# heartbeats were received. Usually client sends 2 heartbeats during
|
||||
# this interval. Using random to make it less lucky that many agents
|
||||
# ping RabbitMQ simultaneously
|
||||
heartbeat_rate = 20 + 20 * random.random()
|
||||
|
||||
self._connection = kombu.Connection(
|
||||
'amqp://{0}:{1}@{2}:{3}/{4}'.format(
|
||||
login,
|
||||
password,
|
||||
host,
|
||||
port,
|
||||
virtual_host
|
||||
), ssl=ssl_params, heartbeat=heartbeat_rate
|
||||
)
|
||||
self._channel = None
|
||||
self._connected = False
|
||||
self._exception = None
|
||||
|
||||
def __enter__(self):
|
||||
self.connect()
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
if exc_type:
|
||||
self._connected = False
|
||||
else:
|
||||
self.close()
|
||||
return False
|
||||
|
||||
def connect(self):
|
||||
self._connection.connect()
|
||||
self._channel = self._connection.channel()
|
||||
if not self._connected:
|
||||
self._connected = True
|
||||
eventlet.spawn(self._heartbeater)
|
||||
|
||||
def close(self):
|
||||
if self._connected:
|
||||
self._connection.close()
|
||||
self._connected = False
|
||||
|
||||
def _check_exception(self):
|
||||
ex = self._exception
|
||||
if ex:
|
||||
self._exception = None
|
||||
raise ex
|
||||
|
||||
def _heartbeater(self):
|
||||
while self._connected:
|
||||
eventlet.sleep(1)
|
||||
try:
|
||||
self._connection.heartbeat_check()
|
||||
except Exception as ex:
|
||||
self._exception = ex
|
||||
self._connected = False
|
||||
|
||||
def declare(self, queue, exchange='', enable_ha=False, ttl=0):
|
||||
self._check_exception()
|
||||
if not self._connected:
|
||||
raise RuntimeError('Not connected to RabbitMQ')
|
||||
|
||||
queue_arguments = {}
|
||||
if enable_ha is True:
|
||||
# To use mirrored queues feature in RabbitMQ 2.x
|
||||
# we need to declare this policy on the queue itself.
|
||||
#
|
||||
# Warning: this option has no effect on RabbitMQ 3.X,
|
||||
# to enable mirrored queues feature in RabbitMQ 3.X, please
|
||||
# configure RabbitMQ.
|
||||
queue_arguments['x-ha-policy'] = 'all'
|
||||
if ttl > 0:
|
||||
queue_arguments['x-expires'] = ttl
|
||||
|
||||
exchange = kombu.Exchange(exchange, type='direct', durable=True)
|
||||
queue = kombu.Queue(queue, exchange, queue, durable=False,
|
||||
queue_arguments=queue_arguments)
|
||||
bound_queue = queue(self._connection)
|
||||
bound_queue.declare()
|
||||
|
||||
def send(self, message, key, exchange=''):
|
||||
self._check_exception()
|
||||
if not self._connected:
|
||||
raise RuntimeError('Not connected to RabbitMQ')
|
||||
|
||||
producer = kombu.Producer(self._connection)
|
||||
producer.publish(
|
||||
exchange=str(exchange),
|
||||
routing_key=str(key),
|
||||
body=json.dumps(message.body),
|
||||
message_id=str(message.id)
|
||||
)
|
||||
|
||||
def open(self, queue, prefetch_count=1):
|
||||
self._check_exception()
|
||||
if not self._connected:
|
||||
raise RuntimeError('Not connected to RabbitMQ')
|
||||
|
||||
return subscription.Subscription(self._connection, queue,
|
||||
prefetch_count, self._check_exception)
|
|
@ -1,68 +0,0 @@
|
|||
# Copyright (c) 2013 Mirantis Inc.
|
||||
#
|
||||
# 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 collections
|
||||
import socket
|
||||
import time
|
||||
|
||||
import kombu
|
||||
from muranoagent.common.messaging import message
|
||||
|
||||
|
||||
class Subscription(object):
|
||||
def __init__(self, connection, queue, prefetch_count, exception_func):
|
||||
self._buffer = collections.deque()
|
||||
self._connection = connection
|
||||
self._queue = kombu.Queue(name=queue, exchange=None)
|
||||
self._consumer = kombu.Consumer(self._connection, auto_declare=False)
|
||||
self._consumer.register_callback(self._receive)
|
||||
self._consumer.qos(prefetch_count=prefetch_count)
|
||||
self._check_exception = exception_func
|
||||
|
||||
def __enter__(self):
|
||||
self._consumer.add_queue(self._queue)
|
||||
self._consumer.consume()
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
if self._consumer is not None and not exc_type:
|
||||
self._consumer.cancel()
|
||||
return False
|
||||
|
||||
def get_message(self, timeout=None):
|
||||
self._check_exception()
|
||||
msg_handle = self._get(timeout=timeout)
|
||||
if msg_handle is None:
|
||||
return None
|
||||
return message.Message(self._connection, msg_handle)
|
||||
|
||||
def _get(self, timeout=None):
|
||||
self._check_exception()
|
||||
elapsed = 0.0
|
||||
remaining = timeout
|
||||
while True:
|
||||
time_start = time.time()
|
||||
if self._buffer:
|
||||
return self._buffer.pop()
|
||||
try:
|
||||
self._connection.drain_events(timeout=timeout and remaining)
|
||||
except socket.timeout:
|
||||
return None
|
||||
elapsed += time.time() - time_start
|
||||
remaining = timeout and timeout - elapsed or None
|
||||
|
||||
def _receive(self, message_data, message):
|
||||
self._check_exception()
|
||||
self._buffer.append(message)
|
|
@ -1,39 +0,0 @@
|
|||
# Copyright (c) 2013 Mirantis Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
class AgentException(Exception):
|
||||
def __init__(self, code, message=None, additional_data=None):
|
||||
self._error_code = code
|
||||
self._additional_data = additional_data
|
||||
super(AgentException, self).__init__(message)
|
||||
|
||||
@property
|
||||
def error_code(self):
|
||||
return self._error_code
|
||||
|
||||
@property
|
||||
def additional_data(self):
|
||||
return self._additional_data
|
||||
|
||||
|
||||
class CustomException(AgentException):
|
||||
def __init__(self, code, message=None, additional_data=None):
|
||||
super(CustomException, self).__init__(
|
||||
code + 100, message, additional_data)
|
||||
|
||||
|
||||
class IncorrectFormat(AgentException):
|
||||
pass
|
|
@ -1,164 +0,0 @@
|
|||
# Copyright (c) 2013 Mirantis Inc.
|
||||
#
|
||||
# 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 json
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
|
||||
from cryptography.hazmat import backends
|
||||
from cryptography.hazmat.primitives.asymmetric import padding
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from oslo_log import log as logging
|
||||
|
||||
from muranoagent import bunch
|
||||
from muranoagent.common import config
|
||||
from muranoagent import util
|
||||
|
||||
CONF = config.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ExecutionPlanQueue(object):
|
||||
plan_filename = 'plan.json'
|
||||
result_filename = 'result.json'
|
||||
stamp_filename = 'stamp'
|
||||
|
||||
def __init__(self):
|
||||
self._plans_folder = os.path.join(CONF.storage, 'plans')
|
||||
if not os.path.exists(self._plans_folder):
|
||||
os.makedirs(self._plans_folder, 0o700)
|
||||
else:
|
||||
try:
|
||||
os.chmod(self._plans_folder, 0o700)
|
||||
except OSError:
|
||||
pass
|
||||
self._key = None if not CONF.engine_key \
|
||||
else serialization.load_pem_public_key(
|
||||
CONF.engine_key, backends.default_backend())
|
||||
self._load_stamp()
|
||||
|
||||
def put_execution_plan(self, execution_plan, signature, msg_id, reply_to):
|
||||
timestamp = str(int(time.time() * 10000))
|
||||
# execution_plan['_timestamp'] = timestamp
|
||||
folder_path = os.path.join(self._plans_folder, timestamp)
|
||||
os.mkdir(folder_path)
|
||||
plan_file_path = os.path.join(
|
||||
folder_path, ExecutionPlanQueue.plan_filename)
|
||||
json_plan = json.dumps({
|
||||
'Data': util.b64encode(execution_plan),
|
||||
'Signature': util.b64encode(signature or ''),
|
||||
'ID': msg_id,
|
||||
'ReplyTo': reply_to
|
||||
})
|
||||
with open(plan_file_path, 'w') as out_file:
|
||||
out_file.write(json_plan)
|
||||
|
||||
def _get_first_timestamp(self, filename):
|
||||
def predicate(folder):
|
||||
path = os.path.join(self._plans_folder, folder, filename)
|
||||
return os.path.exists(path)
|
||||
|
||||
timestamps = [
|
||||
name for name in os.listdir(self._plans_folder)
|
||||
if predicate(name)
|
||||
]
|
||||
timestamps.sort()
|
||||
return None if len(timestamps) == 0 else timestamps[0]
|
||||
|
||||
def _get_first_file(self, filename):
|
||||
timestamp = self._get_first_timestamp(filename)
|
||||
if not timestamp:
|
||||
return None, None
|
||||
path = os.path.join(self._plans_folder, timestamp, filename)
|
||||
with open(path) as json_file:
|
||||
return json.loads(json_file.read()), timestamp
|
||||
|
||||
def get_execution_plan(self):
|
||||
while True:
|
||||
ep_info, timestamp = self._get_first_file(
|
||||
ExecutionPlanQueue.plan_filename)
|
||||
if ep_info is None:
|
||||
return None
|
||||
|
||||
try:
|
||||
data = util.b64decode(ep_info['Data'])
|
||||
if self._key:
|
||||
signature = util.b64decode(ep_info['Signature'])
|
||||
self._verify_signature(data, signature)
|
||||
|
||||
ep = json.loads(data)
|
||||
if not isinstance(ep, dict):
|
||||
raise ValueError('Message is not a document')
|
||||
|
||||
stamp = ep.get('Stamp', -1)
|
||||
if stamp >= 0:
|
||||
if stamp <= self._last_stamp:
|
||||
raise ValueError('Dropping old/duplicate message')
|
||||
self._save_stamp(stamp)
|
||||
|
||||
if 'ID' not in ep:
|
||||
ep['ID'] = ep_info['ID']
|
||||
if 'ReplyTo' not in ep:
|
||||
ep['ReplyTo'] = ep_info['ReplyTo']
|
||||
|
||||
ep['_timestamp'] = timestamp
|
||||
return bunch.Bunch(ep)
|
||||
except Exception as ex:
|
||||
LOG.exception(ex)
|
||||
self.remove(timestamp)
|
||||
|
||||
def _verify_signature(self, data, signature):
|
||||
if not signature:
|
||||
raise ValueError("Required signature was not found")
|
||||
self._key.verify(
|
||||
signature,
|
||||
CONF.rabbitmq.input_queue + data,
|
||||
padding.PKCS1v15(), hashes.SHA256())
|
||||
|
||||
def put_execution_result(self, result, execution_plan):
|
||||
timestamp = execution_plan['_timestamp']
|
||||
if 'ReplyTo' in execution_plan:
|
||||
result['ReplyTo'] = execution_plan.get('ReplyTo')
|
||||
path = os.path.join(
|
||||
self._plans_folder, timestamp,
|
||||
ExecutionPlanQueue.result_filename)
|
||||
with open(path, 'w') as out_file:
|
||||
out_file.write(json.dumps(result))
|
||||
|
||||
def remove(self, timestamp):
|
||||
path = os.path.join(self._plans_folder, timestamp)
|
||||
shutil.rmtree(path)
|
||||
|
||||
def get_execution_plan_result(self):
|
||||
return self._get_first_file(
|
||||
ExecutionPlanQueue.result_filename)
|
||||
|
||||
def _load_stamp(self):
|
||||
plan_file_path = os.path.join(
|
||||
self._plans_folder, ExecutionPlanQueue.stamp_filename)
|
||||
if os.path.exists(plan_file_path):
|
||||
with open(plan_file_path) as f:
|
||||
self._last_stamp = int(f.read())
|
||||
else:
|
||||
self._last_stamp = 0
|
||||
|
||||
def _save_stamp(self, stamp):
|
||||
plan_file_path = os.path.join(
|
||||
self._plans_folder, ExecutionPlanQueue.stamp_filename)
|
||||
with open(plan_file_path, 'w') as f:
|
||||
f.write(str(stamp))
|
||||
self._last_stamp = stamp
|
|
@ -1,78 +0,0 @@
|
|||
# Copyright (c) 2013 Mirantis Inc.
|
||||
#
|
||||
# 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 sys
|
||||
|
||||
from muranoagent import bunch
|
||||
from muranoagent import files_manager as fm
|
||||
from muranoagent import script_runner
|
||||
|
||||
|
||||
class ExecutionPlanRunner(object):
|
||||
def __init__(self, execution_plan):
|
||||
self._execution_plan = execution_plan
|
||||
self._main_script = self._prepare_script(execution_plan.Body)
|
||||
self._script_funcs = {}
|
||||
self._files_manager = fm.FilesManager(execution_plan)
|
||||
self._prepare_executors(execution_plan)
|
||||
|
||||
def run(self):
|
||||
script_globals = {
|
||||
"args": bunch.Bunch(self._execution_plan.get('Parameters') or {})
|
||||
}
|
||||
script_globals.update(self._script_funcs)
|
||||
exec(self._main_script, script_globals)
|
||||
if '__execution_plan_exception' in script_globals:
|
||||
raise script_globals['__execution_plan_exception']
|
||||
return script_globals['__execution_plan_result']
|
||||
|
||||
@staticmethod
|
||||
def _unindent(script, initial_indent):
|
||||
lines = script.expandtabs(4).split('\n')
|
||||
min_indent = sys.maxsize
|
||||
for line in lines:
|
||||
indent = -1
|
||||
for i, c in enumerate(line):
|
||||
if c != ' ':
|
||||
indent = i
|
||||
break
|
||||
if 0 <= indent < min_indent:
|
||||
min_indent = indent
|
||||
return '\n'.join([' ' * initial_indent + line[min_indent:]
|
||||
for line in lines])
|
||||
|
||||
def _prepare_executors(self, execution_plan):
|
||||
for key, value in execution_plan.Scripts.items():
|
||||
self._script_funcs[key] = script_runner.ScriptRunner(
|
||||
key, bunch.Bunch(value), self._files_manager)
|
||||
|
||||
@staticmethod
|
||||
def _prepare_script(body):
|
||||
script = 'def __execution_plan_main():\n'
|
||||
script += ExecutionPlanRunner._unindent(body, 4)
|
||||
script += """
|
||||
try:
|
||||
__execution_plan_result = __execution_plan_main()
|
||||
except Exception as e:
|
||||
__execution_plan_exception = e
|
||||
"""
|
||||
return script
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self._files_manager.clear()
|
||||
return False
|
|
@ -1,65 +0,0 @@
|
|||
# Copyright (c) 2013 Mirantis Inc.
|
||||
#
|
||||
# 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 oslo_utils import timeutils
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from muranoagent import exceptions as exc
|
||||
|
||||
|
||||
class ExecutionResult(object):
|
||||
@staticmethod
|
||||
def from_result(result, execution_plan):
|
||||
if 'ID' not in execution_plan:
|
||||
raise ValueError('ID attribute is missing from execution plan')
|
||||
|
||||
return {
|
||||
'FormatVersion': '2.0.0',
|
||||
'ID': uuidutils.generate_uuid(dashed=False),
|
||||
'SourceID': execution_plan.ID,
|
||||
'Action': 'Execution:Result',
|
||||
'ErrorCode': 0,
|
||||
'Body': result,
|
||||
'Time': str(timeutils.utcnow())
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def from_error(error, execution_plan):
|
||||
if 'ID' not in execution_plan:
|
||||
raise ValueError('ID attribute is missing from execution plan')
|
||||
|
||||
error_code = 1
|
||||
additional_info = None
|
||||
message = None
|
||||
if isinstance(error, int):
|
||||
error_code = error
|
||||
elif isinstance(error, Exception):
|
||||
message = str(error)
|
||||
if isinstance(error, exc.AgentException):
|
||||
error_code = error.error_code
|
||||
additional_info = error.additional_data
|
||||
|
||||
return {
|
||||
'FormatVersion': '2.0.0',
|
||||
'ID': uuidutils.generate_uuid(dashed=False),
|
||||
'SourceID': execution_plan.ID,
|
||||
'Action': 'Execution:Result',
|
||||
'ErrorCode': error_code,
|
||||
'Body': {
|
||||
'Message': message,
|
||||
'AdditionalInfo': additional_info
|
||||
},
|
||||
'Time': str(timeutils.utcnow())
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
# Copyright (c) 2013 Mirantis Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
class ExecutorsRepo(object):
|
||||
def __init__(self):
|
||||
self._executors = {}
|
||||
|
||||
def register_executor(self, name, cls):
|
||||
self._executors[name] = cls
|
||||
|
||||
def create_executor(self, type, name):
|
||||
if type not in self._executors:
|
||||
return None
|
||||
return self._executors[type](name)
|
||||
|
||||
|
||||
Executors = ExecutorsRepo()
|
||||
|
||||
|
||||
def executor(name):
|
||||
def wrapper(cls):
|
||||
Executors.register_executor(name, cls)
|
||||
return cls
|
||||
return wrapper
|
|
@ -1,93 +0,0 @@
|
|||
# Copyright (c) 2013 Mirantis Inc.
|
||||
#
|
||||
# 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 os
|
||||
import signal
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from muranoagent import bunch
|
||||
import muranoagent.exceptions
|
||||
from muranoagent import executors
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@executors.executor('Application')
|
||||
class ApplicationExecutor(object):
|
||||
def __init__(self, name):
|
||||
self._name = name
|
||||
|
||||
def load(self, path, options):
|
||||
self._path = path
|
||||
self._capture_stdout = options.get('captureStdout', True)
|
||||
self._capture_stderr = options.get('captureStderr', True)
|
||||
self._verify_exitcode = options.get('verifyExitcode', True)
|
||||
|
||||
def run(self, function, commandline='', input=None):
|
||||
dir_name = os.path.dirname(self._path)
|
||||
os.chdir(dir_name)
|
||||
app = '"{0}" {1}'.format(os.path.basename(self._path), commandline)
|
||||
|
||||
if not sys.platform == 'win32':
|
||||
os.chmod(self._path, stat.S_IEXEC | stat.S_IREAD)
|
||||
app = './' + app
|
||||
|
||||
stdout = subprocess.PIPE if self._capture_stdout else None
|
||||
stderr = subprocess.PIPE if self._capture_stderr else None
|
||||
script_name = os.path.relpath(self._path)
|
||||
LOG.debug("Starting '{0}' script execution".format(script_name))
|
||||
|
||||
popen_kwargs = {
|
||||
'stdout': stdout,
|
||||
'stderr': stderr,
|
||||
'universal_newlines': True,
|
||||
'cwd': dir_name,
|
||||
'shell': True
|
||||
}
|
||||
if os.name != 'nt':
|
||||
popen_kwargs['preexec_fn'] = lambda: signal.signal(
|
||||
signal.SIGPIPE, signal.SIG_DFL)
|
||||
|
||||
process = subprocess.Popen(app, **popen_kwargs)
|
||||
stdout, stderr = process.communicate(input)
|
||||
retcode = process.poll()
|
||||
LOG.debug("Script {0} execution finished "
|
||||
"with retcode: {1}".format(script_name, retcode))
|
||||
if stdout is not None:
|
||||
if hasattr(stdout, 'decode'):
|
||||
stdout = stdout.decode('utf-8')
|
||||
LOG.debug(u"'{0}' execution stdout: "
|
||||
u"'{1}'".format(script_name, stdout))
|
||||
if stderr is not None:
|
||||
if hasattr(stderr, 'decode'):
|
||||
stderr = stderr.decode('utf-8')
|
||||
LOG.debug(u"'{0}' execution stderr: "
|
||||
u"'{1}'".format(script_name, stderr))
|
||||
result = {
|
||||
'exitCode': retcode,
|
||||
'stdout': stdout.strip() if stdout else None,
|
||||
'stderr': stderr.strip() if stderr else None
|
||||
}
|
||||
if self._verify_exitcode and retcode != 0:
|
||||
raise muranoagent.exceptions.CustomException(
|
||||
0,
|
||||
message='Script {0} returned error code'.format(self._name),
|
||||
additional_data=result)
|
||||
|
||||
return bunch.Bunch(result)
|
|
@ -1,137 +0,0 @@
|
|||
# Copyright (c) 2015 Telefonica I+D
|
||||
#
|
||||
# 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 json
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from muranoagent import bunch
|
||||
import muranoagent.exceptions
|
||||
from muranoagent import executors
|
||||
from muranoagent.executors import chef_puppet_executor_base
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@executors.executor('Chef')
|
||||
class ChefExecutor(chef_puppet_executor_base.ChefPuppetExecutorBase):
|
||||
|
||||
def load(self, path, options):
|
||||
super(ChefExecutor, self).load(path, options)
|
||||
self._use_berkshelf = options.get('useBerkshelf', False)
|
||||
self._berksfile_path = options.get('berksfilePath', None)
|
||||
|
||||
def run(self, function, recipe_attributes=None, input=None):
|
||||
"""It runs the chef executor.
|
||||
|
||||
:param function: The function
|
||||
:param recipe_attributes: recipe attributes
|
||||
:param input:
|
||||
"""
|
||||
self._valid_module_name()
|
||||
|
||||
cookbook_path = self._create_cookbook_path(self.module_name)
|
||||
|
||||
try:
|
||||
self._configure_chef(cookbook_path)
|
||||
self._generate_manifest(self.module_name,
|
||||
self.module_recipe, recipe_attributes)
|
||||
except Exception as e:
|
||||
result = {
|
||||
'exitCode': 2,
|
||||
'stdout': None,
|
||||
'stderr': e.strerror
|
||||
}
|
||||
raise muranoagent.exceptions.CustomException(
|
||||
0,
|
||||
message='Cookbook {0} returned error code {1}: {2}'.format(
|
||||
self.module_name, self.module_recipe, e.strerror,
|
||||
), additional_data=result)
|
||||
|
||||
solo_file = os.path.join(self._path, "solo.rb")
|
||||
command = 'chef-solo -j node.json -c {0}'.format(solo_file)
|
||||
result = self._execute_command(command)
|
||||
return bunch.Bunch(result)
|
||||
|
||||
def _create_cookbook_path(self, cookbook_name):
|
||||
"""It defines a path where all required cookbooks are located."""
|
||||
path = os.path.abspath(self._path)
|
||||
|
||||
if self._use_berkshelf:
|
||||
LOG.debug('Using Berkshelf')
|
||||
|
||||
# Get Berksfile
|
||||
if self._berksfile_path is None:
|
||||
self._berksfile_path = cookbook_name + '/Berksfile'
|
||||
berksfile = os.path.join(path, self._berksfile_path)
|
||||
if not os.path.isfile(berksfile):
|
||||
msg = "Berskfile {0} not found".format(berksfile)
|
||||
LOG.debug(msg)
|
||||
raise muranoagent.exceptions.CustomException(
|
||||
0,
|
||||
message=msg,
|
||||
additional_data=None)
|
||||
|
||||
# Create cookbooks path
|
||||
cookbook_path = os.path.join(path, "berks-cookbooks")
|
||||
if not os.path.isdir(cookbook_path):
|
||||
os.makedirs(cookbook_path)
|
||||
|
||||
# Vendor cookbook and its dependencies to cookbook_path
|
||||
command = 'berks vendor --berksfile={0} {1}'.format(
|
||||
berksfile,
|
||||
cookbook_path)
|
||||
result = self._execute_command(command)
|
||||
if result['exitCode'] != 0:
|
||||
raise muranoagent.exceptions.CustomException(
|
||||
0,
|
||||
message='Berks returned error code',
|
||||
additional_data=result)
|
||||
|
||||
return cookbook_path
|
||||
|
||||
else:
|
||||
return path
|
||||
|
||||
def _configure_chef(self, cookbook_path):
|
||||
"""It generates the chef files for configuration."""
|
||||
solo_file = os.path.join(self._path, 'solo.rb')
|
||||
if not os.path.exists(solo_file):
|
||||
if not os.path.isdir(self._path):
|
||||
os.makedirs(self._path)
|
||||
with open(solo_file, "w+") as f:
|
||||
f.write('cookbook_path \"' + cookbook_path + '\"')
|
||||
|
||||
def _generate_manifest(self, cookbook_name,
|
||||
cookbook_recipe, recipe_attributes):
|
||||
"""It generates the chef manifest."""
|
||||
node = self._create_manifest(cookbook_name, cookbook_recipe,
|
||||
recipe_attributes)
|
||||
with open("node.json", "w+") as f:
|
||||
f.write(node)
|
||||
|
||||
def _create_manifest(self, cookbook_name, cookbook_recipe,
|
||||
recipe_attributes):
|
||||
node = {}
|
||||
node["run_list"] = [u"recipe[{0}::{1}]".format(
|
||||
cookbook_name, cookbook_recipe)]
|
||||
|
||||
if recipe_attributes:
|
||||
node[cookbook_name] = recipe_attributes.copy()
|
||||
|
||||
return json.dumps(node)
|
|
@ -1,100 +0,0 @@
|
|||
# Copyright (c) 2015 Telefonica I+D
|
||||
#
|
||||
# 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 json
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from muranoagent import bunch
|
||||
import muranoagent.exceptions
|
||||
from muranoagent import executors
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ChefPuppetExecutorBase(object):
|
||||
def __init__(self, name):
|
||||
self._name = name
|
||||
|
||||
def load(self, path, options):
|
||||
"""Load the path and options from template into the executor.
|
||||
|
||||
:param path: The path
|
||||
:param options: execution plan options.
|
||||
|
||||
"""
|
||||
self._path = path
|
||||
self._capture_stdout = options.get('captureStdout', True)
|
||||
self._capture_stderr = options.get('captureStderr', True)
|
||||
self._verify_exitcode = options.get('verifyExitcode', True)
|
||||
|
||||
def _valid_module_name(self):
|
||||
if not self._valid_name(self._name):
|
||||
msg = ("Module recipe name format {0} is not valid".
|
||||
format(self._name))
|
||||
LOG.debug(msg)
|
||||
raise muranoagent.exceptions.CustomException(
|
||||
0,
|
||||
message=msg,
|
||||
additional_data=None)
|
||||
|
||||
self.module_name = self._name[0:self._name.rfind('::')]
|
||||
self.module_recipe = self._name[self._name.rfind('::') + 2:]
|
||||
|
||||
def _valid_name(self, name):
|
||||
return '::' in name
|
||||
|
||||
def _execute_command(self, command):
|
||||
stdout = subprocess.PIPE if self._capture_stdout else None
|
||||
stderr = subprocess.PIPE if self._capture_stderr else None
|
||||
process = subprocess.Popen(
|
||||
command,
|
||||
stdout=stdout,
|
||||
stderr=stderr,
|
||||
universal_newlines=True,
|
||||
cwd=os.getcwd(),
|
||||
shell=True)
|
||||
stdout, stderr = process.communicate(input)
|
||||
retcode = process.poll()
|
||||
|
||||
if stdout is not None:
|
||||
if not isinstance(stdout, str):
|
||||
stdout = stdout.decode('utf-8')
|
||||
LOG.debug(u"'{0}' execution stdout: "
|
||||
u"'{1}'".format(self.module_name, stdout))
|
||||
if stderr is not None:
|
||||
for line in stdout.splitlines():
|
||||
if 'ERROR' in line:
|
||||
stderr += line + "\n"
|
||||
LOG.debug(u"'{0}' execution stderr: "
|
||||
u"'{1}'".format(self.module_name, stderr))
|
||||
|
||||
LOG.debug('Script {0} execution finished \
|
||||
with retcode: {1} {2}'.format(self.module_name, retcode, stderr))
|
||||
result = {
|
||||
'exitCode': retcode,
|
||||
'stdout': stdout.strip() if stdout else None,
|
||||
'stderr': stderr.strip() if stderr else None
|
||||
}
|
||||
if self._verify_exitcode and retcode != 0:
|
||||
raise muranoagent.exceptions.CustomException(
|
||||
0,
|
||||
message='Script {0} returned error code'.format(self._name),
|
||||
additional_data=result)
|
||||
|
||||
return result
|
|
@ -1,102 +0,0 @@
|
|||
# Copyright (c) 2015 Telefonica I+D
|
||||
#
|
||||
# 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 os
|
||||
import subprocess
|
||||
import yaml
|
||||
|
||||
from muranoagent import bunch
|
||||
import muranoagent.exceptions
|
||||
from muranoagent import executors
|
||||
from muranoagent.executors import chef_puppet_executor_base
|
||||
|
||||
|
||||
@executors.executor('Puppet')
|
||||
class PuppetExecutor(chef_puppet_executor_base.ChefPuppetExecutorBase):
|
||||
|
||||
def run(self, function, recipe_attributes=None, input=None):
|
||||
"""It runs the puppet executor.
|
||||
|
||||
:param function: The function
|
||||
:param recipe_attributes: recipe attributes
|
||||
:param input:
|
||||
"""
|
||||
self._valid_module_name()
|
||||
|
||||
try:
|
||||
self._configure_puppet()
|
||||
self._generate_files(self.module_name, self.module_recipe,
|
||||
recipe_attributes)
|
||||
except Exception as e:
|
||||
result = {
|
||||
'exitCode': 2,
|
||||
'stdout': None,
|
||||
'stderr': e.strerror
|
||||
}
|
||||
raise muranoagent.exceptions.CustomException(
|
||||
0,
|
||||
message='Module %s returned error code %s: %s' %
|
||||
(self.module_name, self.module_recipe, e.strerror),
|
||||
additional_data=result)
|
||||
|
||||
command = 'puppet apply --hiera_config=hiera.yaml --modulepath ' \
|
||||
'{0} manifest.pp'.format(self._path)
|
||||
result = self._execute_command(command)
|
||||
return bunch.Bunch(result)
|
||||
|
||||
def _configure_puppet(self):
|
||||
|
||||
if os.path.exists('hiera.yaml'):
|
||||
return
|
||||
|
||||
data = dict(
|
||||
backends='yaml',
|
||||
logger='console',
|
||||
hierarchy='%{env}',
|
||||
yaml=dict(datadir='/etc/puppet/hieradata')
|
||||
)
|
||||
|
||||
self._write_yaml_file('hiera.yaml', data)
|
||||
|
||||
def _generate_files(self, module, module_recipe, recipe_attributes):
|
||||
manifest = self._create_manifest(module, module_recipe)
|
||||
with open("manifest.pp", "w+") as f:
|
||||
f.write(str(manifest))
|
||||
|
||||
if recipe_attributes is None:
|
||||
return
|
||||
|
||||
hiera_data = self._create_hiera_data(module, recipe_attributes)
|
||||
self._write_yaml_file('default.yaml', hiera_data)
|
||||
|
||||
def _create_manifest(self, module_name, module_recipe):
|
||||
|
||||
if len(module_recipe) == 0:
|
||||
return "node 'default' {{ class {{ {0}:}}}}".format(module_name)
|
||||
return "node 'default' {{ class {{ {0}::{1}:}}}}".\
|
||||
format(module_name, module_recipe)
|
||||
|
||||
def _create_hiera_data(self, cookbook_name,
|
||||
recipe_attributes):
|
||||
if recipe_attributes is None:
|
||||
return
|
||||
atts = {}
|
||||
for att_name, att_value in recipe_attributes.items():
|
||||
atts[cookbook_name + '::' + att_name] = att_value
|
||||
|
||||
return atts
|
||||
|
||||
def _write_yaml_file(self, file, data):
|
||||
with open(file, 'w') as outfile:
|
||||
outfile.write(yaml.dump(data, default_flow_style=False))
|
|
@ -1,192 +0,0 @@
|
|||
# Copyright (c) 2013 Mirantis Inc.
|
||||
#
|
||||
# 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 git
|
||||
import os
|
||||
import re
|
||||
import requests
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import encodeutils
|
||||
import urllib
|
||||
|
||||
from muranoagent.common import config
|
||||
from muranoagent import util
|
||||
|
||||
CONF = config.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FilesManager(object):
|
||||
def __init__(self, execution_plan):
|
||||
self._fetched_files = {}
|
||||
self._files = execution_plan.get('Files') or {}
|
||||
|
||||
self._cache_folder = os.path.join(
|
||||
CONF.storage, 'files', execution_plan.ID)
|
||||
if os.path.exists(self._cache_folder):
|
||||
self.clear()
|
||||
os.makedirs(self._cache_folder, 0o700)
|
||||
|
||||
def put_file(self, file_id, script):
|
||||
if type(file_id) is dict:
|
||||
file_name = list(file_id.keys())[0]
|
||||
file_def = file_id[file_name]
|
||||
else:
|
||||
file_def = self._files[file_id]
|
||||
file_name = file_def['Name']
|
||||
|
||||
if file_def.get('Type') == 'Downloadable':
|
||||
cache_folder = self._download_url_file(file_def, file_id)
|
||||
return self._make_symlink(cache_folder, file_name, script)
|
||||
else:
|
||||
cache_path = self._fetch_file(file_id)
|
||||
return self._make_symlink(cache_path, file_name, script)
|
||||
|
||||
def _make_symlink(self, cache_path, file_name, script):
|
||||
script_folder = os.path.join(self._cache_folder, script)
|
||||
if not os.path.isdir(script_folder):
|
||||
os.mkdir(script_folder)
|
||||
|
||||
file_folder = os.path.join(script_folder,
|
||||
os.path.dirname(file_name))
|
||||
if not os.path.isdir(file_folder):
|
||||
os.makedirs(file_folder)
|
||||
|
||||
if cache_path is not None:
|
||||
script_path = os.path.join(script_folder, file_name)
|
||||
if not os.path.lexists(script_path):
|
||||
os.symlink(cache_path, script_path)
|
||||
return script_path
|
||||
|
||||
def _fetch_file(self, file_id):
|
||||
if file_id in self._fetched_files:
|
||||
return self._fetched_files[file_id]
|
||||
|
||||
filedef = self._files[file_id]
|
||||
out_path = os.path.join(self._cache_folder, file_id)
|
||||
body_type = filedef.get('BodyType', 'Text')
|
||||
with open(out_path, 'w') as out_file:
|
||||
if body_type == 'Text':
|
||||
out_file.write(filedef['Body'])
|
||||
elif body_type == 'Base64':
|
||||
out_file.write(util.b64decode(filedef['Body']))
|
||||
|
||||
self._fetched_files[file_id] = out_path
|
||||
return out_path
|
||||
|
||||
def _download_url_file(self, file_def, file_id):
|
||||
"""It download the file in the murano-agent.
|
||||
|
||||
It can proceed from a git file or any other internal URL
|
||||
:param file_def: file description
|
||||
:param file_id: the ID file to download
|
||||
:param input:
|
||||
"""
|
||||
folder = os.path.join(self._cache_folder, file_id)
|
||||
if os.path.isdir(folder):
|
||||
return folder
|
||||
|
||||
if 'URL' not in file_def:
|
||||
raise ValueError("No valid URL in file {0}".
|
||||
format(file_def))
|
||||
url_file = file_def['URL']
|
||||
|
||||
if not self._url(url_file):
|
||||
raise ValueError("Provided URL is not valid {0}".
|
||||
format(url_file))
|
||||
|
||||
if not os.path.isdir(folder):
|
||||
os.makedirs(folder)
|
||||
|
||||
try:
|
||||
if self._is_git_repository(url_file):
|
||||
git.Git().clone(url_file, folder)
|
||||
elif self._is_svn_repository(url_file):
|
||||
self._download_svn(url_file, folder)
|
||||
else:
|
||||
self._download_file(url_file, folder)
|
||||
except Exception as e:
|
||||
if self._is_git_repository(url_file):
|
||||
mns = ("Error to clone the git repository {0}: {1}".
|
||||
format(url_file, e.message))
|
||||
else:
|
||||
mns = ("Error to download the file {0}: {1}".
|
||||
format(url_file, e.message))
|
||||
LOG.warning(mns)
|
||||
raise ValueError(mns)
|
||||
return folder
|
||||
|
||||
def clear(self):
|
||||
shutil.rmtree(self._cache_folder, ignore_errors=True)
|
||||
|
||||
def _download_file(self, url, path):
|
||||
local_filename = url.split('/')[-1]
|
||||
r = requests.get(url, stream=True)
|
||||
with open(os.path.join(path, local_filename), 'wb') as f:
|
||||
for chunk in r.iter_content(chunk_size=1024):
|
||||
if chunk:
|
||||
f.write(chunk)
|
||||
f.flush()
|
||||
return local_filename
|
||||
|
||||
def _url(self, file):
|
||||
return (urllib.parse.urlsplit(file).scheme or
|
||||
urllib.parse.urlsplit(file).netloc)
|
||||
|
||||
def _is_git_repository(self, url):
|
||||
return (url.startswith(("git://",
|
||||
"git+http://", "git+https:/"))
|
||||
or url.endswith('.git'))
|
||||
|
||||
def _is_svn_repository(self, url):
|
||||
http_regex = "https?://(.*)/svn/(.*)"
|
||||
http_matches = re.search(http_regex, url)
|
||||
svn_regex = "svn://(.*)"
|
||||
svn_matches = re.search(svn_regex, url)
|
||||
if http_matches is None and svn_matches is None:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def _download_svn(self, url_file, folder):
|
||||
self._execute_command("svn checkout {0} --non-interactive "
|
||||
"--trust-server-cert {1}".
|
||||
format(url_file, folder))
|
||||
|
||||
def _execute_command(self, command):
|
||||
|
||||
process = subprocess.Popen(
|
||||
command,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
universal_newlines=True,
|
||||
cwd=os.getcwd(),
|
||||
shell=True)
|
||||
stdout, stderr = process.communicate(input)
|
||||
retcode = process.poll()
|
||||
|
||||
if stdout is not None:
|
||||
stdout = encodeutils.safe_decode('utf-8')
|
||||
LOG.debug(stdout)
|
||||
|
||||
if stderr is not None:
|
||||
stderr = encodeutils.safe_decode('utf-8')
|
||||
LOG.error(stderr)
|
||||
|
||||
if retcode != 0:
|
||||
raise ValueError(stderr)
|
|
@ -1,52 +0,0 @@
|
|||
# Copyright (c) 2014 Mirantis, Inc.
|
||||
#
|
||||
# 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 copy
|
||||
import itertools
|
||||
|
||||
import muranoagent.common.config
|
||||
|
||||
|
||||
def build_list(opt_list):
|
||||
return list(itertools.chain(*opt_list))
|
||||
|
||||
|
||||
# List of *all* options in [DEFAULT] namespace of murano.
|
||||
# Any new option list or option needs to be registered here.
|
||||
_opt_lists = [
|
||||
('rabbitmq', muranoagent.common.config.rabbit_opts),
|
||||
(None, build_list([
|
||||
muranoagent.common.config.opts,
|
||||
]))
|
||||
]
|
||||
|
||||
|
||||
def list_opts():
|
||||
"""Return a list of oslo.config options available in Murano-Agent.
|
||||
|
||||
Each element of the list is a tuple. The first element is the name of the
|
||||
group under which the list of elements in the second element will be
|
||||
registered. A group name of None corresponds to the [DEFAULT] group in
|
||||
config files.
|
||||
|
||||
This function is also discoverable via the 'muranoagent' entry point
|
||||
under the 'oslo.config.opts' namespace.
|
||||
|
||||
The purpose of this is to allow tools like the Oslo sample config file
|
||||
generator to discover the options exposed to users by Murano.
|
||||
|
||||
:returns: a list of (group_name, opts) tuples
|
||||
"""
|
||||
return [(g, copy.deepcopy(o)) for g, o in _opt_lists]
|
|
@ -1,75 +0,0 @@
|
|||
# Copyright (c) 2013 Mirantis Inc.
|
||||
#
|
||||
# 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 os
|
||||
|
||||
from muranoagent import executors as exe
|
||||
|
||||
|
||||
class FunctionRunner(object):
|
||||
def __init__(self, name, script_runner):
|
||||
self._name = name
|
||||
self._script_executor = script_runner
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
return self._script_executor.execute_function(
|
||||
self._name, *args, **kwargs)
|
||||
|
||||
|
||||
class ScriptRunner(object):
|
||||
def __init__(self, name, script_info, files_manager):
|
||||
self._name = name
|
||||
self._executor = self._get_executor(script_info['Type'], name,
|
||||
script_info['EntryPoint'])
|
||||
self._script_info = script_info
|
||||
self._script_loaded = False
|
||||
self._files_manager = files_manager
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
return self.execute_function(None, *args, **kwargs)
|
||||
|
||||
def _get_executor(self, script_type, script_name, entry_point):
|
||||
create_executor = exe.Executors.create_executor
|
||||
if script_type != 'Application':
|
||||
executor = create_executor(script_type, entry_point)
|
||||
else:
|
||||
executor = create_executor(script_type, script_name)
|
||||
if executor is None:
|
||||
raise ValueError('The application type in {0} is not a valid '
|
||||
'executor {1}'.format(script_name, script_type))
|
||||
return executor
|
||||
|
||||
def execute_function(self, name, *args, **kwargs):
|
||||
self._load()
|
||||
return self._executor.run(name, *args, **kwargs)
|
||||
|
||||
def __getattr__(self, item):
|
||||
return FunctionRunner(item, self)
|
||||
|
||||
def _load(self):
|
||||
if not self._script_loaded:
|
||||
self._executor.load(
|
||||
self._prepare_files(),
|
||||
self._script_info.get("Options") or {})
|
||||
self._script_loaded = True
|
||||
|
||||
def _prepare_files(self):
|
||||
for file_id in self._script_info.get('Files', []):
|
||||
self._files_manager.put_file(file_id, self._name)
|
||||
|
||||
if self._script_info["Type"] == 'Application':
|
||||
return self._files_manager.put_file(
|
||||
self._script_info["EntryPoint"], self._name)
|
||||
return os.path.join(self._files_manager._cache_folder, self._name)
|
|
@ -1,31 +0,0 @@
|
|||
# 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 fixtures
|
||||
from oslo_config import cfg
|
||||
import testtools
|
||||
|
||||
from muranoagent.common import config # noqa
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class MuranoAgentTestCase(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(MuranoAgentTestCase, self).setUp()
|
||||
self.useFixture(fixtures.FakeLogger('murano-agent'))
|
||||
|
||||
def override_config(self, name, override, group=None):
|
||||
CONF.set_override(name, override, group)
|
||||
self.addCleanup(CONF.clear_override, name, group)
|
|
@ -1,304 +0,0 @@
|
|||
# Copyright (c) 2015 Telefonica I+D.
|
||||
|
||||
# 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 fixtures
|
||||
|
||||
from muranoagent import bunch
|
||||
|
||||
|
||||
class ExPlanDownloable(fixtures.Fixture):
|
||||
def setUp(self):
|
||||
super(ExPlanDownloable, self).setUp()
|
||||
self.execution_plan = bunch.Bunch(
|
||||
Action='Execute',
|
||||
Body='return deploy(args.appName).stdout\n',
|
||||
Files={
|
||||
'ID1': {
|
||||
'Name': 'tomcat.git',
|
||||
'Type': 'Downloadable',
|
||||
'URL': 'https://github.com/tomcat.git'
|
||||
},
|
||||
'ID2': {
|
||||
'Name': 'java',
|
||||
'Type': 'Downloadable',
|
||||
'URL': 'https://github.com/java.git'
|
||||
},
|
||||
|
||||
},
|
||||
FormatVersion='2.1.0',
|
||||
ID='ID',
|
||||
Name='Deploy Chef',
|
||||
Parameters={
|
||||
'appName': '$appName'
|
||||
},
|
||||
Scripts={
|
||||
'deploy': {
|
||||
'EntryPoint': 'cookbook::recipe',
|
||||
'Files': [
|
||||
'ID1',
|
||||
'ID2'
|
||||
],
|
||||
'Options': {
|
||||
'captureStderr': True,
|
||||
'captureStdout': True
|
||||
},
|
||||
'Type': 'Chef',
|
||||
'Version': '1.0.0'
|
||||
}
|
||||
},
|
||||
Version='1.0.0'
|
||||
)
|
||||
self.addCleanup(delattr, self, 'execution_plan')
|
||||
|
||||
|
||||
class ExPlanApplication(fixtures.Fixture):
|
||||
def setUp(self):
|
||||
super(ExPlanApplication, self).setUp()
|
||||
self.execution_plan = bunch.Bunch(
|
||||
Action='Execute',
|
||||
Body='return deploy(args.appName).stdout',
|
||||
Files={
|
||||
'ID1': {
|
||||
'Body': 'text',
|
||||
'BodyType': 'Text',
|
||||
'Name': 'deployTomcat.sh'
|
||||
},
|
||||
'ID2': {
|
||||
'Body': 'dGV4dA==\n',
|
||||
'BodyType': 'Base64',
|
||||
'Name': 'installer'
|
||||
},
|
||||
'ID3': {
|
||||
'Body': 'dGV4dA==\n',
|
||||
'BodyType': 'Base64',
|
||||
'Name': 'common.sh'
|
||||
}
|
||||
},
|
||||
FormatVersion='2.1.0',
|
||||
ID='ID',
|
||||
Name='Deploy Tomcat',
|
||||
Parameters={
|
||||
'appName': '$appName'
|
||||
},
|
||||
Scripts={
|
||||
'deploy': {
|
||||
'EntryPoint': 'ID1',
|
||||
'Files': [
|
||||
'ID2',
|
||||
'ID3'
|
||||
],
|
||||
'Options': {
|
||||
'captureStderr': True,
|
||||
'captureStdout': True
|
||||
},
|
||||
'Type': 'Application',
|
||||
'Version': '1.0.0'
|
||||
}
|
||||
},
|
||||
Version='1.0.0'
|
||||
)
|
||||
self.addCleanup(delattr, self, 'execution_plan')
|
||||
|
||||
|
||||
class ExPlanDownloableWrongFormat(fixtures.Fixture):
|
||||
def setUp(self):
|
||||
super(ExPlanDownloableWrongFormat, self).setUp()
|
||||
self.execution_plan = bunch.Bunch(
|
||||
ID='ID',
|
||||
FormatVersion='0.0.0'
|
||||
)
|
||||
self.addCleanup(delattr, self, 'execution_plan')
|
||||
|
||||
|
||||
class ExPlanDownloableNoFiles(fixtures.Fixture):
|
||||
def setUp(self):
|
||||
super(ExPlanDownloableNoFiles, self).setUp()
|
||||
self.execution_plan = bunch.Bunch(
|
||||
ID='ID',
|
||||
FormatVersion='2.1.0',
|
||||
Scripts={
|
||||
'deploy': {
|
||||
'EntryPoint': 'cookbook::recipe',
|
||||
'Files': [
|
||||
'https://github.com/tomcat.git',
|
||||
{'java': 'https://github.com/java.git'}
|
||||
],
|
||||
'Options': {
|
||||
'captureStderr': True,
|
||||
'captureStdout': True
|
||||
},
|
||||
'Type': 'Chef',
|
||||
'Version': '1.0.0'
|
||||
}
|
||||
}
|
||||
)
|
||||
self.addCleanup(delattr, self, 'execution_plan')
|
||||
|
||||
|
||||
class PuppetExPlanDownloable(fixtures.Fixture):
|
||||
def setUp(self):
|
||||
super(PuppetExPlanDownloable, self).setUp()
|
||||
self.execution_plan = bunch.Bunch(
|
||||
Action='Execute',
|
||||
Body='return deploy(args.appName).stdout\n',
|
||||
Files={
|
||||
'ID1': {
|
||||
'Name': 'tomcat.git',
|
||||
'Type': 'Downloadable',
|
||||
'URL': 'https://github.com/tomcat.git'
|
||||
},
|
||||
'ID2': {
|
||||
'Name': 'java',
|
||||
'Type': 'Downloadable',
|
||||
'URL': 'https://github.com/java.git'
|
||||
},
|
||||
|
||||
},
|
||||
FormatVersion='2.0.0',
|
||||
ID='ID',
|
||||
Name='Deploy Puppet',
|
||||
Parameters={
|
||||
'appName': '$appName'
|
||||
},
|
||||
Scripts={
|
||||
'deploy': {
|
||||
'EntryPoint': 'cookbook::recipe',
|
||||
'Files': [
|
||||
'ID1',
|
||||
'ID2'
|
||||
],
|
||||
'Options': {
|
||||
'captureStderr': True,
|
||||
'captureStdout': True
|
||||
},
|
||||
'Type': 'Puppet',
|
||||
'Version': '1.0.0'
|
||||
}
|
||||
},
|
||||
Version='1.0.0'
|
||||
)
|
||||
self.addCleanup(delattr, self, 'execution_plan')
|
||||
|
||||
|
||||
class ExPlanBerkshelf(fixtures.Fixture):
|
||||
def setUp(self):
|
||||
super(ExPlanBerkshelf, self).setUp()
|
||||
self.execution_plan = bunch.Bunch(
|
||||
Action='Execute',
|
||||
Body='return deploy(args.appName).stdout\n',
|
||||
Files={
|
||||
'ID1': {
|
||||
'Name': 'tomcat.git',
|
||||
'Type': 'Downloadable',
|
||||
'URL': 'https://github.com/tomcat.git'
|
||||
}
|
||||
},
|
||||
FormatVersion='2.2.0',
|
||||
ID='ID',
|
||||
Name='Deploy Chef',
|
||||
Parameters={},
|
||||
Scripts={
|
||||
'deploy': {
|
||||
'EntryPoint': 'cookbook::recipe',
|
||||
'Files': [
|
||||
'ID1'
|
||||
],
|
||||
'Options': {
|
||||
'captureStderr': True,
|
||||
'captureStdout': True,
|
||||
'useBerkshelf': True
|
||||
},
|
||||
'Type': 'Chef',
|
||||
'Version': '1.0.0'
|
||||
}
|
||||
},
|
||||
Version='1.0.0'
|
||||
)
|
||||
self.addCleanup(delattr, self, 'execution_plan')
|
||||
|
||||
|
||||
class ExPlanCustomBerskfile(fixtures.Fixture):
|
||||
def setUp(self):
|
||||
super(ExPlanCustomBerskfile, self).setUp()
|
||||
self.execution_plan = bunch.Bunch(
|
||||
Action='Execute',
|
||||
Body='return deploy(args.appName).stdout\n',
|
||||
Files={
|
||||
'ID1': {
|
||||
'Name': 'tomcat.git',
|
||||
'Type': 'Downloadable',
|
||||
'URL': 'https://github.com/tomcat.git'
|
||||
}
|
||||
},
|
||||
FormatVersion='2.2.0',
|
||||
ID='ID',
|
||||
Name='Deploy Chef',
|
||||
Parameters={},
|
||||
Scripts={
|
||||
'deploy': {
|
||||
'EntryPoint': 'cookbook::recipe',
|
||||
'Files': [
|
||||
'ID1'
|
||||
],
|
||||
'Options': {
|
||||
'captureStderr': True,
|
||||
'captureStdout': True,
|
||||
'useBerkshelf': True,
|
||||
'berksfilePath': 'custom/customFile'
|
||||
},
|
||||
'Type': 'Chef',
|
||||
'Version': '1.0.0'
|
||||
}
|
||||
},
|
||||
Version='1.0.0'
|
||||
)
|
||||
self.addCleanup(delattr, self, 'execution_plan')
|
||||
|
||||
|
||||
class ExPlanBerkWrongVersion(fixtures.Fixture):
|
||||
def setUp(self):
|
||||
super(ExPlanBerkWrongVersion, self).setUp()
|
||||
self.execution_plan = bunch.Bunch(
|
||||
Action='Execute',
|
||||
Body='return deploy(args.appName).stdout\n',
|
||||
Files={
|
||||
'ID1': {
|
||||
'Name': 'tomcat.git',
|
||||
'Type': 'Downloadable',
|
||||
'URL': 'https://github.com/tomcat.git'
|
||||
}
|
||||
},
|
||||
FormatVersion='2.1.0',
|
||||
ID='ID',
|
||||
Name='Deploy Chef',
|
||||
Parameters={},
|
||||
Scripts={
|
||||
'deploy': {
|
||||
'EntryPoint': 'cookbook::recipe',
|
||||
'Files': [
|
||||
'ID1'
|
||||
],
|
||||
'Options': {
|
||||
'captureStderr': True,
|
||||
'captureStdout': True,
|
||||
'useBerkshelf': True
|
||||
},
|
||||
'Type': 'Chef',
|
||||
'Version': '1.0.0'
|
||||
}
|
||||
},
|
||||
Version='1.0.0'
|
||||
)
|
||||
self.addCleanup(delattr, self, 'execution_plan')
|
|
@ -1,259 +0,0 @@
|
|||
# Copyright (c) 2015 Telefonica I+D
|
||||
#
|
||||
# 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 fixtures
|
||||
import json
|
||||
import os
|
||||
from unittest import mock
|
||||
from unittest.mock import ANY
|
||||
|
||||
from muranoagent import bunch
|
||||
from muranoagent import exceptions as ex
|
||||
from muranoagent.executors import chef
|
||||
from muranoagent.tests.unit import base
|
||||
from muranoagent.tests.unit import execution_plan as ep
|
||||
|
||||
|
||||
class TestChefExecutor(base.MuranoAgentTestCase, fixtures.TestWithFixtures):
|
||||
def setUp(self):
|
||||
super(TestChefExecutor, self).setUp()
|
||||
self.chef_executor = chef.ChefExecutor('cookbook::recipe')
|
||||
|
||||
def test_create_nodejson_noatts(self):
|
||||
"""It tests the manifest without attributes."""
|
||||
node = self.chef_executor._create_manifest('cookbook', 'recipe', None)
|
||||
self.assertEqual(json.loads(node), self.get_node_no_atts())
|
||||
|
||||
def test_create_nodejson(self):
|
||||
"""It tests a manifest with attributes."""
|
||||
atts = {
|
||||
'att1': 'value1',
|
||||
'att2': 'value2'
|
||||
}
|
||||
|
||||
node = self.chef_executor._create_manifest('cookbook', 'recipe', atts)
|
||||
self.assertEqual(json.loads(node), self.get_node_atts())
|
||||
|
||||
@mock.patch('subprocess.Popen')
|
||||
@mock.patch('builtins.open')
|
||||
@mock.patch('os.path.exists')
|
||||
@mock.patch('os.path.isdir')
|
||||
def test_cookbook(self, mock_isdir, mock_exist, open_mock,
|
||||
mock_subproc_popen):
|
||||
"""It tests chef executor."""
|
||||
self._open_mock(open_mock)
|
||||
mock_exist.return_value = True
|
||||
mock_isdir.return_value = True
|
||||
|
||||
process_mock = mock.Mock()
|
||||
attrs = {'communicate.return_value': ('output', 'ok'),
|
||||
'poll.return_value': 0}
|
||||
process_mock.configure_mock(**attrs)
|
||||
mock_subproc_popen.return_value = process_mock
|
||||
|
||||
template = self.useFixture(ep.ExPlanDownloable()).execution_plan
|
||||
script = list(template['Scripts'].values())
|
||||
self.chef_executor.load('path',
|
||||
script[0]['Options'])
|
||||
self.chef_executor.run('test')
|
||||
|
||||
@mock.patch('subprocess.Popen')
|
||||
@mock.patch('builtins.open')
|
||||
@mock.patch('os.path.exists')
|
||||
@mock.patch('os.path.isdir')
|
||||
def test_cookbook_error(self, mock_isdir, mock_exist, open_mock,
|
||||
mock_subproc_popen):
|
||||
"""It tests chef executor with error in the request."""
|
||||
self._open_mock(open_mock)
|
||||
mock_exist.return_value = True
|
||||
mock_isdir.return_value = True
|
||||
|
||||
process_mock = mock.Mock()
|
||||
attrs = {'communicate.return_value': ('output', 'error'),
|
||||
'poll.return_value': 2}
|
||||
process_mock.configure_mock(**attrs)
|
||||
mock_subproc_popen.return_value = process_mock
|
||||
|
||||
template = self.useFixture(ep.ExPlanDownloable()).execution_plan
|
||||
script = list(template['Scripts'].values())[0]
|
||||
self.chef_executor.load('path',
|
||||
script['Options'])
|
||||
self.assertRaises(ex.CustomException, self.chef_executor.run,
|
||||
'test')
|
||||
|
||||
def test_chef_cookbook_wrong(self):
|
||||
"""It tests a wrong cookbook name."""
|
||||
chef_executor = chef.ChefExecutor('wrong')
|
||||
self.assertRaises(ex.CustomException, chef_executor.run,
|
||||
'test')
|
||||
|
||||
def test_chef_no_berkshelf(self):
|
||||
"""It tests the cookbook path if Berkshelf is not enabled"""
|
||||
template = self.useFixture(ep.ExPlanDownloable()).execution_plan
|
||||
script = list(template['Scripts'].values())[0]
|
||||
self.chef_executor.load('path',
|
||||
script['Options'])
|
||||
cookbook_path = self.chef_executor._create_cookbook_path('cookbook')
|
||||
self.assertEqual(cookbook_path, os.path.abspath('path'))
|
||||
|
||||
@mock.patch('subprocess.Popen')
|
||||
@mock.patch('os.path.isfile')
|
||||
def test_chef_berkshelf_default_berksfile(self, mock_isfile,
|
||||
mock_subproc_popen):
|
||||
"""It tests Berkshelf usage if no Berksfile path is provided"""
|
||||
mock_isfile.return_value = True
|
||||
|
||||
process_mock = mock.Mock()
|
||||
attrs = {'communicate.return_value': ('output', 'ok'),
|
||||
'poll.return_value': 0}
|
||||
process_mock.configure_mock(**attrs)
|
||||
mock_subproc_popen.return_value = process_mock
|
||||
|
||||
template = self.useFixture(ep.ExPlanBerkshelf()).execution_plan
|
||||
script = list(template['Scripts'].values())[0]
|
||||
self.chef_executor.load('path',
|
||||
script['Options'])
|
||||
self.chef_executor.module_name = 'test'
|
||||
cookbook_path = self.chef_executor._create_cookbook_path('cookbook')
|
||||
|
||||
self.assertEqual(cookbook_path,
|
||||
os.path.abspath('path/berks-cookbooks'))
|
||||
expected_command = 'berks vendor --berksfile={0} {1}'.format(
|
||||
os.path.abspath('path/cookbook/Berksfile'),
|
||||
cookbook_path)
|
||||
mock_subproc_popen.assert_called_once_with(expected_command,
|
||||
cwd=ANY,
|
||||
shell=ANY,
|
||||
stdout=ANY,
|
||||
stderr=ANY,
|
||||
universal_newlines=ANY)
|
||||
|
||||
@mock.patch('subprocess.Popen')
|
||||
@mock.patch('os.path.isfile')
|
||||
def test_chef_berkshelf_custom_berksfile(self, mock_isfile,
|
||||
mock_subproc_popen):
|
||||
"""It tests Berkshelf usage if a custom Berksfile is provided"""
|
||||
mock_isfile.return_value = True
|
||||
|
||||
process_mock = mock.Mock()
|
||||
attrs = {'communicate.return_value': ('output', 'ok'),
|
||||
'poll.return_value': 0}
|
||||
process_mock.configure_mock(**attrs)
|
||||
mock_subproc_popen.return_value = process_mock
|
||||
|
||||
template = self.useFixture(ep.ExPlanCustomBerskfile()).execution_plan
|
||||
script = list(template['Scripts'].values())[0]
|
||||
self.chef_executor.load('path',
|
||||
script['Options'])
|
||||
self.chef_executor.module_name = 'test'
|
||||
cookbook_path = self.chef_executor._create_cookbook_path('cookbook')
|
||||
|
||||
self.assertEqual(cookbook_path,
|
||||
os.path.abspath('path/berks-cookbooks'))
|
||||
expected_command = 'berks vendor --berksfile={0} {1}'.format(
|
||||
os.path.abspath('path/custom/customFile'),
|
||||
cookbook_path)
|
||||
mock_subproc_popen.assert_called_once_with(expected_command,
|
||||
cwd=ANY,
|
||||
shell=ANY,
|
||||
stdout=ANY,
|
||||
stderr=ANY,
|
||||
universal_newlines=ANY)
|
||||
|
||||
@mock.patch('subprocess.Popen')
|
||||
@mock.patch('os.path.isfile')
|
||||
def test_chef_berkshelf_error(self, mock_isfile,
|
||||
mock_subproc_popen):
|
||||
"""It tests if Berkshelf throws an error"""
|
||||
mock_isfile.return_value = True
|
||||
|
||||
process_mock = mock.Mock()
|
||||
attrs = {'communicate.return_value': ('output', 'error'),
|
||||
'poll.return_value': 2}
|
||||
process_mock.configure_mock(**attrs)
|
||||
mock_subproc_popen.return_value = process_mock
|
||||
|
||||
template = self.useFixture(ep.ExPlanBerkshelf()).execution_plan
|
||||
script = list(template['Scripts'].values())[0]
|
||||
self.chef_executor.load('path',
|
||||
script['Options'])
|
||||
self.chef_executor.module_name = 'test'
|
||||
self.assertRaises(ex.CustomException,
|
||||
self.chef_executor._create_cookbook_path,
|
||||
'cookbook')
|
||||
|
||||
def _open_mock(self, open_mock):
|
||||
context_manager_mock = mock.Mock()
|
||||
open_mock.return_value = context_manager_mock
|
||||
file_mock = mock.Mock()
|
||||
file_mock.read.return_value = ''
|
||||
enter_mock = mock.Mock()
|
||||
enter_mock.return_value = file_mock
|
||||
exit_mock = mock.Mock()
|
||||
setattr(context_manager_mock, '__enter__', enter_mock)
|
||||
setattr(context_manager_mock, '__exit__', exit_mock)
|
||||
|
||||
def _stub_uuid(self, values=None):
|
||||
class FakeUUID(object):
|
||||
def __init__(self, v):
|
||||
self.hex = v
|
||||
|
||||
if values is None:
|
||||
values = []
|
||||
mock_uuid4 = mock.patch('uuid.uuid4').start()
|
||||
mock_uuid4.side_effect = [FakeUUID(v) for v in values]
|
||||
return mock_uuid4
|
||||
|
||||
def get_template_downloable(self):
|
||||
return bunch.Bunch(
|
||||
ID='ID',
|
||||
Files={
|
||||
'file': {
|
||||
'Name': 'myfile',
|
||||
'URL': 'https://github.com'
|
||||
'/apache/tomcat/blob/trunk/LICENSE',
|
||||
'Type': 'Downloadable'
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
def get_template_file(self):
|
||||
return bunch.Bunch(
|
||||
ID='ID',
|
||||
Files={
|
||||
'test': {
|
||||
'Body': 'dGV4dA==\n',
|
||||
'BodyType': 'Base64',
|
||||
'Name': 'installer'
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
def get_node_atts(self):
|
||||
return {
|
||||
"run_list": [
|
||||
"recipe[cookbook::recipe]"
|
||||
],
|
||||
"cookbook": {
|
||||
"att1": "value1",
|
||||
"att2": "value2"
|
||||
}
|
||||
}
|
||||
|
||||
def get_node_no_atts(self):
|
||||
return {
|
||||
"run_list": [
|
||||
"recipe[cookbook::recipe]"
|
||||
]
|
||||
}
|
|
@ -1,140 +0,0 @@
|
|||
# Copyright (c) 2015 Telefonica I+D
|
||||
#
|
||||
# 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 unittest import mock
|
||||
|
||||
import fixtures
|
||||
|
||||
from muranoagent import bunch
|
||||
from muranoagent import exceptions as ex
|
||||
from muranoagent.executors import puppet
|
||||
from muranoagent.tests.unit import base
|
||||
from muranoagent.tests.unit import execution_plan as ep
|
||||
|
||||
|
||||
class TestPuppetExecutor(base.MuranoAgentTestCase, fixtures.TestWithFixtures):
|
||||
def setUp(self):
|
||||
super(TestPuppetExecutor, self).setUp()
|
||||
self.puppet_executor = puppet.PuppetExecutor('module::recipe')
|
||||
|
||||
def test_create_manifest(self):
|
||||
node = self.puppet_executor._create_manifest('cookbook', 'recipe')
|
||||
self.assertEqual(node, self.get_manifest('cookbook', 'recipe'))
|
||||
|
||||
def test_create_manifest_norecipe(self):
|
||||
node = self.puppet_executor._create_manifest('cookbook', '')
|
||||
self.assertEqual(node, self.get_manifest_norecipe('cookbook'))
|
||||
|
||||
def test_create_hierdata(self):
|
||||
atts = {
|
||||
'att1': 'value1',
|
||||
'att2': 'value2'
|
||||
}
|
||||
|
||||
node = self.puppet_executor._create_hiera_data('cookbook', atts)
|
||||
self.assertEqual(node, self.get_hieradata())
|
||||
|
||||
@mock.patch('builtins.open')
|
||||
def test_generate_files(self, open_mock):
|
||||
self._open_mock(open_mock)
|
||||
atts = {
|
||||
'att1': 'value1',
|
||||
'att2': 'value2'
|
||||
}
|
||||
|
||||
self.puppet_executor._generate_files('cookbook', 'recipe', atts)
|
||||
|
||||
@mock.patch('builtins.open')
|
||||
def test_configure_puppet(self, open_mock):
|
||||
self._open_mock(open_mock)
|
||||
self.puppet_executor._configure_puppet()
|
||||
|
||||
@mock.patch('subprocess.Popen')
|
||||
@mock.patch('builtins.open')
|
||||
def test_module(self, open_mock, mock_subproc_popen):
|
||||
#
|
||||
# setup
|
||||
#
|
||||
self._open_mock(open_mock)
|
||||
|
||||
process_mock = mock.Mock()
|
||||
attrs = {'communicate.return_value': ('ouput', 'ok'),
|
||||
'poll.return_value': 0}
|
||||
process_mock.configure_mock(**attrs)
|
||||
mock_subproc_popen.return_value = process_mock
|
||||
|
||||
template = self.useFixture(ep.PuppetExPlanDownloable()).execution_plan
|
||||
script = list(template['Scripts'].values())[0]
|
||||
self.puppet_executor.load('path',
|
||||
script['Options'])
|
||||
self.puppet_executor.run('test')
|
||||
|
||||
@mock.patch('subprocess.Popen')
|
||||
@mock.patch('builtins.open')
|
||||
def test_module_error(self, open_mock, mock_subproc_popen):
|
||||
#
|
||||
# setup
|
||||
#
|
||||
self._open_mock(open_mock)
|
||||
|
||||
process_mock = mock.Mock()
|
||||
attrs = {'communicate.return_value': ('ouput', 'error'),
|
||||
'poll.return_value': 2}
|
||||
process_mock.configure_mock(**attrs)
|
||||
mock_subproc_popen.return_value = process_mock
|
||||
|
||||
template = self.useFixture(ep.PuppetExPlanDownloable()).execution_plan
|
||||
script = list(template['Scripts'].values())[0]
|
||||
self.puppet_executor.load('path',
|
||||
script['Options'])
|
||||
self.assertRaises(ex.CustomException, self.puppet_executor.run,
|
||||
'test')
|
||||
|
||||
def test_puppet_module_wrong(self):
|
||||
puppet_executor = puppet.PuppetExecutor('wrong')
|
||||
self.assertRaises(ex.CustomException, puppet_executor.run,
|
||||
'test')
|
||||
|
||||
def _stub_uuid(self, values=None):
|
||||
class FakeUUID(object):
|
||||
def __init__(self, v):
|
||||
self.hex = v
|
||||
|
||||
if values is None:
|
||||
values = []
|
||||
mock_uuid4 = mock.patch('uuid.uuid4').start()
|
||||
mock_uuid4.side_effect = [FakeUUID(v) for v in values]
|
||||
return mock_uuid4
|
||||
|
||||
def _open_mock(self, open_mock):
|
||||
context_manager_mock = mock.Mock()
|
||||
open_mock.return_value = context_manager_mock
|
||||
file_mock = mock.Mock()
|
||||
file_mock.read.return_value = ''
|
||||
enter_mock = mock.Mock()
|
||||
enter_mock.return_value = file_mock
|
||||
exit_mock = mock.Mock()
|
||||
setattr(context_manager_mock, '__enter__', enter_mock)
|
||||
setattr(context_manager_mock, '__exit__', exit_mock)
|
||||
|
||||
def get_hieradata(self):
|
||||
return {'cookbook::att1': 'value1', 'cookbook::att2': 'value2'}
|
||||
|
||||
def get_manifest(self, cookbook, recipe):
|
||||
return "node \'default\' { " \
|
||||
"class { " + cookbook + '::' + recipe + ':}}'
|
||||
|
||||
def get_manifest_norecipe(self, cookbook):
|
||||
return "node \'default\' { " \
|
||||
"class { " + cookbook + ':}}'
|
|
@ -1,18 +0,0 @@
|
|||
# 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 testtools
|
||||
|
||||
|
||||
class MuranoAgentTest(testtools.TestCase):
|
||||
def test_nothing(self):
|
||||
pass
|
|
@ -1,152 +0,0 @@
|
|||
# Copyright (c) 2015 Telefonica I+D
|
||||
#
|
||||
# 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 unittest import mock
|
||||
|
||||
import fixtures
|
||||
import ssl as ssl_module
|
||||
|
||||
from muranoagent import app
|
||||
from muranoagent import bunch
|
||||
from muranoagent.common import config as cfg
|
||||
from muranoagent.common.messaging import mqclient
|
||||
from muranoagent import exceptions as exc
|
||||
from muranoagent.tests.unit import base
|
||||
from muranoagent.tests.unit import execution_plan as ep
|
||||
from muranoagent import validation
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TestApp(base.MuranoAgentTestCase, fixtures.FunctionFixture):
|
||||
|
||||
@mock.patch('os.chmod')
|
||||
@mock.patch('os.path.exists')
|
||||
def setUp(self, mock_path, mock_chmod):
|
||||
super(TestApp, self).setUp()
|
||||
mock_path.side_effect = self._exists
|
||||
self.agent = app.MuranoAgent()
|
||||
CONF.set_override('storage', 'cache')
|
||||
self.addCleanup(CONF.clear_override, 'storage')
|
||||
|
||||
@staticmethod
|
||||
def _exists(path):
|
||||
return 'stamp' not in path
|
||||
|
||||
def test_verify_execution_plan_downloable(self):
|
||||
template = self.useFixture(ep.ExPlanDownloable()).execution_plan
|
||||
self.agent._verify_plan(template)
|
||||
|
||||
def test_verify_execution_plan_wrong_format(self):
|
||||
template = bunch.Bunch(
|
||||
ID='ID',
|
||||
FormatVersion='0.0.0',
|
||||
)
|
||||
self.assertRaises(exc.IncorrectFormat,
|
||||
validation.validate_plan, template)
|
||||
|
||||
def test_verify_over_max_execution_plan(self):
|
||||
template = self.useFixture(ep.ExPlanApplication()).execution_plan
|
||||
template['FormatVersion'] = '1000.0.0'
|
||||
self.assertRaises(exc.IncorrectFormat,
|
||||
validation.validate_plan, template)
|
||||
|
||||
def test_verify_execution_application(self):
|
||||
template = self.useFixture(ep.ExPlanApplication()).execution_plan
|
||||
self.agent._verify_plan(template)
|
||||
|
||||
def test_verify_wrong_execution_application(self):
|
||||
template = self.useFixture(ep.ExPlanApplication()).execution_plan
|
||||
template['Files']['ID1'] = {
|
||||
'Name': 'tomcat.git',
|
||||
'Type': 'Downloadable',
|
||||
'URL': 'https://github.com/tomcat.git'
|
||||
}
|
||||
template['FormatVersion'] = '2.0.0'
|
||||
self.assertRaises(exc.IncorrectFormat,
|
||||
validation.validate_plan, template)
|
||||
|
||||
def test_verify_execution_plan_no_files(self):
|
||||
template = self.useFixture(ep.ExPlanDownloableNoFiles()).execution_plan
|
||||
self.assertRaises(exc.IncorrectFormat,
|
||||
validation.validate_plan, template)
|
||||
|
||||
def test_verify_execution_plan_berkshelf(self):
|
||||
template = self.useFixture(ep.ExPlanBerkshelf()).execution_plan
|
||||
self.agent._verify_plan(template)
|
||||
|
||||
def test_verify_execution_plan_berkshelf_wrong_version(self):
|
||||
template = self.useFixture(ep.ExPlanBerkWrongVersion()).execution_plan
|
||||
self.assertRaises(exc.IncorrectFormat,
|
||||
validation.validate_plan, template)
|
||||
|
||||
@mock.patch.object(mqclient, 'random', autospec=True)
|
||||
@mock.patch.object(mqclient, 'kombu', autospec=True)
|
||||
def test_rmq_client_initialization_with_ssl_version(self, mock_kombu,
|
||||
mock_random):
|
||||
expected_heartbeat = 20 # 20 = 20 + 20 * 0, due to mocked value below.
|
||||
mock_random.random.return_value = 0
|
||||
|
||||
ssl_versions = (
|
||||
('tlsv1', getattr(ssl_module, 'PROTOCOL_TLSv1', None)),
|
||||
('tlsv1_1', getattr(ssl_module, 'PROTOCOL_TLSv1_1', None)),
|
||||
('tlsv1_2', getattr(ssl_module, 'PROTOCOL_TLSv1_2', None)),
|
||||
('sslv2', getattr(ssl_module, 'PROTOCOL_SSLv2', None)),
|
||||
('sslv23', getattr(ssl_module, 'PROTOCOL_SSLv23', None)),
|
||||
('sslv3', getattr(ssl_module, 'PROTOCOL_SSLv3', None)))
|
||||
exception_count = 0
|
||||
|
||||
for ssl_name, ssl_version in ssl_versions:
|
||||
ssl_kwargs = {
|
||||
'login': 'test_login',
|
||||
'password': 'test_password',
|
||||
'host': 'test_host',
|
||||
'port': 'test_port',
|
||||
'virtual_host': 'test_virtual_host',
|
||||
'ssl': True,
|
||||
'ssl_version': ssl_name,
|
||||
'ca_certs': ['cert1'],
|
||||
'insecure': False
|
||||
}
|
||||
|
||||
# If a ssl_version is not valid, a RuntimeError is thrown.
|
||||
# According to the ssl_version docs in config.py, certain versions
|
||||
# of TLS may be available depending on the system. So, just
|
||||
# check that at least 1 ssl_version works.
|
||||
if ssl_version is None:
|
||||
e = self.assertRaises(RuntimeError, mqclient.MqClient,
|
||||
**ssl_kwargs)
|
||||
self.assertEqual('Invalid SSL version: %s' % ssl_name,
|
||||
e.__str__())
|
||||
exception_count += 1
|
||||
continue
|
||||
|
||||
self.ssl_client = mqclient.MqClient(**ssl_kwargs)
|
||||
|
||||
mock_kombu.Connection.assert_called_once_with(
|
||||
'amqp://{0}:{1}@{2}:{3}/{4}'.format(
|
||||
'test_login', 'test_password', 'test_host', 'test_port',
|
||||
'test_virtual_host'),
|
||||
heartbeat=expected_heartbeat,
|
||||
ssl={'ca_certs': ['cert1'],
|
||||
'cert_reqs': ssl_module.CERT_REQUIRED,
|
||||
'ssl_version': ssl_version})
|
||||
self.assertEqual(
|
||||
mock_kombu.Connection(), self.ssl_client._connection)
|
||||
self.assertIsNone(self.ssl_client._channel)
|
||||
self.assertFalse(self.ssl_client._connected)
|
||||
mock_kombu.Connection.reset_mock()
|
||||
|
||||
# Check that at least one ssl_version worked.
|
||||
self.assertGreater(len(ssl_versions), exception_count)
|
|
@ -1,69 +0,0 @@
|
|||
# Copyright (c) 2015 Telefonica I+D
|
||||
#
|
||||
# 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 unittest import mock
|
||||
|
||||
import builtins
|
||||
import fixtures
|
||||
|
||||
from muranoagent import execution_plan_queue
|
||||
|
||||
from muranoagent import app
|
||||
from muranoagent import bunch
|
||||
from muranoagent.common import config as cfg
|
||||
from muranoagent.common.messaging import mqclient
|
||||
from muranoagent import exceptions as exc
|
||||
from muranoagent.tests.unit import base
|
||||
from muranoagent.tests.unit import execution_plan as ep
|
||||
from muranoagent import validation
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TestExecutionPlanQueue(base.MuranoAgentTestCase,
|
||||
fixtures.FunctionFixture):
|
||||
|
||||
@mock.patch('os.chmod')
|
||||
@mock.patch('os.path.exists')
|
||||
def setUp(self, mock_path, mock_chmod):
|
||||
super(TestExecutionPlanQueue, self).setUp()
|
||||
mock_path.side_effect = self._exists
|
||||
self.epq = execution_plan_queue.ExecutionPlanQueue()
|
||||
CONF.set_override('storage', 'cache')
|
||||
self.addCleanup(CONF.clear_override, 'storage')
|
||||
|
||||
@staticmethod
|
||||
def _exists(path):
|
||||
return 'stamp' not in path
|
||||
|
||||
@mock.patch('os.path.lexists')
|
||||
@mock.patch('os.path.isdir')
|
||||
@mock.patch('os.mkdir')
|
||||
def test_put_execution_plan(self, mock_makedir, mock_path,
|
||||
mock_exists):
|
||||
mock_path.return_value = True
|
||||
mock_makedir.return_value = None
|
||||
mock_exists.return_value = True
|
||||
mock_write = mock.mock_open()
|
||||
|
||||
execution_plan = 'myplan'
|
||||
signature = None
|
||||
msg_id = 1
|
||||
reply_to = 'test'
|
||||
expected_content = ('{"Data": "bXlwbGFu", "Signature": "", '
|
||||
'"ID": 1, "ReplyTo": "test"}')
|
||||
with mock.patch.object(builtins, 'open', mock_write) as mocked_file:
|
||||
self.epq.put_execution_plan(execution_plan, signature,
|
||||
msg_id, reply_to)
|
||||
mocked_file().write.assert_called_once_with(expected_content)
|
|
@ -1,221 +0,0 @@
|
|||
# Copyright (c) 2015 Telefonica I+D
|
||||
#
|
||||
# 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 os.path
|
||||
from unittest import mock
|
||||
|
||||
from muranoagent import bunch
|
||||
from muranoagent.common import config as cfg
|
||||
from muranoagent import files_manager
|
||||
from muranoagent.tests.unit import base
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TestFileManager(base.MuranoAgentTestCase):
|
||||
|
||||
@mock.patch('os.path.isdir')
|
||||
@mock.patch('os.mkdir')
|
||||
@mock.patch('os.makedirs')
|
||||
def setUp(self, mock_makedir, mock_mkdir, mock_path):
|
||||
mock_path.return_value = True
|
||||
mock_mkdir.return_value = None
|
||||
mock_makedir.return_value = None
|
||||
super(TestFileManager, self).setUp()
|
||||
CONF.set_override('storage', 'cache')
|
||||
|
||||
def test_is_svn(self):
|
||||
files = files_manager.FilesManager(self.get_template_downloable(1))
|
||||
self.assertTrue(files._is_svn_repository("https://sdfa/svn/ss"))
|
||||
|
||||
def test_is_svn_first(self):
|
||||
files = files_manager.FilesManager(self.get_template_downloable(2))
|
||||
self.assertTrue(files._is_svn_repository("svn://test"))
|
||||
|
||||
def test_is_svn_wrong_http_protocol(self):
|
||||
files = files_manager.FilesManager(self.get_template_downloable(3))
|
||||
self.assertFalse(files._is_svn_repository("httpp://sdfa/svn/ss"))
|
||||
|
||||
def test_is_svn_wrong_svn_slash(self):
|
||||
files = files_manager.FilesManager(self.get_template_downloable(4))
|
||||
self.assertFalse(files._is_svn_repository("svn:sdfa/svn/ss"))
|
||||
|
||||
@mock.patch("git.Git")
|
||||
@mock.patch('os.path.isdir')
|
||||
@mock.patch('os.makedirs')
|
||||
def test_execution_plan_type_downloable_git(self, mock_makedir, mock_path,
|
||||
mock_git):
|
||||
"""Test an execution plan with downloadable git files
|
||||
|
||||
"""
|
||||
mock_makedir.return_value = None
|
||||
mock_path.return_value = True
|
||||
mock_git.clone.return_value = None
|
||||
template = self.get_template_downloable_git()
|
||||
files = files_manager.FilesManager(self.get_template_downloable(5))
|
||||
files._download_url_file(template.Files['mycoockbook'], "script")
|
||||
|
||||
@mock.patch('os.path.isdir')
|
||||
@mock.patch('os.mkdir')
|
||||
@mock.patch('os.makedirs')
|
||||
@mock.patch('builtins.open')
|
||||
@mock.patch('requests.get')
|
||||
def test_execution_plan_type_downloable(self, mock_requests, open_mock,
|
||||
mock_makedir,
|
||||
mock_mkdir, mock_path):
|
||||
"""Test an execution plan with downloadable files
|
||||
|
||||
"""
|
||||
mock_path.return_value = True
|
||||
mock_mkdir.return_value = None
|
||||
mock_makedir.return_value = None
|
||||
mock_requests.return_value = None
|
||||
self._open_mock(open_mock)
|
||||
|
||||
template = self.get_template_downloable(6)
|
||||
files = files_manager.FilesManager(self.get_template_downloable(6))
|
||||
files._download_url_file(template.Files['file'], "script")
|
||||
|
||||
@mock.patch('subprocess.Popen')
|
||||
@mock.patch('os.makedirs')
|
||||
def test_execution_plan_type_svn(self, mock_makedir, mock_subproc_popen):
|
||||
"""Test an execution plan with svn files."""
|
||||
process_mock = mock.Mock()
|
||||
attrs = {'communicate.return_value': ('ouput', 'ok'),
|
||||
'poll.return_value': 0}
|
||||
process_mock.configure_mock(**attrs)
|
||||
mock_subproc_popen.return_value = process_mock
|
||||
|
||||
template = self.get_template_svn()
|
||||
files = files_manager.FilesManager(template)
|
||||
files._download_url_file(template.Files['file'], "script")
|
||||
|
||||
@mock.patch('os.makedirs')
|
||||
def test_execution_plan_type_downloable_no_Url(self, mock_makedir):
|
||||
"""It validates the URL."""
|
||||
mock_makedir.return_value = None
|
||||
template = bunch.Bunch(
|
||||
ID='ID',
|
||||
Files={
|
||||
'mycoockbook': {
|
||||
'Name': 'mycoockbook.txt',
|
||||
'Type': 'Downloadable'
|
||||
}
|
||||
}
|
||||
)
|
||||
files = files_manager.FilesManager(template)
|
||||
self.assertRaises(ValueError, files._download_url_file,
|
||||
template.Files['mycoockbook'], "script")
|
||||
|
||||
@mock.patch("git.Git")
|
||||
@mock.patch('os.path.isdir')
|
||||
@mock.patch('os.makedirs')
|
||||
@mock.patch('os.path.lexists')
|
||||
def test_putfile_downloable(self, mock_exists, mock_makedir,
|
||||
path, mock_git):
|
||||
"""It tests the putfile method when the file is a git URL.
|
||||
|
||||
"""
|
||||
path.return_value = True
|
||||
mock_git.clone.return_value = None
|
||||
mock_makedir.return_value = None
|
||||
mock_exists.return_value = True
|
||||
template = self.get_template_downloable_git()
|
||||
files = files_manager.FilesManager(template)
|
||||
for file in template.get('Files'):
|
||||
files.put_file(file, 'deploy')
|
||||
|
||||
@mock.patch('builtins.open')
|
||||
@mock.patch('os.path.lexists')
|
||||
@mock.patch('os.path.isdir')
|
||||
@mock.patch('os.makedirs')
|
||||
def test_putfile_file(self, mock_makedir, mock_path,
|
||||
mock_exists, open_mock):
|
||||
"""It tests the putfile method."""
|
||||
mock_path.return_value = True
|
||||
mock_makedir.return_value = None
|
||||
mock_exists.return_value = True
|
||||
context_manager_mock = mock.Mock()
|
||||
open_mock.return_value = context_manager_mock
|
||||
file_mock = mock.Mock()
|
||||
file_mock.read.return_value = ''
|
||||
enter_mock = mock.Mock()
|
||||
enter_mock.return_value = file_mock
|
||||
exit_mock = mock.Mock()
|
||||
setattr(context_manager_mock, '__enter__', enter_mock)
|
||||
setattr(context_manager_mock, '__exit__', exit_mock)
|
||||
|
||||
template = self.get_template_file()
|
||||
files = files_manager.FilesManager(template)
|
||||
for file in template.get('Files'):
|
||||
files.put_file(file, 'deploy')
|
||||
|
||||
def get_template_downloable_git(self):
|
||||
return bunch.Bunch(
|
||||
ID='ID',
|
||||
Files={
|
||||
'mycoockbook': {
|
||||
'Name': 'mycoockbook.txt',
|
||||
'URL': 'git://github.com/tomcat.git',
|
||||
'Type': 'Downloadable'
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
def get_template_downloable(self, file_id):
|
||||
return bunch.Bunch(
|
||||
ID='ID',
|
||||
Files={
|
||||
'file': {
|
||||
'Name': 'myfile',
|
||||
'URL': 'https://www.apache.org/licenses',
|
||||
'Type': 'Downloadable'
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
def get_template_svn(self):
|
||||
return bunch.Bunch(
|
||||
ID='ID',
|
||||
Files={
|
||||
'file': {
|
||||
'Name': 'svn',
|
||||
'URL': 'https://mysvn/svn/repo',
|
||||
'Type': 'Downloadable'
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
def get_template_file(self):
|
||||
return bunch.Bunch(
|
||||
ID='ID',
|
||||
Files={
|
||||
'test': {
|
||||
'Body': 'dGV4dA==\n',
|
||||
'BodyType': 'Base64',
|
||||
'Name': 'installer'
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
def _open_mock(self, open_mock):
|
||||
context_manager_mock = mock.Mock()
|
||||
open_mock.return_value = context_manager_mock
|
||||
file_mock = mock.Mock()
|
||||
file_mock.read.return_value = ''
|
||||
enter_mock = mock.Mock()
|
||||
enter_mock.return_value = file_mock
|
||||
exit_mock = mock.Mock()
|
||||
setattr(context_manager_mock, '__enter__', enter_mock)
|
||||
setattr(context_manager_mock, '__exit__', exit_mock)
|
|
@ -1,108 +0,0 @@
|
|||
# Copyright (c) 2015 Telefonica I+D
|
||||
#
|
||||
# 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 git
|
||||
from unittest import mock
|
||||
|
||||
from muranoagent import bunch
|
||||
from muranoagent.common import config as cfg
|
||||
from muranoagent import files_manager as fmanager
|
||||
from muranoagent import script_runner
|
||||
from muranoagent.tests.unit import base
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TestScriptRunner(base.MuranoAgentTestCase):
|
||||
def setUp(self):
|
||||
super(TestScriptRunner, self).setUp()
|
||||
CONF.set_override('storage', 'ss')
|
||||
|
||||
@mock.patch('os.path.join')
|
||||
@mock.patch("muranoagent.files_manager.FilesManager")
|
||||
@mock.patch("muranoagent.executors.Executors")
|
||||
def test_script_runner_downloable(self, mock_file_manager, mock_executors,
|
||||
mock_os):
|
||||
mock_file_manager.put_file.return_value = None
|
||||
mock_executors.create_executor.return_value = None
|
||||
mock_os.return_value = '/tmp/1234'
|
||||
template = self.get_template_downloable_git()
|
||||
scripts = script_runner\
|
||||
.ScriptRunner('deploy',
|
||||
template.get('Scripts')['deploy'],
|
||||
mock_file_manager)
|
||||
scripts._prepare_files()
|
||||
|
||||
def _stub_uuid(self, values=None):
|
||||
class FakeUUID(object):
|
||||
def __init__(self, v):
|
||||
self.hex = v
|
||||
|
||||
if values is None:
|
||||
values = []
|
||||
mock_uuid4 = mock.patch('uuid.uuid4').start()
|
||||
mock_uuid4.side_effect = [FakeUUID(v) for v in values]
|
||||
return mock_uuid4
|
||||
|
||||
def get_template_downloable_git(self):
|
||||
return bunch.Bunch(
|
||||
ID='ID',
|
||||
Files={
|
||||
'mycoockbook': {
|
||||
'Name': 'mycoockbook.txt',
|
||||
'URL': 'https://github.com/tomcat.git',
|
||||
'Type': 'Downloadable'
|
||||
}
|
||||
},
|
||||
Scripts={
|
||||
'deploy': {
|
||||
'EntryPoint': 'cookbook/recipe',
|
||||
'Files': [
|
||||
'https://github.com/tomcat.git',
|
||||
{'java': 'https://github.com/java.git'}
|
||||
],
|
||||
'Options': {
|
||||
'captureStderr': True,
|
||||
'captureStdout': True
|
||||
},
|
||||
'Type': 'Chef',
|
||||
'Version': '1.0.0'
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
def get_template_downloable(self):
|
||||
return bunch.Bunch(
|
||||
ID='ID',
|
||||
Files={
|
||||
'file': {
|
||||
'Name': 'myfile',
|
||||
'URL': 'https://github.com'
|
||||
'/apache/tomcat/blob/trunk/LICENSE',
|
||||
'Type': 'Downloadable'
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
def get_template_file(self):
|
||||
return bunch.Bunch(
|
||||
ID='ID',
|
||||
Files={
|
||||
'test': {
|
||||
'Body': 'dGV4dA==\n',
|
||||
'BodyType': 'Base64',
|
||||
'Name': 'installer'
|
||||
}
|
||||
}
|
||||
)
|
|
@ -1,39 +0,0 @@
|
|||
# Copyright (c) 2015 Telefonica I+D
|
||||
#
|
||||
# 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 unittest import mock
|
||||
|
||||
from muranoagent.tests.unit import base
|
||||
from muranoagent import util
|
||||
|
||||
|
||||
class TestUtils(base.MuranoAgentTestCase):
|
||||
|
||||
def test_str_to_bytes(self):
|
||||
self.assertEqual(util._to_bytes('test'), b'test')
|
||||
|
||||
def test_bytes_to_bytes(self):
|
||||
self.assertEqual(util._to_bytes(b'test'), b'test')
|
||||
|
||||
def test_b64encode_str(self):
|
||||
self.assertEqual(util.b64encode('test'), 'dGVzdA==')
|
||||
|
||||
def test_b64encode_bytes(self):
|
||||
self.assertEqual(util.b64encode(b'test'), 'dGVzdA==')
|
||||
|
||||
def test_b64decode_str(self):
|
||||
self.assertEqual(util.b64decode('dGVzdA=='), 'test')
|
||||
|
||||
def test_b64decode_bytes(self):
|
||||
self.assertEqual(util.b64decode(b'dGVzdA=='), 'test')
|
|
@ -1,40 +0,0 @@
|
|||
# Copyright (c) 2014 Mirantis, Inc.
|
||||
#
|
||||
# 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 base64
|
||||
|
||||
|
||||
def _to_bytes(string):
|
||||
"""Coerce a string into bytes
|
||||
|
||||
Since Python 3 now handles bytes and str differently, this helper
|
||||
will coerce a string to bytes if possible for use with base64
|
||||
"""
|
||||
try:
|
||||
string = string.encode()
|
||||
except AttributeError:
|
||||
pass
|
||||
return string
|
||||
|
||||
|
||||
def b64encode(string):
|
||||
"""Base64 encode a string to a string"""
|
||||
string = _to_bytes(string)
|
||||
return base64.b64encode(string).decode()
|
||||
|
||||
|
||||
def b64decode(string):
|
||||
"""Base64 decode a string to a string"""
|
||||
string = _to_bytes(string)
|
||||
return base64.b64decode(string).decode()
|
|
@ -1,138 +0,0 @@
|
|||
# Copyright (c) 2017 Mirantis Inc.
|
||||
#
|
||||
# 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 semantic_version
|
||||
|
||||
from muranoagent import exceptions as exc
|
||||
|
||||
|
||||
max_format_version = semantic_version.Spec('<=2.2.0')
|
||||
|
||||
|
||||
def validate_plan(plan):
|
||||
plan_format_version = semantic_version.Version(
|
||||
plan.get('FormatVersion', '1.0.0'))
|
||||
|
||||
if plan_format_version not in max_format_version:
|
||||
# NOTE(kazitsev) this is Version in Spec not str in str
|
||||
raise exc.IncorrectFormat(
|
||||
9,
|
||||
"Unsupported format version {0} "
|
||||
"(I support versions {1})".format(
|
||||
plan_format_version, max_format_version))
|
||||
|
||||
for attr in ('Scripts', 'Files'):
|
||||
if attr not in plan:
|
||||
raise exc.IncorrectFormat(
|
||||
2, '{0} is not in the execution plan'.format(attr))
|
||||
|
||||
for attr in ('Scripts', 'Files', 'Options'):
|
||||
if attr in plan and not isinstance(
|
||||
plan[attr], dict):
|
||||
raise exc.IncorrectFormat(
|
||||
2, '{0} is not a dictionary'.format(attr))
|
||||
|
||||
for name, script in plan.get('Scripts', {}).items():
|
||||
_validate_script(name, script, plan_format_version, plan)
|
||||
|
||||
for key, plan_file in plan.get('Files', {}).items():
|
||||
_validate_file(plan_file, key, plan_format_version)
|
||||
|
||||
|
||||
def _validate_script(name, script, plan_format_version, plan):
|
||||
for attr in ('Type', 'EntryPoint'):
|
||||
if attr not in script or not isinstance(script[attr], str):
|
||||
raise exc.IncorrectFormat(
|
||||
2, 'Incorrect {0} entry in script {1}'.format(
|
||||
attr, name))
|
||||
|
||||
if plan_format_version in semantic_version.Spec('>=2.0.0,<2.1.0'):
|
||||
if script['Type'] != 'Application':
|
||||
raise exc.IncorrectFormat(
|
||||
2, 'Type {0} is not valid for format {1}'.format(
|
||||
script['Type'], plan_format_version))
|
||||
if script['EntryPoint'] not in plan.get('Files', {}):
|
||||
raise exc.IncorrectFormat(
|
||||
2, 'Script {0} misses entry point {1}'.format(
|
||||
name, script['EntryPoint']))
|
||||
|
||||
if plan_format_version in semantic_version.Spec('>=2.1.0'):
|
||||
if script['Type'] not in ('Application', 'Chef', 'Puppet'):
|
||||
raise exc.IncorrectFormat(
|
||||
2, 'Script has not a valid type {0}'.format(
|
||||
script['Type']))
|
||||
if (script['Type'] == 'Application' and script['EntryPoint']
|
||||
not in plan.get('Files', {})):
|
||||
raise exc.IncorrectFormat(
|
||||
2, 'Script {0} misses entry point {1}'.format(
|
||||
name, script['EntryPoint']))
|
||||
elif (script['Type'] != 'Application' and
|
||||
"::" not in script['EntryPoint']):
|
||||
raise exc.IncorrectFormat(
|
||||
2, 'Wrong EntryPoint {0} for Puppet/Chef '
|
||||
'executors. :: needed'.format(script['EntryPoint']))
|
||||
|
||||
for option in script['Options']:
|
||||
if option in ('useBerkshelf', 'berksfilePath'):
|
||||
if plan_format_version in semantic_version.Spec('<2.2.0'):
|
||||
raise exc.IncorrectFormat(
|
||||
2, 'Script has an option {0} invalid '
|
||||
'for version {1}'.format(option,
|
||||
plan_format_version))
|
||||
elif script['Type'] != 'Chef':
|
||||
raise exc.IncorrectFormat(
|
||||
2, 'Script has an option {0} invalid '
|
||||
'for type {1}'.format(option, script['Type']))
|
||||
|
||||
for additional_file in script.get('Files', []):
|
||||
mns_error = ('Script {0} misses file {1}'.format(
|
||||
name, additional_file))
|
||||
if isinstance(additional_file, dict):
|
||||
if (list(additional_file.keys())[0] not in
|
||||
plan.get('Files', {}).keys()):
|
||||
raise exc.IncorrectFormat(2, mns_error)
|
||||
elif additional_file not in plan.get('Files', {}):
|
||||
raise exc.IncorrectFormat(2, mns_error)
|
||||
|
||||
|
||||
def _validate_file(plan_file, key, format_version):
|
||||
if format_version in semantic_version.Spec('>=2.0.0,<2.1.0'):
|
||||
for plan in plan_file.keys():
|
||||
if plan in ('Type', 'URL'):
|
||||
raise exc.IncorrectFormat(
|
||||
2, 'Download file is {0} not valid for this '
|
||||
'version {1}'.format(key, format_version))
|
||||
|
||||
if 'Type' in plan_file:
|
||||
for attr in ('Type', 'URL', 'Name'):
|
||||
if attr not in plan_file:
|
||||
raise exc.IncorrectFormat(
|
||||
2,
|
||||
'Incorrect {0} entry in file {1}'.format(attr, key))
|
||||
|
||||
elif 'Body' in plan_file:
|
||||
for attr in ('BodyType', 'Body', 'Name'):
|
||||
if attr not in plan_file:
|
||||
raise exc.IncorrectFormat(
|
||||
2, 'Incorrect {0} entry in file {1}'.format(
|
||||
attr, key))
|
||||
|
||||
if plan_file['BodyType'] not in ('Text', 'Base64'):
|
||||
raise exc.IncorrectFormat(
|
||||
2, 'Incorrect BodyType in file {0}'.format(key))
|
||||
else:
|
||||
raise exc.IncorrectFormat(
|
||||
2, 'Invalid file {0}: {1}'.format(
|
||||
key, plan_file))
|
|
@ -1,16 +0,0 @@
|
|||
# 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 pbr.version
|
||||
|
||||
version_info = pbr.version.VersionInfo('murano-agent')
|
|
@ -1,27 +0,0 @@
|
|||
# Copyright (c) 2013 Mirantis Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
try:
|
||||
import os
|
||||
import win32file
|
||||
|
||||
def symlink(source, link_name):
|
||||
src = os.path.abspath(source)
|
||||
dest = os.path.abspath(link_name)
|
||||
win32file.CreateSymbolicLink(dest, src, 0)
|
||||
|
||||
os.symlink = symlink
|
||||
except ImportError:
|
||||
pass
|
|
@ -1,6 +0,0 @@
|
|||
---
|
||||
upgrade:
|
||||
- |
|
||||
Python 2.7 support has been dropped. Last release of murano-agent
|
||||
to support python 2.7 is OpenStack Train. The minimum version of Python now
|
||||
supported by murano-agent is Python 3.6.
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
fixes:
|
||||
- Fixed too many arguments provided for format exception message for wrong
|
||||
entrypoint in Puppet/Chef executors.
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
fixes:
|
||||
- Elements were updated to allow building murano fedora images with chef installed.
|
||||
Chef is installed from https://packages.chef.io
|
|
@ -1,3 +0,0 @@
|
|||
---
|
||||
prelude: >
|
||||
murano-agent has been ported to python3
|
|
@ -1,3 +0,0 @@
|
|||
---
|
||||
fixes:
|
||||
- Fixed a string handling issue with base64 when using Python 3
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue