├── .gitignore ├── .travis.yml ├── LICENSE ├── c_src ├── erlang_mesos.hpp ├── erlang_mesos_util.c ├── executor.c ├── executor_c_api.cpp ├── executor_c_api.hpp ├── scheduler.c ├── scheduler_c_api.cpp ├── scheduler_c_api.hpp └── utils.hpp ├── include └── mesos_erlang.hrl ├── proto └── mesos.proto ├── readme.md ├── rebar ├── rebar.config ├── src ├── erlang_mesos.app.src ├── example_executor.erl ├── example_framework.erl ├── executor.erl ├── nif_executor.erl ├── nif_scheduler.erl └── scheduler.erl └── test ├── mesos_scheduler_integration_tests.erl └── mesos_scheduler_integration_tests_acknowledgements.erl /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.beam 3 | *.plt 4 | .eunit 5 | deps 6 | ebin 7 | priv 8 | erl_crash.dump 9 | include/mesos_pb.hrl 10 | include/containerizer_pb.hrl 11 | src/mesos_pb.erl 12 | src/containerizer_pb.erl 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: erlang 2 | otp_release: 3 | - 17.5 4 | - 18.0 5 | # 6 | # 1. install mesos 7 | # 2. upgrade gcc 4.8 - Travis runs an older version (4.6) by default 8 | # 3. start the mesos master and slave services 9 | # 4. download and build protobuffer 2.5 - known issue with Travis 10 | # 11 | before_install: 12 | - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv E56151BF 13 | - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test 14 | - DISTRO=$(lsb_release -is | tr '[:upper:]' '[:lower:]') 15 | - CODENAME=$(lsb_release -cs) 16 | - echo "deb http://repos.mesosphere.io/${DISTRO} ${CODENAME} main" | sudo tee /etc/apt/sources.list.d/mesosphere.list 17 | - sudo apt-get -y update 18 | - sudo apt-get -y install mesos g++-4.8 19 | - export CXX="g++-4.8" CC="gcc-4.8" 20 | - sudo service mesos-master start 21 | - sudo service mesos-slave start 22 | - wget http://protobuf.googlecode.com/files/protobuf-2.5.0.tar.gz 23 | - tar xzvf protobuf-2.5.0.tar.gz 24 | - cd protobuf-2.5.0 && ./configure && make && sudo make install && cd .. 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | -------------------------------------------------------------------------------- /c_src/erlang_mesos.hpp: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------- 2 | // Copyright (c) 2015 Mark deVilliers. All Rights Reserved. 3 | // 4 | // This file is provided to you under the Apache License, 5 | // Version 2.0 (the "License"); you may not use this file 6 | // except in compliance with the License. You may obtain 7 | // a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | // 18 | // ------------------------------------------------------------------- 19 | 20 | #ifndef MESOS_API_C_H 21 | #define MESOS_API_C_H 22 | 23 | #include "erl_nif.h" 24 | 25 | typedef void* ExecutorDriverPtr; 26 | 27 | typedef struct { 28 | void* executor; 29 | void* driver; 30 | } ExecutorPtrPair; 31 | 32 | typedef int ExecutorDriverStatus; 33 | 34 | typedef struct { 35 | void* scheduler; 36 | void* driver; 37 | } SchedulerPtrPair; 38 | 39 | typedef int SchedulerDriverStatus ; 40 | 41 | struct state_t 42 | { 43 | int initilised; 44 | SchedulerPtrPair scheduler_state; 45 | ExecutorPtrPair executor_state; 46 | }; 47 | 48 | typedef struct state_t* state_ptr; 49 | 50 | typedef struct{ 51 | unsigned int length; 52 | ErlNifBinary* obj; 53 | } BinaryNifArray; 54 | 55 | #endif // MESOS_API_C_H 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /c_src/erlang_mesos_util.c: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------- 2 | // Copyright (c) 2015 Mark deVilliers. All Rights Reserved. 3 | // 4 | // This file is provided to you under the Apache License, 5 | // Version 2.0 (the "License"); you may not use this file 6 | // except in compliance with the License. You may obtain 7 | // a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | // 18 | // ------------------------------------------------------------------- 19 | 20 | 21 | #include "erl_nif.h" 22 | 23 | //helper method to turn status into an erlang atom 24 | ERL_NIF_TERM get_atom_from_status(ErlNifEnv* env, int status) 25 | { 26 | if(status == 1) //DRIVER_NOT_STARTED 27 | { 28 | return enif_make_atom(env, "driver_not_started"); 29 | 30 | }else if(status == 2) // DRIVER_RUNNING 31 | { 32 | return enif_make_atom(env, "driver_running"); 33 | 34 | }else if(status == 3) // DRIVER_ABORTED 35 | { 36 | return enif_make_atom(env, "driver_aborted"); 37 | 38 | }else if(status == 4) //DRIVER_STOPPED 39 | { 40 | return enif_make_atom(env, "driver_stopped"); 41 | 42 | }else{ 43 | return enif_make_atom(env, "unknown"); 44 | } 45 | } 46 | 47 | //helper method to return status to erlang 48 | ERL_NIF_TERM get_return_value_from_status(ErlNifEnv* env, int status) 49 | { 50 | if(status == 2) // DRIVER_RUNNING 51 | { 52 | return enif_make_tuple2(env, 53 | enif_make_atom(env, "ok"), 54 | get_atom_from_status(env, status)); 55 | }else 56 | { 57 | return enif_make_tuple2(env, 58 | enif_make_atom(env, "error"), 59 | get_atom_from_status(env, status)); 60 | } 61 | } 62 | 63 | //helper method to process an array of binary objects 64 | int inspect_array_of_binary_objects(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifBinary * binary_arr ) 65 | { 66 | ERL_NIF_TERM head, tail; 67 | tail = term; 68 | 69 | int i = 0; 70 | while(enif_get_list_cell(env, tail, &head, &tail)) 71 | { 72 | ErlNifBinary request_binary; 73 | if(!enif_inspect_binary(env, head, &request_binary)) 74 | { 75 | return 0; 76 | } 77 | binary_arr[i++] = request_binary; 78 | } 79 | return 1; 80 | } 81 | 82 | // helper method to make an argument error object 83 | ERL_NIF_TERM make_argument_error(ErlNifEnv* env, const char* reason, char* invalid_parameter) 84 | { 85 | return enif_make_tuple2(env, 86 | enif_make_atom(env, "error"), 87 | enif_make_tuple2(env, 88 | enif_make_atom(env, reason), 89 | enif_make_atom(env, invalid_parameter)) 90 | ); 91 | } -------------------------------------------------------------------------------- /c_src/executor.c: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------- 2 | // Copyright (c) 2015 Mark deVilliers. All Rights Reserved. 3 | // 4 | // This file is provided to you under the Apache License, 5 | // Version 2.0 (the "License"); you may not use this file 6 | // except in compliance with the License. You may obtain 7 | // a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | // 18 | // ------------------------------------------------------------------- 19 | 20 | 21 | #include 22 | #include "erl_nif.h" 23 | #include "erlang_mesos_util.c" 24 | #include "erlang_mesos.hpp" 25 | #include "executor_c_api.hpp" 26 | 27 | #define MAXBUFLEN 1024 28 | 29 | static int 30 | executor_load(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info) 31 | { 32 | state_ptr state = (state_ptr) enif_alloc(sizeof(struct state_t)); 33 | state->initilised = 0; 34 | *priv = (void*) state; 35 | return 0; 36 | } 37 | 38 | static void 39 | executor_unload(ErlNifEnv* env, void* priv) 40 | { 41 | state_ptr state = (state_ptr) priv; 42 | enif_free(state); 43 | } 44 | 45 | static int 46 | executor_upgrade(ErlNifEnv* env, void** priv, void** old_priv_data, ERL_NIF_TERM load_info) 47 | { 48 | return executor_load(env, priv, load_info); 49 | } 50 | 51 | static ERL_NIF_TERM 52 | nif_executor_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 53 | { 54 | 55 | state_ptr state = (state_ptr) enif_priv_data(env); 56 | 57 | if(state->initilised == 1) 58 | { 59 | return enif_make_tuple2(env, 60 | enif_make_atom(env, "error"), 61 | enif_make_atom(env, "executor_already_inited")); 62 | } 63 | 64 | ErlNifPid* pid = (ErlNifPid*) enif_alloc(sizeof(ErlNifPid)); 65 | 66 | if(!enif_get_local_pid(env, argv[0], pid)) 67 | { 68 | return make_argument_error(env, "invalid_or_corrupted_parameter", "pid"); 69 | } 70 | state->executor_state = executor_init(pid); 71 | 72 | state->initilised = 1; 73 | return enif_make_atom(env, "ok"); 74 | } 75 | 76 | static ERL_NIF_TERM 77 | nif_executor_start(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 78 | { 79 | state_ptr state = (state_ptr) enif_priv_data(env); 80 | 81 | if(state->initilised == 0 ) 82 | { 83 | return enif_make_tuple2(env, 84 | enif_make_atom(env, "error"), 85 | enif_make_atom(env, "executor_not_inited")); 86 | } 87 | 88 | ExecutorDriverStatus status = executor_start( state->executor_state ); 89 | 90 | return get_return_value_from_status(env, status); 91 | } 92 | 93 | 94 | static ERL_NIF_TERM 95 | nif_executor_abort(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 96 | { 97 | state_ptr state = (state_ptr) enif_priv_data(env); 98 | 99 | if(state->initilised == 0 ) 100 | { 101 | return enif_make_tuple2(env, 102 | enif_make_atom(env, "error"), 103 | enif_make_atom(env, "executor_not_inited")); 104 | } 105 | 106 | ExecutorDriverStatus status = executor_abort( state->executor_state ); 107 | 108 | if(status == 3){ // DRIVER_ABORTED 109 | return enif_make_tuple2(env, 110 | enif_make_atom(env, "ok"), 111 | get_atom_from_status(env, status)); 112 | }else{ 113 | return enif_make_tuple2(env, 114 | enif_make_atom(env, "error"), 115 | get_atom_from_status(env, status)); 116 | } 117 | } 118 | 119 | 120 | static ERL_NIF_TERM 121 | nif_executor_join(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 122 | { 123 | state_ptr state = (state_ptr) enif_priv_data(env); 124 | 125 | if(state->initilised == 0 ) 126 | { 127 | return enif_make_tuple2(env, 128 | enif_make_atom(env, "error"), 129 | enif_make_atom(env, "executor_not_inited")); 130 | } 131 | 132 | ExecutorDriverStatus status = executor_join( state->executor_state ); 133 | 134 | return get_return_value_from_status(env, status); 135 | } 136 | 137 | 138 | static ERL_NIF_TERM 139 | nif_executor_stop(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 140 | { 141 | 142 | state_ptr state = (state_ptr) enif_priv_data(env); 143 | 144 | if(state->initilised == 0 ) 145 | { 146 | return enif_make_tuple2(env, 147 | enif_make_atom(env, "error"), 148 | enif_make_atom(env, "executor_not_inited")); 149 | } 150 | 151 | ExecutorDriverStatus status = executor_stop( state->executor_state ); 152 | 153 | if(status == 4){ // driver_stopped 154 | return enif_make_tuple2(env, 155 | enif_make_atom(env, "ok"), 156 | get_atom_from_status(env, status)); 157 | }else{ 158 | return enif_make_tuple2(env, 159 | enif_make_atom(env, "error"), 160 | get_atom_from_status(env, status)); 161 | } 162 | } 163 | 164 | static ERL_NIF_TERM 165 | nif_executor_sendFrameworkMessage(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]){ 166 | 167 | char data[MAXBUFLEN]; 168 | 169 | state_ptr state = (state_ptr) enif_priv_data(env); 170 | 171 | if(state->initilised == 0 ) 172 | { 173 | return enif_make_tuple2(env, 174 | enif_make_atom(env, "error"), 175 | enif_make_atom(env, "executor_not_inited")); 176 | } 177 | 178 | //REVIEW : buffer length 179 | if(!enif_get_string(env, argv[0], data , MAXBUFLEN, ERL_NIF_LATIN1 )) 180 | { 181 | return make_argument_error(env, "invalid_or_corrupted_parameter", "data"); 182 | } 183 | 184 | ExecutorDriverStatus status = executor_sendFrameworkMessage( state->executor_state, 185 | data); 186 | return get_return_value_from_status(env, status); 187 | } 188 | 189 | static ERL_NIF_TERM 190 | nif_executor_sendStatusUpdate(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]){ 191 | 192 | ErlNifBinary taskStatus_binary; 193 | state_ptr state = (state_ptr) enif_priv_data(env); 194 | 195 | if(state->initilised == 0 ) 196 | { 197 | return enif_make_tuple2(env, 198 | enif_make_atom(env, "error"), 199 | enif_make_atom(env, "executor_not_inited")); 200 | } 201 | 202 | if (!enif_inspect_binary(env, argv[0], &taskStatus_binary)) 203 | { 204 | return make_argument_error(env, "invalid_or_corrupted_parameter", "task_status"); 205 | } 206 | 207 | ExecutorDriverStatus status = executor_sendStatusUpdate( state->executor_state, 208 | &taskStatus_binary); 209 | return get_return_value_from_status(env, status); 210 | } 211 | 212 | static ERL_NIF_TERM 213 | nif_executor_destroy(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]){ 214 | 215 | state_ptr state = (state_ptr) enif_priv_data(env); 216 | 217 | if(state->initilised == 0 ) 218 | { 219 | return enif_make_tuple2(env, 220 | enif_make_atom(env, "error"), 221 | enif_make_atom(env, "executor_not_inited")); 222 | } 223 | executor_destroy(state->executor_state); 224 | state->initilised = 0; 225 | return enif_make_atom(env, "ok"); 226 | } 227 | 228 | static ErlNifFunc executor_nif_funcs[] = { 229 | {"nif_executor_init", 1, nif_executor_init}, 230 | {"nif_executor_start", 0, nif_executor_start}, 231 | {"nif_executor_join", 0, nif_executor_join}, 232 | {"nif_executor_abort", 0, nif_executor_abort}, 233 | {"nif_executor_stop", 0, nif_executor_stop}, 234 | {"nif_executor_sendFrameworkMessage", 1,nif_executor_sendFrameworkMessage}, 235 | {"nif_executor_sendStatusUpdate", 1,nif_executor_sendStatusUpdate}, 236 | {"nif_executor_destroy" , 0, nif_executor_destroy} 237 | 238 | }; 239 | 240 | ERL_NIF_INIT(nif_executor, executor_nif_funcs, executor_load, NULL, executor_upgrade, executor_unload); -------------------------------------------------------------------------------- /c_src/executor_c_api.cpp: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------- 2 | // Copyright (c) 2015 Mark deVilliers. All Rights Reserved. 3 | // 4 | // This file is provided to you under the Apache License, 5 | // Version 2.0 (the "License"); you may not use this file 6 | // except in compliance with the License. You may obtain 7 | // a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | // 18 | // ------------------------------------------------------------------- 19 | 20 | 21 | #include 22 | #include 23 | 24 | #include "erl_nif.h" 25 | 26 | #include "erlang_mesos.hpp" 27 | #include "executor_c_api.hpp" 28 | 29 | #include 30 | #include 31 | #include "mesos/mesos.pb.h" 32 | #include "utils.hpp" 33 | 34 | using namespace mesos; 35 | using namespace std; 36 | 37 | #define DRIVER_ABORTED 3; 38 | 39 | class CExecutor : public Executor { 40 | public: 41 | CExecutor() {} 42 | 43 | virtual ~CExecutor() {} 44 | 45 | /** 46 | * Invoked once the executor driver has been able to successfully 47 | * connect with Mesos. In particular, a scheduler can pass some 48 | * data to its executors through the FrameworkInfo.ExecutorInfo's 49 | * data field. 50 | */ 51 | virtual void registered( 52 | ExecutorDriver* driver, 53 | const ExecutorInfo& executorInfo, 54 | const FrameworkInfo& frameworkInfo, 55 | const SlaveInfo& slaveInfo); 56 | 57 | /** 58 | * Invoked when the executor re-registers with a restarted slave. 59 | */ 60 | virtual void reregistered( 61 | ExecutorDriver* driver, 62 | const SlaveInfo& slaveInfo); 63 | /** 64 | * Invoked when the executor becomes "disconnected" from the slave 65 | * (e.g., the slave is being restarted due to an upgrade). 66 | */ 67 | virtual void disconnected(ExecutorDriver* driver); 68 | 69 | /** 70 | * Invoked when a task has been launched on this executor (initiated 71 | * via Scheduler::launchTasks). Note that this task can be realized 72 | * with a thread, a process, or some simple computation, however, no 73 | * other callbacks will be invoked on this executor until this 74 | * callback has returned. 75 | */ 76 | virtual void launchTask(ExecutorDriver* driver, const TaskInfo& task); 77 | 78 | /** 79 | * Invoked when a task running within this executor has been killed 80 | * (via SchedulerDriver::killTask). Note that no status update will 81 | * be sent on behalf of the executor, the executor is responsible 82 | * for creating a new TaskStatus (i.e., with TASK_KILLED) and 83 | * invoking ExecutorDriver::sendStatusUpdate. 84 | */ 85 | virtual void killTask(ExecutorDriver* driver, const TaskID& taskId); 86 | 87 | /** 88 | * Invoked when a framework message has arrived for this 89 | * executor. These messages are best effort; do not expect a 90 | * framework message to be retransmitted in any reliable fashion. 91 | */ 92 | virtual void frameworkMessage(ExecutorDriver* driver, const string& data); 93 | 94 | /** 95 | * Invoked when the executor should terminate all of its currently 96 | * running tasks. Note that after a Mesos has determined that an 97 | * executor has terminated any tasks that the executor did not send 98 | * terminal status updates for (e.g., TASK_KILLED, TASK_FINISHED, 99 | * TASK_FAILED, etc) a TASK_LOST status update will be created. 100 | */ 101 | virtual void shutdown(ExecutorDriver* driver); 102 | 103 | /** 104 | * Invoked when a fatal error has occured with the executor and/or 105 | * executor driver. The driver will be aborted BEFORE invoking this 106 | * callback. 107 | */ 108 | virtual void error(ExecutorDriver* driver, const string& message); 109 | 110 | ErlNifPid* pid; 111 | }; 112 | 113 | ExecutorPtrPair executor_init(ErlNifPid* pid) 114 | { 115 | 116 | ExecutorPtrPair ret ; 117 | 118 | CExecutor* executor = new CExecutor(); 119 | executor->pid = pid; 120 | 121 | MesosExecutorDriver* driver = new MesosExecutorDriver(executor); 122 | 123 | ret.driver = driver; 124 | ret.executor = executor; 125 | return ret; 126 | } 127 | 128 | ExecutorDriverStatus executor_start(ExecutorPtrPair state) 129 | { 130 | assert(state.driver != NULL); 131 | 132 | MesosExecutorDriver* driver = reinterpret_cast (state.driver); 133 | return driver->start(); 134 | } 135 | 136 | ExecutorDriverStatus executor_stop(ExecutorPtrPair state) 137 | { 138 | assert(state.driver != NULL); 139 | 140 | MesosExecutorDriver* driver = reinterpret_cast (state.driver); 141 | return driver->stop(); 142 | } 143 | 144 | ExecutorDriverStatus executor_abort(ExecutorPtrPair state) 145 | { 146 | assert(state.driver != NULL); 147 | 148 | MesosExecutorDriver* driver = reinterpret_cast (state.driver); 149 | return driver->abort(); 150 | } 151 | 152 | ExecutorDriverStatus executor_join(ExecutorPtrPair state) 153 | { 154 | assert(state.driver != NULL); 155 | 156 | MesosExecutorDriver* driver = reinterpret_cast (state.driver); 157 | return driver->join(); 158 | } 159 | 160 | ExecutorDriverStatus executor_run(ExecutorPtrPair state) 161 | { 162 | assert(state.driver != NULL); 163 | 164 | MesosExecutorDriver* driver = reinterpret_cast (state.driver); 165 | return driver->run(); 166 | 167 | } 168 | ExecutorDriverStatus executor_sendFrameworkMessage(ExecutorPtrPair state, const char* data) 169 | { 170 | assert(state.driver != NULL); 171 | assert(data != NULL); 172 | 173 | MesosExecutorDriver* driver = reinterpret_cast (state.driver); 174 | return driver->sendFrameworkMessage(data); 175 | } 176 | ExecutorDriverStatus executor_sendStatusUpdate(ExecutorPtrPair state, ErlNifBinary* taskStatus) 177 | { 178 | assert(state.driver != NULL); 179 | assert(taskStatus != NULL); 180 | 181 | TaskStatus taskStatus_pb; 182 | 183 | if(!deserialize(taskStatus_pb,taskStatus)) { return DRIVER_ABORTED; }; 184 | 185 | MesosExecutorDriver* driver = reinterpret_cast (state.driver); 186 | return driver->sendStatusUpdate(taskStatus_pb); 187 | } 188 | 189 | void executor_destroy(ExecutorPtrPair state) 190 | { 191 | assert(state.driver != NULL); 192 | assert(state.executor != NULL); 193 | 194 | MesosExecutorDriver* driver = reinterpret_cast(state.driver); 195 | CExecutor* executor = reinterpret_cast(state.executor); 196 | 197 | delete driver; 198 | delete executor; 199 | } 200 | 201 | // callbacks 202 | void CExecutor::registered(ExecutorDriver* driver, 203 | const ExecutorInfo& executorInfo, 204 | const FrameworkInfo& frameworkInfo, 205 | const SlaveInfo& slaveInfo) 206 | { 207 | assert(this->pid != NULL); 208 | 209 | ErlNifEnv* env = enif_alloc_env(); 210 | 211 | ERL_NIF_TERM executorInfo_pb = pb_obj_to_binary(env, executorInfo); 212 | ERL_NIF_TERM frameworkInfo_pb = pb_obj_to_binary(env, frameworkInfo); 213 | ERL_NIF_TERM slaveInfo_pb = pb_obj_to_binary(env, slaveInfo); 214 | 215 | ERL_NIF_TERM message = enif_make_tuple4(env, 216 | enif_make_atom(env, "registered"), 217 | executorInfo_pb, 218 | frameworkInfo_pb, 219 | slaveInfo_pb); 220 | 221 | enif_send(NULL, this->pid, env, message); 222 | enif_clear_env(env); 223 | } 224 | 225 | void CExecutor::reregistered(ExecutorDriver* driver, 226 | const SlaveInfo& slaveInfo) 227 | { 228 | assert(this->pid != NULL); 229 | 230 | ErlNifEnv* env = enif_alloc_env(); 231 | 232 | ERL_NIF_TERM slaveInfo_pb = pb_obj_to_binary(env, slaveInfo); 233 | 234 | ERL_NIF_TERM message = enif_make_tuple2(env, 235 | enif_make_atom(env, "reregistered"), 236 | slaveInfo_pb); 237 | 238 | enif_send(NULL, this->pid, env, message); 239 | enif_clear_env(env); 240 | } 241 | 242 | void CExecutor::disconnected(ExecutorDriver* driver) 243 | { 244 | assert(this->pid != NULL); 245 | 246 | ErlNifEnv* env = enif_alloc_env(); 247 | 248 | ERL_NIF_TERM message = enif_make_tuple(env, 249 | enif_make_atom(env, "disconnected")); 250 | 251 | enif_send(NULL, this->pid, env, message); 252 | enif_clear_env(env); 253 | } 254 | 255 | void CExecutor::launchTask(ExecutorDriver* driver, const TaskInfo& task) 256 | { 257 | assert(this->pid != NULL); 258 | 259 | ErlNifEnv* env = enif_alloc_env(); 260 | 261 | ERL_NIF_TERM task_pb = pb_obj_to_binary(env, task); 262 | 263 | ERL_NIF_TERM message = enif_make_tuple2(env, 264 | enif_make_atom(env, "launchTask"), 265 | task_pb); 266 | 267 | enif_send(NULL, this->pid, env, message); 268 | enif_clear_env(env); 269 | } 270 | 271 | void CExecutor::killTask(ExecutorDriver* driver, const TaskID& taskId) 272 | { 273 | assert(this->pid != NULL); 274 | 275 | ErlNifEnv* env = enif_alloc_env(); 276 | 277 | ERL_NIF_TERM taskid_pb = pb_obj_to_binary(env, taskId); 278 | 279 | ERL_NIF_TERM message = enif_make_tuple2(env, 280 | enif_make_atom(env, "killTask"), 281 | taskid_pb); 282 | 283 | enif_send(NULL, this->pid, env, message); 284 | enif_clear_env(env); 285 | } 286 | 287 | void CExecutor::frameworkMessage(ExecutorDriver* driver, const string& data) 288 | { 289 | assert(this->pid != NULL); 290 | 291 | ErlNifEnv* env = enif_alloc_env(); 292 | 293 | ERL_NIF_TERM message = enif_make_tuple2(env, 294 | enif_make_atom(env, "frameworkMessage"), 295 | enif_make_string(env, data.c_str(), ERL_NIF_LATIN1)); 296 | 297 | enif_send(NULL, this->pid, env, message); 298 | enif_clear_env(env); 299 | } 300 | 301 | 302 | void CExecutor::shutdown(ExecutorDriver* driver) 303 | { 304 | assert(this->pid != NULL); 305 | 306 | ErlNifEnv* env = enif_alloc_env(); 307 | 308 | ERL_NIF_TERM message = enif_make_tuple(env, 309 | enif_make_atom(env, "shutdown")); 310 | 311 | enif_send(NULL, this->pid, env, message); 312 | enif_clear_env(env); 313 | } 314 | 315 | void CExecutor::error(ExecutorDriver* driver, const string& messageStr) 316 | { 317 | assert(this->pid != NULL); 318 | 319 | ErlNifEnv* env = enif_alloc_env(); 320 | 321 | ERL_NIF_TERM message = enif_make_tuple2(env, 322 | enif_make_atom(env, "error"), 323 | enif_make_string(env, messageStr.c_str(), ERL_NIF_LATIN1)); 324 | 325 | enif_send(NULL, this->pid, env, message); 326 | enif_clear_env(env); 327 | 328 | } -------------------------------------------------------------------------------- /c_src/executor_c_api.hpp: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------- 2 | // Copyright (c) 2015 Mark deVilliers. All Rights Reserved. 3 | // 4 | // This file is provided to you under the Apache License, 5 | // Version 2.0 (the "License"); you may not use this file 6 | // except in compliance with the License. You may obtain 7 | // a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | // 18 | // ------------------------------------------------------------------- 19 | 20 | 21 | #ifndef MESOS_EXECUTOR_API_C_H 22 | #define MESOS_EXECUTOR_API_C_H 23 | 24 | #include "erl_nif.h" 25 | #include "erlang_mesos.hpp" 26 | #ifdef __cplusplus 27 | 28 | #include "erl_nif.h" 29 | 30 | extern "C" { 31 | #endif 32 | 33 | ExecutorPtrPair executor_init(ErlNifPid* pid); 34 | ExecutorDriverStatus executor_start(ExecutorPtrPair state); 35 | ExecutorDriverStatus executor_stop(ExecutorPtrPair state); 36 | ExecutorDriverStatus executor_abort(ExecutorPtrPair state); 37 | ExecutorDriverStatus executor_join(ExecutorPtrPair state); 38 | ExecutorDriverStatus executor_run(ExecutorPtrPair state); 39 | ExecutorDriverStatus executor_sendFrameworkMessage(ExecutorPtrPair state, const char* data); 40 | ExecutorDriverStatus executor_sendStatusUpdate(ExecutorPtrPair state, ErlNifBinary* taskStatus); 41 | void executor_destroy(ExecutorPtrPair state); 42 | 43 | #ifdef __cplusplus 44 | } 45 | #endif 46 | #endif // MESOS_EXECUTOR_API_C_H 47 | -------------------------------------------------------------------------------- /c_src/scheduler.c: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------- 2 | // Copyright (c) 2015 Mark deVilliers. All Rights Reserved. 3 | // 4 | // This file is provided to you under the Apache License, 5 | // Version 2.0 (the "License"); you may not use this file 6 | // except in compliance with the License. You may obtain 7 | // a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | // 18 | // ------------------------------------------------------------------- 19 | 20 | 21 | #include 22 | #include "erl_nif.h" 23 | #include "erlang_mesos_util.c" 24 | #include "erlang_mesos.hpp" 25 | #include "scheduler_c_api.hpp" 26 | 27 | #define MAXBUFLEN 1024 28 | 29 | static int 30 | scheduler_load(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info) 31 | { 32 | state_ptr state = (state_ptr) enif_alloc(sizeof(struct state_t)); 33 | state->initilised = 0; 34 | *priv = (void*) state; 35 | return 0; 36 | } 37 | 38 | static void 39 | scheduler_unload(ErlNifEnv* env, void* priv) 40 | { 41 | state_ptr state = (state_ptr) priv; 42 | enif_free(state); 43 | } 44 | 45 | static int 46 | scheduler_upgrade(ErlNifEnv* env, void** priv, void** old_priv_data, ERL_NIF_TERM load_info) 47 | { 48 | return scheduler_load(env, priv, load_info); 49 | } 50 | 51 | static ERL_NIF_TERM 52 | nif_scheduler_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 53 | { 54 | ErlNifBinary frameworkInfo_binary; 55 | ErlNifBinary credentials_binary; 56 | char masterUrl[MAXBUFLEN]; 57 | int implicitAcknowledgements = 1 ; 58 | 59 | state_ptr state = (state_ptr) enif_priv_data(env); 60 | 61 | if(state->initilised == 1) 62 | { 63 | return enif_make_tuple2(env, 64 | enif_make_atom(env, "error"), 65 | enif_make_atom(env, "scheduler_already_inited")); 66 | } 67 | 68 | ErlNifPid* pid = (ErlNifPid*) enif_alloc(sizeof(ErlNifPid)); 69 | 70 | if(!enif_get_local_pid(env, argv[0], pid)) 71 | { 72 | return make_argument_error(env, "invalid_or_corrupted_parameter", "pid"); 73 | } 74 | 75 | if (!enif_inspect_binary(env, argv[1], &frameworkInfo_binary)) 76 | { 77 | return make_argument_error(env, "invalid_or_corrupted_parameter", "framework_info"); 78 | } 79 | 80 | if(!enif_get_string(env, argv[2], masterUrl , MAXBUFLEN, ERL_NIF_LATIN1 )) 81 | { 82 | return make_argument_error(env, "invalid_or_corrupted_parameter", "master_info"); 83 | } 84 | 85 | if(!enif_get_int(env, argv[3], &implicitAcknowledgements)) 86 | { 87 | return make_argument_error(env, "invalid_or_corrupted_parameter", "implicit_acknowledgements"); 88 | } 89 | 90 | if(argc == 5 ) 91 | { 92 | if(!enif_inspect_binary(env,argv[4], &credentials_binary)) 93 | { 94 | return make_argument_error(env, "invalid_or_corrupted_parameter", "credential"); 95 | } 96 | state->scheduler_state = scheduler_init(pid, &frameworkInfo_binary, masterUrl, implicitAcknowledgements, 1, &credentials_binary); 97 | } 98 | else 99 | { 100 | state->scheduler_state = scheduler_init(pid, &frameworkInfo_binary, masterUrl, implicitAcknowledgements, 0, &credentials_binary); 101 | } 102 | state->initilised = 1; 103 | return enif_make_atom(env, "ok"); 104 | } 105 | 106 | static ERL_NIF_TERM 107 | nif_scheduler_start(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 108 | { 109 | state_ptr state = (state_ptr) enif_priv_data(env); 110 | 111 | if(state->initilised == 0 ) 112 | { 113 | return enif_make_tuple2(env, 114 | enif_make_atom(env, "error"), 115 | enif_make_atom(env, "scheduler_not_inited")); 116 | } 117 | 118 | SchedulerDriverStatus status = scheduler_start( state->scheduler_state ); 119 | 120 | return get_return_value_from_status(env, status); 121 | } 122 | 123 | static ERL_NIF_TERM 124 | nif_scheduler_join(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 125 | { 126 | state_ptr state = (state_ptr) enif_priv_data(env); 127 | 128 | if(state->initilised == 0 ) 129 | { 130 | return enif_make_tuple2(env, 131 | enif_make_atom(env, "error"), 132 | enif_make_atom(env, "scheduler_not_inited")); 133 | } 134 | 135 | SchedulerDriverStatus status = scheduler_join( state->scheduler_state ); 136 | 137 | return get_return_value_from_status(env, status); 138 | } 139 | 140 | static ERL_NIF_TERM 141 | nif_scheduler_abort(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 142 | { 143 | state_ptr state = (state_ptr) enif_priv_data(env); 144 | 145 | if(state->initilised == 0 ) 146 | { 147 | return enif_make_tuple2(env, 148 | enif_make_atom(env, "error"), 149 | enif_make_atom(env, "scheduler_not_inited")); 150 | } 151 | 152 | SchedulerDriverStatus status = scheduler_abort( state->scheduler_state ); 153 | 154 | if(status == 3){ // DRIVER_ABORTED 155 | return enif_make_tuple2(env, 156 | enif_make_atom(env, "ok"), 157 | get_atom_from_status(env, status)); 158 | }else{ 159 | return enif_make_tuple2(env, 160 | enif_make_atom(env, "error"), 161 | get_atom_from_status(env, status)); 162 | } 163 | } 164 | 165 | static ERL_NIF_TERM 166 | nif_scheduler_stop(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 167 | { 168 | int failover; 169 | state_ptr state = (state_ptr) enif_priv_data(env); 170 | 171 | if(state->initilised == 0 ) 172 | { 173 | return enif_make_tuple2(env, 174 | enif_make_atom(env, "error"), 175 | enif_make_atom(env, "sheduler_not_inited")); 176 | } 177 | 178 | if(!enif_get_int( env, argv[0], &failover)) 179 | { 180 | return make_argument_error(env, "invalid_or_corrupted_parameter", "failover"); 181 | } 182 | 183 | if(failover < 0 || failover > 1) 184 | { 185 | return make_argument_error(env, "invalid_or_corrupted_parameter", "failover"); 186 | } 187 | 188 | SchedulerDriverStatus status = scheduler_stop( state->scheduler_state, failover ); 189 | 190 | if(status == 4){ // driver_stopped 191 | return enif_make_tuple2(env, 192 | enif_make_atom(env, "ok"), 193 | get_atom_from_status(env, status)); 194 | }else{ 195 | return enif_make_tuple2(env, 196 | enif_make_atom(env, "error"), 197 | get_atom_from_status(env, status)); 198 | } 199 | } 200 | 201 | static ERL_NIF_TERM 202 | nif_scheduler_acceptOffers(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]){ 203 | unsigned int ids_length ; 204 | unsigned int ops_length ; 205 | 206 | state_ptr state = (state_ptr) enif_priv_data(env); 207 | 208 | if(state->initilised == 0 ) 209 | { 210 | return enif_make_tuple2(env, 211 | enif_make_atom(env, "error"), 212 | enif_make_atom(env, "scheduler_not_inited")); 213 | } 214 | 215 | if(!enif_is_list(env, argv[0])) 216 | { 217 | return make_argument_error(env, "invalid_or_corrupted_parameter", "offerid_array"); 218 | }; 219 | 220 | if(!enif_get_list_length(env, argv[0], &ids_length)) 221 | { 222 | return make_argument_error(env, "invalid_or_corrupted_parameter", "offerid_array"); 223 | } 224 | if(!enif_is_list(env, argv[1])) 225 | { 226 | return make_argument_error(env, "invalid_or_corrupted_parameter", "operations_array"); 227 | }; 228 | 229 | if(!enif_get_list_length(env, argv[1], &ops_length)) 230 | { 231 | return make_argument_error(env, "invalid_or_corrupted_parameter", "operations_array"); 232 | } 233 | 234 | ErlNifBinary ids_binary_arr[ids_length]; 235 | if(!inspect_array_of_binary_objects(env, argv[0], ids_binary_arr )) 236 | { 237 | return make_argument_error(env, "invalid_or_corrupted_parameter", "offerid_array"); 238 | } 239 | ErlNifBinary ops_binary_arr[ops_length]; 240 | if(!inspect_array_of_binary_objects(env, argv[1], ops_binary_arr )) 241 | { 242 | return make_argument_error(env, "invalid_or_corrupted_parameter", "operations_array"); 243 | } 244 | ErlNifBinary filters_binary; 245 | if (!enif_inspect_binary(env, argv[2], &filters_binary)) 246 | { 247 | return make_argument_error(env, "invalid_or_corrupted_parameter", "filters"); 248 | } 249 | 250 | BinaryNifArray ids_binaryNifArrayHolder ; 251 | ids_binaryNifArrayHolder.length = ids_length; 252 | ids_binaryNifArrayHolder.obj = &ids_binary_arr[0]; 253 | BinaryNifArray ops_binaryNifArrayHolder ; 254 | ops_binaryNifArrayHolder.length = ops_length; 255 | ops_binaryNifArrayHolder.obj = &ops_binary_arr[0]; 256 | 257 | SchedulerDriverStatus status = scheduler_acceptOffers( 258 | state->scheduler_state, &ids_binaryNifArrayHolder, &ops_binaryNifArrayHolder, &filters_binary); 259 | return get_return_value_from_status(env, status); 260 | } 261 | 262 | static ERL_NIF_TERM 263 | nif_scheduler_declineOffer(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]){ 264 | 265 | ErlNifBinary offerId_binary; 266 | ErlNifBinary filters_binary; 267 | 268 | state_ptr state = (state_ptr) enif_priv_data(env); 269 | 270 | if(state->initilised == 0 ) 271 | { 272 | return enif_make_tuple2(env, 273 | enif_make_atom(env, "error"), 274 | enif_make_atom(env, "scheduler_not_inited")); 275 | } 276 | if (!enif_inspect_binary(env, argv[0], &offerId_binary)) 277 | { 278 | return make_argument_error(env, "invalid_or_corrupted_parameter", "offer_id"); 279 | } 280 | if (!enif_inspect_binary(env, argv[1], &filters_binary)) 281 | { 282 | return make_argument_error(env, "invalid_or_corrupted_parameter", "filters"); 283 | } 284 | 285 | SchedulerDriverStatus status = scheduler_declineOffer( state->scheduler_state, &offerId_binary, &filters_binary ); 286 | 287 | return get_return_value_from_status(env, status); 288 | } 289 | 290 | static ERL_NIF_TERM 291 | nif_scheduler_killTask(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]){ 292 | 293 | ErlNifBinary taskId_binary; 294 | 295 | state_ptr state = (state_ptr) enif_priv_data(env); 296 | 297 | if(state->initilised == 0 ) 298 | { 299 | return enif_make_tuple2(env, 300 | enif_make_atom(env, "error"), 301 | enif_make_atom(env, "scheduler_not_inited")); 302 | } 303 | if (!enif_inspect_binary(env, argv[0], &taskId_binary)) 304 | { 305 | return make_argument_error(env, "invalid_or_corrupted_parameter", "task_id"); 306 | } 307 | 308 | SchedulerDriverStatus status = scheduler_killTask( state->scheduler_state, &taskId_binary); 309 | 310 | return get_return_value_from_status(env, status); 311 | } 312 | 313 | static ERL_NIF_TERM 314 | nif_scheduler_reviveOffers(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 315 | { 316 | state_ptr state = (state_ptr) enif_priv_data(env); 317 | 318 | if(state->initilised == 0 ) 319 | { 320 | return enif_make_tuple2(env, 321 | enif_make_atom(env, "error"), 322 | enif_make_atom(env, "scheduler_not_inited")); 323 | } 324 | 325 | SchedulerDriverStatus status = scheduler_reviveOffers( state->scheduler_state ); 326 | return get_return_value_from_status(env, status); 327 | } 328 | 329 | static ERL_NIF_TERM 330 | nif_scheduler_sendFrameworkMessage(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]){ 331 | 332 | ErlNifBinary executorId_binary; 333 | ErlNifBinary slaveId_binary; 334 | char data[MAXBUFLEN]; 335 | 336 | state_ptr state = (state_ptr) enif_priv_data(env); 337 | 338 | if(state->initilised == 0 ) 339 | { 340 | return enif_make_tuple2(env, 341 | enif_make_atom(env, "error"), 342 | enif_make_atom(env, "scheduler_not_inited")); 343 | } 344 | if (!enif_inspect_binary(env, argv[0], &executorId_binary)) 345 | { 346 | return make_argument_error(env, "invalid_or_corrupted_parameter", "executor_id"); 347 | } 348 | if (!enif_inspect_binary(env, argv[1], &slaveId_binary)) 349 | { 350 | return make_argument_error(env, "invalid_or_corrupted_parameter", "slave_id"); 351 | } 352 | //REVIEW : buffer length 353 | if(!enif_get_string(env, argv[2], data , MAXBUFLEN, ERL_NIF_LATIN1 )) 354 | { 355 | return make_argument_error(env, "invalid_or_corrupted_parameter", "data"); 356 | } 357 | 358 | SchedulerDriverStatus status = scheduler_sendFrameworkMessage( state->scheduler_state , 359 | &executorId_binary, 360 | &slaveId_binary, 361 | data); 362 | return get_return_value_from_status(env, status); 363 | } 364 | 365 | static ERL_NIF_TERM 366 | nif_scheduler_requestResources(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 367 | { 368 | unsigned int length ; 369 | 370 | state_ptr state = (state_ptr) enif_priv_data(env); 371 | 372 | if(state->initilised == 0 ) 373 | { 374 | return enif_make_tuple2(env, 375 | enif_make_atom(env, "error"), 376 | enif_make_atom(env, "scheduler_not_inited")); 377 | } 378 | 379 | if(!enif_is_list(env, argv[0])) 380 | { 381 | return make_argument_error(env, "invalid_or_corrupted_parameter", "request_array"); 382 | }; 383 | 384 | if(!enif_get_list_length(env, argv[0], &length)) 385 | { 386 | return make_argument_error(env, "invalid_or_corrupted_parameter", "request_array"); 387 | } 388 | 389 | ErlNifBinary binary_arr[length]; 390 | if(!inspect_array_of_binary_objects(env, argv[0], binary_arr )) 391 | { 392 | return make_argument_error(env, "invalid_or_corrupted_parameter", "request_array"); 393 | } 394 | 395 | BinaryNifArray binaryNifArrayHolder ; 396 | binaryNifArrayHolder.length = length; 397 | binaryNifArrayHolder.obj = &binary_arr[0]; 398 | 399 | SchedulerDriverStatus status = scheduler_requestResources( state->scheduler_state, &binaryNifArrayHolder); 400 | return get_return_value_from_status(env, status); 401 | } 402 | 403 | static ERL_NIF_TERM 404 | nif_scheduler_reconcileTasks(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 405 | { 406 | unsigned int length ; 407 | 408 | state_ptr state = (state_ptr) enif_priv_data(env); 409 | 410 | if(state->initilised == 0 ) 411 | { 412 | return enif_make_tuple2(env, 413 | enif_make_atom(env, "error"), 414 | enif_make_atom(env, "scheduler_not_inited")); 415 | } 416 | 417 | if(!enif_is_list(env, argv[0])) 418 | { 419 | return make_argument_error(env, "invalid_or_corrupted_parameter", "task_status_array"); 420 | }; 421 | 422 | if(!enif_get_list_length(env, argv[0], &length)) 423 | { 424 | return make_argument_error(env, "invalid_or_corrupted_parameter", "task_status_array"); 425 | } 426 | 427 | ErlNifBinary binary_arr[length]; 428 | if(!inspect_array_of_binary_objects(env, argv[0], binary_arr )) 429 | { 430 | return make_argument_error(env, "invalid_or_corrupted_parameter", "task_status_array"); 431 | } 432 | 433 | BinaryNifArray binaryNifArrayHolder ; 434 | binaryNifArrayHolder.length = length; 435 | binaryNifArrayHolder.obj = &binary_arr[0]; 436 | 437 | SchedulerDriverStatus status = scheduler_reconcileTasks( state->scheduler_state, &binaryNifArrayHolder); 438 | return get_return_value_from_status(env, status); 439 | } 440 | 441 | static ERL_NIF_TERM 442 | nif_scheduler_launchTasks(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 443 | { 444 | unsigned int length ; 445 | ErlNifBinary offerId_binary; 446 | ErlNifBinary filters_binary; 447 | 448 | state_ptr state = (state_ptr) enif_priv_data(env); 449 | 450 | if(state->initilised == 0 ) 451 | { 452 | return enif_make_tuple2(env, 453 | enif_make_atom(env, "error"), 454 | enif_make_atom(env, "scheduler_not_inited")); 455 | } 456 | 457 | if (!enif_inspect_binary(env, argv[0], &offerId_binary)) 458 | { 459 | return make_argument_error(env, "invalid_or_corrupted_parameter", "offer_id"); 460 | } 461 | 462 | if(!enif_is_list(env, argv[1])) 463 | { 464 | return make_argument_error(env, "invalid_or_corrupted_parameter", "task_info_array"); 465 | }; 466 | 467 | if(!enif_get_list_length(env, argv[1], &length)) 468 | { 469 | return make_argument_error(env, "invalid_or_corrupted_parameter", "task_info_array"); 470 | } 471 | 472 | ErlNifBinary task_info_binary_arr[length]; 473 | 474 | if(!inspect_array_of_binary_objects(env, argv[1], task_info_binary_arr )) 475 | { 476 | return make_argument_error(env, "invalid_or_corrupted_parameter", "task_info_array"); 477 | } 478 | 479 | if (!enif_inspect_binary(env, argv[2], &filters_binary)) 480 | { 481 | return make_argument_error(env, "invalid_or_corrupted_parameter", "filters"); 482 | } 483 | 484 | BinaryNifArray binaryNifArrayHolder ; 485 | binaryNifArrayHolder.length = length; 486 | binaryNifArrayHolder.obj = &task_info_binary_arr[0]; 487 | 488 | SchedulerDriverStatus status = scheduler_launchTasks(state->scheduler_state, &offerId_binary, &binaryNifArrayHolder, &filters_binary); 489 | return get_return_value_from_status(env, status); 490 | } 491 | 492 | static ERL_NIF_TERM 493 | nif_scheduler_destroy(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]){ 494 | 495 | state_ptr state = (state_ptr) enif_priv_data(env); 496 | 497 | if(state->initilised == 0 ) 498 | { 499 | return enif_make_tuple2(env, 500 | enif_make_atom(env, "error"), 501 | enif_make_atom(env, "scheduler_not_inited")); 502 | } 503 | scheduler_destroy(state->scheduler_state); 504 | state->initilised = 0; 505 | return enif_make_atom(env, "ok"); 506 | } 507 | 508 | static ERL_NIF_TERM 509 | nif_scheduler_acknowledgeStatusUpdate(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]){ 510 | 511 | state_ptr state = (state_ptr) enif_priv_data(env); 512 | 513 | ErlNifBinary task_status_binary; 514 | 515 | if(state->initilised == 0 ) 516 | { 517 | return enif_make_tuple2(env, 518 | enif_make_atom(env, "error"), 519 | enif_make_atom(env, "scheduler_not_inited")); 520 | } 521 | 522 | if (!enif_inspect_binary(env, argv[0], &task_status_binary)) 523 | { 524 | return make_argument_error(env, "invalid_or_corrupted_parameter", "task_status"); 525 | } 526 | 527 | SchedulerDriverStatus status = scheduler_acknowledgeStatusUpdate(state->scheduler_state, &task_status_binary); 528 | return get_return_value_from_status(env, status); 529 | } 530 | 531 | static ErlNifFunc nif_funcs[] = { 532 | {"nif_scheduler_init", 4, nif_scheduler_init}, 533 | {"nif_scheduler_init", 5, nif_scheduler_init}, 534 | {"nif_scheduler_start", 0, nif_scheduler_start}, 535 | {"nif_scheduler_join", 0, nif_scheduler_join}, 536 | {"nif_scheduler_abort", 0, nif_scheduler_abort}, 537 | {"nif_scheduler_stop", 1, nif_scheduler_stop}, 538 | {"nif_scheduler_acceptOffers", 3,nif_scheduler_acceptOffers}, 539 | {"nif_scheduler_declineOffer", 2,nif_scheduler_declineOffer}, 540 | {"nif_scheduler_killTask", 1,nif_scheduler_killTask}, 541 | {"nif_scheduler_reviveOffers", 0 , nif_scheduler_reviveOffers}, 542 | {"nif_scheduler_sendFrameworkMessage", 3, nif_scheduler_sendFrameworkMessage}, 543 | {"nif_scheduler_requestResources", 1, nif_scheduler_requestResources}, 544 | {"nif_scheduler_reconcileTasks", 1,nif_scheduler_reconcileTasks}, 545 | {"nif_scheduler_launchTasks", 3,nif_scheduler_launchTasks}, 546 | {"nif_scheduler_destroy", 0, nif_scheduler_destroy}, 547 | {"nif_scheduler_acknowledgeStatusUpdate", 1, nif_scheduler_acknowledgeStatusUpdate} 548 | }; 549 | 550 | ERL_NIF_INIT(nif_scheduler, nif_funcs, scheduler_load, NULL, scheduler_upgrade, scheduler_unload); 551 | -------------------------------------------------------------------------------- /c_src/scheduler_c_api.cpp: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------- 2 | // Copyright (c) 2015 Mark deVilliers. All Rights Reserved. 3 | // 4 | // This file is provided to you under the Apache License, 5 | // Version 2.0 (the "License"); you may not use this file 6 | // except in compliance with the License. You may obtain 7 | // a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | // 18 | // ------------------------------------------------------------------- 19 | 20 | 21 | #include 22 | #include 23 | 24 | #include "erl_nif.h" 25 | 26 | #include "erlang_mesos.hpp" 27 | #include "scheduler_c_api.hpp" 28 | 29 | #include 30 | #include 31 | #include "mesos/mesos.pb.h" 32 | #include "utils.hpp" 33 | 34 | using namespace mesos; 35 | using namespace std; 36 | 37 | #define DRIVER_ABORTED 3; 38 | 39 | class CScheduler : public Scheduler 40 | { 41 | public: 42 | CScheduler() {} 43 | 44 | ~CScheduler() {} 45 | 46 | /** 47 | * Invoked when the scheduler successfully registers with a Mesos 48 | * master. A unique ID (generated by the master) used for 49 | * distinguishing this framework from others and MasterInfo 50 | * with the ip and port of the current master are provided as arguments. 51 | */ 52 | virtual void registered(SchedulerDriver* driver, 53 | const FrameworkID& frameworkId, 54 | const MasterInfo& masterInfo); 55 | 56 | 57 | /** 58 | * Invoked when the scheduler re-registers with a newly elected Mesos master. 59 | * This is only called when the scheduler has previously been registered. 60 | * MasterInfo containing the updated information about the elected master 61 | * is provided as an argument. 62 | */ 63 | virtual void reregistered(SchedulerDriver* driver, 64 | const MasterInfo& masterInfo); 65 | 66 | /** 67 | * Invoked when the scheduler becomes "disconnected" from the master 68 | * (e.g., the master fails and another is taking over). 69 | */ 70 | virtual void disconnected(SchedulerDriver* driver); 71 | 72 | /** 73 | * Invoked when resources have been offered to this framework. A 74 | * single offer will only contain resources from a single slave. 75 | * Resources associated with an offer will not be re-offered to 76 | * _this_ framework until either (a) this framework has rejected 77 | * those resources (see SchedulerDriver::launchTasks) or (b) those 78 | * resources have been rescinded (see Scheduler::offerRescinded). 79 | * Note that resources may be concurrently offered to more than one 80 | * framework at a time (depending on the allocator being used). In 81 | * that case, the first framework to launch tasks using those 82 | * resources will be able to use them while the other frameworks 83 | * will have those resources rescinded (or if a framework has 84 | * already launched tasks with those resources then those tasks will 85 | * fail with a TASK_LOST status and a message saying as much). 86 | */ 87 | virtual void resourceOffers(SchedulerDriver* driver, 88 | const std::vector& offers); 89 | 90 | /** 91 | * Invoked when an offer is no longer valid (e.g., the slave was 92 | * lost or another framework used resources in the offer). If for 93 | * whatever reason an offer is never rescinded (e.g., dropped 94 | * message, failing over framework, etc.), a framwork that attempts 95 | * to launch tasks using an invalid offer will receive TASK_LOST 96 | * status updates for those tasks (see Scheduler::resourceOffers). 97 | */ 98 | virtual void offerRescinded(SchedulerDriver* driver, 99 | const OfferID& offerId); 100 | 101 | /** 102 | * Invoked when the status of a task has changed (e.g., a slave is 103 | * lost and so the task is lost, a task finishes and an executor 104 | * sends a status update saying so, etc). Note that returning from 105 | * this callback _acknowledges_ receipt of this status update! If 106 | * for whatever reason the scheduler aborts during this callback (or 107 | * the process exits) another status update will be delivered (note, 108 | * however, that this is currently not true if the slave sending the 109 | * status update is lost/fails during that time). 110 | */ 111 | virtual void statusUpdate(SchedulerDriver* driver, 112 | const TaskStatus& status); 113 | 114 | /** 115 | * Invoked when an executor sends a message. These messages are best 116 | * effort; do not expect a framework message to be retransmitted in 117 | * any reliable fashion. 118 | */ 119 | virtual void frameworkMessage(SchedulerDriver* driver, 120 | const ExecutorID& executorId, 121 | const SlaveID& slaveId, 122 | const std::string& data); 123 | 124 | /** 125 | * Invoked when a slave has been determined unreachable (e.g., 126 | * machine failure, network partition). Most frameworks will need to 127 | * reschedule any tasks launched on this slave on a new slave. 128 | */ 129 | virtual void slaveLost(SchedulerDriver* driver, 130 | const SlaveID& slaveId); 131 | 132 | /** 133 | * Invoked when an executor has exited/terminated. Note that any 134 | * tasks running will have TASK_LOST status updates automagically 135 | * generated. 136 | */ 137 | virtual void executorLost(SchedulerDriver* driver, 138 | const ExecutorID& executorId, 139 | const SlaveID& slaveId, 140 | int status); 141 | 142 | /** 143 | * Invoked when there is an unrecoverable error in the scheduler or 144 | * scheduler driver. The driver will be aborted BEFORE invoking this 145 | * callback. 146 | */ 147 | virtual void error(SchedulerDriver* driver, const std::string& message); 148 | 149 | FrameworkInfo info; 150 | ErlNifPid* pid; 151 | }; 152 | 153 | SchedulerPtrPair scheduler_init(ErlNifPid* pid, 154 | ErlNifBinary* info, 155 | const char* master, 156 | int implicitAcknowledgements, 157 | int credentialssupplied, 158 | ErlNifBinary* credentials) 159 | { 160 | assert(info != NULL); 161 | assert(master != NULL); 162 | 163 | SchedulerPtrPair ret ; 164 | Credential credentials_pb ; 165 | 166 | CScheduler* scheduler = new CScheduler(); 167 | scheduler->pid = pid; 168 | 169 | deserialize(scheduler->info,info); 170 | MesosSchedulerDriver* driver ; 171 | 172 | if(credentialssupplied) 173 | { 174 | 175 | deserialize(credentials_pb,credentials); 176 | 177 | driver = new MesosSchedulerDriver( 178 | scheduler, 179 | scheduler->info, 180 | std::string(master), 181 | implicitAcknowledgements == 1 ? true : false, 182 | credentials_pb); 183 | }else 184 | { 185 | driver = new MesosSchedulerDriver( 186 | scheduler, 187 | scheduler->info, 188 | std::string(master), 189 | implicitAcknowledgements == 1 ? true : false); 190 | } 191 | 192 | ret.driver = driver; 193 | ret.scheduler = scheduler; 194 | return ret; 195 | } 196 | 197 | SchedulerDriverStatus scheduler_start(SchedulerPtrPair state) 198 | { 199 | assert(state.driver != NULL); 200 | 201 | MesosSchedulerDriver* driver = reinterpret_cast (state.driver); 202 | return driver->start(); 203 | } 204 | 205 | SchedulerDriverStatus scheduler_join(SchedulerPtrPair state) 206 | { 207 | assert(state.driver != NULL); 208 | 209 | MesosSchedulerDriver* driver = reinterpret_cast (state.driver); 210 | return driver->join(); 211 | } 212 | 213 | SchedulerDriverStatus scheduler_abort(SchedulerPtrPair state) 214 | { 215 | assert(state.driver != NULL); 216 | 217 | MesosSchedulerDriver* driver = reinterpret_cast (state.driver); 218 | return driver->abort(); 219 | } 220 | 221 | SchedulerDriverStatus scheduler_stop(SchedulerPtrPair state, int failover) 222 | { 223 | assert(state.driver != NULL); 224 | 225 | MesosSchedulerDriver* driver = reinterpret_cast (state.driver); 226 | if(failover){ 227 | return driver->stop(true); 228 | }else{ 229 | return driver->stop(false); 230 | } 231 | } 232 | 233 | SchedulerDriverStatus scheduler_acceptOffers(SchedulerPtrPair state, BinaryNifArray* offerIds, BinaryNifArray* operations, ErlNifBinary* filters) 234 | { 235 | assert(state.driver != NULL); 236 | assert(offerIds != NULL); 237 | assert(operations != NULL); 238 | 239 | vector offerIds_; 240 | if(! deserialize( offerIds_, offerIds)) {return DRIVER_ABORTED;}; 241 | vector operations_; 242 | if(! deserialize( operations_, operations)) {return DRIVER_ABORTED;}; 243 | 244 | Filters filter_pb; 245 | 246 | if(!deserialize(filter_pb,filters)) { return DRIVER_ABORTED; }; 247 | 248 | MesosSchedulerDriver* driver = reinterpret_cast (state.driver); 249 | return driver->acceptOffers(offerIds_, operations_, filter_pb); 250 | } 251 | SchedulerDriverStatus scheduler_declineOffer(SchedulerPtrPair state, ErlNifBinary* offerId, ErlNifBinary* filters) 252 | { 253 | assert(state.driver != NULL); 254 | assert(offerId != NULL); 255 | 256 | OfferID offerid_pb; 257 | Filters filter_pb; 258 | 259 | if(!deserialize(offerid_pb,offerId)) { return DRIVER_ABORTED; }; 260 | if(!deserialize(filter_pb,filters)) { return DRIVER_ABORTED; }; 261 | 262 | MesosSchedulerDriver* driver = reinterpret_cast (state.driver); 263 | return driver->declineOffer(offerid_pb, 264 | filter_pb); 265 | } 266 | 267 | SchedulerDriverStatus scheduler_killTask(SchedulerPtrPair state, ErlNifBinary* taskId) 268 | { 269 | assert(state.driver != NULL); 270 | assert(taskId != NULL); 271 | TaskID taskid_pb; 272 | 273 | if(!deserialize(taskid_pb,taskId)) { return DRIVER_ABORTED; }; 274 | 275 | MesosSchedulerDriver* driver = reinterpret_cast (state.driver); 276 | return driver->killTask(taskid_pb); 277 | } 278 | 279 | SchedulerDriverStatus scheduler_reviveOffers(SchedulerPtrPair state) 280 | { 281 | assert(state.driver != NULL); 282 | 283 | MesosSchedulerDriver* driver = reinterpret_cast (state.driver); 284 | return driver->reviveOffers(); 285 | } 286 | 287 | SchedulerDriverStatus scheduler_sendFrameworkMessage(SchedulerPtrPair state, 288 | ErlNifBinary* executorId, 289 | ErlNifBinary* slaveId, 290 | const char* data) 291 | { 292 | assert(state.driver != NULL); 293 | assert(executorId != NULL); 294 | assert(slaveId != NULL); 295 | assert(data != NULL); 296 | 297 | ExecutorID executorid_pb; 298 | SlaveID slaveid_pb; 299 | 300 | if(!deserialize(executorid_pb,executorId)) { return DRIVER_ABORTED; }; 301 | if(!deserialize(slaveid_pb,slaveId)) { return DRIVER_ABORTED; }; 302 | 303 | MesosSchedulerDriver* driver = reinterpret_cast (state.driver); 304 | return driver->sendFrameworkMessage(executorid_pb, slaveid_pb, data); 305 | } 306 | 307 | SchedulerDriverStatus scheduler_requestResources(SchedulerPtrPair state, BinaryNifArray* requests) 308 | { 309 | assert(state.driver != NULL); 310 | assert(requests != NULL); 311 | 312 | vector requests_; 313 | 314 | if(! deserialize( requests_, requests)) {return DRIVER_ABORTED;}; 315 | 316 | MesosSchedulerDriver* driver = reinterpret_cast (state.driver); 317 | return driver->requestResources(requests_); 318 | } 319 | 320 | SchedulerDriverStatus scheduler_reconcileTasks(SchedulerPtrPair state, BinaryNifArray* taskStatus) 321 | { 322 | assert(state.driver != NULL); 323 | assert(taskStatus != NULL); 324 | 325 | vector taskStatus_; 326 | if(! deserialize( taskStatus_, taskStatus)) {return DRIVER_ABORTED;}; 327 | 328 | MesosSchedulerDriver* driver = reinterpret_cast (state.driver); 329 | return driver->reconcileTasks(taskStatus_); 330 | } 331 | 332 | SchedulerDriverStatus scheduler_launchTasks(SchedulerPtrPair state, 333 | ErlNifBinary* offerId, 334 | BinaryNifArray* taskInfos, 335 | ErlNifBinary* filters) 336 | { 337 | assert(state.driver != NULL); 338 | assert(offerId != NULL); 339 | assert(taskInfos != NULL); 340 | 341 | OfferID offerid_pb; 342 | vector taskInfo_ ; 343 | Filters filter_pb; 344 | 345 | if(!deserialize(offerid_pb,offerId)) { return DRIVER_ABORTED; }; 346 | if(!deserialize( taskInfo_, taskInfos)) {return DRIVER_ABORTED;}; 347 | if(!deserialize(filter_pb,filters)) { return DRIVER_ABORTED; }; 348 | 349 | //offerid_pb.PrintDebugString(); 350 | //taskInfo_[0].PrintDebugString(); 351 | //filter_pb.PrintDebugString(); 352 | 353 | MesosSchedulerDriver* driver = reinterpret_cast (state.driver); 354 | return driver->launchTasks(offerid_pb, taskInfo_,filter_pb); 355 | } 356 | 357 | 358 | void scheduler_destroy (SchedulerPtrPair state) 359 | { 360 | 361 | assert(state.driver != NULL); 362 | assert(state.driver != NULL); 363 | 364 | MesosSchedulerDriver* driver = reinterpret_cast(state.driver); 365 | CScheduler* scheduler = reinterpret_cast(state.scheduler); 366 | 367 | delete driver; 368 | delete scheduler; 369 | } 370 | 371 | SchedulerDriverStatus scheduler_acknowledgeStatusUpdate(SchedulerPtrPair state, 372 | ErlNifBinary* taskStatus) 373 | { 374 | assert(state.driver != NULL); 375 | assert(taskStatus != NULL); 376 | 377 | TaskStatus taskStatus_pb; 378 | 379 | if(!deserialize(taskStatus_pb,taskStatus)) { return DRIVER_ABORTED; }; 380 | 381 | MesosSchedulerDriver* driver = reinterpret_cast (state.driver); 382 | return driver->acknowledgeStatusUpdate(taskStatus_pb); 383 | 384 | } 385 | 386 | 387 | /** 388 | Callbacks 389 | 390 | **/ 391 | 392 | void CScheduler::registered(SchedulerDriver* driver, 393 | const FrameworkID& frameworkId, 394 | const MasterInfo& masterInfo) 395 | { 396 | //fprintf(stderr, "%s \n" , "Registered" ); 397 | assert(this->pid != NULL); 398 | 399 | ErlNifEnv* env = enif_alloc_env(); 400 | 401 | ERL_NIF_TERM framework_pb = pb_obj_to_binary(env, frameworkId); 402 | ERL_NIF_TERM masterInfo_pb = pb_obj_to_binary(env, masterInfo); 403 | 404 | ERL_NIF_TERM message = enif_make_tuple3(env, 405 | enif_make_atom(env, "registered"), 406 | framework_pb, 407 | masterInfo_pb); 408 | 409 | enif_send(NULL, this->pid, env, message); 410 | enif_clear_env(env); 411 | } 412 | 413 | void CScheduler::reregistered(SchedulerDriver* driver, 414 | const MasterInfo& masterInfo) 415 | { 416 | //fprintf(stderr, "%s \n" , "Reregistered" ); 417 | assert(this->pid != NULL); 418 | 419 | ErlNifEnv* env = enif_alloc_env(); 420 | 421 | ERL_NIF_TERM masterInfo_pb = pb_obj_to_binary(env, masterInfo); 422 | 423 | ERL_NIF_TERM message = enif_make_tuple2(env, 424 | enif_make_atom(env, "reregistered"), 425 | masterInfo_pb); 426 | 427 | enif_send(NULL, this->pid, env, message); 428 | enif_clear_env(env); 429 | }; 430 | 431 | void CScheduler::disconnected(SchedulerDriver* driver) 432 | { 433 | //fprintf(stderr, "%s \n" , "Disconnected" ); 434 | assert(this->pid != NULL); 435 | 436 | ErlNifEnv* env = enif_alloc_env(); 437 | 438 | ERL_NIF_TERM message = enif_make_tuple(env, 439 | enif_make_atom(env, "disconnected")); 440 | 441 | enif_send(NULL, this->pid, env, message); 442 | enif_clear_env(env); 443 | }; 444 | 445 | void CScheduler::offerRescinded(SchedulerDriver* driver, 446 | const OfferID& offerId) 447 | { 448 | //fprintf(stderr, "%s \n" , "offerRescinded" ); 449 | assert(this->pid != NULL); 450 | 451 | ErlNifEnv* env = enif_alloc_env(); 452 | 453 | ERL_NIF_TERM message = enif_make_tuple2(env, 454 | enif_make_atom(env, "offerRescinded"), 455 | pb_obj_to_binary(env, offerId)); 456 | 457 | enif_send(NULL, this->pid, env, message); 458 | enif_clear_env(env); 459 | } ; 460 | 461 | void CScheduler::statusUpdate(SchedulerDriver* driver, 462 | const TaskStatus& status){ 463 | //fprintf(stderr, "%s \n" , "statusUpdate" ); 464 | assert(this->pid != NULL); 465 | 466 | ErlNifEnv* env = enif_alloc_env(); 467 | 468 | ERL_NIF_TERM message = enif_make_tuple2(env, 469 | enif_make_atom(env, "statusUpdate"), 470 | pb_obj_to_binary(env, status)); 471 | 472 | enif_send(NULL, this->pid, env, message); 473 | enif_clear_env(env); 474 | } ; 475 | 476 | void CScheduler::frameworkMessage(SchedulerDriver* driver, 477 | const ExecutorID& executorId, 478 | const SlaveID& slaveId, 479 | const std::string& data) { 480 | //fprintf(stderr, "%s \n" , "frameworkMessage" ); 481 | assert(this->pid != NULL); 482 | 483 | ErlNifEnv* env = enif_alloc_env(); 484 | 485 | ERL_NIF_TERM message = enif_make_tuple4(env, 486 | enif_make_atom(env, "frameworkMessage"), 487 | pb_obj_to_binary(env, executorId), 488 | pb_obj_to_binary(env, slaveId), 489 | enif_make_string(env, data.c_str(), ERL_NIF_LATIN1)); 490 | 491 | enif_send(NULL, this->pid, env, message); 492 | }; 493 | 494 | void CScheduler::slaveLost(SchedulerDriver* driver, 495 | const SlaveID& slaveId) 496 | { 497 | //fprintf(stderr, "%s \n" , "slaveLost" ); 498 | assert(this->pid != NULL); 499 | 500 | ErlNifEnv* env = enif_alloc_env(); 501 | 502 | ERL_NIF_TERM message = enif_make_tuple2(env, 503 | enif_make_atom(env, "slaveLost"), 504 | pb_obj_to_binary(env, slaveId)); 505 | 506 | enif_send(NULL, this->pid, env, message); 507 | enif_clear_env(env); 508 | } ; 509 | 510 | void CScheduler::executorLost(SchedulerDriver* driver, 511 | const ExecutorID& executorId, 512 | const SlaveID& slaveId, 513 | int status) 514 | { 515 | //fprintf(stderr, "%s \n" , "executorLost" ); 516 | assert(this->pid != NULL); 517 | 518 | ErlNifEnv* env = enif_alloc_env(); 519 | 520 | ERL_NIF_TERM message = enif_make_tuple4(env, 521 | enif_make_atom(env, "executorLost"), 522 | pb_obj_to_binary(env, executorId), 523 | pb_obj_to_binary(env, slaveId), 524 | enif_make_int(env,status)); 525 | 526 | enif_send(NULL, this->pid, env, message); 527 | enif_clear_env(env); 528 | }; 529 | 530 | void CScheduler::error(SchedulerDriver* driver, const std::string& errormessage) 531 | { 532 | //fprintf(stderr, "%s \n" , "error" ); 533 | assert(this->pid != NULL); 534 | 535 | ErlNifEnv* env = enif_alloc_env(); 536 | 537 | ERL_NIF_TERM message = enif_make_tuple2(env, 538 | enif_make_atom(env, "error"), 539 | enif_make_string(env, errormessage.c_str(), ERL_NIF_LATIN1)); 540 | 541 | enif_send(NULL, this->pid, env, message); 542 | enif_clear_env(env); 543 | }; 544 | 545 | void CScheduler::resourceOffers(SchedulerDriver* driver, 546 | const std::vector& offers) 547 | { 548 | assert(this->pid != NULL); 549 | 550 | ErlNifEnv* env = enif_alloc_env(); 551 | 552 | for(unsigned int i = 0 ; i < offers.size(); i++) 553 | { 554 | Offer offer = offers.at(i); 555 | 556 | ERL_NIF_TERM message = enif_make_tuple2(env, 557 | enif_make_atom(env, "resourceOffers"), 558 | pb_obj_to_binary(env, offer)); 559 | 560 | enif_send(NULL, this->pid, env, message); 561 | } 562 | 563 | enif_clear_env(env); 564 | } ; 565 | -------------------------------------------------------------------------------- /c_src/scheduler_c_api.hpp: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------- 2 | // Copyright (c) 2015 Mark deVilliers. All Rights Reserved. 3 | // 4 | // This file is provided to you under the Apache License, 5 | // Version 2.0 (the "License"); you may not use this file 6 | // except in compliance with the License. You may obtain 7 | // a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | // 18 | // ------------------------------------------------------------------- 19 | 20 | 21 | #ifndef MESOS_SCHEDULER_API_C_H 22 | #define MESOS_SCHEDULER_API_C_H 23 | 24 | #include "erl_nif.h" 25 | 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif 29 | 30 | SchedulerPtrPair scheduler_init(ErlNifPid* pid, ErlNifBinary* info, const char* master, int implicitAcknoledgements, int credentialssupplied, ErlNifBinary* credentials); 31 | SchedulerDriverStatus scheduler_start(SchedulerPtrPair state); 32 | SchedulerDriverStatus scheduler_join(SchedulerPtrPair state); 33 | SchedulerDriverStatus scheduler_abort(SchedulerPtrPair state); 34 | SchedulerDriverStatus scheduler_stop(SchedulerPtrPair state, int failover); 35 | SchedulerDriverStatus scheduler_acceptOffers(SchedulerPtrPair state, BinaryNifArray* offerIds, BinaryNifArray* operations, ErlNifBinary* filters); 36 | SchedulerDriverStatus scheduler_declineOffer(SchedulerPtrPair state, ErlNifBinary* offerId, ErlNifBinary* filters); 37 | SchedulerDriverStatus scheduler_killTask(SchedulerPtrPair state, ErlNifBinary* taskId); 38 | SchedulerDriverStatus scheduler_reviveOffers(SchedulerPtrPair state); 39 | SchedulerDriverStatus scheduler_sendFrameworkMessage(SchedulerPtrPair state, ErlNifBinary* executorId, ErlNifBinary* slaveId, const char* data); 40 | SchedulerDriverStatus scheduler_requestResources(SchedulerPtrPair state, BinaryNifArray* requests); 41 | SchedulerDriverStatus scheduler_reconcileTasks(SchedulerPtrPair state, BinaryNifArray* taskStatus); 42 | SchedulerDriverStatus scheduler_launchTasks(SchedulerPtrPair state, ErlNifBinary* offerId, BinaryNifArray* tasks, ErlNifBinary* filters); 43 | void scheduler_destroy (SchedulerPtrPair state); 44 | SchedulerDriverStatus scheduler_acknowledgeStatusUpdate(SchedulerPtrPair state, ErlNifBinary* taskStatus); 45 | 46 | #ifdef __cplusplus 47 | } 48 | #endif 49 | #endif // MESOS_SCHEDULER_API_C_H 50 | -------------------------------------------------------------------------------- /c_src/utils.hpp: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------- 2 | // Copyright (c) 2015 Mark deVilliers. All Rights Reserved. 3 | // 4 | // This file is provided to you under the Apache License, 5 | // Version 2.0 (the "License"); you may not use this file 6 | // except in compliance with the License. You may obtain 7 | // a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | // 18 | // ------------------------------------------------------------------- 19 | 20 | 21 | #ifndef __MESOS_C_UTILS_HPP__ 22 | #define __MESOS_C_UTILS_HPP__ 23 | 24 | #include "mesos/mesos.pb.h" 25 | #include "erl_nif.h" 26 | 27 | template 28 | ERL_NIF_TERM pb_obj_to_binary(ErlNifEnv *env, const T& obj) { 29 | ErlNifBinary res; 30 | enif_alloc_binary(obj.ByteSize(), &res); // Review : do I need to dealloc this? 31 | obj.SerializeToArray(res.data, res.size); 32 | return enif_make_binary(env, &res); 33 | } 34 | 35 | template inline bool deserialize(T& ret, void* data, size_t size) 36 | { 37 | if (!ret.ParseFromArray(data, size)) { 38 | printf("Deserialization failed\n"); 39 | return false; 40 | } 41 | return true; 42 | } 43 | 44 | template inline bool deserialize(T& ret, ErlNifBinary* obj) { 45 | if (obj == NULL) { 46 | return false; 47 | } 48 | return deserialize(ret, obj->data, obj->size); 49 | } 50 | 51 | template inline bool deserialize( 52 | std::vector& ret, 53 | BinaryNifArray* request) 54 | { 55 | for(int i = 0; i < request->length; i++) 56 | { 57 | T obj; 58 | if(!deserialize(obj, &request->obj[i])){return false;} 59 | ret.push_back(obj); 60 | } 61 | return true; 62 | } 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /include/mesos_erlang.hrl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% Copyright (c) 2015 Mark deVilliers. All Rights Reserved. 3 | %% 4 | %% This file is provided to you under the Apache License, 5 | %% Version 2.0 (the "License"); you may not use this file 6 | %% except in compliance with the License. You may obtain 7 | %% a copy of the License at 8 | %% 9 | %% http://www.apache.org/licenses/LICENSE-2.0 10 | %% 11 | %% Unless required by applicable law or agreed to in writing, 12 | %% software distributed under the License is distributed on an 13 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | %% KIND, either express or implied. See the License for the 15 | %% specific language governing permissions and limitations 16 | %% under the License. 17 | %% 18 | %% ------------------------------------------------------------------- 19 | 20 | 21 | 22 | -type driver_state() :: driver_not_started | driver_running | driver_aborted | driver_stopped | unknown. 23 | 24 | -------------------------------------------------------------------------------- /proto/mesos.proto: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package mesos; 20 | 21 | option java_package = "org.apache.mesos"; 22 | option java_outer_classname = "Protos"; 23 | 24 | 25 | /** 26 | * Status is used to indicate the state of the scheduler and executor 27 | * driver after function calls. 28 | */ 29 | enum Status { 30 | DRIVER_NOT_STARTED = 1; 31 | DRIVER_RUNNING = 2; 32 | DRIVER_ABORTED = 3; 33 | DRIVER_STOPPED = 4; 34 | } 35 | 36 | 37 | /** 38 | * A unique ID assigned to a framework. A framework can reuse this ID 39 | * in order to do failover (see MesosSchedulerDriver). 40 | */ 41 | message FrameworkID { 42 | required string value = 1; 43 | } 44 | 45 | 46 | /** 47 | * A unique ID assigned to an offer. 48 | */ 49 | message OfferID { 50 | required string value = 1; 51 | } 52 | 53 | 54 | /** 55 | * A unique ID assigned to a slave. Currently, a slave gets a new ID 56 | * whenever it (re)registers with Mesos. Framework writers shouldn't 57 | * assume any binding between a slave ID and and a hostname. 58 | */ 59 | message SlaveID { 60 | required string value = 1; 61 | } 62 | 63 | 64 | /** 65 | * A framework generated ID to distinguish a task. The ID must remain 66 | * unique while the task is active. However, a framework can reuse an 67 | * ID _only_ if a previous task with the same ID has reached a 68 | * terminal state (e.g., TASK_FINISHED, TASK_LOST, TASK_KILLED, etc.). 69 | */ 70 | message TaskID { 71 | required string value = 1; 72 | } 73 | 74 | 75 | /** 76 | * A framework generated ID to distinguish an executor. Only one 77 | * executor with the same ID can be active on the same slave at a 78 | * time. 79 | */ 80 | message ExecutorID { 81 | required string value = 1; 82 | } 83 | 84 | 85 | /** 86 | * A slave generated ID to distinguish a container. The ID must be unique 87 | * between any active or completed containers on the slave. In particular, 88 | * containers for different runs of the same (framework, executor) pair must be 89 | * unique. 90 | */ 91 | message ContainerID { 92 | required string value = 1; 93 | } 94 | 95 | 96 | /** 97 | * Represents time since the epoch, in nanoseconds. 98 | */ 99 | message TimeInfo { 100 | required int64 nanoseconds = 1; 101 | 102 | // TODO(josephw): Add time zone information, if necessary. 103 | } 104 | 105 | 106 | /** 107 | * Represents duration in nanoseconds. 108 | */ 109 | message DurationInfo { 110 | required int64 nanoseconds = 1; 111 | } 112 | 113 | 114 | /** 115 | * A network address. 116 | * 117 | * TODO(bmahler): Use this more widely. 118 | */ 119 | message Address { 120 | // May contain a hostname, IP address, or both. 121 | optional string hostname = 1; 122 | optional string ip = 2; 123 | 124 | required int32 port = 3; 125 | } 126 | 127 | 128 | /** 129 | * Represents a URL. 130 | */ 131 | message URL { 132 | required string scheme = 1; 133 | required Address address = 2; 134 | optional string path = 3; 135 | repeated Parameter query = 4; 136 | optional string fragment = 5; 137 | } 138 | 139 | 140 | /** 141 | * Represents an interval, from a given start time over a given duration. 142 | * This interval pertains to an unavailability event, such as maintenance, 143 | * and is not a generic interval. 144 | */ 145 | message Unavailability { 146 | required TimeInfo start = 1; 147 | 148 | // When added to `start`, this represents the end of the interval. 149 | // If unspecified, the duration is assumed to be infinite. 150 | optional DurationInfo duration = 2; 151 | 152 | // TODO(josephw): Add additional fields for expressing the purpose and 153 | // urgency of the unavailability event. 154 | } 155 | 156 | 157 | /** 158 | * Represents a single machine, which may hold one or more slaves. 159 | * 160 | * NOTE: In order to match a slave to a machine, both the `hostname` and 161 | * `ip` must match the values advertised by the slave to the master. 162 | * Hostname is not case-sensitive. 163 | */ 164 | message MachineID { 165 | optional string hostname = 1; 166 | optional string ip = 2; 167 | } 168 | 169 | 170 | /** 171 | * Holds information about a single machine, its `mode`, and any other 172 | * relevant information which may affect the behavior of the machine. 173 | */ 174 | message MachineInfo { 175 | // Describes the several states that a machine can be in. A `Mode` 176 | // applies to a machine and to all associated slaves on the machine. 177 | enum Mode { 178 | // In this mode, a machine is behaving normally; 179 | // offering resources, executing tasks, etc. 180 | UP = 1; 181 | 182 | // In this mode, all slaves on the machine are expected to cooperate with 183 | // frameworks to drain resources. In general, draining is done ahead of 184 | // a pending `unavailability`. The resources should be drained so as to 185 | // maximize utilization prior to the maintenance but without knowingly 186 | // violating the frameworks' requirements. 187 | DRAINING = 2; 188 | 189 | // In this mode, a machine is not running any tasks and will not offer 190 | // any of its resources. Slaves on the machine will not be allowed to 191 | // register with the master. 192 | DOWN = 3; 193 | } 194 | 195 | required MachineID id = 1; 196 | optional Mode mode = 2; 197 | 198 | // Signifies that the machine may be unavailable during the given interval. 199 | // See comments in `Unavailability` and for the `unavailability` fields 200 | // in `Offer` and `InverseOffer` for more information. 201 | optional Unavailability unavailability = 3; 202 | } 203 | 204 | 205 | /** 206 | * Describes a framework. 207 | */ 208 | message FrameworkInfo { 209 | // Used to determine the Unix user that an executor or task should 210 | // be launched as. If the user field is set to an empty string Mesos 211 | // will automagically set it to the current user. 212 | required string user = 1; 213 | 214 | // Name of the framework that shows up in the Mesos Web UI. 215 | required string name = 2; 216 | 217 | // Note that 'id' is only available after a framework has 218 | // registered, however, it is included here in order to facilitate 219 | // scheduler failover (i.e., if it is set then the 220 | // MesosSchedulerDriver expects the scheduler is performing 221 | // failover). 222 | optional FrameworkID id = 3; 223 | 224 | // The amount of time that the master will wait for the scheduler to 225 | // failover before it tears down the framework by killing all its 226 | // tasks/executors. This should be non-zero if a framework expects 227 | // to reconnect after a failover and not lose its tasks/executors. 228 | optional double failover_timeout = 4 [default = 0.0]; 229 | 230 | // If set, framework pid, executor pids and status updates are 231 | // checkpointed to disk by the slaves. Checkpointing allows a 232 | // restarted slave to reconnect with old executors and recover 233 | // status updates, at the cost of disk I/O. 234 | optional bool checkpoint = 5 [default = false]; 235 | 236 | // Used to group frameworks for allocation decisions, depending on 237 | // the allocation policy being used. 238 | optional string role = 6 [default = "*"]; 239 | 240 | // Used to indicate the current host from which the scheduler is 241 | // registered in the Mesos Web UI. If set to an empty string Mesos 242 | // will automagically set it to the current hostname if one is 243 | // available. 244 | optional string hostname = 7; 245 | 246 | // This field should match the credential's principal the framework 247 | // uses for authentication. This field is used for framework API 248 | // rate limiting and dynamic reservations. It should be set even 249 | // if authentication is not enabled if these features are desired. 250 | optional string principal = 8; 251 | 252 | // This field allows a framework to advertise its web UI, so that 253 | // the Mesos web UI can link to it. It is expected to be a full URL, 254 | // for example http://my-scheduler.example.com:8080/. 255 | optional string webui_url = 9; 256 | 257 | message Capability { 258 | enum Type { 259 | // Receive offers with revocable resources. See 'Resource' 260 | // message for details. 261 | // TODO(vinod): This is currently a no-op. 262 | REVOCABLE_RESOURCES = 1; 263 | } 264 | 265 | required Type type = 1; 266 | } 267 | 268 | // This field allows a framework to advertise its set of 269 | // capabilities (e.g., ability to receive offers for revocable 270 | // resources). 271 | repeated Capability capabilities = 10; 272 | 273 | // Labels are free-form key value pairs supplied by the framework 274 | // scheduler (e.g., to describe additional functionality offered by 275 | // the framework). These labels are not interpreted by Mesos itself. 276 | optional Labels labels = 11; 277 | } 278 | 279 | 280 | /** 281 | * Describes a health check for a task or executor (or any arbitrary 282 | * process/command). A "strategy" is picked by specifying one of the 283 | * optional fields; currently only 'command' is supported. 284 | * Specifying more than one strategy is an error. 285 | */ 286 | message HealthCheck { 287 | // Describes an HTTP health check. This is not fully implemented and not 288 | // recommended for use - see MESOS-2533. 289 | message HTTP { 290 | // Port to send the HTTP request. 291 | required uint32 port = 1; 292 | 293 | // HTTP request path. 294 | optional string path = 2 [default = "/"]; 295 | 296 | // TODO(benh): Implement: 297 | // Whether or not to use HTTPS. 298 | // optional bool ssl = 3 [default = false]; 299 | 300 | // Expected response statuses. Not specifying any statuses implies 301 | // that any returned status is acceptable. 302 | repeated uint32 statuses = 4; 303 | 304 | // TODO(benh): Include an 'optional bytes data' field for checking 305 | // for specific data in the response. 306 | } 307 | 308 | // HTTP health check - not yet recommended for use, see MESOS-2533. 309 | optional HTTP http = 1; 310 | 311 | // TODO(benh): Consider adding a URL health check strategy which 312 | // allows doing something similar to the HTTP strategy but 313 | // encapsulates all the details in a single string field. 314 | 315 | // TODO(benh): Other possible health check strategies could include 316 | // one for TCP/UDP. 317 | 318 | // Amount of time to wait until starting the health checks. 319 | optional double delay_seconds = 2 [default = 15.0]; 320 | 321 | // Interval between health checks. 322 | optional double interval_seconds = 3 [default = 10.0]; 323 | 324 | // Amount of time to wait for the health check to complete. 325 | optional double timeout_seconds = 4 [default = 20.0]; 326 | 327 | // Number of consecutive failures until considered unhealthy. 328 | optional uint32 consecutive_failures = 5 [default = 3]; 329 | 330 | // Amount of time to allow failed health checks since launch. 331 | optional double grace_period_seconds = 6 [default = 10.0]; 332 | 333 | // Command health check. 334 | optional CommandInfo command = 7; 335 | } 336 | 337 | 338 | /** 339 | * Describes a command, executed via: '/bin/sh -c value'. Any URIs specified 340 | * are fetched before executing the command. If the executable field for an 341 | * uri is set, executable file permission is set on the downloaded file. 342 | * Otherwise, if the downloaded file has a recognized archive extension 343 | * (currently [compressed] tar and zip) it is extracted into the executor's 344 | * working directory. This extraction can be disabled by setting `extract` to 345 | * false. In addition, any environment variables are set before executing 346 | * the command (so they can be used to "parameterize" your command). 347 | */ 348 | message CommandInfo { 349 | message URI { 350 | required string value = 1; 351 | optional bool executable = 2; 352 | 353 | // In case the fetched file is recognized as an archive, extract 354 | // its contents into the sandbox. Note that a cached archive is 355 | // not copied from the cache to the sandbox in case extraction 356 | // originates from an archive in the cache. 357 | optional bool extract = 3 [default = true]; 358 | 359 | // If this field is "true", the fetcher cache will be used. If not, 360 | // fetching bypasses the cache and downloads directly into the 361 | // sandbox directory, no matter whether a suitable cache file is 362 | // available or not. The former directs the fetcher to download to 363 | // the file cache, then copy from there to the sandbox. Subsequent 364 | // fetch attempts with the same URI will omit downloading and copy 365 | // from the cache as long as the file is resident there. Cache files 366 | // may get evicted at any time, which then leads to renewed 367 | // downloading. See also "docs/fetcher.md" and 368 | // "docs/fetcher-cache-internals.md". 369 | optional bool cache = 4; 370 | } 371 | 372 | // Describes a container. 373 | // Not all containerizers currently implement ContainerInfo, so it 374 | // is possible that a launched task will fail due to supplying this 375 | // attribute. 376 | // NOTE: The containerizer API is currently in an early beta or 377 | // even alpha state. Some details, like the exact semantics of an 378 | // "image" or "options" are not yet hardened. 379 | // TODO(tillt): Describe the exact scheme and semantics of "image" 380 | // and "options". 381 | message ContainerInfo { 382 | // URI describing the container image name. 383 | required string image = 1; 384 | 385 | // Describes additional options passed to the containerizer. 386 | repeated string options = 2; 387 | } 388 | 389 | // NOTE: MesosContainerizer does currently not support this 390 | // attribute and tasks supplying a 'container' will fail. 391 | optional ContainerInfo container = 4; 392 | 393 | repeated URI uris = 1; 394 | 395 | optional Environment environment = 2; 396 | 397 | // There are two ways to specify the command: 398 | // 1) If 'shell == true', the command will be launched via shell 399 | // (i.e., /bin/sh -c 'value'). The 'value' specified will be 400 | // treated as the shell command. The 'arguments' will be ignored. 401 | // 2) If 'shell == false', the command will be launched by passing 402 | // arguments to an executable. The 'value' specified will be 403 | // treated as the filename of the executable. The 'arguments' 404 | // will be treated as the arguments to the executable. This is 405 | // similar to how POSIX exec families launch processes (i.e., 406 | // execlp(value, arguments(0), arguments(1), ...)). 407 | // NOTE: The field 'value' is changed from 'required' to 'optional' 408 | // in 0.20.0. It will only cause issues if a new framework is 409 | // connecting to an old master. 410 | optional bool shell = 6 [default = true]; 411 | optional string value = 3; 412 | repeated string arguments = 7; 413 | 414 | // Enables executor and tasks to run as a specific user. If the user 415 | // field is present both in FrameworkInfo and here, the CommandInfo 416 | // user value takes precedence. 417 | optional string user = 5; 418 | } 419 | 420 | 421 | /** 422 | * Describes information about an executor. The 'data' field can be 423 | * used to pass arbitrary bytes to an executor. 424 | */ 425 | message ExecutorInfo { 426 | required ExecutorID executor_id = 1; 427 | optional FrameworkID framework_id = 8; // TODO(benh): Make this required. 428 | required CommandInfo command = 7; 429 | // Executor provided with a container will launch the container 430 | // with the executor's CommandInfo and we expect the container to 431 | // act as a Mesos executor. 432 | optional ContainerInfo container = 11; 433 | repeated Resource resources = 5; 434 | optional string name = 9; 435 | 436 | // Source is an identifier style string used by frameworks to track 437 | // the source of an executor. This is useful when it's possible for 438 | // different executor ids to be related semantically. 439 | // NOTE: Source is exposed alongside the resource usage of the 440 | // executor via JSON on the slave. This allows users to import 441 | // usage information into a time series database for monitoring. 442 | optional string source = 10; 443 | optional bytes data = 4; 444 | 445 | // Service discovery information for the executor. It is not 446 | // interpreted or acted upon by Mesos. It is up to a service 447 | // discovery system to use this information as needed and to handle 448 | // executors without service discovery information. 449 | optional DiscoveryInfo discovery = 12; 450 | } 451 | 452 | 453 | /** 454 | * Describes a master. This will probably have more fields in the 455 | * future which might be used, for example, to link a framework webui 456 | * to a master webui. 457 | */ 458 | message MasterInfo { 459 | required string id = 1; 460 | 461 | // The IP address (only IPv4) as a packed 4-bytes integer, 462 | // stored in network order. Deprecated, use `address.ip` instead. 463 | required uint32 ip = 2; 464 | 465 | // The TCP port the Master is listening on for incoming 466 | // HTTP requests; deprecated, use `address.port` instead. 467 | required uint32 port = 3 [default = 5050]; 468 | 469 | // In the default implementation, this will contain information 470 | // about both the IP address, port and Master name; it should really 471 | // not be relied upon by external tooling/frameworks and be 472 | // considered an "internal" implementation field. 473 | optional string pid = 4; 474 | 475 | // The server's hostname, if available; it may be unreliable 476 | // in environments where the DNS configuration does not resolve 477 | // internal hostnames (eg, some public cloud providers). 478 | // Deprecated, use `address.hostname` instead. 479 | optional string hostname = 5; 480 | 481 | // The running Master version, as a string; taken from the 482 | // generated "master/version.hpp". 483 | optional string version = 6; 484 | 485 | // The full IP address (supports both IPv4 and IPv6 formats) 486 | // and supersedes the use of `ip`, `port` and `hostname`. 487 | // Since Mesos 0.24. 488 | optional Address address = 7; 489 | } 490 | 491 | 492 | /** 493 | * Describes a slave. Note that the 'id' field is only available after 494 | * a slave is registered with the master, and is made available here 495 | * to facilitate re-registration. If checkpoint is set, the slave is 496 | * checkpointing its own information and potentially frameworks' 497 | * information (if a framework has checkpointing enabled). 498 | */ 499 | message SlaveInfo { 500 | required string hostname = 1; 501 | optional int32 port = 8 [default = 5051]; 502 | repeated Resource resources = 3; 503 | repeated Attribute attributes = 5; 504 | optional SlaveID id = 6; 505 | // TODO(joerg84): Remove checkpoint field as with 0.22.0 506 | // slave checkpointing is enabled for all slaves (MESOS-2317). 507 | optional bool checkpoint = 7 [default = false]; 508 | } 509 | 510 | 511 | /** 512 | * Describes an Attribute or Resource "value". A value is described 513 | * using the standard protocol buffer "union" trick. 514 | */ 515 | message Value { 516 | enum Type { 517 | SCALAR = 0; 518 | RANGES = 1; 519 | SET = 2; 520 | TEXT = 3; 521 | } 522 | 523 | message Scalar { 524 | required double value = 1; 525 | } 526 | 527 | message Range { 528 | required uint64 begin = 1; 529 | required uint64 end = 2; 530 | } 531 | 532 | message Ranges { 533 | repeated Range range = 1; 534 | } 535 | 536 | message Set { 537 | repeated string item = 1; 538 | } 539 | 540 | message Text { 541 | required string value = 1; 542 | } 543 | 544 | required Type type = 1; 545 | optional Scalar scalar = 2; 546 | optional Ranges ranges = 3; 547 | optional Set set = 4; 548 | optional Text text = 5; 549 | } 550 | 551 | 552 | /** 553 | * Describes an attribute that can be set on a machine. For now, 554 | * attributes and resources share the same "value" type, but this may 555 | * change in the future and attributes may only be string based. 556 | */ 557 | message Attribute { 558 | required string name = 1; 559 | required Value.Type type = 2; 560 | optional Value.Scalar scalar = 3; 561 | optional Value.Ranges ranges = 4; 562 | optional Value.Set set = 6; 563 | optional Value.Text text = 5; 564 | } 565 | 566 | 567 | /** 568 | * Describes a resource on a machine. A resource can take on one of 569 | * three types: scalar (double), a list of finite and discrete ranges 570 | * (e.g., [1-10, 20-30]), or a set of items. A resource is described 571 | * using the standard protocol buffer "union" trick. 572 | * 573 | * TODO(benh): Add better support for "expected" resources (e.g., 574 | * cpus, memory, disk, network). 575 | */ 576 | message Resource { 577 | required string name = 1; 578 | required Value.Type type = 2; 579 | optional Value.Scalar scalar = 3; 580 | optional Value.Ranges ranges = 4; 581 | optional Value.Set set = 5; 582 | optional string role = 6 [default = "*"]; 583 | 584 | message ReservationInfo { 585 | // Describes a dynamic reservation. A dynamic reservation is 586 | // acquired by an operator via the '/reserve' HTTP endpoint or by 587 | // a framework via the offer cycle by sending back an 588 | // 'Offer::Operation::Reserve' message. 589 | // NOTE: We currently do not allow frameworks with role "*" to 590 | // make dynamic reservations. 591 | 592 | // This field indicates the principal of the operator or framework 593 | // that reserved this resource. It is used in conjunction with the 594 | // "unreserve" ACL to determine whether the entity attempting to 595 | // unreserve this resource is permitted to do so. 596 | // NOTE: This field should match the FrameworkInfo.principal of 597 | // the framework that reserved this resource. 598 | required string principal = 1; 599 | } 600 | 601 | // If this is set, this resource was dynamically reserved by an 602 | // operator or a framework. Otherwise, this resource is either unreserved 603 | // or statically reserved by an operator via the --resources flag. 604 | optional ReservationInfo reservation = 8; 605 | 606 | message DiskInfo { 607 | // Describes a persistent disk volume. 608 | // A persistent disk volume will not be automatically garbage 609 | // collected if the task/executor/slave terminates, but is 610 | // re-offered to the framework(s) belonging to the 'role'. 611 | // A framework can set the ID (if it is not set yet) to express 612 | // the intention to create a new persistent disk volume from a 613 | // regular disk resource. To reuse a previously created volume, a 614 | // framework can launch a task/executor when it receives an offer 615 | // with a persistent volume, i.e., ID is set. 616 | // NOTE: Currently, we do not allow a persistent disk volume 617 | // without a reservation (i.e., 'role' should not be '*'). 618 | message Persistence { 619 | // A unique ID for the persistent disk volume. 620 | // NOTE: The ID needs to be unique per role on each slave. 621 | required string id = 1; 622 | } 623 | 624 | optional Persistence persistence = 1; 625 | 626 | // Describes how this disk resource will be mounted in the 627 | // container. If not set, the disk resource will be used as the 628 | // sandbox. Otherwise, it will be mounted according to the 629 | // 'container_path' inside 'volume'. The 'host_path' inside 630 | // 'volume' is ignored. 631 | // NOTE: If 'volume' is set but 'persistence' is not set, the 632 | // volume will be automatically garbage collected after 633 | // task/executor terminates. Currently, if 'persistence' is set, 634 | // 'volume' must be set. 635 | optional Volume volume = 2; 636 | } 637 | 638 | optional DiskInfo disk = 7; 639 | 640 | message RevocableInfo {} 641 | 642 | // If this is set, the resources are revocable, i.e., any tasks or 643 | // executors launched using these resources could get preempted or 644 | // throttled at any time. This could be used by frameworks to run 645 | // best effort tasks that do not need strict uptime or performance 646 | // guarantees. Note that if this is set, 'disk' or 'reservation' 647 | // cannot be set. 648 | optional RevocableInfo revocable = 9; 649 | } 650 | 651 | /** 652 | * When the network bandwidth caps are enabled and the container 653 | * is over its limit, outbound packets may be either delayed or 654 | * dropped completely either because it exceeds the maximum bandwidth 655 | * allocation for a single container (the cap) or because the combined 656 | * network traffic of multiple containers on the host exceeds the 657 | * transmit capacity of the host (the share). We can report the 658 | * following statistics for each of these conditions exported directly 659 | * from the Linux Traffic Control Queueing Discipline. 660 | * 661 | * id : name of the limiter, e.g. 'tx_bw_cap' 662 | * backlog : number of packets currently delayed 663 | * bytes : total bytes seen 664 | * drops : number of packets dropped in total 665 | * overlimits : number of packets which exceeded allocation 666 | * packets : total packets seen 667 | * qlen : number of packets currently queued 668 | * rate_bps : throughput in bytes/sec 669 | * rate_pps : throughput in packets/sec 670 | * requeues : number of times a packet has been delayed due to 671 | * locking or device contention issues 672 | * 673 | * More information on the operation of Linux Traffic Control can be 674 | * found at http://www.lartc.org/lartc.html. 675 | */ 676 | message TrafficControlStatistics { 677 | required string id = 1; 678 | optional uint64 backlog = 2; 679 | optional uint64 bytes = 3; 680 | optional uint64 drops = 4; 681 | optional uint64 overlimits = 5; 682 | optional uint64 packets = 6; 683 | optional uint64 qlen = 7; 684 | optional uint64 ratebps = 8; 685 | optional uint64 ratepps = 9; 686 | optional uint64 requeues = 10; 687 | } 688 | 689 | 690 | /** 691 | * A snapshot of resource usage statistics. 692 | */ 693 | message ResourceStatistics { 694 | required double timestamp = 1; // Snapshot time, in seconds since the Epoch. 695 | 696 | optional uint32 processes = 30; 697 | optional uint32 threads = 31; 698 | 699 | // CPU Usage Information: 700 | // Total CPU time spent in user mode, and kernel mode. 701 | optional double cpus_user_time_secs = 2; 702 | optional double cpus_system_time_secs = 3; 703 | 704 | // Number of CPUs allocated. 705 | optional double cpus_limit = 4; 706 | 707 | // cpu.stat on process throttling (for contention issues). 708 | optional uint32 cpus_nr_periods = 7; 709 | optional uint32 cpus_nr_throttled = 8; 710 | optional double cpus_throttled_time_secs = 9; 711 | 712 | // Memory Usage Information: 713 | 714 | // mem_total_bytes was added in 0.23.0 to represent the total memory 715 | // of a process in RAM (as opposed to in Swap). This was previously 716 | // reported as mem_rss_bytes, which was also changed in 0.23.0 to 717 | // represent only the anonymous memory usage, to keep in sync with 718 | // Linux kernel's (arguably erroneous) use of terminology. 719 | optional uint64 mem_total_bytes = 36; 720 | 721 | // Total memory + swap usage. This is set if swap is enabled. 722 | optional uint64 mem_total_memsw_bytes = 37; 723 | 724 | // Hard memory limit for a container. 725 | optional uint64 mem_limit_bytes = 6; 726 | 727 | // Soft memory limit for a container. 728 | optional uint64 mem_soft_limit_bytes = 38; 729 | 730 | // Broken out memory usage information: pagecache, rss (anonymous), 731 | // mmaped files and swap. 732 | 733 | // TODO(chzhcn) mem_file_bytes and mem_anon_bytes are deprecated in 734 | // 0.23.0 and will be removed in 0.24.0. 735 | optional uint64 mem_file_bytes = 10; 736 | optional uint64 mem_anon_bytes = 11; 737 | 738 | // mem_cache_bytes is added in 0.23.0 to represent page cache usage. 739 | optional uint64 mem_cache_bytes = 39; 740 | 741 | // Since 0.23.0, mem_rss_bytes is changed to represent only 742 | // anonymous memory usage. Note that neither its requiredness, type, 743 | // name nor numeric tag has been changed. 744 | optional uint64 mem_rss_bytes = 5; 745 | 746 | optional uint64 mem_mapped_file_bytes = 12; 747 | // This is only set if swap is enabled. 748 | optional uint64 mem_swap_bytes = 40; 749 | optional uint64 mem_unevictable_bytes = 41; 750 | 751 | // Number of occurrences of different levels of memory pressure 752 | // events reported by memory cgroup. Pressure listening (re)starts 753 | // with these values set to 0 when slave (re)starts. See 754 | // https://www.kernel.org/doc/Documentation/cgroups/memory.txt for 755 | // more details. 756 | optional uint64 mem_low_pressure_counter = 32; 757 | optional uint64 mem_medium_pressure_counter = 33; 758 | optional uint64 mem_critical_pressure_counter = 34; 759 | 760 | // Disk Usage Information for executor working directory. 761 | optional uint64 disk_limit_bytes = 26; 762 | optional uint64 disk_used_bytes = 27; 763 | 764 | // Perf statistics. 765 | optional PerfStatistics perf = 13; 766 | 767 | // Network Usage Information: 768 | optional uint64 net_rx_packets = 14; 769 | optional uint64 net_rx_bytes = 15; 770 | optional uint64 net_rx_errors = 16; 771 | optional uint64 net_rx_dropped = 17; 772 | optional uint64 net_tx_packets = 18; 773 | optional uint64 net_tx_bytes = 19; 774 | optional uint64 net_tx_errors = 20; 775 | optional uint64 net_tx_dropped = 21; 776 | 777 | // The kernel keeps track of RTT (round-trip time) for its TCP 778 | // sockets. RTT is a way to tell the latency of a container. 779 | optional double net_tcp_rtt_microsecs_p50 = 22; 780 | optional double net_tcp_rtt_microsecs_p90 = 23; 781 | optional double net_tcp_rtt_microsecs_p95 = 24; 782 | optional double net_tcp_rtt_microsecs_p99 = 25; 783 | 784 | optional double net_tcp_active_connections = 28; 785 | optional double net_tcp_time_wait_connections = 29; 786 | 787 | // Network traffic flowing into or out of a container can be delayed 788 | // or dropped due to congestion or policy inside and outside the 789 | // container. 790 | repeated TrafficControlStatistics net_traffic_control_statistics = 35; 791 | } 792 | 793 | 794 | /** 795 | * Describes a snapshot of the resource usage for executors. 796 | */ 797 | message ResourceUsage { 798 | message Executor { 799 | required ExecutorInfo executor_info = 1; 800 | 801 | // This includes resources used by the executor itself 802 | // as well as its active tasks. 803 | repeated Resource allocated = 2; 804 | 805 | // Current resource usage. If absent, the containerizer 806 | // cannot provide resource usage. 807 | optional ResourceStatistics statistics = 3; 808 | 809 | // The container id for the executor specified in the executor_info field. 810 | required ContainerID container_id = 4; 811 | } 812 | 813 | repeated Executor executors = 1; 814 | 815 | // Slave's total resources including checkpointed dynamic 816 | // reservations and persistent volumes. 817 | repeated Resource total = 2; 818 | } 819 | 820 | 821 | /** 822 | * Describes a sample of events from "perf stat". Only available on 823 | * Linux. 824 | * 825 | * NOTE: Each optional field matches the name of a perf event (see 826 | * "perf list") with the following changes: 827 | * 1. Names are downcased. 828 | * 2. Hyphens ('-') are replaced with underscores ('_'). 829 | * 3. Events with alternate names use the name "perf stat" returns, 830 | * e.g., for the event "cycles OR cpu-cycles" perf always returns 831 | * cycles. 832 | */ 833 | message PerfStatistics { 834 | required double timestamp = 1; // Start of sample interval, in seconds since the Epoch. 835 | required double duration = 2; // Duration of sample interval, in seconds. 836 | 837 | // Hardware event. 838 | optional uint64 cycles = 3; 839 | optional uint64 stalled_cycles_frontend = 4; 840 | optional uint64 stalled_cycles_backend = 5; 841 | optional uint64 instructions = 6; 842 | optional uint64 cache_references = 7; 843 | optional uint64 cache_misses = 8; 844 | optional uint64 branches = 9; 845 | optional uint64 branch_misses = 10; 846 | optional uint64 bus_cycles = 11; 847 | optional uint64 ref_cycles = 12; 848 | 849 | // Software event. 850 | optional double cpu_clock = 13; 851 | optional double task_clock = 14; 852 | optional uint64 page_faults = 15; 853 | optional uint64 minor_faults = 16; 854 | optional uint64 major_faults = 17; 855 | optional uint64 context_switches = 18; 856 | optional uint64 cpu_migrations = 19; 857 | optional uint64 alignment_faults = 20; 858 | optional uint64 emulation_faults = 21; 859 | 860 | // Hardware cache event. 861 | optional uint64 l1_dcache_loads = 22; 862 | optional uint64 l1_dcache_load_misses = 23; 863 | optional uint64 l1_dcache_stores = 24; 864 | optional uint64 l1_dcache_store_misses = 25; 865 | optional uint64 l1_dcache_prefetches = 26; 866 | optional uint64 l1_dcache_prefetch_misses = 27; 867 | optional uint64 l1_icache_loads = 28; 868 | optional uint64 l1_icache_load_misses = 29; 869 | optional uint64 l1_icache_prefetches = 30; 870 | optional uint64 l1_icache_prefetch_misses = 31; 871 | optional uint64 llc_loads = 32; 872 | optional uint64 llc_load_misses = 33; 873 | optional uint64 llc_stores = 34; 874 | optional uint64 llc_store_misses = 35; 875 | optional uint64 llc_prefetches = 36; 876 | optional uint64 llc_prefetch_misses = 37; 877 | optional uint64 dtlb_loads = 38; 878 | optional uint64 dtlb_load_misses = 39; 879 | optional uint64 dtlb_stores = 40; 880 | optional uint64 dtlb_store_misses = 41; 881 | optional uint64 dtlb_prefetches = 42; 882 | optional uint64 dtlb_prefetch_misses = 43; 883 | optional uint64 itlb_loads = 44; 884 | optional uint64 itlb_load_misses = 45; 885 | optional uint64 branch_loads = 46; 886 | optional uint64 branch_load_misses = 47; 887 | optional uint64 node_loads = 48; 888 | optional uint64 node_load_misses = 49; 889 | optional uint64 node_stores = 50; 890 | optional uint64 node_store_misses = 51; 891 | optional uint64 node_prefetches = 52; 892 | optional uint64 node_prefetch_misses = 53; 893 | } 894 | 895 | 896 | /** 897 | * Describes a request for resources that can be used by a framework 898 | * to proactively influence the allocator. If 'slave_id' is provided 899 | * then this request is assumed to only apply to resources on that 900 | * slave. 901 | */ 902 | message Request { 903 | optional SlaveID slave_id = 1; 904 | repeated Resource resources = 2; 905 | } 906 | 907 | 908 | /** 909 | * Describes some resources available on a slave. An offer only 910 | * contains resources from a single slave. 911 | */ 912 | message Offer { 913 | required OfferID id = 1; 914 | required FrameworkID framework_id = 2; 915 | required SlaveID slave_id = 3; 916 | required string hostname = 4; 917 | 918 | // URL for reaching the slave running on the host. 919 | optional URL url = 8; 920 | 921 | repeated Resource resources = 5; 922 | repeated Attribute attributes = 7; 923 | repeated ExecutorID executor_ids = 6; 924 | 925 | // Signifies that the resources in this Offer may be unavailable during 926 | // the given interval. Any tasks launched using these resources may be 927 | // killed when the interval arrives. For example, these resources may be 928 | // part of a planned maintenance schedule. 929 | // 930 | // This field only provides information about a planned unavailability. 931 | // The unavailability interval may not necessarily start at exactly this 932 | // interval, nor last for exactly the duration of this interval. 933 | // The unavailability may also be forever! See comments in 934 | // `Unavailability` for more details. 935 | optional Unavailability unavailability = 9; 936 | 937 | // Defines an operation that can be performed against offers. 938 | message Operation { 939 | enum Type { 940 | LAUNCH = 1; 941 | RESERVE = 2; 942 | UNRESERVE = 3; 943 | CREATE = 4; 944 | DESTROY = 5; 945 | } 946 | 947 | message Launch { 948 | repeated TaskInfo task_infos = 1; 949 | } 950 | 951 | message Reserve { 952 | repeated Resource resources = 1; 953 | } 954 | 955 | message Unreserve { 956 | repeated Resource resources = 1; 957 | } 958 | 959 | message Create { 960 | repeated Resource volumes = 1; 961 | } 962 | 963 | message Destroy { 964 | repeated Resource volumes = 1; 965 | } 966 | 967 | required Type type = 1; 968 | optional Launch launch = 2; 969 | optional Reserve reserve = 3; 970 | optional Unreserve unreserve = 4; 971 | optional Create create = 5; 972 | optional Destroy destroy = 6; 973 | } 974 | } 975 | 976 | 977 | /** 978 | * A request to return some resources occupied by a framework. 979 | */ 980 | message InverseOffer { 981 | // This is the same OfferID as found in normal offers, which allows 982 | // re-use of some of the OfferID-only messages. 983 | required OfferID id = 1; 984 | 985 | // URL for reaching the slave running on the host. This enables some 986 | // optimizations as described in MESOS-3012, such as allowing the 987 | // scheduler driver to bypass the master and talk directly with a slave. 988 | optional URL url = 2; 989 | 990 | // The framework that should release its resources. 991 | // If no specifics are provided (i.e. which slave), all the framework's 992 | // resources are requested back. 993 | required FrameworkID framework_id = 3; 994 | 995 | // Specified if the resources need to be released from a particular slave. 996 | // All the framework's resources on this slave are requested back, 997 | // unless further qualified by the `resources` field. 998 | optional SlaveID slave_id = 4; 999 | 1000 | // This InverseOffer represents a planned unavailability event in the 1001 | // specified interval. Any tasks running on the given framework or slave 1002 | // may be killed when the interval arrives. Therefore, frameworks should 1003 | // aim to gracefully terminate tasks prior to the arrival of the interval. 1004 | // 1005 | // For reserved resources, the resources are expected to be returned to the 1006 | // framework after the unavailability interval. This is an expectation, 1007 | // not a guarantee. For example, if the unavailiability duration is not set, 1008 | // the resources may be removed permenantly. 1009 | // 1010 | // For other resources, there is no guarantee that requested resources will 1011 | // be returned after the unavailiability interval. The allocator has no 1012 | // obligation to re-offer these resources to the prior framework after 1013 | // the unavailability. 1014 | required Unavailability unavailability = 5; 1015 | 1016 | // A list of resources being requested back from the framework, 1017 | // on the slave identified by `slave_id`. If no resources are specified 1018 | // then all resources are being requested back. For the purpose of 1019 | // maintenance, this field is always empty (maintenance always requests 1020 | // all resources back). 1021 | repeated Resource resources = 6; 1022 | 1023 | // TODO(josephw): Add additional options for narrowing down the resources 1024 | // being requested back. Such as specific executors, tasks, etc. 1025 | } 1026 | 1027 | 1028 | /** 1029 | * Describes a task. Passed from the scheduler all the way to an 1030 | * executor (see SchedulerDriver::launchTasks and 1031 | * Executor::launchTask). Either ExecutorInfo or CommandInfo should be set. 1032 | * A different executor can be used to launch this task, and subsequent tasks 1033 | * meant for the same executor can reuse the same ExecutorInfo struct. 1034 | */ 1035 | message TaskInfo { 1036 | required string name = 1; 1037 | required TaskID task_id = 2; 1038 | required SlaveID slave_id = 3; 1039 | repeated Resource resources = 4; 1040 | optional ExecutorInfo executor = 5; 1041 | optional CommandInfo command = 7; 1042 | // Task provided with a container will launch the container as part 1043 | // of this task paired with the task's CommandInfo. 1044 | optional ContainerInfo container = 9; 1045 | optional bytes data = 6; 1046 | // A health check for the task (currently in *alpha* and initial 1047 | // support will only be for TaskInfo's that have a CommandInfo). 1048 | optional HealthCheck health_check = 8; 1049 | 1050 | // Labels are free-form key value pairs which are exposed through 1051 | // master and slave endpoints. Labels will not be interpreted or 1052 | // acted upon by Mesos itself. As opposed to the data field, labels 1053 | // will be kept in memory on master and slave processes. Therefore, 1054 | // labels should be used to tag tasks with light-weight meta-data. 1055 | optional Labels labels = 10; 1056 | 1057 | // Service discovery information for the task. It is not interpreted 1058 | // or acted upon by Mesos. It is up to a service discovery system 1059 | // to use this information as needed and to handle tasks without 1060 | // service discovery information. 1061 | optional DiscoveryInfo discovery = 11; 1062 | } 1063 | 1064 | 1065 | /** 1066 | * Describes possible task states. IMPORTANT: Mesos assumes tasks that 1067 | * enter terminal states (see below) imply the task is no longer 1068 | * running and thus clean up any thing associated with the task 1069 | * (ultimately offering any resources being consumed by that task to 1070 | * another task). 1071 | */ 1072 | enum TaskState { 1073 | TASK_STAGING = 6; // Initial state. Framework status updates should not use. 1074 | TASK_STARTING = 0; 1075 | TASK_RUNNING = 1; 1076 | TASK_FINISHED = 2; // TERMINAL. The task finished successfully. 1077 | TASK_FAILED = 3; // TERMINAL. The task failed to finish successfully. 1078 | TASK_KILLED = 4; // TERMINAL. The task was killed by the executor. 1079 | TASK_LOST = 5; // TERMINAL. The task failed but can be rescheduled. 1080 | TASK_ERROR = 7; // TERMINAL. The task description contains an error. 1081 | } 1082 | 1083 | 1084 | /** 1085 | * Describes the current status of a task. 1086 | */ 1087 | message TaskStatus { 1088 | // Describes the source of the task status update. 1089 | enum Source { 1090 | SOURCE_MASTER = 0; 1091 | SOURCE_SLAVE = 1; 1092 | SOURCE_EXECUTOR = 2; 1093 | } 1094 | 1095 | // Detailed reason for the task status update. 1096 | // 1097 | // TODO(bmahler): Differentiate between slave removal reasons 1098 | // (e.g. unhealthy vs. unregistered for maintenance). 1099 | enum Reason { 1100 | REASON_COMMAND_EXECUTOR_FAILED = 0; 1101 | REASON_EXECUTOR_PREEMPTED = 17; 1102 | REASON_EXECUTOR_TERMINATED = 1; 1103 | REASON_EXECUTOR_UNREGISTERED = 2; 1104 | REASON_FRAMEWORK_REMOVED = 3; 1105 | REASON_GC_ERROR = 4; 1106 | REASON_INVALID_FRAMEWORKID = 5; 1107 | REASON_INVALID_OFFERS = 6; 1108 | REASON_MASTER_DISCONNECTED = 7; 1109 | REASON_MEMORY_LIMIT = 8; 1110 | REASON_RECONCILIATION = 9; 1111 | REASON_RESOURCES_UNKNOWN = 18; 1112 | REASON_SLAVE_DISCONNECTED = 10; 1113 | REASON_SLAVE_REMOVED = 11; 1114 | REASON_SLAVE_RESTARTED = 12; 1115 | REASON_SLAVE_UNKNOWN = 13; 1116 | REASON_TASK_INVALID = 14; 1117 | REASON_TASK_UNAUTHORIZED = 15; 1118 | REASON_TASK_UNKNOWN = 16; 1119 | } 1120 | 1121 | required TaskID task_id = 1; 1122 | required TaskState state = 2; 1123 | optional string message = 4; // Possible message explaining state. 1124 | optional Source source = 9; 1125 | optional Reason reason = 10; 1126 | optional bytes data = 3; 1127 | optional SlaveID slave_id = 5; 1128 | optional ExecutorID executor_id = 7; // TODO(benh): Use in master/slave. 1129 | optional double timestamp = 6; 1130 | 1131 | // Statuses that are delivered reliably to the scheduler will 1132 | // include a 'uuid'. The status is considered delivered once 1133 | // it is acknowledged by the scheduler. Schedulers can choose 1134 | // to either explicitly acknowledge statuses or let the scheduler 1135 | // driver implicitly acknowledge (default). 1136 | // 1137 | // TODO(bmahler): This is currently overwritten in the scheduler 1138 | // driver and executor driver, but executors will need to set this 1139 | // to a valid RFC-4122 UUID if using the HTTP API. 1140 | optional bytes uuid = 11; 1141 | 1142 | // Describes whether the task has been determined to be healthy 1143 | // (true) or unhealthy (false) according to the HealthCheck field in 1144 | // the command info. 1145 | optional bool healthy = 8; 1146 | 1147 | // Labels are free-form key value pairs which are exposed through 1148 | // master and slave endpoints. Labels will not be interpreted or 1149 | // acted upon by Mesos itself. As opposed to the data field, labels 1150 | // will be kept in memory on master and slave processes. Therefore, 1151 | // labels should be used to tag TaskStatus message with light-weight 1152 | // meta-data. 1153 | optional Labels labels = 12; 1154 | 1155 | // Container related information that is resolved dynamically such as 1156 | // network address. 1157 | optional ContainerStatus container_status = 13; 1158 | } 1159 | 1160 | 1161 | /** 1162 | * Describes possible filters that can be applied to unused resources 1163 | * (see SchedulerDriver::launchTasks) to influence the allocator. 1164 | */ 1165 | message Filters { 1166 | // Time to consider unused resources refused. Note that all unused 1167 | // resources will be considered refused and use the default value 1168 | // (below) regardless of whether Filters was passed to 1169 | // SchedulerDriver::launchTasks. You MUST pass Filters with this 1170 | // field set to change this behavior (i.e., get another offer which 1171 | // includes unused resources sooner or later than the default). 1172 | optional double refuse_seconds = 1 [default = 5.0]; 1173 | } 1174 | 1175 | 1176 | /** 1177 | * Describes a collection of environment variables. This is used with 1178 | * CommandInfo in order to set environment variables before running a 1179 | * command. 1180 | */ 1181 | message Environment { 1182 | message Variable { 1183 | required string name = 1; 1184 | required string value = 2; 1185 | } 1186 | 1187 | repeated Variable variables = 1; 1188 | } 1189 | 1190 | 1191 | /** 1192 | * A generic (key, value) pair used in various places for parameters. 1193 | */ 1194 | message Parameter { 1195 | required string key = 1; 1196 | required string value = 2; 1197 | } 1198 | 1199 | 1200 | /** 1201 | * Collection of Parameter. 1202 | */ 1203 | message Parameters { 1204 | repeated Parameter parameter = 1; 1205 | } 1206 | 1207 | 1208 | /** 1209 | * Credential used in various places for authentication and 1210 | * authorization. 1211 | * 1212 | * NOTE: A 'principal' is different from 'FrameworkInfo.user'. The 1213 | * former is used for authentication and authorization while the 1214 | * latter is used to determine the default user under which the 1215 | * framework's executors/tasks are run. 1216 | */ 1217 | message Credential { 1218 | required string principal = 1; 1219 | optional bytes secret = 2; 1220 | } 1221 | 1222 | 1223 | /** 1224 | * Credentials used for framework authentication, HTTP authentication 1225 | * (where the common 'username' and 'password' are captured as 1226 | * 'principal' and 'secret' respectively), etc. 1227 | */ 1228 | message Credentials { 1229 | repeated Credential credentials = 1; 1230 | } 1231 | 1232 | 1233 | /** 1234 | * Rate (queries per second, QPS) limit for messages from a framework to master. 1235 | * Strictly speaking they are the combined rate from all frameworks of the same 1236 | * principal. 1237 | */ 1238 | message RateLimit { 1239 | // Leaving QPS unset gives it unlimited rate (i.e., not throttled), 1240 | // which also implies unlimited capacity. 1241 | optional double qps = 1; 1242 | 1243 | // Principal of framework(s) to be throttled. Should match 1244 | // FrameworkInfo.princpal and Credential.principal (if using authentication). 1245 | required string principal = 2; 1246 | 1247 | // Max number of outstanding messages from frameworks of this principal 1248 | // allowed by master before the next message is dropped and an error is sent 1249 | // back to the sender. Messages received before the capacity is reached are 1250 | // still going to be processed after the error is sent. 1251 | // If unspecified, this principal is assigned unlimited capacity. 1252 | // NOTE: This value is ignored if 'qps' is not set. 1253 | optional uint64 capacity = 3; 1254 | } 1255 | 1256 | 1257 | /** 1258 | * Collection of RateLimit. 1259 | * Frameworks without rate limits defined here are not throttled unless 1260 | * 'aggregate_default_qps' is specified. 1261 | */ 1262 | message RateLimits { 1263 | // Items should have unique principals. 1264 | repeated RateLimit limits = 1; 1265 | 1266 | // All the frameworks not specified in 'limits' get this default rate. 1267 | // This rate is an aggregate rate for all of them, i.e., their combined 1268 | // traffic is throttled together at this rate. 1269 | optional double aggregate_default_qps = 2; 1270 | 1271 | // All the frameworks not specified in 'limits' get this default capacity. 1272 | // This is an aggregate value similar to 'aggregate_default_qps'. 1273 | optional uint64 aggregate_default_capacity = 3; 1274 | } 1275 | 1276 | 1277 | /** 1278 | * Describe an image used by tasks or executors. Note that it's only 1279 | * for tasks or executors launched by MesosContainerizer currently. 1280 | * TODO(jieyu): This feature not fully supported in 0.24.0. Please do 1281 | * not use it until this feature is announced. 1282 | */ 1283 | message Image { 1284 | enum Type { 1285 | APPC = 1; 1286 | DOCKER = 2; 1287 | } 1288 | 1289 | // Protobuf for specifying an Appc container image. See: 1290 | // https://github.com/appc/spec/blob/master/spec/aci.md 1291 | message Appc { 1292 | // The name of the image. 1293 | required string name = 1; 1294 | 1295 | // An image ID is a string of the format "hash-value", where 1296 | // "hash" is the hash algorithm used and "value" is the hex 1297 | // encoded string of the digest. Currently the only permitted 1298 | // hash algorithm is sha512. 1299 | optional string id = 2; 1300 | 1301 | // Optional labels. Suggested labels: "version", "os", and "arch". 1302 | optional Labels labels = 3; 1303 | } 1304 | 1305 | message Docker { 1306 | // The name of the image. Expected in format repository[:tag]. 1307 | required string name = 1; 1308 | } 1309 | 1310 | required Type type = 1; 1311 | 1312 | // Only one of the following image messages should be set to match 1313 | // the type. 1314 | optional Appc appc = 2; 1315 | optional Docker docker = 3; 1316 | } 1317 | 1318 | 1319 | /** 1320 | * Describes a volume mapping either from host to container or vice 1321 | * versa. Both paths can either refer to a directory or a file. 1322 | */ 1323 | message Volume { 1324 | enum Mode { 1325 | RW = 1; // read-write. 1326 | RO = 2; // read-only. 1327 | } 1328 | 1329 | required Mode mode = 3; 1330 | 1331 | // Path pointing to a directory or file in the container. If the 1332 | // path is a relative path, it is relative to the container work 1333 | // directory. If the path is an absolute path, that path must 1334 | // already exist. 1335 | required string container_path = 1; 1336 | 1337 | // The following specifies the source of this volume. At most one of 1338 | // the following should be set. 1339 | 1340 | // Absolute path pointing to a directory or file on the host or a 1341 | // path relative to the container work directory. 1342 | optional string host_path = 2; 1343 | 1344 | // The source of the volume is an Image which describes a root 1345 | // filesystem which will be provisioned by Mesos. 1346 | optional Image image = 4; 1347 | } 1348 | 1349 | 1350 | /** 1351 | * Describes a network request by framework as well as network resolution 1352 | * provided by the the executor or Agent. 1353 | * 1354 | * A framework may request the network isolator on the Agent to assign an IP 1355 | * address to the container being launched. Alternatively, it can provide a 1356 | * specific IP address to be assigned to the container. The NetworkInfo message 1357 | * is not interpreted by the Master or Agent and is intended to be use by Agent 1358 | * modules implementing network isolation. If the modules are missing, the 1359 | * message is simply ignored. In future, the task launch will fail if there is 1360 | * no module providing the network isolation capabilities (MESOS-3390). 1361 | * 1362 | * An executor, Agent, or an Agent module may append NetworkInfos inside 1363 | * TaskStatus::container_status to provide information such as the container IP 1364 | * address and isolation groups. 1365 | */ 1366 | message NetworkInfo { 1367 | enum Protocol { 1368 | IPv4 = 1; 1369 | IPv6 = 2; 1370 | } 1371 | 1372 | // Specify IP address requirement. Set protocol to the desired value to 1373 | // request the network isolator on the Agent to assign an IP address to the 1374 | // container being launched. If a specific IP address is specified in 1375 | // ip_address, this field should not be set. 1376 | optional Protocol protocol = 1; 1377 | 1378 | // Statically assigned IP provided by the Framework. This IP will be assigned 1379 | // to the container by the network isolator module on the Agent. This field 1380 | // should not be used with the protocol field above. 1381 | // NOTE: It is up to the networking 'provider' (IPAM/Isolator) to interpret 1382 | // this either as a hint of as a requirement for assigning the IP. 1383 | optional string ip_address = 2; 1384 | 1385 | // A group is the name given to a set of logically-related IPs that are 1386 | // allowed to communicate within themselves. For example, one might want 1387 | // to create separate groups for isolating dev, testing, qa and prod 1388 | // deployment environments. 1389 | repeated string groups = 3; 1390 | 1391 | // To tag certain metadata to be used by Isolator/IPAM, e.g., rack, etc. 1392 | optional Labels labels = 4; 1393 | }; 1394 | 1395 | 1396 | /** 1397 | * Describes a container configuration and allows extensible 1398 | * configurations for different container implementations. 1399 | */ 1400 | message ContainerInfo { 1401 | // All container implementation types. 1402 | enum Type { 1403 | DOCKER = 1; 1404 | MESOS = 2; 1405 | } 1406 | 1407 | message DockerInfo { 1408 | // The docker image that is going to be passed to the registry. 1409 | required string image = 1; 1410 | 1411 | // Network options. 1412 | enum Network { 1413 | HOST = 1; 1414 | BRIDGE = 2; 1415 | NONE = 3; 1416 | } 1417 | 1418 | optional Network network = 2 [default = HOST]; 1419 | 1420 | message PortMapping { 1421 | required uint32 host_port = 1; 1422 | required uint32 container_port = 2; 1423 | // Protocol to expose as (ie: tcp, udp). 1424 | optional string protocol = 3; 1425 | } 1426 | 1427 | repeated PortMapping port_mappings = 3; 1428 | 1429 | optional bool privileged = 4 [default = false]; 1430 | 1431 | // Allowing arbitrary parameters to be passed to docker CLI. 1432 | // Note that anything passed to this field is not guaranteed 1433 | // to be supported moving forward, as we might move away from 1434 | // the docker CLI. 1435 | repeated Parameter parameters = 5; 1436 | 1437 | // With this flag set to true, the docker containerizer will 1438 | // pull the docker image from the registry even if the image 1439 | // is already downloaded on the slave. 1440 | optional bool force_pull_image = 6; 1441 | } 1442 | 1443 | message MesosInfo { 1444 | optional Image image = 1; 1445 | } 1446 | 1447 | required Type type = 1; 1448 | repeated Volume volumes = 2; 1449 | optional string hostname = 4; 1450 | 1451 | // Only one of the following *Info messages should be set to match 1452 | // the type. 1453 | optional DockerInfo docker = 3; 1454 | optional MesosInfo mesos = 5; 1455 | 1456 | // A list of network requests. A framework can request multiple IP addresses 1457 | // for the container. 1458 | repeated NetworkInfo network_infos = 7; 1459 | } 1460 | 1461 | 1462 | /** 1463 | * Container related information that is resolved during container setup. The 1464 | * information is sent back to the framework as part of the TaskStatus message. 1465 | */ 1466 | message ContainerStatus { 1467 | // This field can be reliably used to identify the container IP address. 1468 | repeated NetworkInfo network_infos = 1; 1469 | } 1470 | 1471 | 1472 | /** 1473 | * Collection of labels. 1474 | */ 1475 | message Labels { 1476 | repeated Label labels = 1; 1477 | } 1478 | 1479 | 1480 | /** 1481 | * Key, value pair used to store free form user-data. 1482 | */ 1483 | message Label { 1484 | required string key = 1; 1485 | optional string value = 2; 1486 | } 1487 | 1488 | 1489 | /** 1490 | * Named port used for service discovery. 1491 | */ 1492 | message Port { 1493 | required uint32 number = 1; 1494 | optional string name = 2; 1495 | optional string protocol = 3; 1496 | } 1497 | 1498 | 1499 | /** 1500 | * Collection of ports. 1501 | */ 1502 | message Ports { 1503 | repeated Port ports = 1; 1504 | } 1505 | 1506 | 1507 | /** 1508 | * Service discovery information. 1509 | * The visibility field restricts discovery within a framework 1510 | * (FRAMEWORK), within a Mesos cluster (CLUSTER), or places no 1511 | * restrictions (EXTERNAL). 1512 | * The environment, location, and version fields provide first class 1513 | * support for common attributes used to differentiate between 1514 | * similar services. The environment may receive values such as 1515 | * PROD/QA/DEV, the location field may receive values like 1516 | * EAST-US/WEST-US/EUROPE/AMEA, and the version field may receive 1517 | * values like v2.0/v0.9. The exact use of these fields is up to each 1518 | * service discovery system. 1519 | */ 1520 | message DiscoveryInfo { 1521 | enum Visibility { 1522 | FRAMEWORK = 0; 1523 | CLUSTER = 1; 1524 | EXTERNAL = 2; 1525 | } 1526 | 1527 | required Visibility visibility = 1; 1528 | optional string name = 2; 1529 | optional string environment = 3; 1530 | optional string location = 4; 1531 | optional string version = 5; 1532 | optional Ports ports = 6; 1533 | optional Labels labels = 7; 1534 | } 1535 | 1536 | 1537 | /** 1538 | * Protobuf for the Appc image manifest JSON schema: 1539 | * https://github.com/appc/spec/blob/master/spec/aci.md#image-manifest-schema 1540 | * Where possible, any field required in the schema is required in the protobuf 1541 | * but some cannot be expressed, e.g., a repeated string that has at least one 1542 | * element. Further validation should be performed after parsing the JSON into 1543 | * the protobuf. 1544 | * This version of Appc protobuf is based on Appc spec version 0.6.1. 1545 | * TODO(xujyan): This protobuf currently defines a subset of fields in the spec 1546 | * that Mesos makes use of to avoid confusion. New fields are going to be added 1547 | * when Mesos starts to support them. 1548 | */ 1549 | message AppcImageManifest { 1550 | required string acKind = 1; 1551 | required string acVersion = 2; 1552 | required string name = 3; 1553 | 1554 | message Label { 1555 | required string name = 1; 1556 | required string value = 2; 1557 | } 1558 | 1559 | repeated Label labels = 4; 1560 | 1561 | message Annotation { 1562 | required string name = 1; 1563 | required string value = 2; 1564 | } 1565 | 1566 | repeated Annotation annotations = 5; 1567 | } 1568 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | erlang-mesos 2 | ------------ 3 | 4 | An erlang binding for [mesos](http://mesos.apache.org/). 5 | Currently supports the 0.25.0 version - see [releases](https://github.com/mdevilliers/erlang-mesos/releases) for previous releases. 6 | 7 | [![Build Status](https://travis-ci.org/mdevilliers/erlang-mesos.svg?branch=master)](https://travis-ci.org/mdevilliers/erlang-mesos) 8 | 9 | How does it work 10 | ---------------- 11 | 12 | erlang-mesos has been implemented as a [nif](http://www.erlang.org/doc/tutorial/nif.html). 13 | Messages are sent via the nif to mesos and mesos callbacks into erlang asynchronously. Although nifs are never ideal 14 | the asynchronous implmentaion of mesos lends itself nicely in this case. 15 | 16 | The modules - scheduler.erl and executor.erl are directly equivialant to their counterparts in mesos. 17 | To add your own mesos scheduler or framework you implement the behaviours that they expose. 18 | 19 | For example a basic scheduler would look like this - 20 | 21 | ``` 22 | -behaviour (scheduler). 23 | 24 | init(_) -> 25 | FrameworkInfo = #'FrameworkInfo'{user="", name="My Framework"}, 26 | MasterLocation = "127.0.1.1:5050", 27 | State = [], 28 | {FrameworkInfo, MasterLocation, State}. 29 | 30 | registered(FrameworkID, MasterInfo, State) -> {ok,State}. 31 | 32 | reregistered(MasterInfo, State) -> {ok,State}. 33 | 34 | resourceOffers(Offer, State) -> {ok,State}. 35 | 36 | disconnected(State) -> {ok,State}. 37 | 38 | offerRescinded(OfferID, State) -> {ok,State}. 39 | 40 | statusUpdate(StatusUpdate, State) -> {ok,State}. 41 | 42 | frameworkMessage(ExecutorID, SlaveID, Message, State) -> {ok,State}. 43 | 44 | slaveLost(SlaveID, State) -> {ok,State}. 45 | 46 | executorLost(ExecutorID, SlaveID, Status, State)-> {ok,State}. 47 | 48 | error(Message, State) -> {ok,State}. 49 | 50 | ``` 51 | 52 | 53 | There is an example framework (scheduler) and executor in the src directory. 54 | 55 | There is also an example of using erlang-mesos in an OTP application at [merkxx](https://github.com/mdevilliers/merkxx). 56 | 57 | Future 58 | ------ 59 | 60 | I want to move the implementation to use the low level wire protocol. As details emerge this api wrapper will 61 | move away from the nif implementation to becoming a native erlang implementation. This will of course be 62 | "safer" in an erlang context 63 | 64 | Getting started 65 | --------------- 66 | 67 | [Install mesos](http://mesos.apache.org/gettingstarted/) 68 | 69 | The vagrant file I use for dev testing is at https://github.com/mdevilliers/vagrant-mesos-development-environment. It can be pointed at the version of mesos you wish to develop against plus installs erlang, gcc, git ect. 70 | 71 | Install the protobuf tools. 72 | 73 | ``` 74 | sudo apt-get install libprotobuf-dev protobuf-compiler 75 | ``` 76 | 77 | ``` 78 | git clone ..... 79 | cd erlang-mesos 80 | ``` 81 | 82 | Get the project dependancies 83 | 84 | ``` 85 | ./rebar get-deps 86 | ``` 87 | 88 | Compile the application including the generated modules from the .proto file 89 | 90 | ``` 91 | ./rebar compile 92 | ``` 93 | 94 | After that you should be all set with a 95 | 96 | ``` 97 | ./rebar compile skip_deps=true 98 | ``` 99 | 100 | To run the example framework from a command window 101 | 102 | ``` 103 | erl -pa ebin 104 | scheduler:start_link( example_framework, "127.0.1.1:5050"). 105 | ``` 106 | 107 | Note this will only work on a single node (development) cluster due to the way the example executor is found. 108 | This is only for convenience whilst developing. 109 | 110 | Best Practice 111 | ------------- 112 | 113 | erlang-mesos is a new library interfacing over a nif to a rapidly changing library so at least initally I would recommend running it in another erlang node. 114 | 115 | Help 116 | ----- 117 | 118 | To debug mesos you can use the following enviromental variable before starting up the erlang runtime. 119 | 120 | ``` 121 | GLOG_v=1 erl -pa ebin 122 | ``` 123 | 124 | Thanks 125 | ------ 126 | 127 | Special thanks goes to [mokele](https://github.com/mokele) for the acceptOffer implementation. 128 | 129 | [mesos-go](https://github.com/mesosphere/mesos-go) was an extremely helpful examplar 130 | -------------------------------------------------------------------------------- /rebar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdevilliers/erlang-mesos/aaac011f50a86da54baad7eba548ee12c30b1ed5/rebar -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | %% 2 | %% erlang compilation configuration 3 | %% 4 | {erl_first_files, [ 5 | "src/executor.erl","src/scheduler.erl" 6 | ]}. 7 | 8 | {erl_opts, [debug_info, 9 | fail_on_warning, 10 | warnings_as_errors 11 | ]}. 12 | 13 | %% 14 | %% eunit configuration 15 | %% 16 | 17 | {cover_enabled , true}. 18 | {cover_print_enabled , true}. 19 | {eunit_opts, [verbose, 20 | {report, {eunit_surefire, [{dir, "."}]}}]}. 21 | 22 | 23 | %% 24 | %% nif compilation configuration 25 | %% 26 | 27 | 28 | 29 | {port_sources, ["c_src/*.c", "c_src/*.cpp"]}. 30 | 31 | {port_envs, [ 32 | {"(linux|solaris)", "LDFLAGS", "$LDFLAGS -lstdc++ /usr/local/lib/libmesos.so"}, 33 | {"CXXFLAGS", "$CXXFLAGS -Wall -O2 -static -std=c++11 -I/usr/local/include -I/usr/local/include/mesos -L/usr/local/lib -L/usr/lib "}] 34 | }. 35 | 36 | {port_specs, [{"priv/executor.so", ["c_src/executor.c", "c_src/*.cpp"]}, 37 | {"priv/scheduler.so", ["c_src/scheduler.c", "c_src/*.cpp"]} 38 | ]}. 39 | 40 | % 41 | % protobuffer compilation configuration 42 | % 43 | 44 | {pre_hooks, 45 | [{compile, "mkdir -p include"}, %% ensure the include dir exists 46 | {compile, 47 | "erl +B -noinput -pa /deps/gpb/ebin " 48 | " -I`pwd`/proto -o-erl src -o-hrl include -modsuffix _pb -il" 49 | " -s gpb_compile c `pwd`/proto/*.proto" 50 | }]}. 51 | 52 | {post_hooks, 53 | [{clean, 54 | "bash -c 'for f in proto/*.proto; " 55 | "do " 56 | " rm -f src/$(basename $f .proto).erl; " 57 | " rm -f include/$(basename $f .proto).hrl; " 58 | "done'"} 59 | ]}. 60 | 61 | {deps, [ 62 | {gpb , ".*", {git, "git://github.com/tomas-abrahamsson/gpb.git", {tag, "3.17.2"}} }, 63 | {meck, ".*", {git, "https://github.com/eproxus/meck.git", {tag, "0.8.2"}}} 64 | ]}. 65 | -------------------------------------------------------------------------------- /src/erlang_mesos.app.src: -------------------------------------------------------------------------------- 1 | {application, erlang_mesos, 2 | [ 3 | {description, ""}, 4 | {vsn, "0.25.0"}, 5 | {registered, []}, 6 | {applications, [kernel,stdlib]} 7 | ]}. 8 | 9 | -------------------------------------------------------------------------------- /src/example_executor.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% Copyright (c) 2015 Mark deVilliers. All Rights Reserved. 3 | %% 4 | %% This file is provided to you under the Apache License, 5 | %% Version 2.0 (the "License"); you may not use this file 6 | %% except in compliance with the License. You may obtain 7 | %% a copy of the License at 8 | %% 9 | %% http://www.apache.org/licenses/LICENSE-2.0 10 | %% 11 | %% Unless required by applicable law or agreed to in writing, 12 | %% software distributed under the License is distributed on an 13 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | %% KIND, either express or implied. See the License for the 15 | %% specific language governing permissions and limitations 16 | %% under the License. 17 | %% 18 | %% ------------------------------------------------------------------- 19 | 20 | -module (example_executor). 21 | 22 | -behaviour (executor). 23 | 24 | -include_lib("mesos_pb.hrl"). 25 | 26 | % api 27 | -export ([main/0, exit/0]). 28 | 29 | % from gen_executor 30 | -export ([init/1, 31 | registered/4, 32 | reregistered/2, 33 | disconnected/1, 34 | launchTask/2, 35 | killTask/2, 36 | frameworkMessage/2, 37 | shutdown/1, 38 | error/2]). 39 | 40 | % 41 | % Example executor 42 | % 43 | % Starts up, sends a framework message, sends some task updates, sleeps for a while then closes. 44 | % 45 | 46 | main() -> 47 | executor:start_link( example_executor, []). 48 | 49 | init(_State)-> 50 | {ok, []}. 51 | 52 | exit() -> 53 | {ok,driver_stopped} = executor:stop(), % stop the executor 54 | ok = executor:destroy(), % destroy and cleanup the nif 55 | io:format("Stopping! Bye...."), 56 | init:stop(). % exit the process 57 | 58 | % call backs 59 | registered(ExecutorInfo, FrameworkInfo, SlaveInfo, State) -> 60 | io:format("Registered callback : ~p ~p ~p~n", [ExecutorInfo, FrameworkInfo, SlaveInfo]), 61 | executor:sendFrameworkMessage("hello from the executor's registered callback"), 62 | {ok,State}. 63 | 64 | reregistered(SlaveInfo, State) -> 65 | io:format("Reregistered callback : ~p ~n", [SlaveInfo]), 66 | {ok,State}. 67 | 68 | disconnected(State) -> 69 | io:format("Disconnected callback~n", []), 70 | {ok,State}. 71 | 72 | launchTask(TaskInfo, State) -> 73 | io:format("LaunchTask callback : ~p ~n", [TaskInfo]), 74 | 75 | TaskId = TaskInfo#'TaskInfo'.task_id, 76 | 77 | executor:sendStatusUpdate(#'TaskStatus'{task_id = TaskId , state='TASK_RUNNING'}), 78 | 79 | timer:sleep(5000), % do some work 80 | executor:sendStatusUpdate(#'TaskStatus'{task_id = TaskId , state='TASK_FINISHED'}), 81 | timer:sleep(50), % artifically slow the process down just to send the task_finished message 82 | spawn(?MODULE, exit, []), % start closing down 83 | {ok,State}. 84 | 85 | killTask(TaskID, State) -> 86 | io:format("KillTask callback : ~p ~n", [TaskID]), 87 | {ok,State}. 88 | 89 | frameworkMessage(Message, State) -> 90 | io:format("FrameworkMessage callback : ~p ~n", [Message]), 91 | {ok,State}. 92 | 93 | shutdown(State) -> 94 | io:format("Shutdown callback~n", []), 95 | {ok,State}. 96 | 97 | error(Message, State) -> 98 | io:format("Error callback : ~p ~n", [Message]), 99 | {ok,State}. 100 | -------------------------------------------------------------------------------- /src/example_framework.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% Copyright (c) 2015 Mark deVilliers. All Rights Reserved. 3 | %% 4 | %% This file is provided to you under the Apache License, 5 | %% Version 2.0 (the "License"); you may not use this file 6 | %% except in compliance with the License. You may obtain 7 | %% a copy of the License at 8 | %% 9 | %% http://www.apache.org/licenses/LICENSE-2.0 10 | %% 11 | %% Unless required by applicable law or agreed to in writing, 12 | %% software distributed under the License is distributed on an 13 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | %% KIND, either express or implied. See the License for the 15 | %% specific language governing permissions and limitations 16 | %% under the License. 17 | %% 18 | %% ------------------------------------------------------------------- 19 | 20 | 21 | -module (example_framework). 22 | -behaviour (scheduler). 23 | 24 | -include_lib("mesos_pb.hrl"). 25 | 26 | % api 27 | -export ([exit/0]). 28 | 29 | % from scheduler 30 | -export ([init/1, 31 | registered/3, 32 | reregistered/2, 33 | disconnected/1, 34 | offerRescinded/2, 35 | statusUpdate/2, 36 | frameworkMessage/4, 37 | slaveLost/2, 38 | executorLost/4, 39 | error/2, 40 | resourceOffers/2]). 41 | 42 | -include_lib("mesos_pb.hrl"). 43 | 44 | -record (framework_state, { tasks_started = 0 }). 45 | 46 | % 47 | % Example framework (scheduler) 48 | % 49 | % Starts up, listens for resource offers, starts one task (the example executor), listens for updates 50 | % When the task stops listens for more resource offers.... 51 | % 52 | % scheduler:start_link( example_framework, "127.0.1.1:5050"). 53 | 54 | % api 55 | init(MasterLocation) -> 56 | FrameworkInfo = #'FrameworkInfo'{user="", name="Erlang Test Framework"}, 57 | State = #framework_state{}, 58 | {FrameworkInfo, MasterLocation, State}. 59 | 60 | exit() -> 61 | {ok,driver_stopped} = scheduler:stop(0), % stop the scheduler 62 | ok = scheduler:destroy(). % destroy and cleanup the nif 63 | 64 | % call backs 65 | registered(FrameworkID, MasterInfo, State) -> 66 | io:format("Registered callback : ~p ~p~n", [FrameworkID, MasterInfo]), 67 | {ok,State}. 68 | 69 | reregistered(MasterInfo, State) -> 70 | io:format("ReRegistered callback : ~p ~n", [MasterInfo]), 71 | {ok,State}. 72 | 73 | resourceOffers(Offer,#framework_state{ tasks_started = 1} = State) -> 74 | io:format("Reached max tasks [1] so declining offer.~n", []), 75 | scheduler:declineOffer(Offer#'Offer'.id), 76 | {ok,State}; 77 | resourceOffers(Offer, State) -> 78 | io:format("ResourceOffers callback : ~p ~n", [Offer]), 79 | State1 = State#framework_state{tasks_started = 1}, 80 | io:format("Launching Task.", []), 81 | 82 | Scalar = mesos_pb:enum_symbol_by_value('Value.Type', 0), 83 | Resource1 = #'Resource'{name="cpus", type=Scalar, scalar=#'Value.Scalar'{value=1}}, 84 | 85 | % example to launch a local executor - the example_executor.erl 86 | % will only work in development but proves the point 87 | {ok, CurrentFolder } = file:get_cwd(), 88 | 89 | Command = "export HOME=. && cd " ++ CurrentFolder ++" && erl -pa ebin -noshell -noinput -run example_executor main", 90 | 91 | {_,{H,M,S}} = calendar:local_time(), 92 | 93 | Id = integer_to_list(H) ++ integer_to_list(M) ++ integer_to_list(S), 94 | 95 | TaskInfo = #'TaskInfo'{ 96 | name = "erlang_task" ++ Id, 97 | task_id = #'TaskID'{ value = "task_id_" ++ Id}, 98 | slave_id = Offer#'Offer'.slave_id, 99 | resources = [Resource1], 100 | executor = #'ExecutorInfo'{ executor_id= #'ExecutorID'{ value = "executor_id_" ++ Id}, 101 | command = #'CommandInfo'{value = Command}} 102 | }, 103 | io:format("TaskInfo : ~p~n", [TaskInfo]), 104 | {ok,driver_running} = scheduler:launchTasks(Offer#'Offer'.id, [TaskInfo]), 105 | {ok,State1}. 106 | 107 | disconnected(State) -> 108 | io:format("Disconnected callback"), 109 | {ok,State}. 110 | 111 | offerRescinded(OfferID, State) -> 112 | io:format("OfferRescinded callback : ~p ~n", [OfferID]), 113 | {ok,State}. 114 | 115 | statusUpdate( #'TaskStatus'{state='TASK_LOST',message=Reason}, State) -> 116 | io:format("StatusUpdate callback : ~p -> task lost. Reason : ~p .~n", ['TASK_LOST', Reason]), 117 | {ok,State}; 118 | statusUpdate(#'TaskStatus'{state='TASK_RUNNING'}, State) -> 119 | io:format("StatusUpdate callback : ~p -> task running.~n", ['TASK_RUNNING']), 120 | {ok,State}; 121 | statusUpdate( #'TaskStatus'{ state='TASK_FINISHED'}, State) -> 122 | io:format("StatusUpdate callback : ~p -> decrementing current tasks.~n", ['TASK_FINISHED']), 123 | State1 = State#framework_state{tasks_started = 0}, 124 | {ok,State1}; 125 | statusUpdate(StatusUpdate, State) -> 126 | io:format("StatusUpdate callback : ~p ~n", [StatusUpdate]), 127 | {ok,State}. 128 | 129 | frameworkMessage(ExecutorID, SlaveID, Message, State) -> 130 | io:format("FrameworkMessage callback : ~p ~p ~p ~n", [ExecutorID, SlaveID, Message]), 131 | {ok,State}. 132 | 133 | slaveLost(SlaveID, State) -> 134 | io:format("SlaveLost callback : ~p ~n", [SlaveID]), 135 | {ok,State}. 136 | 137 | executorLost(ExecutorID, SlaveID, Status, State) -> 138 | io:format("ExecutorLost callback : ~p ~p ~p ~n", [ExecutorID, SlaveID, Status]), 139 | {ok,State}. 140 | 141 | error(Message, State) -> 142 | io:format("Error callback : ~p ~n", [Message]), 143 | {ok,State}. 144 | -------------------------------------------------------------------------------- /src/executor.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% Copyright (c) 2015 Mark deVilliers. All Rights Reserved. 3 | %% 4 | %% This file is provided to you under the Apache License, 5 | %% Version 2.0 (the "License"); you may not use this file 6 | %% except in compliance with the License. You may obtain 7 | %% a copy of the License at 8 | %% 9 | %% http://www.apache.org/licenses/LICENSE-2.0 10 | %% 11 | %% Unless required by applicable law or agreed to in writing, 12 | %% software distributed under the License is distributed on an 13 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | %% KIND, either express or implied. See the License for the 15 | %% specific language governing permissions and limitations 16 | %% under the License. 17 | %% 18 | %% ------------------------------------------------------------------- 19 | 20 | -module (executor). 21 | -behaviour(gen_server). 22 | 23 | %api 24 | -export ([ start/2, 25 | start_link/2, 26 | join/0, 27 | abort/0, 28 | stop/0, 29 | sendFrameworkMessage/1, 30 | sendStatusUpdate/1, 31 | destroy/0]). 32 | 33 | %gen server 34 | -export([init/1, handle_call/3, handle_info/2, terminate/2, handle_cast/2,code_change/3]). 35 | 36 | -include_lib("mesos_pb.hrl"). 37 | -include_lib("mesos_erlang.hrl"). 38 | 39 | %% callback specifications 40 | -callback init(Args :: any()) -> {ok, State :: any}. 41 | 42 | -callback registered( ExecutorInfo :: #'ExecutorInfo'{}, 43 | FrameworkInfo :: #'FrameworkInfo'{}, 44 | SlaveInfo :: #'SlaveInfo'{}, 45 | State :: any())-> {ok, State :: any()}. 46 | 47 | -callback reregistered(SlaveInfo :: #'SlaveInfo'{}, State :: any()) -> {ok, State :: any()}. 48 | 49 | -callback disconnected(State :: any()) -> {ok, State :: any()}. 50 | 51 | -callback launchTask(TaskInfo :: #'TaskInfo'{}, State :: any()) -> {ok, State :: any()}. 52 | 53 | -callback killTask(TaskID :: #'TaskID'{}, State :: any()) -> {ok, State :: any()}. 54 | 55 | -callback frameworkMessage(Message :: string(), State :: any()) -> {ok, State :: any()}. 56 | 57 | -callback shutdown(State :: any()) -> {ok, State :: any()}. 58 | 59 | -callback error(Message :: string(), State :: any()) -> {ok, State :: any()}. 60 | 61 | %% ----------------------------------------------------------------------------------------- 62 | 63 | %% implementation 64 | 65 | %% ----------------------------------------------------------------------------------------- 66 | 67 | -record(state, { 68 | handler_module, %% Handler callback module 69 | handler_state %% Handler state 70 | }). 71 | 72 | %% ----------------------------------------------------------------------------------------- 73 | 74 | -spec start( Module :: atom(), Args :: term()) -> 75 | {ok, Server :: pid()} | {error, Reason :: term()}. 76 | start(Module, Args) -> 77 | gen_server:start(?MODULE, {Module, Args}, []). 78 | 79 | %% ----------------------------------------------------------------------------------------- 80 | 81 | -spec start_link( Module :: atom(), Args :: term()) -> 82 | {ok, Server :: pid()} | {error, Reason :: term()}. 83 | start_link(Module, Args ) -> 84 | gen_server:start_link(?MODULE, {Module, Args}, []). 85 | 86 | %% ----------------------------------------------------------------------------------------- 87 | 88 | -spec join() -> {ok, driver_running } | { error, executor_not_inited} | {error, driver_state()}. 89 | join() -> 90 | nif_executor:join(). 91 | 92 | %% ----------------------------------------------------------------------------------------- 93 | 94 | -spec abort() -> {ok, driver_running } | { error, executor_not_inited} | {error, driver_state()}. 95 | abort() -> 96 | nif_executor:abort(). 97 | 98 | %% ----------------------------------------------------------------------------------------- 99 | 100 | -spec stop() -> {ok, driver_running } | { error, executor_not_inited} | {error, driver_state()}. 101 | stop() -> 102 | nif_executor:stop(). 103 | 104 | %% ----------------------------------------------------------------------------------------- 105 | 106 | -spec sendFrameworkMessage( Message :: string() ) -> 107 | {ok, driver_running } 108 | | {error, {invalid_or_corrupted_parameter, data }} 109 | | {error, executor_not_inited} 110 | | {error, driver_state()}. 111 | 112 | sendFrameworkMessage(Data) when is_list(Data) -> 113 | nif_executor:sendFrameworkMessage(Data). 114 | %% ----------------------------------------------------------------------------------------- 115 | 116 | -spec sendStatusUpdate( TaskStatus :: #'TaskStatus'{} ) -> 117 | {ok, driver_running } 118 | | {error, {invalid_or_corrupted_parameter, task_status }} 119 | | {error, executor_not_inited} 120 | | {error, driver_state()}. 121 | 122 | sendStatusUpdate(TaskStatus) when is_record(TaskStatus, 'TaskStatus') -> 123 | nif_executor:sendStatusUpdate(TaskStatus). 124 | %% ----------------------------------------------------------------------------------------- 125 | 126 | -spec destroy() -> ok | {error, executor_not_inited}. 127 | 128 | destroy() -> 129 | Response = nif_executor:destroy(), 130 | 131 | case whereis(?MODULE) of 132 | undefined -> ok; 133 | _ -> unregister(?MODULE) 134 | end, 135 | 136 | Response. 137 | 138 | %% ----------------------------------------------------------------------------------------- 139 | %% ----------------------------------------------------------------------------------------- 140 | %% ----------------------------------------------------------------------------------------- 141 | %% ----------------------------------------------------------------------------------------- 142 | %% Gen Server Implementation 143 | %% ----------------------------------------------------------------------------------------- 144 | %% ----------------------------------------------------------------------------------------- 145 | %% ----------------------------------------------------------------------------------------- 146 | %% ----------------------------------------------------------------------------------------- 147 | init({Module, Args}) -> 148 | 149 | case whereis(?MODULE) of 150 | undefined -> 151 | register(?MODULE, self()), 152 | case Module:init(Args) of 153 | {ok, State} -> 154 | ok = nif_executor:init(self()), 155 | {ok,driver_running} = nif_executor:start(), 156 | {ok, #state{ 157 | handler_module = Module, 158 | handler_state = State 159 | }}; 160 | Else -> 161 | Error = {bad_return_value, Else}, 162 | {stop, Error} 163 | end; 164 | Pid -> 165 | {stop, {already_started,Pid}} 166 | end. 167 | 168 | handle_call(_Request, _From, State) -> 169 | {reply, ok, State}. 170 | 171 | handle_cast(_Msg, State) -> 172 | {noreply, State}. 173 | 174 | 175 | handle_info({registered , ExecutorInfoBin, FrameworkInfoBin, SlaveInfoBin }, #state{ handler_module = Module, handler_state = HandlerState }) -> 176 | ExecutorInfo = mesos_pb:decode_msg(ExecutorInfoBin, 'ExecutorInfo'), 177 | FrameworkInfo = mesos_pb:decode_msg(FrameworkInfoBin, 'FrameworkInfo'), 178 | SlaveInfo = mesos_pb:decode_msg(SlaveInfoBin, 'SlaveInfo'), 179 | 180 | {ok, State1} = Module:registered(ExecutorInfo, FrameworkInfo, SlaveInfo, HandlerState), 181 | {noreply, #state{ handler_module = Module, handler_state = State1 }}; 182 | 183 | handle_info({reregistered, SlaveInfoBin}, #state{ handler_module = Module, handler_state = HandlerState }) -> 184 | SlaveInfo = mesos_pb:decode_msg(SlaveInfoBin, 'SlaveInfo'), 185 | 186 | {ok, State1} = Module:reregistered(SlaveInfo, HandlerState), 187 | {noreply, #state{ handler_module = Module, handler_state = State1 }}; 188 | 189 | handle_info({disconnected}, #state{ handler_module = Module, handler_state = HandlerState }) -> 190 | 191 | {ok, State1} = Module:disconnected(HandlerState), 192 | {noreply, #state{ handler_module = Module, handler_state = State1 }}; 193 | 194 | handle_info({launchTask, TaskInfoBin}, #state{ handler_module = Module, handler_state = HandlerState }) -> 195 | TaskInfo = mesos_pb:decode_msg(TaskInfoBin, 'TaskInfo'), 196 | 197 | {ok, State1} = Module:launchTask(TaskInfo, HandlerState), 198 | {noreply, #state{ handler_module = Module, handler_state = State1 }}; 199 | 200 | handle_info({killTask, TaskIDBin} , #state{ handler_module = Module, handler_state = HandlerState }) -> 201 | TaskID = mesos_pb:decode_msg(TaskIDBin, 'TaskID'), 202 | 203 | {ok, State1} = Module:killTask(TaskID, HandlerState), 204 | {noreply, #state{ handler_module = Module, handler_state = State1 }}; 205 | 206 | handle_info({frameworkMessage, Message}, #state{ handler_module = Module, handler_state = HandlerState }) -> 207 | {ok, State1} = Module:frameworkMessage(Message, HandlerState), 208 | {noreply, #state{ handler_module = Module, handler_state = State1 }}; 209 | 210 | handle_info({shutdown}, #state{ handler_module = Module, handler_state = HandlerState }) -> 211 | {ok, State1} = Module:shutdown(HandlerState), 212 | {noreply, #state{ handler_module = Module, handler_state = State1 }}; 213 | 214 | handle_info({error, Message}, #state{ handler_module = Module, handler_state = HandlerState }) -> 215 | {ok, State1} = Module:error(Message, HandlerState), 216 | {noreply, #state{ handler_module = Module, handler_state = State1 }}. 217 | 218 | code_change(_, State, _) -> 219 | {ok, State}. 220 | 221 | terminate(_Reason, _State) -> 222 | do_terminate(), 223 | ok. 224 | 225 | % helpers 226 | do_terminate()-> 227 | executor:stop(), 228 | executor:destroy(). -------------------------------------------------------------------------------- /src/nif_executor.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% Copyright (c) 2015 Mark deVilliers. All Rights Reserved. 3 | %% 4 | %% This file is provided to you under the Apache License, 5 | %% Version 2.0 (the "License"); you may not use this file 6 | %% except in compliance with the License. You may obtain 7 | %% a copy of the License at 8 | %% 9 | %% http://www.apache.org/licenses/LICENSE-2.0 10 | %% 11 | %% Unless required by applicable law or agreed to in writing, 12 | %% software distributed under the License is distributed on an 13 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | %% KIND, either express or implied. See the License for the 15 | %% specific language governing permissions and limitations 16 | %% under the License. 17 | %% 18 | %% ------------------------------------------------------------------- 19 | 20 | -module (nif_executor). 21 | 22 | -include_lib("mesos_pb.hrl"). 23 | 24 | -export ([ init/1, 25 | start/0, 26 | join/0, 27 | abort/0, 28 | stop/0, 29 | sendFrameworkMessage/1, 30 | sendStatusUpdate/1, 31 | destroy/0]). 32 | 33 | -on_load(init/0). 34 | 35 | -define(APPNAME, erlang_mesos). 36 | -define(LIBNAME, executor). 37 | 38 | init(Pid) when is_pid(Pid) -> 39 | nif_executor_init(Pid). 40 | 41 | start() -> 42 | nif_executor_start(). 43 | 44 | join() -> 45 | nif_executor_join(). 46 | 47 | abort() -> 48 | nif_executor_abort(). 49 | 50 | stop() -> 51 | nif_executor_stop(). 52 | 53 | sendFrameworkMessage(Data) when is_list(Data)-> 54 | nif_executor_sendFrameworkMessage(Data). 55 | 56 | sendStatusUpdate(TaskStatus) when is_record(TaskStatus, 'TaskStatus') -> 57 | nif_executor_sendStatusUpdate(mesos_pb:encode_msg(TaskStatus)). 58 | 59 | destroy() -> 60 | nif_executor_destroy(). 61 | 62 | % nif functions 63 | 64 | nif_executor_init(_)-> 65 | not_loaded(?LINE). 66 | nif_executor_start() -> 67 | not_loaded(?LINE). 68 | nif_executor_join() -> 69 | not_loaded(?LINE). 70 | nif_executor_abort() -> 71 | not_loaded(?LINE). 72 | nif_executor_stop() -> 73 | not_loaded(?LINE). 74 | nif_executor_sendFrameworkMessage(_)-> 75 | not_loaded(?LINE). 76 | nif_executor_sendStatusUpdate(_) -> 77 | not_loaded(?LINE). 78 | nif_executor_destroy() -> 79 | not_loaded(?LINE). 80 | 81 | init() -> 82 | SoName = case code:priv_dir(?APPNAME) of 83 | {error, bad_name} -> 84 | case filelib:is_dir(filename:join(["..", priv])) of 85 | true -> 86 | filename:join(["..", priv, ?LIBNAME]); 87 | _ -> 88 | filename:join([priv, ?LIBNAME]) 89 | end; 90 | Dir -> 91 | filename:join(Dir, ?LIBNAME) 92 | end, 93 | erlang:load_nif(SoName, 0). 94 | 95 | not_loaded(Line) -> 96 | exit({not_loaded, [{module, ?MODULE}, {line, Line}]}). 97 | -------------------------------------------------------------------------------- /src/nif_scheduler.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% Copyright (c) 2015 Mark deVilliers. All Rights Reserved. 3 | %% 4 | %% This file is provided to you under the Apache License, 5 | %% Version 2.0 (the "License"); you may not use this file 6 | %% except in compliance with the License. You may obtain 7 | %% a copy of the License at 8 | %% 9 | %% http://www.apache.org/licenses/LICENSE-2.0 10 | %% 11 | %% Unless required by applicable law or agreed to in writing, 12 | %% software distributed under the License is distributed on an 13 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | %% KIND, either express or implied. See the License for the 15 | %% specific language governing permissions and limitations 16 | %% under the License. 17 | %% 18 | %% ------------------------------------------------------------------- 19 | 20 | 21 | -module (nif_scheduler). 22 | 23 | -include_lib("mesos_pb.hrl"). 24 | 25 | -export ([ init/5, 26 | init/4, 27 | start/0, 28 | join/0, 29 | abort/0, 30 | stop/1, 31 | acceptOffers/2, 32 | acceptOffers/3, 33 | declineOffer/1, 34 | declineOffer/2, 35 | killTask/1, 36 | reviveOffers/0, 37 | sendFrameworkMessage/3, 38 | requestResources/1, 39 | reconcileTasks/1, 40 | launchTasks/2, 41 | launchTasks/3, 42 | destroy/0, 43 | acknowledgeStatusUpdate/1]). 44 | 45 | -on_load(init/0). 46 | 47 | -define(APPNAME, erlang_mesos). 48 | -define(LIBNAME, scheduler). 49 | 50 | init(Pid, FrameworkInfo, MasterLocation, ImplicitAcknowledgements, Credential) when is_pid(Pid), 51 | is_record(FrameworkInfo, 'FrameworkInfo'), 52 | is_list(MasterLocation), 53 | is_boolean(ImplicitAcknowledgements), 54 | is_record(Credential,'Credential')-> 55 | nif_scheduler_init(Pid, mesos_pb:encode_msg(FrameworkInfo), MasterLocation, bool_to_int(ImplicitAcknowledgements), mesos_pb:encode_msg(Credential)). 56 | 57 | init(Pid, FrameworkInfo, MasterLocation, ImplicitAcknowledgements) when is_pid(Pid), 58 | is_record(FrameworkInfo, 'FrameworkInfo'), 59 | is_list(MasterLocation), 60 | is_boolean(ImplicitAcknowledgements)-> 61 | nif_scheduler_init(Pid, mesos_pb:encode_msg(FrameworkInfo), MasterLocation, bool_to_int(ImplicitAcknowledgements)). 62 | 63 | start() -> 64 | nif_scheduler_start(). 65 | 66 | join() -> 67 | nif_scheduler_join(). 68 | 69 | abort() -> 70 | nif_scheduler_abort(). 71 | 72 | stop(Failover) when is_integer(Failover), 73 | Failover > -1, 74 | Failover < 2 -> 75 | nif_scheduler_stop(Failover). 76 | 77 | acceptOffers(OfferIDs, Operations) when is_list(OfferIDs), 78 | is_list(Operations) -> 79 | acceptOffers(OfferIDs, Operations, #'Filters'{}). 80 | acceptOffers(OfferIDs, Operations, Filters) when is_list(OfferIDs), 81 | is_list(Operations), 82 | is_record(Filters, 'Filters') -> 83 | nif_scheduler_acceptOffers( encode_array(OfferIDs, []), encode_array(Operations, []), mesos_pb:encode_msg(Filters)). 84 | 85 | declineOffer(OfferId) when is_record(OfferId, 'OfferID') -> 86 | Filter = #'Filters'{}, 87 | nif_scheduler_declineOffer(mesos_pb:encode_msg(OfferId), mesos_pb:encode_msg(Filter)). 88 | 89 | declineOffer(OfferId,Filter) when is_record(OfferId, 'OfferID'), 90 | is_record(Filter, 'Filters') -> 91 | nif_scheduler_declineOffer(mesos_pb:encode_msg(OfferId), mesos_pb:encode_msg(Filter)). 92 | 93 | killTask(TaskId) when is_record(TaskId,'TaskID')-> 94 | nif_scheduler_killTask(mesos_pb:encode_msg(TaskId)). 95 | 96 | reviveOffers() -> 97 | nif_scheduler_reviveOffers(). 98 | 99 | sendFrameworkMessage(ExecutorId,SlaveId,Data) when is_record(ExecutorId, 'ExecutorID'), 100 | is_record(SlaveId, 'SlaveID'), 101 | is_list(Data)-> 102 | nif_scheduler_sendFrameworkMessage(mesos_pb:encode_msg(ExecutorId), mesos_pb:encode_msg(SlaveId), Data). 103 | 104 | requestResources(Requests) when is_list(Requests) -> 105 | EncodedRequests = encode_array(Requests, []), 106 | nif_scheduler_requestResources(EncodedRequests). 107 | 108 | reconcileTasks(TaskStatuss) when is_list(TaskStatuss)-> 109 | EncodedTaskStatus = encode_array(TaskStatuss, []), 110 | nif_scheduler_reconcileTasks(EncodedTaskStatus). 111 | 112 | launchTasks(OfferId, TaskInfos ) when is_record(OfferId, 'OfferID'), 113 | is_list(TaskInfos) -> 114 | EncodedTaskInfos = encode_array(TaskInfos, []), 115 | Filter = #'Filters'{}, 116 | nif_scheduler_launchTasks(mesos_pb:encode_msg(OfferId), EncodedTaskInfos, mesos_pb:encode_msg(Filter)). 117 | 118 | launchTasks(OfferId, TaskInfos, Filter ) when is_record(OfferId, 'OfferID'), 119 | is_list(TaskInfos), 120 | is_record(Filter, 'Filters') -> 121 | EncodedTaskInfos = encode_array(TaskInfos, []), 122 | nif_scheduler_launchTasks(mesos_pb:encode_msg(OfferId), EncodedTaskInfos, mesos_pb:encode_msg(Filter)). 123 | 124 | destroy()-> 125 | nif_scheduler_destroy(). 126 | 127 | acknowledgeStatusUpdate(TaskStatus) when is_record(TaskStatus, 'TaskStatus') -> 128 | nif_scheduler_acknowledgeStatusUpdate(mesos_pb:encode_msg(TaskStatus)). 129 | 130 | % nif functions 131 | nif_scheduler_init(_, _, _, _, _)-> 132 | not_loaded(?LINE). 133 | nif_scheduler_init(_, _, _, _)-> 134 | not_loaded(?LINE). 135 | nif_scheduler_start() -> 136 | not_loaded(?LINE). 137 | nif_scheduler_join() -> 138 | not_loaded(?LINE). 139 | nif_scheduler_abort() -> 140 | not_loaded(?LINE). 141 | nif_scheduler_stop(_) -> 142 | not_loaded(?LINE). 143 | nif_scheduler_acceptOffers(_, _, _)-> 144 | not_loaded(?LINE). 145 | nif_scheduler_declineOffer(_,_)-> 146 | not_loaded(?LINE). 147 | nif_scheduler_killTask(_) -> 148 | not_loaded(?LINE). 149 | nif_scheduler_reviveOffers() -> 150 | not_loaded(?LINE). 151 | nif_scheduler_sendFrameworkMessage(_,_,_) -> 152 | not_loaded(?LINE). 153 | nif_scheduler_requestResources(_) -> 154 | not_loaded(?LINE). 155 | nif_scheduler_reconcileTasks(_) -> 156 | not_loaded(?LINE). 157 | nif_scheduler_launchTasks(_,_,_) -> 158 | not_loaded(?LINE). 159 | nif_scheduler_destroy() -> 160 | not_loaded(?LINE). 161 | nif_scheduler_acknowledgeStatusUpdate(_) -> 162 | not_loaded(?LINE). 163 | 164 | init() -> 165 | SoName = case code:priv_dir(?APPNAME) of 166 | {error, bad_name} -> 167 | case filelib:is_dir(filename:join(["..", priv])) of 168 | true -> 169 | filename:join(["..", priv, ?LIBNAME]); 170 | _ -> 171 | filename:join([priv, ?LIBNAME]) 172 | end; 173 | Dir -> 174 | filename:join(Dir, ?LIBNAME) 175 | end, 176 | erlang:load_nif(SoName, 0). 177 | 178 | not_loaded(Line) -> 179 | exit({not_loaded, [{module, ?MODULE}, {line, Line}]}). 180 | 181 | % helpers 182 | bool_to_int(true) -> 1; 183 | bool_to_int(false) -> 0. 184 | 185 | encode_array([], Acc) -> Acc; 186 | encode_array([H|T], Acc) -> 187 | encode_array(T, [mesos_pb:encode_msg(H) | Acc]). 188 | -------------------------------------------------------------------------------- /src/scheduler.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% Copyright (c) 2015 Mark deVilliers. All Rights Reserved. 3 | %% 4 | %% This file is provided to you under the Apache License, 5 | %% Version 2.0 (the "License"); you may not use this file 6 | %% except in compliance with the License. You may obtain 7 | %% a copy of the License at 8 | %% 9 | %% http://www.apache.org/licenses/LICENSE-2.0 10 | %% 11 | %% Unless required by applicable law or agreed to in writing, 12 | %% software distributed under the License is distributed on an 13 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | %% KIND, either express or implied. See the License for the 15 | %% specific language governing permissions and limitations 16 | %% under the License. 17 | %% 18 | %% ------------------------------------------------------------------- 19 | 20 | 21 | -module (scheduler). 22 | -behaviour(gen_server). 23 | 24 | %api 25 | -export([ 26 | start/2, 27 | start_link/2, 28 | join/0, 29 | abort/0, 30 | stop/1, 31 | acceptOffers/2, 32 | acceptOffers/3, 33 | declineOffer/1, 34 | declineOffer/2, 35 | killTask/1, 36 | reviveOffers/0, 37 | sendFrameworkMessage/3, 38 | requestResources/1, 39 | reconcileTasks/1, 40 | launchTasks/2, 41 | launchTasks/3, 42 | destroy/0, 43 | acknowledgeStatusUpdate/1]). 44 | 45 | %gen server 46 | -export([init/1, handle_call/3, handle_info/2, terminate/2, handle_cast/2, 47 | code_change/3]). 48 | 49 | -include_lib("mesos_pb.hrl"). 50 | -include_lib("mesos_erlang.hrl"). 51 | 52 | % callback specifications 53 | -callback init( Args :: any()) -> {FrameworkInfo :: #'FrameworkInfo'{}, MasterLocation :: string(), State :: any()} | 54 | {FrameworkInfo :: #'FrameworkInfo'{}, MasterLocation :: string(), ImplicitAcknowledgements :: boolean(), State :: any()} | 55 | {FrameworkInfo :: #'FrameworkInfo'{}, MasterLocation :: string(), Credential :: #'Credential'{}, State :: any()} | 56 | {FrameworkInfo :: #'FrameworkInfo'{}, MasterLocation :: string(), ImplicitAcknowledgements :: boolean(), Credential :: #'Credential'{}, State :: any()}. 57 | 58 | -callback registered( FrameworkInfo :: #'FrameworkInfo'{}, 59 | MasterInfo :: #'MasterInfo'{}, 60 | State :: any()) -> {ok, State :: any()}. 61 | 62 | -callback reregistered( MasterInfo :: #'MasterInfo'{}, State :: any()) -> {ok, State :: any()}. 63 | 64 | -callback disconnected( State :: any()) -> {ok, State :: any()}. 65 | 66 | -callback resourceOffers( Offer :: #'Offer'{}, State :: any()) -> {ok, State :: any()}. 67 | 68 | -callback offerRescinded( OfferID :: #'OfferID'{}, State :: any()) -> {ok, State :: any()}. 69 | 70 | -callback statusUpdate( TaskStatus :: #'TaskStatus'{},State :: any()) -> {ok, State :: any()}. 71 | 72 | -callback frameworkMessage( ExecutorId :: #'ExecutorID'{}, 73 | SlaveId :: #'SlaveID'{}, 74 | Message :: string(), 75 | State :: any()) -> {ok, State :: any()}. 76 | 77 | -callback slaveLost( SlaveId :: #'SlaveID'{},State :: any()) -> {ok, State :: any()}. 78 | 79 | -callback executorLost( ExecutorId :: #'ExecutorID'{}, 80 | SlaveId :: #'SlaveID'{}, 81 | Status :: pos_integer(),State :: any()) -> {ok, State :: any()}. 82 | 83 | -callback error(Message :: string(),State :: any()) -> {ok, State :: any()}. 84 | 85 | %% ----------------------------------------------------------------------------------------- 86 | 87 | -record(state, { 88 | handler_module, %% Handler callback module 89 | handler_state %% Handler state 90 | }). 91 | 92 | %% ----------------------------------------------------------------------------------------- 93 | 94 | -spec start( Module :: atom(), Args :: term()) -> 95 | {ok, Server :: pid()} | {error, Reason :: term()}. 96 | start(Module, Args) -> 97 | gen_server:start(?MODULE, {Module, Args}, []). 98 | 99 | %% ----------------------------------------------------------------------------------------- 100 | 101 | -spec start_link( Module :: atom(), Args :: term()) -> 102 | {ok, Server :: pid()} | {error, Reason :: term()}. 103 | start_link(Module, Args ) -> 104 | gen_server:start_link(?MODULE, {Module, Args}, []). 105 | 106 | %% ----------------------------------------------------------------------------------------- 107 | 108 | -spec join() -> {ok, driver_running } | { error, scheduler_not_inited} | {error, driver_state()}. 109 | join() -> 110 | nif_scheduler:join(). 111 | 112 | %% ----------------------------------------------------------------------------------------- 113 | 114 | -spec abort() -> {ok, driver_aborted } | { state_error, scheduler_not_inited} | {error, driver_state()}. 115 | abort() -> 116 | nif_scheduler:abort(). 117 | 118 | %% ----------------------------------------------------------------------------------------- 119 | 120 | -spec stop(integer()) -> {ok, driver_aborted } | { error, scheduler_not_inited} | {error, driver_state()}. 121 | stop(Failover) when is_integer(Failover), 122 | Failover > -1, 123 | Failover < 2 -> 124 | nif_scheduler:stop(Failover). 125 | 126 | %% ----------------------------------------------------------------------------------------- 127 | 128 | -spec acceptOffers( OfferIDs :: list(#'OfferID'{}), 129 | Operations :: list(#'Offer.Operation'{})) -> 130 | {ok, driver_running } 131 | | {error, scheduler_not_inited} 132 | | {error, {invalid_or_corrupted_parameter, offerid_array}} 133 | | {error, {invalid_or_corrupted_parameter, operations_array}} 134 | | {error, driver_state()}. 135 | acceptOffers(OfferIDs, Operations) -> 136 | nif_scheduler:acceptOffers(OfferIDs, Operations). 137 | 138 | -spec acceptOffers( OfferIDs :: list(#'OfferID'{}), 139 | Operations :: list(#'Offer.Operation'{}), 140 | Filters :: #'Filters'{}) -> 141 | {ok, driver_running } 142 | | {error, scheduler_not_inited} 143 | | {error, {invalid_or_corrupted_parameter, offerid_array}} 144 | | {error, {invalid_or_corrupted_parameter, operations_array}} 145 | | {error, {invalid_or_corrupted_parameter, filters}} 146 | | {error, driver_state()}. 147 | acceptOffers(OfferIDs, Operations, Filters) -> 148 | nif_scheduler:acceptOffers(OfferIDs, Operations, Filters). 149 | 150 | %% ----------------------------------------------------------------------------------------- 151 | 152 | -spec declineOffer( OfferId :: #'OfferID'{}) -> 153 | {ok, driver_running } 154 | | {error, scheduler_not_inited} 155 | | {error, {invalid_or_corrupted_parameter, offer_id}} 156 | | {error, driver_state()}. 157 | 158 | declineOffer(OfferId) when is_record(OfferId, 'OfferID') -> 159 | nif_scheduler:declineOffer(OfferId). 160 | 161 | -spec declineOffer( OfferId :: #'OfferID'{}, 162 | Filter :: #'Filters'{}) -> 163 | {ok, driver_running } 164 | | {error, scheduler_not_inited} 165 | | {error, {invalid_or_corrupted_parameter, offer_id}} 166 | | {error, {invalid_or_corrupted_parameter, filters}} 167 | | {error, driver_state()}. 168 | 169 | declineOffer(OfferId,Filter) when is_record(OfferId, 'OfferID'), 170 | is_record(Filter, 'Filters') -> 171 | nif_scheduler:declineOffer(OfferId, Filter). 172 | 173 | %% ----------------------------------------------------------------------------------------- 174 | 175 | -spec killTask( TaskId :: #'TaskID'{}) -> 176 | {ok, driver_running } 177 | | {error, scheduler_not_inited} 178 | | {error, {invalid_or_corrupted_parameter, task_id}} 179 | | {error, driver_state()}. 180 | 181 | killTask(TaskId) when is_record(TaskId,'TaskID') -> 182 | nif_scheduler:killTask(TaskId). 183 | 184 | %% ----------------------------------------------------------------------------------------- 185 | 186 | -spec reviveOffers() -> {ok, driver_aborted } | { error, scheduler_not_inited} | {error, driver_state()}. 187 | 188 | reviveOffers()-> 189 | nif_scheduler:reviveOffers(). 190 | 191 | %% ----------------------------------------------------------------------------------------- 192 | 193 | -spec sendFrameworkMessage( ExecutorId :: #'ExecutorID'{}, 194 | SlaveId :: #'SlaveID'{}, 195 | Data ::string()) -> 196 | {ok, driver_running } 197 | | {error, scheduler_not_inited} 198 | | {error, {invalid_or_corrupted_parameter, executor_id}} 199 | | {error, {invalid_or_corrupted_parameter, slave_id}} 200 | | {error, {invalid_or_corrupted_parameter, data}} 201 | | {error, driver_state()}. 202 | 203 | sendFrameworkMessage(ExecutorId,SlaveId,Data) when is_record(ExecutorId, 'ExecutorID'), 204 | is_record(SlaveId, 'SlaveID'), 205 | is_list(Data)-> 206 | nif_scheduler:sendFrameworkMessage(ExecutorId,SlaveId,Data). 207 | 208 | %% ----------------------------------------------------------------------------------------- 209 | 210 | -spec requestResources( Requests :: [ #'Request'{} ]) -> 211 | {ok, driver_running } 212 | | {error, scheduler_not_inited} 213 | | {error, {invalid_or_corrupted_parameter, request_array}} 214 | | {error, driver_state()}. 215 | 216 | requestResources(Requests) when is_list(Requests) -> 217 | nif_scheduler:requestResources(Requests). 218 | 219 | %% ----------------------------------------------------------------------------------------- 220 | 221 | -spec reconcileTasks( TaskStatus :: [ #'TaskStatus'{} ]) -> 222 | {ok, driver_running } 223 | | {error, scheduler_not_inited} 224 | | {error, {invalid_or_corrupted_parameter, task_status_array}} 225 | | {error, driver_state()}. 226 | 227 | reconcileTasks(TaskStatus)when is_list(TaskStatus)-> 228 | nif_scheduler:reconcileTasks(TaskStatus). 229 | 230 | %% ----------------------------------------------------------------------------------------- 231 | 232 | -spec launchTasks( OfferId :: #'OfferID'{}, 233 | TaskInfos :: [ #'TaskInfo'{}]) -> 234 | {ok, driver_running } 235 | | {error, scheduler_not_inited} 236 | | {error, {invalid_or_corrupted_parameter, offer_id}} 237 | | {error, {invalid_or_corrupted_parameter, task_info_array}} 238 | | {error, driver_state()}. 239 | 240 | launchTasks(OfferId, TaskInfos) when is_record(OfferId, 'OfferID'), 241 | is_list(TaskInfos) -> 242 | nif_scheduler:launchTasks(OfferId, TaskInfos). 243 | 244 | -spec launchTasks( OfferId :: #'OfferID'{}, 245 | TaskInfos :: [ #'TaskInfo'{}], 246 | Filter :: #'Filters'{}) -> 247 | {ok, driver_running } 248 | | {error, scheduler_not_inited} 249 | | {error, {invalid_or_corrupted_parameter, offer_id}} 250 | | {error, {invalid_or_corrupted_parameter, task_info_array}} 251 | | {error, {invalid_or_corrupted_parameter, filters}} 252 | | {error, driver_state()}. 253 | 254 | launchTasks(OfferId, TaskInfos, Filter) when is_record(OfferId, 'OfferID'), 255 | is_list(TaskInfos), 256 | is_record(Filter, 'Filters') -> 257 | nif_scheduler:launchTasks(OfferId, TaskInfos, Filter). 258 | 259 | %% ----------------------------------------------------------------------------------------- 260 | 261 | -spec destroy() -> ok | {error, scheduler_not_inited}. 262 | destroy() -> 263 | Response = nif_scheduler:destroy(), 264 | 265 | case whereis(?MODULE) of 266 | undefined -> ok; 267 | _ -> unregister(?MODULE) 268 | end, 269 | 270 | Response. 271 | 272 | %% ----------------------------------------------------------------------------------------- 273 | 274 | -spec acknowledgeStatusUpdate(TaskStatus :: #'TaskStatus'{}) -> 275 | {ok, driver_running } 276 | | {error, scheduler_not_inited} 277 | | {error, {invalid_or_corrupted_parameter, task_status}} 278 | | {error, driver_state()}. 279 | acknowledgeStatusUpdate( TaskStatus ) when is_record(TaskStatus, 'TaskStatus') -> 280 | nif_scheduler:acknowledgeStatusUpdate(TaskStatus). 281 | 282 | %% ----------------------------------------------------------------------------------------- 283 | %% ----------------------------------------------------------------------------------------- 284 | %% ----------------------------------------------------------------------------------------- 285 | %% Gen Server Implementation 286 | %% ----------------------------------------------------------------------------------------- 287 | %% ----------------------------------------------------------------------------------------- 288 | %% ----------------------------------------------------------------------------------------- 289 | init({Module, Args}) -> 290 | 291 | case whereis(?MODULE) of 292 | undefined -> 293 | register(?MODULE, self()), 294 | case Module:init(Args) of 295 | {FrameworkInfo, MasterLocation, ImplicitAcknowledgements, State} when is_record(FrameworkInfo, 'FrameworkInfo'), 296 | is_list(MasterLocation), 297 | is_boolean(ImplicitAcknowledgements) -> 298 | 299 | ok = nif_scheduler:init(self(), FrameworkInfo, MasterLocation, ImplicitAcknowledgements), 300 | {ok,driver_running} = nif_scheduler:start(), 301 | {ok, #state{ 302 | handler_module = Module, 303 | handler_state = State 304 | }}; 305 | {FrameworkInfo, MasterLocation, State} when is_record(FrameworkInfo, 'FrameworkInfo'), 306 | is_list(MasterLocation) -> 307 | ok = nif_scheduler:init(self(), FrameworkInfo, MasterLocation, true), 308 | {ok,driver_running} = nif_scheduler:start(), 309 | {ok, #state{ 310 | handler_module = Module, 311 | handler_state = State 312 | }}; 313 | {FrameworkInfo, MasterLocation, ImplicitAcknowledgements, Credential, State} when is_record(FrameworkInfo, 'FrameworkInfo'), 314 | is_list(MasterLocation), 315 | is_boolean(ImplicitAcknowledgements), 316 | is_record(Credential, 'Credential') -> 317 | ok = nif_scheduler:init(self(), FrameworkInfo, MasterLocation, ImplicitAcknowledgements, Credential), 318 | {ok,driver_running} = nif_scheduler:start(), 319 | {ok, #state{ 320 | handler_module = Module, 321 | handler_state = State 322 | }}; 323 | {FrameworkInfo, MasterLocation, Credential, State} when is_record(FrameworkInfo, 'FrameworkInfo'), 324 | is_record(Credential, 'Credential'), 325 | is_list(MasterLocation) -> 326 | ok = nif_scheduler:init(self(), FrameworkInfo, MasterLocation, true, Credential), 327 | {ok,driver_running} = nif_scheduler:start(), 328 | {ok, #state{ 329 | handler_module = Module, 330 | handler_state = State 331 | }}; 332 | Else -> 333 | Error = {bad_return_value, Else}, 334 | {stop, Error} 335 | end; 336 | Pid -> 337 | {stop, {already_started,Pid}} 338 | end. 339 | 340 | handle_call(_Request, _From, State) -> 341 | {reply, ok, State}. 342 | 343 | handle_cast(_Msg, State) -> 344 | {noreply, State}. 345 | 346 | handle_info({registered , FrameworkIdBin, MasterInfoBin }, #state{ handler_module = Module, handler_state = HandlerState }) -> 347 | 348 | FrameworkId = mesos_pb:decode_msg(FrameworkIdBin, 'FrameworkID'), 349 | MasterInfo = mesos_pb:decode_msg(MasterInfoBin, 'MasterInfo'), 350 | MasterInfo2 = MasterInfo#'MasterInfo'{ip = int_to_ip(MasterInfo#'MasterInfo'.ip)}, 351 | 352 | {ok, State1} = Module:registered(FrameworkId, MasterInfo2, HandlerState), 353 | {noreply, #state{ handler_module = Module, handler_state = State1 }}; 354 | 355 | handle_info({resourceOffers, OfferBin}, #state{ handler_module = Module, handler_state = HandlerState }) -> 356 | 357 | Offer = mesos_pb:decode_msg(OfferBin, 'Offer'), 358 | {ok, State1} = Module:resourceOffers(Offer, HandlerState), 359 | {noreply, #state{ handler_module = Module, handler_state = State1 }}; 360 | 361 | handle_info({reregistered, MasterInfoBin}, #state{ handler_module = Module, handler_state = HandlerState }) -> 362 | 363 | MasterInfo = mesos_pb:decode_msg(MasterInfoBin, 'MasterInfo'), 364 | MasterInfo2 = MasterInfo#'MasterInfo'{ip = int_to_ip(MasterInfo#'MasterInfo'.ip)}, 365 | {ok, State1} = Module:reregistered(MasterInfo2, HandlerState), 366 | {noreply, #state{ handler_module = Module, handler_state = State1 }}; 367 | 368 | handle_info({disconnected}, #state{ handler_module = Module, handler_state = HandlerState }) -> 369 | 370 | {ok, State1} = Module:disconnected(HandlerState), 371 | {noreply, #state{ handler_module = Module, handler_state = State1 }}; 372 | 373 | handle_info({offerRescinded, OfferIdBin}, #state{ handler_module = Module, handler_state = HandlerState }) -> 374 | OfferId = mesos_pb:decode_msg(OfferIdBin, 'OfferID'), 375 | {ok, State1} = Module:offerRescinded(OfferId, HandlerState), 376 | {noreply, #state{ handler_module = Module, handler_state = State1 }}; 377 | 378 | handle_info({statusUpdate, TaskStatusBin}, #state{ handler_module = Module, handler_state = HandlerState }) -> 379 | TaskStatus = mesos_pb:decode_msg(TaskStatusBin, 'TaskStatus'), 380 | {ok, State1} = Module:statusUpdate(TaskStatus, HandlerState), 381 | {noreply, #state{ handler_module = Module, handler_state = State1 }}; 382 | 383 | handle_info({frameworkMessage, ExecutorIdBin, SlaveIdBin, Message}, #state{ handler_module = Module, handler_state = HandlerState }) -> 384 | ExecutorId = mesos_pb:decode_msg(ExecutorIdBin, 'ExecutorID'), 385 | SlaveId = mesos_pb:decode_msg(SlaveIdBin, 'SlaveID'), 386 | {ok, State1} = Module:frameworkMessage(ExecutorId, SlaveId, Message, HandlerState), 387 | {noreply, #state{ handler_module = Module, handler_state = State1 }}; 388 | 389 | handle_info({slaveLost, SlaveIdBin}, #state{ handler_module = Module, handler_state = HandlerState }) -> 390 | SlaveId = mesos_pb:decode_msg(SlaveIdBin, 'SlaveID'), 391 | {ok, State1} = Module:slaveLost(SlaveId, HandlerState), 392 | {noreply, #state{ handler_module = Module, handler_state = State1 }}; 393 | 394 | handle_info({executorLost, ExecutorIdBin, SlaveIdBin, Status}, #state{ handler_module = Module, handler_state = HandlerState }) -> 395 | ExecutorId = mesos_pb:decode_msg(ExecutorIdBin, 'ExecutorID'), 396 | SlaveId = mesos_pb:decode_msg(SlaveIdBin, 'SlaveID'), 397 | {ok, State1} = Module:executorLost(ExecutorId, SlaveId, Status, HandlerState), 398 | {noreply, #state{ handler_module = Module, handler_state = State1 }}; 399 | 400 | handle_info({error, Message}, #state{ handler_module = Module, handler_state = HandlerState }) -> 401 | {ok, State1} = Module:error(Message, HandlerState), 402 | {noreply, #state{ handler_module = Module, handler_state = State1 }}. 403 | 404 | terminate(_Reason, _) -> 405 | do_terminate(), 406 | ok. 407 | 408 | code_change(_, State, _) -> 409 | {ok, State}. 410 | 411 | % helpers 412 | int_to_ip(Ip)-> {Ip bsr 24, (Ip band 16711680) bsr 16, (Ip band 65280) bsr 8, Ip band 255}. 413 | 414 | do_terminate() -> 415 | scheduler:stop(0), 416 | scheduler:destroy(). 417 | -------------------------------------------------------------------------------- /test/mesos_scheduler_integration_tests.erl: -------------------------------------------------------------------------------- 1 | -module (mesos_scheduler_integration_tests). 2 | -include_lib("eunit/include/eunit.hrl"). 3 | -include ("mesos_pb.hrl"). 4 | -include ("mesos_erlang.hrl"). 5 | 6 | % these tests connect to a running instance of mesos 7 | % change the location used here... 8 | -define (MASTER_LOCATION, "127.0.0.1:5050"). 9 | 10 | init_and_clean_exit_init_scheduler_test()-> 11 | 12 | {ok, _Pid} = scheduler:start_link( example_framework, ?MASTER_LOCATION), 13 | example_framework:exit(), 14 | {ok, _Pid2} = scheduler:start_link( example_framework, ?MASTER_LOCATION), 15 | example_framework:exit(). 16 | 17 | inited_scheduler_can_not_be_reinited_test()-> 18 | 19 | meck:new(test_framework, [non_strict]), 20 | 21 | FrameworkInfo = #'FrameworkInfo'{user="", name="Erlang Test Framework"}, 22 | 23 | meck:expect(test_framework, init , fun(_) -> { FrameworkInfo, ?MASTER_LOCATION, []} end), 24 | meck:expect(test_framework, registered , fun(_FrameworkID, _MasterInfo, State) -> {ok,State} end), 25 | meck:expect(test_framework, resourceOffers , fun(_Offer, State) -> {ok,State} end), 26 | 27 | {ok, _} = scheduler:start_link( test_framework, ?MASTER_LOCATION), 28 | {error,{already_started, _}} = scheduler:start( test_framework, ?MASTER_LOCATION), 29 | 30 | {ok, driver_stopped} = scheduler:stop(0), 31 | ok = scheduler:destroy(), 32 | 33 | meck:unload(test_framework). 34 | 35 | unknown_message_to_scheduler_will_not_crash_scheduler_test() -> 36 | 37 | meck:new(test_framework, [non_strict]), 38 | 39 | FrameworkInfo = #'FrameworkInfo'{user="", name="Erlang Test Framework"}, 40 | 41 | meck:expect(test_framework, init , fun(_) -> { FrameworkInfo, ?MASTER_LOCATION, []} end), 42 | meck:expect(test_framework, registered , fun(_FrameworkID, _MasterInfo, State) -> {ok,State} end), 43 | meck:expect(test_framework, resourceOffers , fun(_Offer, State) -> {ok,State} end), 44 | 45 | {ok,CheekyPid} = scheduler:start( test_framework, ?MASTER_LOCATION), 46 | 47 | CheekyPid = whereis(scheduler), 48 | CheekyPid ! {booya}, 49 | CheekyPid = whereis(scheduler), 50 | 51 | {ok, driver_stopped} = scheduler:stop(0), 52 | ok = scheduler:destroy(), 53 | 54 | meck:unload(test_framework). 55 | 56 | crash_in_scheduler_closes_nif_connections_gracefully_test()-> 57 | 58 | % naughty framework 59 | meck:new(test_framework, [non_strict]), 60 | 61 | FrameworkInfo = #'FrameworkInfo'{user="", name="Erlang Test Framework"}, 62 | meck:expect(test_framework, init , fun(_) -> { FrameworkInfo, ?MASTER_LOCATION, []} end), 63 | meck:expect(test_framework, registered , fun(_FrameworkID, _MasterInfo,_State) -> meck:exception(error, boom) end), 64 | meck:expect(test_framework, resourceOffers , fun(_Offer, State) -> {ok,State} end), 65 | 66 | {ok,_} = scheduler:start(test_framework, ?MASTER_LOCATION), 67 | 68 | io:format(user, "waiting for crash~n", []), 69 | timer:sleep(10), % wait for crash 70 | io:format(user, "starting up again~n", []), 71 | meck:unload(test_framework), 72 | 73 | % nice framework 74 | meck:new(test_framework, [non_strict]), 75 | meck:expect(test_framework, init , fun(_) -> { FrameworkInfo, ?MASTER_LOCATION, []} end), 76 | meck:expect(test_framework, registered , fun(_FrameworkID, _MasterInfo, State) -> {ok,State} end), 77 | meck:expect(test_framework, resourceOffers , fun(_Offer, State) -> {ok,State} end), 78 | 79 | {ok,_} = scheduler:start(test_framework, ?MASTER_LOCATION), 80 | 81 | {ok, driver_stopped} = scheduler:stop(0), 82 | ok = scheduler:destroy(), 83 | 84 | meck:unload(test_framework). 85 | -------------------------------------------------------------------------------- /test/mesos_scheduler_integration_tests_acknowledgements.erl: -------------------------------------------------------------------------------- 1 | -module (mesos_scheduler_integration_tests_acknowledgements). 2 | 3 | -include_lib("eunit/include/eunit.hrl"). 4 | -include ("mesos_pb.hrl"). 5 | -include ("mesos_erlang.hrl"). 6 | 7 | % these tests connect to a running instance of mesos 8 | % change the location used here... 9 | -define (MASTER_LOCATION, "127.0.0.1:5050"). 10 | 11 | explicit_acknowledgement_test() -> 12 | 13 | meck:new(test_framework, [non_strict]), 14 | 15 | FrameworkInfo = #'FrameworkInfo'{user="", name="AcknowledgeTests"}, 16 | 17 | % NOTE : returns false to enable explicit acknowledgements 18 | meck:expect(test_framework, init , fun(_) -> { FrameworkInfo, ?MASTER_LOCATION, false , []} end), 19 | 20 | meck:expect(test_framework, registered , fun(_FrameworkID, _MasterInfo, State) -> {ok,State} end), 21 | 22 | meck:expect(test_framework, resourceOffers , fun(Offer, State) -> 23 | 24 | Scalar = mesos_pb:enum_symbol_by_value('Value.Type', 0), 25 | Resource1 = #'Resource'{name="cpus", type=Scalar, scalar=#'Value.Scalar'{value=1}}, 26 | 27 | Command = "sleep 10", 28 | 29 | {_,{H,M,S}} = calendar:local_time(), 30 | Id = integer_to_list(H) ++ integer_to_list(M) ++ integer_to_list(S), 31 | 32 | TaskInfo = #'TaskInfo'{ 33 | name = "erlang_task" ++ Id, 34 | task_id = #'TaskID'{ value = "task_id_" ++ Id}, 35 | slave_id = Offer#'Offer'.slave_id, 36 | resources = [Resource1], 37 | command = #'CommandInfo'{value = Command} 38 | }, 39 | 40 | {ok,driver_running} = scheduler:launchTasks(Offer#'Offer'.id, [TaskInfo]), 41 | 42 | {ok,State} 43 | end), 44 | 45 | meck:expect(test_framework, statusUpdate, fun(TaskStatus, State) -> 46 | scheduler:acknowledgeStatusUpdate(TaskStatus), 47 | {ok,State} 48 | end), 49 | 50 | {ok, _} = scheduler:start_link( test_framework, ?MASTER_LOCATION), 51 | 52 | timer:sleep(1000), 53 | 54 | scheduler:stop(0), 55 | scheduler:destroy(), 56 | 57 | meck:unload(test_framework). --------------------------------------------------------------------------------