From 888049f626fbd7d6ad349ffb2270bcc3886958f1 Mon Sep 17 00:00:00 2001 From: Rudra Rugge Date: Fri, 8 May 2015 10:54:27 -0700 Subject: [PATCH] Generate loadbalancer config in json format Currently the agent generates loadbalancer configuration in haproxy specific format. Going forward agent will generate a generic json based loadbalancer config. This config will be handled by driver specific configuration parser. Currently only haproxy parsing is supported. Closes-Bug: #1452928 Change-Id: I2d198aff0a569615ac5c331e4b6c582b93d9d3a3 Conflicts: src/vnsw/agent/oper/loadbalancer_haproxy.cc LBAAS haproxy process manager Manage haproxy daemon for lbaas. Two options avaialable: - Manage through supervisor. This will run on non-daemon mode as the process cannot be managed by supervisord if it runs in background. Process monitoring provided by supervisor. - Start/stop the daemon as we do today. Need additional changes to ensure monitoring/restarting of the process. Additional commit needed to enable this code from vrouter_netns. Change-Id: I05c13d7c96c86bee2fcddc73342ba28c6010c8e6 Partial-Bug: #1452928 Enable haproxy config translation Enable haproxy config translation from json format Also enable haproxy daemon handling by supervisord Change-Id: If3489ea66430ec0ac50bb6198093a0689fa16219 Closes-Bug: #1452928 Conflicts: src/nodemgr/haproxy_stats.py Generate mac from instance ip for service VMs Generate the same mac-address for all interfaces sharing the same IP. In addition a change to daemonize the haproxy process instead of managing through supervisor. Change-Id: I2394f29c4a11bffeee4b0184ce6cd6867b01e0e9 Closes-Bug: #1461882 Haproxy config generation fixes for HTTPS protocol Change-Id: I140361ad4785be2a87d23a04181e73ca999e8e2b Closes-bug: #1466318 Fix for poodle vulnerability; ChangeId: I9432d035eb59b1ff53cb5d33350cd5f8063e077c; Closes-Bug: #1475392 Change-Id: I390a77261bc0d3257108c06951c79f1d2c3dadaa Fix for FREAK SSL vulnerability This fix pushes selected set of secure ciphers into haproxy config file Change-Id: Idfc11ce0411024e7154d3b2c46a095fb4f80337d Closes-Bug: #1477400 HAProxy Performance Tuning HAProxy's default config is non-performant. This fix updates following config in HAProxy: 1) Increase TCP client/server timeouts. 2) Increase ulimit globally per HAProxy process. 3) Increase maxconn globally per HAProxy process. Change-Id: I28be29d5ab3dcb2a35fcbe9168300edf18b2c23c Closes-Bug: #1477781 Allow custom configs with LBaaS This fix takes care of haproxy parsing and validation changes on vrouter agent. Removing extra white spaces Closes-Bug: #1475393 Change-Id: I822e27792f78168a178d555db5703fa1e73d0cc9 Allow custom configs with LBaaS This fix enables a new field "custom-attr" in loadbalancer_pool properties in the schema. Change-Id: I17eecc2fedea4d1d3889b7e114e99732ac2eecc9 Closes-Bug: #1475393 Allow custom configs with LBaaS This fix commits the vrouter agent code to read the custom_attributes from ifmap node and copy it to config.json file which the haproxy parser would read. Added missing '}'. Incorporating the comments Closes-Bug: #1475393 Change-Id: I6f22f4f537c97c48b2283971b2959c9be5931361 Conflicts: src/vnsw/agent/oper/loadbalancer.cc src/vnsw/agent/oper/loadbalancer_config.cc src/vnsw/agent/oper/loadbalancer_config.h Change-Id: Iea0aff5589a21e3c802e4e63633a1d74f22cdeaf Conflicts: src/vnsw/agent/oper/loadbalancer.cc WIP: Tenant SSL Cert Support This fix adds tenant SSL support to existing custom attributes. User can provide barbican container ref in custom attributes and haproxy parser then downloads the container/secrets and populates the certificate. Also, the keystone auth credentials need to specified in a separate auth file whose path should be provided in contrail-vrouter-agent.conf file. Renaming to file as keystone_auth_cfg_file Change-Id: I2b85733820031033a05dfc27cbfa4fa3a3485611 Partial-Bug: #1499903 Conflicts: src/nodemgr/haproxy_stats.py src/vnsw/agent/oper/instance_manager.cc src/vnsw/agent/oper/netns_instance_adapter.cc src/vnsw/agent/oper/test/instance_manager_test.cc src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/vrouter_netns.py Change-Id: I31535a590867263588d00e889db5e41eec711545 --- src/ksync/ksync_object.cc | 8 +- src/nodemgr/haproxy_stats.py | 2 +- src/vnsw/agent/init/agent_param.cc | 15 +- src/vnsw/agent/init/agent_param.h | 10 +- src/vnsw/agent/oper/SConscript | 2 +- src/vnsw/agent/oper/instance_manager.cc | 78 +++--- src/vnsw/agent/oper/instance_manager.h | 6 +- src/vnsw/agent/oper/loadbalancer.cc | 1 + src/vnsw/agent/oper/loadbalancer_config.cc | 169 +++++++++++++ src/vnsw/agent/oper/loadbalancer_config.h | 38 +++ src/vnsw/agent/oper/loadbalancer_haproxy.cc | 231 ------------------ src/vnsw/agent/oper/loadbalancer_haproxy.h | 50 ---- .../agent/oper/loadbalancer_properties.cc | 50 ++++ src/vnsw/agent/oper/loadbalancer_properties.h | 9 + src/vnsw/agent/oper/netns_instance_adapter.cc | 10 +- src/vnsw/agent/oper/test/SConscript | 10 +- .../agent/oper/test/instance_manager_test.cc | 11 +- ...xy_test.cc => loadbalancer_config_test.cc} | 24 +- .../opencontrail-vrouter-netns/SConscript | 4 + .../haproxy_cert.py | 104 ++++++++ .../haproxy_config.py | 111 +++++++-- .../haproxy_process.py | 137 +++++++++++ .../haproxy_validator.py | 167 +++++++++++++ .../keystone_auth.py | 151 ++++++++++++ .../vrouter_netns.py | 84 ++----- 25 files changed, 1045 insertions(+), 437 deletions(-) create mode 100644 src/vnsw/agent/oper/loadbalancer_config.cc create mode 100644 src/vnsw/agent/oper/loadbalancer_config.h delete mode 100644 src/vnsw/agent/oper/loadbalancer_haproxy.cc delete mode 100644 src/vnsw/agent/oper/loadbalancer_haproxy.h rename src/vnsw/agent/oper/test/{loadbalancer_haproxy_test.cc => loadbalancer_config_test.cc} (85%) create mode 100644 src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/haproxy_cert.py create mode 100644 src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/haproxy_process.py create mode 100644 src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/haproxy_validator.py create mode 100644 src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/keystone_auth.py diff --git a/src/ksync/ksync_object.cc b/src/ksync/ksync_object.cc index 08eb6f632dc..a7d28707997 100644 --- a/src/ksync/ksync_object.cc +++ b/src/ksync/ksync_object.cc @@ -29,7 +29,7 @@ KSyncObject::FwdRefTree KSyncObject::fwd_ref_tree_; KSyncObject::BackRefTree KSyncObject::back_ref_tree_; -KSyncObjectManager *KSyncObjectManager::singleton_; +KSyncObjectManager *KSyncObjectManager::singleton_ = NULL; std::auto_ptr KSyncObjectManager::default_defer_entry_; bool KSyncDebug::debug_; @@ -46,7 +46,7 @@ KSyncObject::KSyncObject() : need_index_(false), index_table_(), stale_entries_per_intvl_(0) { } -KSyncObject::KSyncObject(int max_index) : +KSyncObject::KSyncObject(int max_index) : need_index_(true), index_table_(max_index), delete_scheduled_(false), stale_entry_tree_(), stale_entry_cleanup_timer_(NULL), @@ -1361,7 +1361,9 @@ KSyncObjectManager::~KSyncObjectManager() { SandeshTraceBufferPtr KSyncTraceBuf(SandeshTraceBufferCreate("KSync", 1000)); KSyncObjectManager *KSyncObjectManager::Init() { - singleton_ = new KSyncObjectManager(); + if (singleton_ == NULL) { + singleton_ = new KSyncObjectManager(); + } return singleton_; } diff --git a/src/nodemgr/haproxy_stats.py b/src/nodemgr/haproxy_stats.py index 06424f821ae..3170e2a55bf 100644 --- a/src/nodemgr/haproxy_stats.py +++ b/src/nodemgr/haproxy_stats.py @@ -33,7 +33,7 @@ def __init__(self): pass def get_stats(self, pool_id): - sock_path = LB_BASE_DIR + pool_id + ".haproxy.cfg.sock"; + sock_path = LB_BASE_DIR + pool_id + ".haproxy.sock"; if not os.path.exists(sock_path): sys.stderr.write('\nStats socket not found for pool ' + pool_id) return {} diff --git a/src/vnsw/agent/init/agent_param.cc b/src/vnsw/agent/init/agent_param.cc index ff1437ad2d2..864e6339d4d 100644 --- a/src/vnsw/agent/init/agent_param.cc +++ b/src/vnsw/agent/init/agent_param.cc @@ -517,9 +517,10 @@ void AgentParam::ParseServiceInstance() { "SERVICE-INSTANCE.netns_workers"); GetValueFromTree(si_netns_timeout_, "SERVICE-INSTANCE.netns_timeout"); - GetValueFromTree(si_haproxy_ssl_cert_path_, - "SERVICE-INSTANCE.haproxy_ssl_cert_path"); - + GetValueFromTree(si_lb_ssl_cert_path_, + "SERVICE-INSTANCE.lb_ssl_cert_path"); + GetValueFromTree(si_lb_keystone_auth_conf_path_, + "SERVICE-INSTANCE.lb_keystone_auth_conf_path"); } void AgentParam::ParseNexthopServer() { @@ -688,8 +689,8 @@ void AgentParam::ParseServiceInstanceArguments GetOptValue(var_map, si_docker_command_, "SERVICE-INSTANCE.docker_command"); GetOptValue(var_map, si_netns_workers_, "SERVICE-INSTANCE.netns_workers"); GetOptValue(var_map, si_netns_timeout_, "SERVICE-INSTANCE.netns_timeout"); - GetOptValue(var_map, si_haproxy_ssl_cert_path_, - "SERVICE-INSTANCE.haproxy_ssl_cert_path"); + GetOptValue(var_map, si_lb_ssl_cert_path_, + "SERVICE-INSTANCE.lb_ssl_cert_path"); } @@ -1019,7 +1020,7 @@ void AgentParam::LogConfig() const { LOG(DEBUG, "Service instance docker cmd : " << si_docker_command_); LOG(DEBUG, "Service instance workers : " << si_netns_workers_); LOG(DEBUG, "Service instance timeout : " << si_netns_timeout_); - LOG(DEBUG, "Service instance HAProxy ssl: " << si_haproxy_ssl_cert_path_); + LOG(DEBUG, "Service instance lb ssl : " << si_lb_ssl_cert_path_); if (hypervisor_mode_ == MODE_KVM) { LOG(DEBUG, "Hypervisor mode : kvm"); return; @@ -1106,7 +1107,7 @@ AgentParam::AgentParam(Agent *agent, bool enable_flow_options, xmpp_auth_enable_(false), xmpp_server_cert_(""), simulate_evpn_tor_(false), si_netns_command_(), si_docker_command_(), si_netns_workers_(0), - si_netns_timeout_(0), si_haproxy_ssl_cert_path_(), + si_netns_timeout_(0), si_lb_ssl_cert_path_(), vmware_mode_(ESXI_NEUTRON), nexthop_server_endpoint_(), nexthop_server_add_pid_(0), vrouter_on_nic_mode_(false), diff --git a/src/vnsw/agent/init/agent_param.h b/src/vnsw/agent/init/agent_param.h index 11463b85122..1f94587ac12 100644 --- a/src/vnsw/agent/init/agent_param.h +++ b/src/vnsw/agent/init/agent_param.h @@ -117,8 +117,11 @@ class AgentParam { std::string si_docker_command() const {return si_docker_command_;} const int si_netns_workers() const {return si_netns_workers_;} const int si_netns_timeout() const {return si_netns_timeout_;} - std::string si_haproxy_ssl_cert_path() const { - return si_haproxy_ssl_cert_path_; + std::string si_lb_ssl_cert_path() const { + return si_lb_ssl_cert_path_; + } + std::string si_lb_keystone_auth_conf_path() const { + return si_lb_keystone_auth_conf_path_; } std::string nexthop_server_endpoint() const { @@ -388,7 +391,8 @@ class AgentParam { std::string si_docker_command_; int si_netns_workers_; int si_netns_timeout_; - std::string si_haproxy_ssl_cert_path_; + std::string si_lb_ssl_cert_path_; + std::string si_lb_keystone_auth_conf_path_; VmwareMode vmware_mode_; // List of IP addresses on the compute node. AddressList compute_node_address_list_; diff --git a/src/vnsw/agent/oper/SConscript b/src/vnsw/agent/oper/SConscript index 93b0713904e..3ac931870e6 100644 --- a/src/vnsw/agent/oper/SConscript +++ b/src/vnsw/agent/oper/SConscript @@ -42,7 +42,7 @@ vnswoperdb = env.Library('vnswoperdb', 'inet_unicast_route.cc', 'interface.cc', 'loadbalancer.cc', - 'loadbalancer_haproxy.cc', + 'loadbalancer_config.cc', 'loadbalancer_properties.cc', 'logical_interface.cc', 'mirror_table.cc', diff --git a/src/vnsw/agent/oper/instance_manager.cc b/src/vnsw/agent/oper/instance_manager.cc index a00613727ef..7b1ddec5ef3 100644 --- a/src/vnsw/agent/oper/instance_manager.cc +++ b/src/vnsw/agent/oper/instance_manager.cc @@ -15,7 +15,7 @@ #include "io/event_manager.h" #include "oper/instance_task.h" #include "oper/loadbalancer.h" -#include "oper/loadbalancer_haproxy.h" +#include "oper/loadbalancer_config.h" #include "oper/loadbalancer_properties.h" #include "oper/operdb_init.h" #include "oper/service_instance.h" @@ -99,20 +99,37 @@ class InstanceManager::NamespaceStaleCleaner { //If Loadbalncer, delete the config files as well if (prop.service_type == ServiceInstance::LoadBalancer) { + std::stringstream pathgen; + std::stringstream conf_path, json_path, sock_path; - std::stringstream cfg_path; - cfg_path << - manager_->loadbalancer_config_path_ << prop.pool_id - << ".haproxy.cfg"; + pathgen << manager_->loadbalancer_config_path_ << prop.pool_id; + conf_path << pathgen.str() << ".haproxy.conf"; + json_path << pathgen.str() << ".conf.json"; + sock_path << pathgen.str() << ".haproxy.sock"; boost::system::error_code error; - if (fs::exists(cfg_path.str())) { - fs::remove_all(cfg_path.str(), error); + if (fs::exists(conf_path.str())) { + fs::remove_all(conf_path.str(), error); + if (error) { + LOG(ERROR, "Stale loadbalancer conf fle delete error" + << error.message()); + } } - cfg_path << ".sock"; - if (fs::exists(cfg_path.str())) { - fs::remove_all(cfg_path.str(), error); + if (fs::exists(sock_path.str())) { + fs::remove_all(sock_path.str(), error); + if (error) { + LOG(ERROR, "Stale loadbalancer sock fle delete error" + << error.message()); + } + } + + if (fs::exists(json_path.str())) { + fs::remove_all(json_path.str(), error); + if (error) { + LOG(ERROR, "Stale loadbalancer json file delete error" + << error.message()); + } } } } @@ -137,7 +154,7 @@ InstanceManager::InstanceManager(Agent *agent) loadbalancer_config_path_(loadbalancer_config_path_default), namespace_store_path_(namespace_store_path_default), stale_timer_interval_(5 * 60 * 1000), - haproxy_(new LoadbalancerHaproxy(agent)), + lb_config_(new LoadbalancerConfig(agent)), stale_timer_(TimerManager::CreateTimer(*(agent->event_manager()->io_service()), "NameSpaceStaleTimer", TaskScheduler::GetInstance()-> GetTaskId("db::DBTable"), 0)), agent_(agent) { @@ -621,7 +638,7 @@ void InstanceManager::StopStaleNetNS(ServiceInstance::Properties &props) { cmd_str << " " << UuidToString(boost::uuids::nil_uuid()); if (props.service_type == ServiceInstance::LoadBalancer) { cmd_str << " --cfg-file " << loadbalancer_config_path_default << - props.pool_id << ".haproxy.cfg"; + props.pool_id << ".conf.json"; cmd_str << " --pool-id " << props.pool_id; } @@ -714,7 +731,6 @@ void InstanceManager::LoadbalancerObserver( std::stringstream pathgen; pathgen << loadbalancer_config_path_ << loadbalancer->uuid(); - pathgen << ".haproxy.cfg"; boost::system::error_code error; if (!loadbalancer->IsDeleted() && loadbalancer->properties() != NULL) { @@ -726,24 +742,28 @@ void InstanceManager::LoadbalancerObserver( return; } } - - haproxy_->GenerateConfig(pathgen.str(), loadbalancer->uuid(), - *loadbalancer->properties()); + pathgen << ".conf.json"; + lb_config_->GenerateConfig(pathgen.str(), loadbalancer->uuid(), + *loadbalancer->properties()); } else { - boost::filesystem::path file(pathgen.str()); - if (boost::filesystem::exists(file, error)) { - boost::filesystem::remove_all(pathgen.str(), error); - if (error) { - LOG(ERROR, error.message()); - return; - } + std::stringstream conf_path, json_path, sock_path; + conf_path << pathgen.str() << ".haproxy.conf"; + json_path << pathgen.str() << ".conf.json"; + sock_path << pathgen.str() << ".haproxy.sock"; + + boost::filesystem::path conf_file(conf_path.str()); + if (boost::filesystem::exists(conf_file, error)) { + boost::filesystem::remove_all(conf_path.str(), error); + } - pathgen << ".sock"; - boost::filesystem::remove_all(pathgen.str(), error); - if (error) { - LOG(ERROR, error.message()); - return; - } + boost::filesystem::path sock_file(sock_path.str()); + if (boost::filesystem::exists(sock_file, error)) { + boost::filesystem::remove_all(sock_path.str(), error); + } + + boost::filesystem::path json_file(json_path.str()); + if (boost::filesystem::exists(json_file, error)) { + boost::filesystem::remove_all(json_path.str(), error); } } } diff --git a/src/vnsw/agent/oper/instance_manager.h b/src/vnsw/agent/oper/instance_manager.h index f1b81c8f268..ad43ae26b7a 100644 --- a/src/vnsw/agent/oper/instance_manager.h +++ b/src/vnsw/agent/oper/instance_manager.h @@ -13,7 +13,7 @@ class Agent; class DB; -class LoadbalancerHaproxy; +class LoadbalancerConfig; class InstanceState; class InstanceTask; class InstanceTaskQueue; @@ -73,7 +73,7 @@ class InstanceManager { InstanceState *GetState(ServiceInstance *) const; bool StaleTimeout(); - const LoadbalancerHaproxy &haproxy() const { return *(haproxy_.get()); } + const LoadbalancerConfig &lb_config() const { return *(lb_config_.get()); } void SetStaleTimerInterval(int minutes); int StaleTimerInterval() { return stale_timer_interval_;} void SetNamespaceStorePath(std::string path); @@ -151,7 +151,7 @@ class InstanceManager { std::string loadbalancer_config_path_; std::string namespace_store_path_; int stale_timer_interval_; - std::auto_ptr haproxy_; + std::auto_ptr lb_config_; Timer *stale_timer_; std::auto_ptr stale_cleaner_; Agent *agent_; diff --git a/src/vnsw/agent/oper/loadbalancer.cc b/src/vnsw/agent/oper/loadbalancer.cc index 3d5e07afbac..fc47093bcd2 100644 --- a/src/vnsw/agent/oper/loadbalancer.cc +++ b/src/vnsw/agent/oper/loadbalancer.cc @@ -95,6 +95,7 @@ void Loadbalancer::CalculateProperties(DBGraph *graph, Properties *properties) { autogen::LoadbalancerPool *pool = static_cast(node_->GetObject()); properties->set_pool_properties(pool->properties()); + properties->set_custom_attributes(pool->custom_attributes()); for (DBGraphVertex::adjacency_iterator iter = node_->begin(graph); iter != node_->end(graph); ++iter) { diff --git a/src/vnsw/agent/oper/loadbalancer_config.cc b/src/vnsw/agent/oper/loadbalancer_config.cc new file mode 100644 index 00000000000..b048ce2f5c9 --- /dev/null +++ b/src/vnsw/agent/oper/loadbalancer_config.cc @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2014 Juniper Networks, Inc. All rights reserved. + */ + +#include "loadbalancer_config.h" + +#include +#include +#include +#include "base/logging.h" +#include "agent.h" +#include "init/agent_param.h" + +#include "loadbalancer_properties.h" + +using namespace std; +using boost::assign::map_list_of; + +LoadbalancerConfig::LoadbalancerConfig(Agent *agent) + : agent_(agent) { +} + +void LoadbalancerConfig::GeneratePool( + ostream *out, const boost::uuids::uuid &pool_id, + const LoadbalancerProperties &props) const { + const autogen::LoadbalancerPoolType &pool = props.pool_properties(); + + ostringstream ostr; + ostr << " \"pool\":{" << endl + << " \"id\":\"" << pool_id << "\"," << endl + << " \"protocol\":\"" << pool.protocol << "\"," << endl + << " \"method\":\"" << pool.loadbalancer_method << "\"," << endl + << " \"admin-state\":" << std::boolalpha << pool.admin_state + << endl + << " }," << endl; + *out << ostr.str(); +} + +void LoadbalancerConfig::GenerateVip( + ostream *out, const LoadbalancerProperties &props) const { + + const autogen::VirtualIpType &vip = props.vip_properties(); + ostringstream ostr; + ostr << " \"vip\":{" << endl + << " \"id\":\"" << props.vip_uuid() << "\"," << endl + << " \"address\":\"" << vip.address <<"\"," << endl + << " \"port\":" << vip.protocol_port << "," << endl + << " \"protocol\":\"" << vip.protocol <<"\"," << endl + << " \"connection-limit\":" << vip.connection_limit << "," + << endl + << " \"persistence-cookie-name\": \"" + << vip.persistence_cookie_name << "\"," << endl + << " \"persistence-type\": \"" << vip.persistence_type << "\"," + << endl + << " \"admin-state\":" << std::boolalpha << vip.admin_state << endl + << " }," << endl; + *out << ostr.str(); +} + +void LoadbalancerConfig::GenerateMembers( + ostream *out, const LoadbalancerProperties &props) const { + + ostringstream ostr; + ostr << " \"members\":[" << endl; + int count = 0; + for (LoadbalancerProperties::MemberMap::const_iterator iter = + props.members().begin(); + iter != props.members().end(); ++iter) { + const autogen::LoadbalancerMemberType &member = iter->second; + if (count) { + ostr << "," << endl; + } + ostr << " {" << endl + << " \"id\":\"" << iter->first << "\"," << endl + << " \"address\":\"" << member.address << "\"," << endl + << " \"port\":" << member.protocol_port << "," << endl + << " \"weight\":" << member.weight << "," << endl + << " \"admin-state\":" << std::boolalpha + << member.admin_state << endl + << " }"; + count++; + } + if (count) { + ostr << endl; + } + ostr << " ]," << endl; + *out << ostr.str(); +} + +void LoadbalancerConfig::GenerateHealthMonitors( + ostream *out, const LoadbalancerProperties &props) const { + + ostringstream ostr; + ostr << " \"healthmonitors\":[" << endl; + int count = 0; + for (LoadbalancerProperties::HealthmonitorMap::const_iterator iter = + props.healthmonitors().begin(); + iter != props.healthmonitors().end(); ++iter) { + const autogen::LoadbalancerHealthmonitorType &hm = iter->second; + if (count) { + ostr << "," << endl; + } + ostr << " {" << endl + << " \"id\":\"" << iter->first << "\"," << endl + << " \"type\": \"" << hm.monitor_type << "\"," << endl + << " \"delay\":" << hm.delay << "," << endl + << " \"timeout\":" << hm.timeout << "," << endl + << " \"max-retries\":" << hm.max_retries << "," << endl + << " \"http-method\": \"" << hm.http_method << "\"," << endl + << " \"url\": \"" << hm.url_path << "\"," << endl + << " \"expected-codes\": \"" << hm.expected_codes << "\"," + << endl + << " \"admin-state\":" << std::boolalpha << hm.admin_state + << endl + << " }"; + count++; + } + if (count) { + ostr << endl; + } + ostr << " ]," << endl; + *out << ostr.str(); +} + +void LoadbalancerConfig::GenerateCustomAttributes( + ostream *out, const LoadbalancerProperties &props) const { + const std::vector + &custom_attributes = props.custom_attributes(); + ostringstream ostr; + autogen::KeyValuePairs::const_iterator curr_iter, next_iter, end_iter; + curr_iter = custom_attributes.begin(); + end_iter = custom_attributes.end(); + + ostr << " \"custom-attributes\":{" << endl; + + while (curr_iter != end_iter) { + next_iter = curr_iter + 1; + const autogen::KeyValuePair element = (*curr_iter); + ostr << " \"" << element.key << "\":\"" << element.value << "\""; + if (next_iter == end_iter) { + ostr << endl; + break; + } + ostr << "," << endl; + curr_iter = next_iter; + } + ostr << " }" << endl; + *out << ostr.str(); +} + +void LoadbalancerConfig::GenerateConfig( + const string &filename, const boost::uuids::uuid &pool_id, + const LoadbalancerProperties &props) const { + ofstream fs(filename.c_str()); + if (fs.fail()) { + LOG(ERROR, "File create " << filename << ": " << strerror(errno)); + } + + fs << "{" << endl; + fs << " \"ssl-crt\":\"" << agent_->params()->si_lb_ssl_cert_path() + << "\"," << endl; + GeneratePool(&fs, pool_id, props); + GenerateVip(&fs, props); + GenerateMembers(&fs, props); + GenerateHealthMonitors(&fs, props); + GenerateCustomAttributes(&fs, props); + fs << "}" << endl; + fs.close(); +} diff --git a/src/vnsw/agent/oper/loadbalancer_config.h b/src/vnsw/agent/oper/loadbalancer_config.h new file mode 100644 index 00000000000..55c6c3769a4 --- /dev/null +++ b/src/vnsw/agent/oper/loadbalancer_config.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014 Juniper Networks, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include "base/util.h" + +class LoadbalancerProperties; +class Agent; + +class LoadbalancerConfig { +public: + LoadbalancerConfig(Agent *); + + void GenerateConfig(const std::string &filename, + const boost::uuids::uuid &pool_id, + const LoadbalancerProperties &props) const; + +private: + void GenerateVip(std::ostream *out, + const LoadbalancerProperties &props) const; + void GeneratePool(std::ostream *out, + const boost::uuids::uuid &pool_id, + const LoadbalancerProperties &props) const; + void GenerateMembers(std::ostream *out, + const LoadbalancerProperties &props) const; + void GenerateHealthMonitors(std::ostream *out, + const LoadbalancerProperties &props) const; + void GenerateCustomAttributes(std::ostream *out, + const LoadbalancerProperties &props) const; + + Agent *agent_; + + DISALLOW_COPY_AND_ASSIGN(LoadbalancerConfig); +}; diff --git a/src/vnsw/agent/oper/loadbalancer_haproxy.cc b/src/vnsw/agent/oper/loadbalancer_haproxy.cc deleted file mode 100644 index ee13c81d379..00000000000 --- a/src/vnsw/agent/oper/loadbalancer_haproxy.cc +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright (c) 2014 Juniper Networks, Inc. All rights reserved. - */ - -#include "loadbalancer_haproxy.h" - -#include -#include -#include -#include "base/logging.h" -#include "agent.h" -#include "init/agent_param.h" - -#include "loadbalancer_properties.h" - -using namespace std; -using boost::assign::map_list_of; - -LoadbalancerHaproxy::LoadbalancerHaproxy(Agent *agent) - : protocol_default_("tcp"), - balance_default_("roundrobin"), agent_(agent) { - protocol_map_ = map_list_of - ("TCP", "tcp") - ("HTTP", "http") - ("HTTPS", "tcp"); - balance_map_ = map_list_of - ("ROUND_ROBIN", "roundrobin") - ("LEAST_CONNECTIONS", "leastconn") - ("SOURCE_IP", "source"); -} - -const string &LoadbalancerHaproxy::ProtocolMap(const string &proto) const { - map::const_iterator loc = protocol_map_.find(proto); - if (loc == protocol_map_.end()) { - return protocol_default_; - } - return loc->second; -} - -const string &LoadbalancerHaproxy::BalanceMap(const string &proto) const { - map::const_iterator loc = balance_map_.find(proto); - if (loc == balance_map_.end()) { - return balance_default_; - } - return loc->second; -} - -/* - * global - * daemon - * user nobody - * group nogroup - * log /dev/log local0 - * log /dev/log local1 notice - * stats socket mode 0666 level user - */ -void LoadbalancerHaproxy::GenerateGlobal( - ostream *out, const string &filename, - const LoadbalancerProperties &props) const { - - *out << "global" << endl; - *out << string(4, ' ') << "daemon" << endl; - *out << string(4, ' ') << "user nobody" << endl; - *out << string(4, ' ') << "group nogroup" << endl; - *out << string(4, ' ') << "stats socket " << filename - << ".sock mode 0666 level user" << endl; - *out << string(4, ' ') << "tune.ssl.default-dh-param 2048" << endl; - *out << string(4, ' ') << "ssl-default-bind-ciphers " << - "ECDH+AESGCM:DH+AESGCM:ECDH+AES256:" << - "DH+AES256:ECDH+AES128:DH+AES:" << - "ECDH+3DES:DH+3DES:RSA+AESGCM:" << - "RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS" << endl; - *out << endl; -} - -void LoadbalancerHaproxy::GenerateDefaults( - ostream *out, const LoadbalancerProperties &props) const { - - *out << "defaults" << endl; - *out << string(4, ' ') << "log global" << endl; - *out << string(4, ' ') << "retries 3" << endl; - *out << string(4, ' ') << "option redispatch" << endl; - *out << string(4, ' ') << "timeout connect 5000" << endl; - *out << string(4, ' ') << "timeout client 50000" << endl; - *out << string(4, ' ') << "timeout server 50000" << endl; - *out << endl; -} - -void LoadbalancerHaproxy::GenerateListen( - ostream *out, const LoadbalancerProperties &props) const { - - *out << "listen contrail-config-stats :5937" << endl; - *out << string(4, ' ') << "mode http" << endl; - *out << string(4, ' ') << "stats enable" << endl; - *out << string(4, ' ') << "stats uri /" << endl; - *out << string(4, ' ') << "stats auth haproxy:contrail123" << endl; - *out << endl; -} - -/* - * frontend vip_id - * bind address:port - * mode [http|tcp] - * default_backend pool_id - */ -void LoadbalancerHaproxy::GenerateFrontend( - ostream *out, const boost::uuids::uuid &pool_id, - const LoadbalancerProperties &props) const { - - *out << "frontend " << props.vip_uuid() << endl; - const autogen::VirtualIpType &vip = props.vip_properties(); - *out << string(4, ' ') - << "bind " << vip.address << ":" << vip.protocol_port; - if (vip.protocol == LB_HAPROXY_TERMINATION_PROTO) { - *out << " ssl crt " << - agent_->params()->si_haproxy_ssl_cert_path() << - " no-sslv3 no-tlsv10"; - } - *out << endl; - - *out << string(4, ' ') - << "mode " << ProtocolMap(vip.protocol) << endl; - *out << string(4, ' ') - << "default_backend " << pool_id << endl; - - if (vip.connection_limit >= 0) { - *out << string(4, ' ') - << "maxconn " << vip.connection_limit << endl; - } - - if (vip.protocol == "HTTP" || vip.protocol == "HTTPS") { - *out << string(4, ' ') - << "option forwardfor" << endl; - } - - *out << endl; -} - - -/* - * backend - * mode - * balance - * server
: weight - */ -void LoadbalancerHaproxy::GenerateBackend( - ostream *out, const boost::uuids::uuid &pool_id, - const LoadbalancerProperties &props) const { - const autogen::LoadbalancerPoolType &pool = props.pool_properties(); - *out << "backend " << pool_id << endl; - *out << string(4, ' ') - << "mode " << ProtocolMap(pool.protocol) << endl; - *out << string(4, ' ') - << "balance " << BalanceMap(pool.loadbalancer_method) << endl; - - - int timeout = 0, max_retries = 0; - if (props.healthmonitors().size()) { - const autogen::LoadbalancerHealthmonitorType &hm = - props.healthmonitors().begin()->second; - timeout = hm.timeout * 1000; //In milliseconds - max_retries = hm.max_retries; - if (!hm.monitor_type.empty() && - (hm.monitor_type == "HTTP" || hm.monitor_type == "HTTPS")) { - if (!hm.url_path.empty()) { - *out << string(4, ' ') - << "option httpchk "; - if (!hm.http_method.empty()) { - *out << hm.http_method; - } - *out << " " << hm.url_path << endl; - } - if (!hm.expected_codes.empty()) { - *out << string(4, ' ') - << "http-check expect status " - << hm.expected_codes << endl; - } - if (hm.monitor_type == "HTTPS") { - *out << string(4, ' ') - << "option ssl-hello-chk " << endl; - } - } - } - - const autogen::VirtualIpType &vip = props.vip_properties(); - if (vip.persistence_type == "HTTP_COOKIE") { - *out << string(4, ' ') << "cookie SRV insert indirect nocache" - << endl; - } - if (vip.persistence_type == "SOURCE_IP") { - *out << string(4, ' ') << "stick-table type ip size 10k" - << endl; - *out << string(4, ' ') << "stick on src" << endl; - } - if (vip.persistence_type == "APP_COOKIE" && - !vip.persistence_cookie_name.empty()) { - *out << string(4, ' ') << "appsession " << - vip.persistence_cookie_name << " len 56 timeout 3h" << endl; - } - - for (LoadbalancerProperties::MemberMap::const_iterator iter = - props.members().begin(); - iter != props.members().end(); ++iter) { - const autogen::LoadbalancerMemberType &member = iter->second; - *out << string(4, ' ') - << "server " << iter->first << " " << member.address - << ":" << member.protocol_port - << " weight " << member.weight; - if (timeout) { - *out << " check inter " << timeout << " rise 1 fall " - << max_retries; - } - *out << endl; - } -} - -void LoadbalancerHaproxy::GenerateConfig( - const string &filename, const boost::uuids::uuid &pool_id, - const LoadbalancerProperties &props) const { - ofstream fs(filename.c_str()); - if (fs.fail()) { - LOG(ERROR, "File create " << filename << ": " << strerror(errno)); - } - - GenerateGlobal(&fs, filename, props); - GenerateDefaults(&fs, props); - GenerateListen(&fs, props); - GenerateFrontend(&fs, pool_id, props); - GenerateBackend(&fs, pool_id, props); - fs.close(); -} diff --git a/src/vnsw/agent/oper/loadbalancer_haproxy.h b/src/vnsw/agent/oper/loadbalancer_haproxy.h deleted file mode 100644 index 011c1d961d1..00000000000 --- a/src/vnsw/agent/oper/loadbalancer_haproxy.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2014 Juniper Networks, Inc. All rights reserved. - */ - -#include -#include -#include -#include -#include "base/util.h" - -#define LB_HAPROXY_SSL_PORT 443 -#define LB_HAPROXY_TERMINATION_PROTO "HTTPS" - -class LoadbalancerProperties; -class Agent; - -class LoadbalancerHaproxy { -public: - LoadbalancerHaproxy(Agent *); - - void GenerateConfig(const std::string &filename, - const boost::uuids::uuid &pool_id, - const LoadbalancerProperties &props) const; - -private: - void GenerateGlobal(std::ostream *out, - const std::string &filename, - const LoadbalancerProperties &props) const; - void GenerateDefaults(std::ostream *out, - const LoadbalancerProperties &props) const; - void GenerateListen(std::ostream *out, - const LoadbalancerProperties &props) const; - void GenerateFrontend(std::ostream *out, - const boost::uuids::uuid &pool_id, - const LoadbalancerProperties &props) const; - void GenerateBackend(std::ostream *out, - const boost::uuids::uuid &pool_id, - const LoadbalancerProperties &props) const; - - const std::string &ProtocolMap(const std::string &proto) const; - const std::string &BalanceMap(const std::string &balance) const; - - std::map protocol_map_; - std::map balance_map_; - std::string protocol_default_; - std::string balance_default_; - Agent *agent_; - - DISALLOW_COPY_AND_ASSIGN(LoadbalancerHaproxy); -}; diff --git a/src/vnsw/agent/oper/loadbalancer_properties.cc b/src/vnsw/agent/oper/loadbalancer_properties.cc index e47429a9c22..5b9de3cc7ca 100644 --- a/src/vnsw/agent/oper/loadbalancer_properties.cc +++ b/src/vnsw/agent/oper/loadbalancer_properties.cc @@ -125,6 +125,48 @@ void MapDiff(const MapType &lhs, const MapType &rhs, Comparator comp, } } +int CustomAttributesCompare(const std::vector &lhs, + const std::vector &rhs) { + autogen::KeyValuePairs::const_iterator iter1 = lhs.begin(); + autogen::KeyValuePairs::const_iterator iter2 = rhs.begin(); + int ret = 0; + + while (iter1 != lhs.end() && iter2 != rhs.end()) { + if ((ret = iter1->key.compare(iter2->key)) != 0) { + return ret; + } + if ((ret = iter1->value.compare(iter2->value)) != 0) { + return ret; + } + ++iter1; + ++iter2; + } + + if (iter1 != lhs.end()) { + return 1; + } + if (iter2 != rhs.end()) { + return -1; + } + return 0; +} + +void CustomAttributesDiff(const std::vector &lhs, + const std::vector &rhs, + std::stringstream *ss) { + autogen::KeyValuePairs::const_iterator iter1 = lhs.begin(); + autogen::KeyValuePairs::const_iterator iter2 = rhs.begin(); + + while (iter1 != lhs.end()) { + *ss << " -" << iter1->key << ": " << iter1->value; + ++iter1; + } + while (iter2 != rhs.end()) { + *ss << " +" << iter2->key << ": " << iter2->value; + ++iter2; + } +} + #define COMPARE_PROPERTY(Xname) \ do { \ int res = compare(Xname, rhs.Xname); \ @@ -152,8 +194,14 @@ int LoadbalancerProperties::CompareTo(const LoadbalancerProperties &rhs) const { } result = MapCompare(monitors_, rhs.monitors_, CompareMonitor); + if (result) { + return result; + } + + result = CustomAttributesCompare(custom_attributes_, rhs.custom_attributes_); return result; } + #undef COMPARE_PROPERTY #define DIFF_PROPERTY(Xss, Xname)\ @@ -187,6 +235,8 @@ std::string LoadbalancerProperties::DiffString( MapDiff(current->members_, members_, CompareMember, &ss); ss << " Monitors:"; MapDiff(current->monitors_, monitors_, CompareMonitor, &ss); + ss << " Custom Attributes:"; + CustomAttributesDiff(current->custom_attributes_, custom_attributes_, &ss); return ss.str(); } diff --git a/src/vnsw/agent/oper/loadbalancer_properties.h b/src/vnsw/agent/oper/loadbalancer_properties.h index 673e6e2b036..522ccbf75f3 100644 --- a/src/vnsw/agent/oper/loadbalancer_properties.h +++ b/src/vnsw/agent/oper/loadbalancer_properties.h @@ -62,12 +62,21 @@ class LoadbalancerProperties { return &monitors_; } + const std::vector &custom_attributes() const { + return custom_attributes_; + } + + void set_custom_attributes(const std::vector &custom_attributes) { + custom_attributes_ = custom_attributes; + } + private: autogen::LoadbalancerPoolType pool_; boost::uuids::uuid vip_uuid_; autogen::VirtualIpType vip_; MemberMap members_; HealthmonitorMap monitors_; + std::vector custom_attributes_; }; #endif // VNSW_AGENT_OPER_LOADBALANCER_PROPERTIES_H__ diff --git a/src/vnsw/agent/oper/netns_instance_adapter.cc b/src/vnsw/agent/oper/netns_instance_adapter.cc index 7554dd62ffb..f3bde8a0d87 100644 --- a/src/vnsw/agent/oper/netns_instance_adapter.cc +++ b/src/vnsw/agent/oper/netns_instance_adapter.cc @@ -1,6 +1,8 @@ #include "oper/netns_instance_adapter.h" #include "oper/service_instance.h" #include "oper/instance_task.h" +#include "agent.h" +#include "init/agent_param.h" InstanceTask* NetNSInstanceAdapter::CreateStartTask(const ServiceInstance::Properties &props, bool update) { std::stringstream cmd_str; @@ -34,8 +36,12 @@ InstanceTask* NetNSInstanceAdapter::CreateStartTask(const ServiceInstance::Prope if (props.service_type == ServiceInstance::LoadBalancer) { cmd_str << " --cfg-file " << loadbalancer_config_path_ << - props.pool_id << ".haproxy.cfg"; + props.pool_id << ".conf.json"; cmd_str << " --pool-id " << props.pool_id; + if (!agent_->params()->si_lb_keystone_auth_conf_path().empty()) { + cmd_str << " --keystone-auth-cfg-file " << + agent_->params()->si_lb_keystone_auth_conf_path(); + } } if (update) { @@ -68,7 +74,7 @@ InstanceTask* NetNSInstanceAdapter::CreateStopTask(const ServiceInstance::Proper cmd_str << " " << UuidToString(props.vmi_outside); if (props.service_type == ServiceInstance::LoadBalancer) { cmd_str << " --cfg-file " << loadbalancer_config_path_ << - props.pool_id << ".haproxy.cfg"; + props.pool_id << ".conf.json"; cmd_str << " --pool-id " << props.pool_id; } diff --git a/src/vnsw/agent/oper/test/SConscript b/src/vnsw/agent/oper/test/SConscript index 20482924195..8d669faec6a 100644 --- a/src/vnsw/agent/oper/test/SConscript +++ b/src/vnsw/agent/oper/test/SConscript @@ -90,11 +90,11 @@ loadbalancer_test = env.UnitTest( env.Alias('agent:loadbalancer_test', loadbalancer_test) oper_test_suite.append(loadbalancer_test) -loadbalancer_haproxy_test = env.UnitTest( - 'loadbalancer_haproxy_test', - ['loadbalancer_haproxy_test.cc']) -env.Alias('agent:loadbalancer_haproxy_test', loadbalancer_haproxy_test) -oper_test_suite.append(loadbalancer_haproxy_test) +loadbalancer_config_test = env.UnitTest( + 'loadbalancer_config_test', + ['loadbalancer_config_test.cc']) +env.Alias('agent:loadbalancer_config_test', loadbalancer_config_test) +oper_test_suite.append(loadbalancer_config_test) test_physical_devices = AgentEnv.MakeTestCmdSrc(env, 'test_physical_devices', [ diff --git a/src/vnsw/agent/oper/test/instance_manager_test.cc b/src/vnsw/agent/oper/test/instance_manager_test.cc index cc417ed8a8c..72391a7b858 100644 --- a/src/vnsw/agent/oper/test/instance_manager_test.cc +++ b/src/vnsw/agent/oper/test/instance_manager_test.cc @@ -543,8 +543,8 @@ TEST_F(InstanceManagerTest, LoadbalancerConfig) { task_util::WaitForIdle(); stringstream pathgen; - pathgen << loadbalancer_config_path() << lbid - << ".haproxy.cfg"; + pathgen << loadbalancer_config_path() << lbid << ".conf.json"; + boost::filesystem::path config(pathgen.str()); std::time_t old_time = boost::filesystem::last_write_time(pathgen.str()); @@ -553,8 +553,8 @@ TEST_F(InstanceManagerTest, LoadbalancerConfig) { lbid = AddLoadbalancer(UuidToString(gen())); pathgen.str(""); pathgen.clear(); - pathgen << loadbalancer_config_path() << lbid - << ".haproxy.cfg"; + pathgen << loadbalancer_config_path() << lbid << ".conf.json"; + boost::filesystem::path config1(pathgen.str()); //Make sure that both files exists @@ -593,7 +593,8 @@ TEST_F(InstanceManagerTest, InstanceStaleCleanup) { LOG(ERROR, "Error : " << error.message() << "in creating directory"); } } - store_path = loadbalancer_config_path() + lb_uuid; + store_path = loadbalancer_config_path(); + store_path += lb_uuid; if (!boost::filesystem::exists(store_path, error)) { boost::filesystem::create_directories(store_path, error); diff --git a/src/vnsw/agent/oper/test/loadbalancer_haproxy_test.cc b/src/vnsw/agent/oper/test/loadbalancer_config_test.cc similarity index 85% rename from src/vnsw/agent/oper/test/loadbalancer_haproxy_test.cc rename to src/vnsw/agent/oper/test/loadbalancer_config_test.cc index 8a2873081ef..a857add04b7 100644 --- a/src/vnsw/agent/oper/test/loadbalancer_haproxy_test.cc +++ b/src/vnsw/agent/oper/test/loadbalancer_config_test.cc @@ -1,7 +1,7 @@ #include "base/os.h" #include "base/logging.h" #include -#include "oper/loadbalancer_haproxy.h" +#include "oper/loadbalancer_config.h" #include "oper/operdb_init.h" #include "oper/instance_manager.h" @@ -22,7 +22,7 @@ void RouterIdDepInit(Agent *agent) { using boost::uuids::uuid; -class LoadbalancerHaproxyTest : public ::testing::Test { +class LoadbalancerConfigTest : public ::testing::Test { protected: virtual void SetUp() { } @@ -31,7 +31,7 @@ class LoadbalancerHaproxyTest : public ::testing::Test { } }; -TEST_F(LoadbalancerHaproxyTest, GenerateConfig) { +TEST_F(LoadbalancerConfigTest, GenerateConfig) { boost::uuids::random_generator gen; uuid pool_id = gen(); @@ -58,7 +58,7 @@ TEST_F(LoadbalancerHaproxyTest, GenerateConfig) { stringstream ss; boost::filesystem::path curr_dir(boost::filesystem::current_path()); ss << curr_dir.string() << "/" << getpid() << ".conf"; - Agent::GetInstance()->oper_db()->instance_manager()->haproxy().GenerateConfig(ss.str(), pool_id, props); + Agent::GetInstance()->oper_db()->instance_manager()->lb_config().GenerateConfig(ss.str(), pool_id, props); boost::system::error_code error; boost::filesystem::remove_all(ss.str(), error); if (error) { @@ -66,8 +66,8 @@ TEST_F(LoadbalancerHaproxyTest, GenerateConfig) { } } -//Test to make sure we don't http-check to haproxy config file unless monitor_type is HTTP -TEST_F(LoadbalancerHaproxyTest, GenerateConfig_with_Monitor) { +//Test to make sure we don't http-check to config file unless monitor_type is HTTP +TEST_F(LoadbalancerConfigTest, GenerateConfig_with_Monitor) { boost::uuids::random_generator gen; uuid pool_id = gen(); @@ -102,7 +102,7 @@ TEST_F(LoadbalancerHaproxyTest, GenerateConfig_with_Monitor) { stringstream ss; boost::filesystem::path curr_dir(boost::filesystem::current_path()); ss << curr_dir.string() << "/" << getpid() << ".conf"; - Agent::GetInstance()->oper_db()->instance_manager()->haproxy().GenerateConfig(ss.str(), pool_id, props); + Agent::GetInstance()->oper_db()->instance_manager()->lb_config().GenerateConfig(ss.str(), pool_id, props); ifstream file(ss.str().c_str()); if (file) { @@ -120,7 +120,7 @@ TEST_F(LoadbalancerHaproxyTest, GenerateConfig_with_Monitor) { } //Test to make sure HTTPS monitor adds SSL check -TEST_F(LoadbalancerHaproxyTest, GenerateConfig_with_SSL_Monitor) { +TEST_F(LoadbalancerConfigTest, GenerateConfig_with_SSL_Monitor) { boost::uuids::random_generator gen; uuid pool_id = gen(); @@ -155,19 +155,15 @@ TEST_F(LoadbalancerHaproxyTest, GenerateConfig_with_SSL_Monitor) { stringstream ss; boost::filesystem::path curr_dir(boost::filesystem::current_path()); ss << curr_dir.string() << "/" << getpid() << ".conf"; - Agent::GetInstance()->oper_db()->instance_manager()->haproxy().GenerateConfig(ss.str(), pool_id, props); + Agent::GetInstance()->oper_db()->instance_manager()->lb_config().GenerateConfig(ss.str(), pool_id, props); ifstream file(ss.str().c_str()); if (file) { string file_str((istreambuf_iterator(file)), istreambuf_iterator()); - string search_str1 = "ssl-hello-chk"; - string search_str2 = "ssl_cert"; + string search_str1 = "HTTPS"; size_t found = file_str.find(search_str1); EXPECT_NE(found, string::npos); - - found = file_str.find(search_str2); - EXPECT_EQ(found, string::npos); } boost::system::error_code error; diff --git a/src/vnsw/opencontrail-vrouter-netns/SConscript b/src/vnsw/opencontrail-vrouter-netns/SConscript index b95185adae1..33b1b8ad138 100644 --- a/src/vnsw/opencontrail-vrouter-netns/SConscript +++ b/src/vnsw/opencontrail-vrouter-netns/SConscript @@ -17,6 +17,10 @@ sources = [ 'opencontrail_vrouter_netns/__init__.py', 'opencontrail_vrouter_netns/vrouter_netns.py', 'opencontrail_vrouter_netns/haproxy_config.py', + 'opencontrail_vrouter_netns/haproxy_process.py', + 'opencontrail_vrouter_netns/haproxy_validator.py', + 'opencontrail_vrouter_netns/haproxy_cert.py', + 'opencontrail_vrouter_netns/keystone_auth.py', 'opencontrail_vrouter_netns/vrouter_docker.py', 'opencontrail_vrouter_netns/daemon_start.py', 'opencontrail_vrouter_netns/daemon_stop.py', diff --git a/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/haproxy_cert.py b/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/haproxy_cert.py new file mode 100644 index 00000000000..a4eff02bd7d --- /dev/null +++ b/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/haproxy_cert.py @@ -0,0 +1,104 @@ +import json +import keystone_auth +import sys +import logging + +class Barbican_Cert_Manager(object): + """Class to download certs from barbican and + populate the pem file as required by HAProxy + """ + def __init__(self, keystone_auth_conf_file): + self.identity = keystone_auth.Identity(keystone_auth_conf_file) + if not self.identity: + raise Exception() + + def _get_barbican_entity(self, barbican_ep, auth_token, + entity_ref, metadata=True): + if metadata: + accept_data = 'application/json' + else: + accept_data = 'text/plain' + + try: + headers = { + "Accept": "%s" % accept_data, + "X-Auth-Token": "%s" % auth_token + } + url = entity_ref + resp = keystone_auth._request(url, headers, 'GET') + if resp.status_code in range(200, 299): + if metadata: + return json.loads(resp.text) + else: + return resp.text + else: + logging.error("%s getting barbican entity %s" % \ + (resp.text, url)) + except Exception as e: + logging.error("%s getting barbican entity %s" % \ + (str(e), url)) + return None + + def _validate_tls_secret(self, tls_container_ref): + try: + if self.identity: + #self.identity = keystone_auth.Identity() + container_detail = self._get_barbican_entity(\ + self.identity.barbican_ep, + self.identity.auth_token, + entity_ref=tls_container_ref, + metadata=True) + + if not container_detail: + return False + + # Validate that secrets are stored plain text + for secret in container_detail['secret_refs']: + secret_meta_data = self._get_barbican_entity(\ + self.identity.barbican_ep, + self.identity.auth_token, + entity_ref=secret['secret_ref'], + metadata=True) + if not secret_meta_data or secret_meta_data\ + ['content_types']['default'] != 'text/plain': + logging.error("Invalid secret format: %s" % \ + secret_meta_data['content_types']['default']) + return False + return True + else: + return False + except Exception as e: + logging.error("%s while validating TLS Container" % str(e)) + return False + + def _populate_tls_pem(self, tls_container_ref): + try: + if self.identity: + #self.identity = keystone_auth.Identity() + container_detail = self._get_barbican_entity(\ + self.identity.barbican_ep, + self.identity.auth_token, + entity_ref=tls_container_ref, + metadata=True) + + if not container_detail: + return False + + # Fetch the secrets stored in plain text + secret_text = '' + for secret in container_detail['secret_refs']: + secret_detail = self._get_barbican_entity(\ + self.identity.barbican_ep, + self.identity.auth_token, + entity_ref=secret['secret_ref'], + metadata=False) + if secret_detail: + secret_text += secret_detail + secret_text += "\n" + + return secret_text + else: + return None + except Exception as e: + logging.error("%s while populating SSL Pem file" % str(e)) + return None diff --git a/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/haproxy_config.py b/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/haproxy_config.py index 73f5468037c..3eb16e6a237 100644 --- a/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/haproxy_config.py +++ b/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/haproxy_config.py @@ -1,3 +1,22 @@ +import json +import os +import logging + +def validate_custom_attributes(config, section, keystone_auth_conf_file=None): + return {} + +try: + from haproxy_validator import validate_custom_attributes as validator + from haproxy_validator import custom_attributes_dict + from haproxy_cert import Barbican_Cert_Manager +except ImportError: + validator = validate_custom_attributes + custom_attributes_dict = {} + +# Setup logger +logging.basicConfig(filename='/var/log/contrail/haproxy_parse.log', level=logging.WARNING) + +# Setup global definitions PROTO_TCP = 'TCP' PROTO_HTTP = 'HTTP' PROTO_HTTPS = 'HTTPS' @@ -5,7 +24,7 @@ PROTO_MAP = { PROTO_TCP: 'tcp', PROTO_HTTP: 'http', - PROTO_HTTPS: 'tcp' + PROTO_HTTPS: 'http' } LB_METHOD_MAP = { @@ -25,47 +44,91 @@ HTTPS_PORT = 443 -def build_config(config, conf_dir): +def build_config(pool_id, conf_file, keystone_auth_conf_file): + with open(conf_file) as data_file: + config = json.load(data_file) + conf_dir = os.path.dirname(conf_file) + conf = [] - sock_path = conf_dir + 'sock' + sock_path = conf_dir + '/' + pool_id + '.haproxy.sock' conf = _set_global_config(config, sock_path) + '\n\n' conf += _set_defaults(config) + '\n\n' - conf += _set_frontend(config) + '\n\n' + conf += _set_frontend(config, conf_dir, keystone_auth_conf_file) + '\n\n' conf += _set_backend(config) + '\n' - print conf - filename = conf_dir + 'conf' + filename = conf_dir + '/' + pool_id + '.haproxy.conf' conf_file = open(filename, 'w') conf_file.write(conf) + return filename + +def _construct_config_block(lb_config, conf, custom_attr_section, custom_attributes): + for key, value in custom_attributes.iteritems(): + cmd = custom_attributes_dict['global'][key]['cmd'] + conf.append(cmd % value) + + res = "\n\t".join(conf) + return res def _set_global_config(config, sock_path): + global_custom_attributes = validator(config, 'global') + maxconn = global_custom_attributes.pop('maxconn', None) \ + if 'maxconn' in global_custom_attributes else 65000 + ssl_ciphers = global_custom_attributes.pop('ssl_ciphers', None) \ + if 'ssl_ciphers' in global_custom_attributes else \ + 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:' \ + 'ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:' \ + 'RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS' + conf = [ 'global', 'daemon', 'user nobody', - 'group nogroup' + 'group nogroup', 'log /dev/log local0', - 'log /dev/log local1 notice' + 'log /dev/log local1 notice', + 'tune.ssl.default-dh-param 2048', + 'ssl-default-bind-ciphers %s' % ssl_ciphers, + 'ulimit-n 200000', + 'maxconn %d' % maxconn ] conf.append('stats socket %s mode 0666 level user' % sock_path) - return ("\n\t".join(conf)) + + return _construct_config_block(config, conf, "global", global_custom_attributes) + def _set_defaults(config): + default_custom_attributes = validator(config, 'default') + client_timeout = default_custom_attributes.pop('client_timeout', None) \ + if 'client_timeout' in default_custom_attributes else 300000 + server_timeout = default_custom_attributes.pop('server_timeout', None) \ + if 'server_timeout' in default_custom_attributes else 300000 + connect_timeout = default_custom_attributes.pop('connect_timeout', None) \ + if 'connect_timeout' in default_custom_attributes else 5000 + conf = [ 'defaults', 'log global', 'retries 3', 'option redispatch', - 'timeout connect 5000', - 'timeout client 50000', - 'timeout server 50000', + 'timeout connect %d' % connect_timeout, + 'timeout client %d' % client_timeout, + 'timeout server %d' % server_timeout, ] - return ("\n\t".join(conf)) -def _set_frontend(config): + return _construct_config_block(config, conf, "default", default_custom_attributes) + +def _set_frontend(config, conf_dir, keystone_auth_conf_file): port = config['vip']['port'] + vip_custom_attributes = validator(config, 'vip', keystone_auth_conf_file) ssl = '' - if port == HTTPS_PORT: - ssl = 'ssl crt %s' % ssl_cert_path + + if 'tls_container' in vip_custom_attributes: + data = vip_custom_attributes.pop('tls_container', None) + crt_file = _populate_pem_file(data, conf_dir) + else: + crt_file = config['ssl-crt'] + + if config['vip']['protocol'] == PROTO_HTTPS: + ssl = 'ssl crt %s no-sslv3' % crt_file conf = [ 'frontend %s' % config['vip']['id'], 'option tcplog', @@ -75,11 +138,14 @@ def _set_frontend(config): ] if config['vip']['connection-limit'] >= 0: conf.append('maxconn %s' % config['vip']['connection-limit']) - if config['vip']['protocol'] == PROTO_HTTP: + if config['vip']['protocol'] == PROTO_HTTP or \ + config['vip']['protocol'] == PROTO_HTTPS: conf.append('option forwardfor') - return ("\n\t".join(conf)) + + return _construct_config_block(config, conf, "vip", vip_custom_attributes) def _set_backend(config): + pool_custom_attributes = validator(config, 'pool') conf = [ 'backend %s' % config['pool']['id'], 'mode %s' % PROTO_MAP[config['pool']['protocol']], @@ -102,7 +168,7 @@ def _set_backend(config): server += ' cookie %d' % config['members'].index(member) conf.append(server) - return ("\n\t".join(conf)) + return _construct_config_block(config, conf, "pool", pool_custom_attributes) def _set_health_monitor(config): for monitor in config['healthmonitors']: @@ -153,3 +219,10 @@ def _get_codes(codes): else: response.add(code) return response + +def _populate_pem_file(data, conf_dir): + crt_filename = conf_dir + '/crtbundle.pem' + with open(crt_filename, 'w+') as outfile: + outfile.write(data) + + return crt_filename diff --git a/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/haproxy_process.py b/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/haproxy_process.py new file mode 100644 index 00000000000..14bdb888e50 --- /dev/null +++ b/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/haproxy_process.py @@ -0,0 +1,137 @@ +import itertools +import os +import shlex +import subprocess +import haproxy_config + +SUPERVISOR_BASE_DIR = '/etc/contrail/supervisord_vrouter_files/lbaas-haproxy-' + +def stop_haproxy(conf_file, daemon_mode=False): + pool_id = conf_file.split('.')[0].split('/')[-1] + try: + if daemon_mode: + _stop_haproxy_daemon(conf_file) + else: + _stop_supervisor_haproxy(pool_id, conf_file) + except Exception as e: + pass + +def start_update_haproxy(conf_file, netns, daemon_mode=False, + keystone_auth_conf_file=None): + pool_id = conf_file.split('.')[0].split('/')[-1] + haproxy_cfg_file = haproxy_config.build_config(pool_id, conf_file, \ + keystone_auth_conf_file) + try: + if daemon_mode: + _start_haproxy_daemon(netns, haproxy_cfg_file) + else: + _start_supervisor_haproxy(pool_id, netns, haproxy_cfg_file) + except Exception as e: + pass + +def _get_lbaas_pid(pid_file): + cmd = """cat %(file)s """ % {'file':pid_file} + try: + if "check_output" not in dir(subprocess): + s = _check_output(cmd) + else: + s = subprocess.check_output(cmd, shell=True) + except subprocess.CalledProcessError: + return None + return int(s) + +def _stop_haproxy_daemon(conf_file): + pid_file = conf_file.split('.')[0] + '.haproxy.pid' + last_pid = _get_lbaas_pid(pid_file) + if last_pid: + cmd_list = shlex.split('kill -9 ' + str(last_pid)) + subprocess.Popen(cmd_list) + + # Delete the old pid file + if not os.path.exists(pid_file): + return + + cmd = """rm -f %(pid_file)s""" % {'pid_file':pid_file} + try: + if "check_output" not in dir(subprocess): + s = _check_output(cmd) + else: + s = subprocess.check_output(cmd, shell=True) + except subprocess.CalledProcessError: + print "Error in deleting pidfile %s %s" % (pid_file, conf_file) + +def _start_haproxy_daemon(netns, conf_file): + pid_file = conf_file.split('.')[0] + '.haproxy.pid' + last_pid = _get_lbaas_pid(pid_file) + if last_pid: + sf_opt = '-sf ' + str(last_pid) + else: + sf_opt = '' + + cmd = 'ip netns exec %s haproxy -f %s -p %s %s' % \ + (netns, conf_file, pid_file, sf_opt) + cmd_list = shlex.split(cmd) + subprocess.Popen(cmd_list) + +def _stop_supervisor_haproxy(pool_id, conf_file): + pool_suffix = _get_pool_suffix(pool_id) + file_name = SUPERVISOR_BASE_DIR + pool_suffix + '.ini' + cmd = "rm " + file_name + cmd_list = shlex.split(cmd) + subprocess.Popen(cmd_list) + _update_supervisor() + +def _start_supervisor_haproxy(pool_id, netns, conf_file): + data = [] + data.extend(_set_config(pool_id, netns, conf_file)) + pool_suffix = _get_pool_suffix(pool_id) + with open(SUPERVISOR_BASE_DIR + pool_suffix + '.ini', "w") as f: + f.write('\n'.join(data) + '\n') + _update_supervisor() + +def _get_pool_suffix(pool_id): + return pool_id.split('-')[0] + +def _update_supervisor(): + cmd = "supervisorctl -s unix:///tmp/supervisord_vrouter.sock update" + cmd_list = shlex.split(cmd) + subprocess.Popen(cmd_list) + +def _set_config(pool_id, netns, conf_file): + pool_suffix = _get_pool_suffix(pool_id) + program_name = 'lbaas-haproxy-%s' % pool_suffix + cmd = "supervisorctl -s unix:///tmp/supervisord_vrouter.sock pid " + cmd += program_name + cmd_list = shlex.split(cmd) + p = subprocess.Popen(cmd_list, stdout=subprocess.PIPE) + last_pid, err = p.communicate() + try: + int(last_pid) + sf_opt = '-sf ' + last_pid + except ValueError: + sf_opt = '' + + opts = [ + '[program:%s]' % program_name, + 'command=ip netns exec %s haproxy -f %s -db %s' % \ + (netns, conf_file, sf_opt), + 'priority=420', + 'autostart=true', + 'killasgroup=true', + 'stdout_capture_maxbytes=1MB', + 'redirect_stderr=true', + 'stdout_logfile=/var/log/contrail/lbaas-haproxy-stdout.log', + 'stderr_logfile=/dev/null', + 'startsecs=5', + 'exitcodes=0' + ] + + return itertools.chain(o for o in opts) + +def _check_output(cmd, flag=True): + proc = subprocess.Popen(cmd, shell=flag, stdout=subprocess.PIPE) + data, err = proc.communicate() + retcode = proc.poll() + if retcode: + raise subprocess.CalledProcessError(retcode, cmd) + return data diff --git a/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/haproxy_validator.py b/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/haproxy_validator.py new file mode 100644 index 00000000000..03b85939252 --- /dev/null +++ b/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/haproxy_validator.py @@ -0,0 +1,167 @@ +import logging +import inspect +from haproxy_cert import Barbican_Cert_Manager +import os + +class CustomAttr(object): + """This type handles non-flat data-types like + int, str, bool. + """ + def __init__(self, key, value): + self._value = value + self._key = key + + def validate(self, conf_list): + return + + def post_validation(self, conf_list): + return + +class CustomAttrTlsContainer(CustomAttr): + def __init__(self, keystone_auth_conf_file, key, value): + super(CustomAttrTlsContainer, self).__init__(key, value) + self.cert_manager = Barbican_Cert_Manager(keystone_auth_conf_file) + + def validate(self): + if self._key != 'tls_container': + return False + + if (self.cert_manager and \ + self.cert_manager._validate_tls_secret(self._value)): + tls_pem_string = self.cert_manager._populate_tls_pem(self._value) + self._value = tls_pem_string + return True + else: + logging.error("TLS container invalid") + return False + + def post_validation(self): + return self._value + +custom_attributes_dict = { + 'global': { + 'max_conn': { + 'type': int, + 'limits': [1, 65535], + 'cmd': 'maxconn %d' + }, + 'max_conn_rate': { + 'type': int, + 'limits': [1, 65535], + 'cmd': 'maxconnrate %d' + }, + 'max_sess_rate': { + 'type': int, + 'limits': [1, 65535], + 'cmd': 'maxsessrate %d' + }, + 'max_ssl_conn': { + 'type': int, + 'limits': [1, 65535], + 'cmd': 'maxsslconn %d' + }, + 'max_ssl_rate': { + 'type': int, + 'limits': [1, 65535], + 'cmd': 'maxsslrate %d' + }, + 'ssl_ciphers': { + 'type': str, + 'limits': [1, 100], + 'cmd': 'ssl-default-bind-ciphers %s' + }, + 'tune_http_max_header': { + 'type': int, + 'limits': [1, 128], + 'cmd': 'tune.http.maxhdr %d' + }, + 'tune_ssl_max_record': { + 'type': int, + 'limits': [1, 16384], + 'cmd': 'tune.ssl.maxrecord %d' + } + }, + 'default': { + 'server_timeout': { + 'type': int, + 'limits': [1, 5000000], + 'cmd': 'timeout server %d' + }, + 'client_timeout': { + 'type': int, + 'limits': [1, 5000000], + 'cmd': 'timeout client %d' + }, + 'connect_timeout': { + 'type': int, + 'limits': [1, 5000000], + 'cmd': 'timeout connect %d' + } + }, + 'vip': { + 'http_server_close': { + 'type': bool, + 'limits': ['True', 'False'], + 'cmd': '%soption http-server-close' + }, + 'rate_limit_sessions': { + 'type': int, + 'limits': [1, 65535], + 'cmd': 'rate-limit sessions %d' + }, + 'tls_container': { + 'type': CustomAttrTlsContainer, + 'limits': None, + 'cmd': None + } + }, + 'pool': {}, +} + +def validate_custom_attributes(config, section, keystone_auth_conf_file=None): + section_dict = {} + if 'custom-attributes' in config and section in custom_attributes_dict: + custom_attributes = config['custom-attributes'] + for key, value in custom_attributes.iteritems(): + if key in custom_attributes_dict[section]: + #Sanitize the value + try: + type_attr = custom_attributes_dict[section][key]['type'] + limits = custom_attributes_dict[section][key]['limits'] + if type_attr == int: + value = type_attr(value) + if value in range(limits[0], limits[1]): + section_dict.update({key:value}) + else: + logging.info("Skipping key: %s, value: %s due to" \ + "validation failure" % (key, value)) + elif type_attr == str: + if len(value) in range(limits[0], limits[1]): + section_dict.update({key:value}) + else: + logging.info("Skipping key: %s, value: %s due to" \ + "validation failure" % (key, value)) + elif type_attr == bool: + if value in limits: + if value == 'True': + value = '' + elif value == 'False': + value = 'no ' + section_dict.update({key:value}) + else: + logging.info("Skipping key: %s, value: %s due to" \ + "validation failure" % (key, value)) + elif inspect.isclass(type_attr): + new_custom_attr = type_attr(keystone_auth_conf_file, \ + key, value) + if new_custom_attr.validate(): + value = new_custom_attr.post_validation() + section_dict.update({key:value}) + else: + logging.info("Skipping key: %s, value: %s due to" \ + "validation failure" % (key, value)) + except Exception as e: + logging.error(str(e)) + continue + + return section_dict diff --git a/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/keystone_auth.py b/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/keystone_auth.py new file mode 100644 index 00000000000..bdacd186486 --- /dev/null +++ b/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/keystone_auth.py @@ -0,0 +1,151 @@ +import requests +import json +import os +import ConfigParser +import logging + +def _request(url, headers=None, body=None, request_type=None): + try: + if request_type == 'PUT': + encoded_body = json.dumps(body) + return requests.put(url, headers=headers, data=encoded_body) + elif request_type == 'POST': + encoded_body = json.dumps(body) + return requests.post(url, headers=headers, data=encoded_body) + else: + return requests.get(url, headers=headers) + + except Exception as e: + logging.error("Failed sending request to keystone") + return None + +class Identity(): + '''Identity class to get project-scoped + tokens for user''' + def __init__(self, conf_file): + config = ConfigParser.RawConfigParser() + config.read(conf_file) + if not self._parse_config(config): + raise Exception() + + def _parse_config(self, config): + try: + self.keystone_ep = config.get('DEFAULT', 'keystone_endpoint') + self.barbican_ep = config.get('DEFAULT', 'barbican_endpoint') + self.domain_name = config.get('DEFAULT', 'domain_name') + self.os_username = config.get('DEFAULT', 'username') + self.os_password = config.get('DEFAULT', 'password') + self.os_project_name = \ + config.get('DEFAULT', 'project_name') + self.identity_version = \ + config.get('DEFAULT', 'keystone_version') + self.auth_token = None + + if not self.keystone_ep: + logging.error("Keystone Endpoint missing in the config file") + return False + + if not self.identity_version: + self.identity_version = 'v2.0' + + self.keystone_ep = self.keystone_ep + '/' + self.identity_version + + if self.identity_version == 'v3' and not self.domain_name: + logging.error("Domain name missing in the config file") + return False + + if not self.os_username: + logging.error("Username missing in the config file") + return False + + if not self.os_password: + logging.error("Password missing in the config file") + return False + + if self.identity_version == 'v2.0': + if not self._get_v2_project_scoped_auth_token(): + logging.error("Failed to fetch v2.0 token") + return False + elif self.identity_version == 'v3' and self.os_project_name is None: + if not self._get_domain_scoped_auth_token(): + logging.error("Failed to fetch domain scoped token") + return False + else: + if not self._get_v3_project_scoped_auth_token(): + logging.error("Failed to fetch project scoped token") + return False + + return True + + except Exception as e: + logging.error(str(e)) + return False + + def _get_v3_project_scoped_auth_token(self): + try: + headers = { + "Content-Type": "application/json", + } + body = { + "auth": { + "identity": { + "methods": ["password"], + "password": { + "user": { + "name": self.os_username, + "domain": { "id": "default" }, + "password": self.os_password + } + } + }, + "scope": { + "project": { + "name": self.os_project_name, + "domain": { "id": "default" } + } + } + } + } + + url = self.keystone_ep + "/auth/tokens" + resp = _request(url, headers, body, 'POST') + if resp and resp.status_code in range(200, 299): + headers = resp.headers + if headers and 'x-subject-token' in headers: + self.auth_token = headers['x-subject-token'] + return True + + return False + except Exception as e: + logging.error(str(e)) + return False + + def _get_v2_project_scoped_auth_token(self): + try: + headers = { + "Content-Type": "application/json", + } + body = { + "auth": { + "tenantName": self.os_project_name, + "passwordCredentials": { + "username": self.os_username, + "password": self.os_password + } + } + } + + url = self.keystone_ep + "/tokens" + resp = _request(url, headers, body, 'POST') + if resp and resp.status_code in range(200, 299): + json_data = json.loads(resp.text) + self.auth_token = json_data['access']['token']['id'] + return True + return False + except Exception as e: + logging.error(str(e)) + return False + + def _get_domain_scoped_auth_token(self): + # Not supported by barbican + return False diff --git a/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/vrouter_netns.py b/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/vrouter_netns.py index 47b1deb805e..2a1000360c9 100644 --- a/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/vrouter_netns.py +++ b/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/vrouter_netns.py @@ -35,6 +35,7 @@ import os from linux import ip_lib +import haproxy_process def validate_uuid(val): @@ -59,7 +60,8 @@ class NetnsManager(object): def __init__(self, vm_uuid, nic_left, nic_right, other_nics=None, root_helper='sudo', cfg_file=None, update=False, - pool_id=None, gw_ip=None, namespace_name=None): + pool_id=None, gw_ip=None, namespace_name=None, + keystone_auth_cfg_file=None): self.vm_uuid = vm_uuid if namespace_name is None: self.namespace = self.NETNS_PREFIX + self.vm_uuid @@ -84,6 +86,7 @@ def __init__(self, vm_uuid, nic_left, nic_right, other_nics=None, self.cfg_file = cfg_file self.update = update self.gw_ip = gw_ip + self.keystone_auth_cfg_file = keystone_auth_cfg_file def _get_tap_name(self, uuid_str): return (self.TAP_PREFIX + uuid_str)[:self.DEV_NAME_LEN] @@ -129,38 +132,14 @@ def set_snat(self): self.SNAT_RT_TABLES_ID, 'via', self.gw_ip, 'dev', str(self.nic_left['name'])]) - def _get_lbaas_pid(self): - pid_file = self.cfg_file + ".pid" - cmd = """cat %(file)s """ % {'file':pid_file} - try: - if "check_output" not in dir(subprocess): - s = _check_output(cmd) - else: - s = subprocess.check_output(cmd, shell=True) - - except subprocess.CalledProcessError: - return None - return int(s) - def set_lbaas(self): if not self.ip_ns.netns.exists(self.namespace): raise ValueError('Need to create the network namespace before set ' 'up the lbaas') - pid_file = self.cfg_file + ".pid" - pid = self._get_lbaas_pid() - if (self.update is False): - if pid is not None: - self.release_lbaas() - - self.ip_ns.netns.execute([self.LBAAS_PROCESS, '-f', self.cfg_file, '-D', - '-p', pid_file]) - self.ip_ns.netns.execute(['route', 'add', 'default', 'gw', self.gw_ip]) - else: - if pid is not None: - self.ip_ns.netns.execute([self.LBAAS_PROCESS, '-f', self.cfg_file, '-D', '-p', pid_file, '-sf', pid]) - else: - self.ip_ns.netns.execute([self.LBAAS_PROCESS, '-f', self.cfg_file, '-D', - '-p', pid_file]) + + haproxy_process.start_update_haproxy(self.cfg_file, self.namespace, True, + self.keystone_auth_cfg_file) + try: self.ip_ns.netns.execute(['route', 'add', 'default', 'gw', self.gw_ip]) except RuntimeError: @@ -170,29 +149,9 @@ def release_lbaas(self): if not self.ip_ns.netns.exists(self.namespace): raise ValueError('Need to create the network namespace before ' 'relasing lbaas') - pid = self._get_lbaas_pid() - if pid is not None: - cmd = """kill -9 %(pid)s""" % {'pid':pid} - try: - if "check_output" not in dir(subprocess): - s = _check_output(cmd) - else: - s = subprocess.check_output(cmd, shell=True) - except subprocess.CalledProcessError: - print ("SIGKILL Error for pid %d %s" %(pid, self.cfg_file), file=sys.stderr) - - pfile = self.cfg_file + ".pid" - if not os.path.exists(pfile): - return - - cmd = """rm -f %(pid_file)s""" % {'pid_file':pfile} - try: - if "check_output" not in dir(subprocess): - s = _check_output(cmd) - else: - s = subprocess.check_output(cmd, shell=True) - except subprocess.CalledProcessError: - print ("Error in deleting pidfile %s %s" %(pfile, self.cfg_file), file=sys.stderr) + + haproxy_process.stop_haproxy(self.cfg_file, True) + try: self.ip_ns.netns.execute(['route', 'del', 'default']) except RuntimeError: @@ -355,6 +314,10 @@ def _parse_args(self, args_str): "--pool-id", default=None, help=("Loadbalancer Pool")) + create_parser.add_argument( + "--keystone-auth-cfg-file", + default=None, + help=("Keystone auth config file for lbaas")) create_parser.set_defaults(func=self.create) destroy_parser = subparsers.add_parser('destroy') @@ -413,10 +376,11 @@ def create(self): nic_right['ip'] = None netns_mgr = NetnsManager(netns_name, nic_left, nic_right, - root_helper=self.args.root_helper, - cfg_file=self.args.cfg_file, - update=self.args.update, gw_ip=self.args.gw_ip, - pool_id=self.args.pool_id) + root_helper=self.args.root_helper, + cfg_file=self.args.cfg_file, + update=self.args.update, gw_ip=self.args.gw_ip, + pool_id=self.args.pool_id, + keystone_auth_cfg_file=self.args.keystone_auth_cfg_file) if (self.args.update is False): if netns_mgr.is_netns_already_exists(): @@ -464,14 +428,6 @@ def destroy(self): self.args.service_type) raise NotImplementedError(msg) -def _check_output(cmd, flag=True): - proc = subprocess.Popen(cmd, shell=flag, stdout=subprocess.PIPE) - data, err = proc.communicate() - retcode = proc.poll() - if retcode: - raise subprocess.CalledProcessError(retcode, cmd) - return data - def main(args_str=None): vrouter_netns = VRouterNetns(args_str) vrouter_netns.args.func()