ccsession.cc 8.66 KB
Newer Older
JINMEI Tatuya's avatar
JINMEI Tatuya committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

// $Id$

17 18 19 20 21 22 23
// 
// todo: generalize this and make it into a specific API for all modules
//       to use (i.e. connect to cc, send config and commands, get config,
//               react on config change announcements)
//


JINMEI Tatuya's avatar
JINMEI Tatuya committed
24
#include <stdexcept>
25
#include <stdlib.h>
JINMEI Tatuya's avatar
JINMEI Tatuya committed
26
#include <string.h>
27
#include <sys/time.h>
JINMEI Tatuya's avatar
JINMEI Tatuya committed
28

29
#include <iostream>
30
#include <fstream>
31
#include <sstream>
32
#include <cerrno>
33 34

#include <boost/foreach.hpp>
35

36
#include <cc/data.h>
37
#include <module_spec.h>
38
#include <cc/session.h>
Jelte Jansen's avatar
Jelte Jansen committed
39
#include <exceptions/exceptions.h>
JINMEI Tatuya's avatar
JINMEI Tatuya committed
40

41
//#include "common.h"
42
#include "ccsession.h"
43
#include "config.h"
JINMEI Tatuya's avatar
JINMEI Tatuya committed
44

45
using namespace std;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
46

Jelte Jansen's avatar
Jelte Jansen committed
47 48 49
using isc::data::Element;
using isc::data::ElementPtr;
using isc::data::ParseError;
50 51 52

namespace isc {
namespace config {
53

Jelte Jansen's avatar
Jelte Jansen committed
54
/// Creates a standard config/command protocol answer message
55 56 57 58 59 60 61 62 63
ElementPtr
createAnswer(const int rcode)
{
    ElementPtr answer = Element::createFromString("{\"result\": [] }");
    ElementPtr answer_content = answer->get("result");
    answer_content->add(Element::create(rcode));
    return answer;
}

64
ElementPtr
Jelte Jansen's avatar
Jelte Jansen committed
65
createAnswer(const int rcode, const ElementPtr arg)
66
{
67
    ElementPtr answer = Element::createFromString("{\"result\": [] }");
68 69 70 71 72 73 74
    ElementPtr answer_content = answer->get("result");
    answer_content->add(Element::create(rcode));
    answer_content->add(arg);
    return answer;
}

ElementPtr
Jelte Jansen's avatar
Jelte Jansen committed
75
createAnswer(const int rcode, const std::string& arg)
76
{
77
    ElementPtr answer = Element::createFromString("{\"result\": [] }");
78 79 80 81 82 83
    ElementPtr answer_content = answer->get("result");
    answer_content->add(Element::create(rcode));
    answer_content->add(Element::create(arg));
    return answer;
}

Jelte Jansen's avatar
Jelte Jansen committed
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
ElementPtr
parseAnswer(int &rcode, const ElementPtr msg)
{
    if (!msg->contains("result")) {
        // TODO: raise CCSessionError exception
        dns_throw(CCSessionError, "No result in answer message");
    } else {
        ElementPtr result = msg->get("result");
        if (result->get(0)->getType() != Element::integer) {
            dns_throw(CCSessionError, "First element of result is not an rcode in answer message");
        } else if (result->get(0)->intValue() != 0 && result->get(1)->getType() != Element::string) {
            dns_throw(CCSessionError, "Rcode in answer message is non-zero, but other argument is not a StringElement");
        }
        rcode = result->get(0)->intValue();
        if (result->size() > 1) {
            return result->get(1);
        } else {
            return ElementPtr();
        }
    }
}
105

106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
ElementPtr
createCommand(const std::string& command, ElementPtr arg)
{
    ElementPtr cmd = Element::createFromString("{}");
    ElementPtr cmd_parts = Element::createFromString("[]");
    cmd_parts->add(Element::create(command));
    if (arg) {
        cmd_parts->add(arg);
    }
    cmd->set("command", cmd_parts);
    return cmd;
}

/// Returns "" and empty ElementPtr() if this does not
/// look like a command
const std::string
parseCommand(ElementPtr& arg, const ElementPtr command)
{
    if (command->getType() == Element::map &&
        command->contains("command")) {
        ElementPtr cmd = command->get("command");
        if (cmd->getType() == Element::list &&
            cmd->size() > 0 &&
            cmd->get(0)->getType() == Element::string) {
            if (cmd->size() > 1) {
                arg = cmd->get(1);
            } else {
                arg = ElementPtr();
            }
            return cmd->get(0)->stringValue();
        }
    }
    arg = ElementPtr();
    return "";
}

142
void
143
ModuleCCSession::read_module_specification(const std::string& filename) {
144 145 146
    std::ifstream file;

    // this file should be declared in a @something@ directive
147
    file.open(filename.c_str());
148
    if (!file) {
149
        cout << "error opening " << filename << ": " << strerror(errno) << endl;
150 151
        exit(1);
    }
152

153
    try {
154
        module_specification_ = moduleSpecFromFile(file, true);
155
    } catch (ParseError pe) {
156
        cout << "Error parsing module specification file: " << pe.what() << endl;
157
        exit(1);
158
    } catch (ModuleSpecError dde) {
159
        cout << "Error reading module specification file: " << dde.what() << endl;
160 161 162 163
        exit(1);
    }
    file.close();
}
164

165
ModuleCCSession::ModuleCCSession(std::string spec_file_name,
Jelte Jansen's avatar
Jelte Jansen committed
166
                               isc::data::ElementPtr(*config_handler)(isc::data::ElementPtr new_config),
Jelte Jansen's avatar
Jelte Jansen committed
167
                               isc::data::ElementPtr(*command_handler)(const std::string& command, const isc::data::ElementPtr args)
168
                              ) throw (isc::cc::SessionError):
Jelte Jansen's avatar
Jelte Jansen committed
169
    session_(isc::cc::Session())
JINMEI Tatuya's avatar
JINMEI Tatuya committed
170
{
171
    read_module_specification(spec_file_name);
172 173
    sleep(1);

174
    module_name_ = module_specification_.getFullSpec()->get("module_name")->stringValue();
175 176 177
    config_handler_ = config_handler;
    command_handler_ = command_handler;

Jelte Jansen's avatar
Jelte Jansen committed
178 179 180 181 182 183 184
    // todo: workaround, let boss wait until msgq is started
    // and remove sleep here
    sleep(1);

    ElementPtr answer, env;

    session_.establish();
185
    session_.subscribe(module_name_, "*");
186 187
    //session_.subscribe("Boss", "*");
    //session_.subscribe("statistics", "*");
Jelte Jansen's avatar
Jelte Jansen committed
188
    // send the data specification
189
    ElementPtr spec_msg = createCommand("module_spec", module_specification_.getFullSpec());
190
    session_.group_sendmsg(spec_msg, "ConfigManager");
Jelte Jansen's avatar
Jelte Jansen committed
191
    session_.group_recvmsg(env, answer, false);
192 193

    config_ = Element::createFromString("{}");
Jelte Jansen's avatar
Jelte Jansen committed
194 195
    // get any stored configuration from the manager
    if (config_handler_) {
196
        ElementPtr cmd = Element::createFromString("{ \"command\": [\"get_config\", {\"module_name\":\"" + module_name_ + "\"} ] }");
Jelte Jansen's avatar
Jelte Jansen committed
197
        session_.group_sendmsg(cmd, "ConfigManager");
198
        session_.group_recvmsg(env, answer, false);
Jelte Jansen's avatar
Jelte Jansen committed
199 200 201 202 203 204 205
        int rcode;
        ElementPtr new_config = parseAnswer(rcode, answer);
        handleConfigUpdate(new_config);
    }
}

/// Validates the new config values, if they are correct,
206
/// call the config handler with the values that have changed
Jelte Jansen's avatar
Jelte Jansen committed
207 208 209 210 211
/// If that results in success, store the new config
ElementPtr
ModuleCCSession::handleConfigUpdate(ElementPtr new_config)
{
    ElementPtr answer;
212 213
    ElementPtr errors = Element::createFromString("[]");
    std::cout << "handleConfigUpdate " << new_config << std::endl;
Jelte Jansen's avatar
Jelte Jansen committed
214 215
    if (!config_handler_) {
        answer = createAnswer(1, module_name_ + " does not have a config handler");
216 217 218 219 220 221 222
    } else if (!module_specification_.validate_config(new_config, false, errors)) {
        std::stringstream ss;
        ss << "Error in config validation: ";
        BOOST_FOREACH(ElementPtr error, errors->listValue()) {
            ss << error->stringValue();
        }
        answer = createAnswer(2, ss.str());
Jelte Jansen's avatar
Jelte Jansen committed
223
    } else {
224 225
        // remove the values that have not changed
        isc::data::removeIdentical(new_config, getConfig());
Jelte Jansen's avatar
Jelte Jansen committed
226
        // handle config update
227
        std::cout << "handleConfigUpdate " << new_config << std::endl;
Jelte Jansen's avatar
Jelte Jansen committed
228 229 230 231
        answer = config_handler_(new_config);
        int rcode;
        parseAnswer(rcode, answer);
        if (rcode == 0) {
232
            isc::data::merge(config_, new_config);
233
        }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
234
    }
235
    std::cout << "end handleConfigUpdate " << new_config << std::endl;
Jelte Jansen's avatar
Jelte Jansen committed
236
    return answer;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
237 238
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
239
int
240
ModuleCCSession::getSocket()
JINMEI Tatuya's avatar
JINMEI Tatuya committed
241 242 243 244
{
    return (session_.getSocket());
}

245
int
246
ModuleCCSession::check_command()
247 248 249 250 251
{
    ElementPtr cmd, routing, data;
    if (session_.group_recvmsg(routing, data, true)) {
        /* ignore result messages (in case we're out of sync, to prevent
         * pingpongs */
252
        if (!data->getType() == Element::map || data->contains("result")) {
253
            return 0;
254
        }
Jelte Jansen's avatar
Jelte Jansen committed
255 256
        ElementPtr arg;
        std::string cmd_str = parseCommand(arg, data);
257
        ElementPtr answer;
Jelte Jansen's avatar
Jelte Jansen committed
258 259 260
        if (cmd_str == "config_update") {
            answer = handleConfigUpdate(arg);
        } else {
261
            if (command_handler_) {
Jelte Jansen's avatar
Jelte Jansen committed
262
                answer = command_handler_(cmd_str, arg);
263
            } else {
Jelte Jansen's avatar
Jelte Jansen committed
264
                answer = createAnswer(1, "Command given but no command handler for module");
265 266
            }
        }
267
        session_.reply(routing, answer);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
268
    }
269 270
    
    return 0;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
271
}
272

273 274
}
}