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:
Ghanshyam Mann 2024-05-10 15:52:57 -07:00
parent 9ae3969bbd
commit 92660e2cdd
126 changed files with 8 additions and 11791 deletions

50
.gitignore vendored
View File

@ -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/

View File

@ -1,4 +0,0 @@
[DEFAULT]
test_path=./muranoagent/tests/unit
top_dir=./

View File

@ -1,5 +0,0 @@
- project:
templates:
- check-requirements
- openstack-python3-jobs
- release-notes-jobs-python3

View File

@ -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

View File

@ -1,4 +0,0 @@
Style Commandments
==================
Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/

176
LICENSE
View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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 */

View File

@ -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 */

View File

@ -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

View File

@ -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.

View File

@ -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;
}

View File

@ -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"

View File

@ -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;
}

View File

@ -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 );
}
}

View File

@ -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

View File

@ -1,2 +0,0 @@
cloud-init-datasources
murano-agent

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>

View File

@ -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>

View File

@ -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)));
}
}
}

View File

@ -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")]

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net45" />
</packages>

View File

@ -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.

View File

@ -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

View File

@ -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}: &lt;${logger:shortName=true}&gt; ${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>

View File

@ -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; }
}
}

View File

@ -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();
}
}
}

View File

@ -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
{
}
}
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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();
}
}
}

View File

@ -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")]

View File

@ -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
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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>

View File

@ -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();
}
}
}

View File

@ -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);
}
}
}

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -1 +0,0 @@
.. include:: ../../README.rst

View File

@ -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

View File

@ -1,5 +0,0 @@
[DEFAULT]
output_file = etc/muranoagent/muranoagent.conf.sample
namespace = muranoagent
namespace = oslo.service.service
namespace = oslo.log

View File

@ -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)

View File

@ -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]

View File

@ -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()

View File

@ -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)

View File

@ -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']

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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())
}

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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))

View File

@ -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)

View File

@ -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]

View File

@ -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)

View File

@ -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)

View File

@ -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')

View File

@ -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]"
]
}

View File

@ -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 + ':}}'

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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'
}
}
)

View File

@ -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')

View File

@ -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()

View File

@ -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))

View 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')

View File

@ -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

View File

@ -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.

View File

@ -1,4 +0,0 @@
---
fixes:
- Fixed too many arguments provided for format exception message for wrong
entrypoint in Puppet/Chef executors.

View File

@ -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

View File

@ -1,3 +0,0 @@
---
prelude: >
murano-agent has been ported to python3

View File

@ -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