ccsession.cc 7.6 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

void
107
ModuleCCSession::read_module_specification(const std::string& filename) {
108 109 110
    std::ifstream file;

    // this file should be declared in a @something@ directive
111
    file.open(filename.c_str());
112
    if (!file) {
113
        cout << "error opening " << filename << ": " << strerror(errno) << endl;
114 115
        exit(1);
    }
116

117
    try {
118
        module_specification_ = moduleSpecFromFile(file, true);
119
    } catch (ParseError pe) {
120
        cout << "Error parsing module specification file: " << pe.what() << endl;
121
        exit(1);
122
    } catch (ModuleSpecError dde) {
123
        cout << "Error reading module specification file: " << dde.what() << endl;
124 125 126 127
        exit(1);
    }
    file.close();
}
128

129
ModuleCCSession::ModuleCCSession(std::string spec_file_name,
Jelte Jansen's avatar
Jelte Jansen committed
130 131
                               isc::data::ElementPtr(*config_handler)(isc::data::ElementPtr new_config),
                               isc::data::ElementPtr(*command_handler)(isc::data::ElementPtr command)
132
                              ) throw (isc::cc::SessionError):
Jelte Jansen's avatar
Jelte Jansen committed
133
    session_(isc::cc::Session())
JINMEI Tatuya's avatar
JINMEI Tatuya committed
134
{
135
    read_module_specification(spec_file_name);
136 137
    sleep(1);

138
    module_name_ = module_specification_.getFullSpec()->get("module_spec")->get("module_name")->stringValue();
139 140 141
    config_handler_ = config_handler;
    command_handler_ = command_handler;

Jelte Jansen's avatar
Jelte Jansen committed
142 143 144 145 146 147 148
    // todo: workaround, let boss wait until msgq is started
    // and remove sleep here
    sleep(1);

    ElementPtr answer, env;

    session_.establish();
149
    session_.subscribe(module_name_, "*");
150 151
    //session_.subscribe("Boss", "*");
    //session_.subscribe("statistics", "*");
Jelte Jansen's avatar
Jelte Jansen committed
152
    // send the data specification
153
    session_.group_sendmsg(module_specification_.getFullSpec(), "ConfigManager");
Jelte Jansen's avatar
Jelte Jansen committed
154 155 156 157
    session_.group_recvmsg(env, answer, false);
    
    // get any stored configuration from the manager
    if (config_handler_) {
158
        ElementPtr cmd = Element::createFromString("{ \"command\": [\"get_config\", {\"module_name\":\"" + module_name_ + "\"} ] }");
Jelte Jansen's avatar
Jelte Jansen committed
159
        session_.group_sendmsg(cmd, "ConfigManager");
160
        session_.group_recvmsg(env, answer, false);
Jelte Jansen's avatar
Jelte Jansen committed
161
        cout << "[XX] got config: " << endl << answer->str() << endl;
Jelte Jansen's avatar
Jelte Jansen committed
162 163 164 165 166 167 168 169 170 171 172 173 174
        int rcode;
        ElementPtr new_config = parseAnswer(rcode, answer);
        handleConfigUpdate(new_config);
    }
}

/// Validates the new config values, if they are correct,
/// call the config handler
/// If that results in success, store the new config
ElementPtr
ModuleCCSession::handleConfigUpdate(ElementPtr new_config)
{
    ElementPtr answer;
175 176
    ElementPtr errors = Element::createFromString("[]");
    std::cout << "handleConfigUpdate " << new_config << std::endl;
Jelte Jansen's avatar
Jelte Jansen committed
177 178
    if (!config_handler_) {
        answer = createAnswer(1, module_name_ + " does not have a config handler");
179 180 181 182 183 184 185
    } 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
186 187
    } else {
        // handle config update
188
        std::cout << "handleConfigUpdate " << new_config << std::endl;
Jelte Jansen's avatar
Jelte Jansen committed
189 190 191 192 193
        answer = config_handler_(new_config);
        int rcode;
        parseAnswer(rcode, answer);
        if (rcode == 0) {
            config_ = new_config;
194
        }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
195
    }
196
    std::cout << "end handleConfigUpdate " << new_config << std::endl;
Jelte Jansen's avatar
Jelte Jansen committed
197
    return answer;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
198 199
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
200
int
201
ModuleCCSession::getSocket()
JINMEI Tatuya's avatar
JINMEI Tatuya committed
202 203 204 205
{
    return (session_.getSocket());
}

206
int
207
ModuleCCSession::check_command()
208 209 210 211 212 213
{
    cout << "[XX] check for command" << endl;
    ElementPtr cmd, routing, data;
    if (session_.group_recvmsg(routing, data, true)) {
        /* ignore result messages (in case we're out of sync, to prevent
         * pingpongs */
214
        if (!data->getType() == Element::map || data->contains("result")) {
215
            return 0;
216
        }
217 218 219
        cout << "[XX] got something!" << endl << data->str() << endl;
        ElementPtr answer;
        if (data->contains("config_update")) {
220
            ElementPtr new_config = data->get("config_update");
Jelte Jansen's avatar
Jelte Jansen committed
221
            answer = handleConfigUpdate(new_config);
222
        }
223 224 225 226
        if (data->contains("command")) {
            if (command_handler_) {
                answer = command_handler_(data->get("command"));
            } else {
227
                answer = Element::createFromString("{ \"result\": [0] }");
228 229
            }
        }
230
        session_.reply(routing, answer);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
231
    }
232 233
    
    return 0;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
234
}
235

236 237
}
}