mirror of https://github.com/apache/cloudstack.git
CLOUDSTACK-10013: Make the generated VR/json files unique (ports #1470)
This ports PR #1470 by @remibergsma. Make the generated json files unique to prevent concurrency issues: The json files now have UUIDs to prevent them from getting overwritten before they've been executed. Prevents config to be pushed to the wrong router. 2016-02-25 18:32:23,797 DEBUG [c.c.a.t.Request] (AgentManager-Handler-1:null) (logid:) Seq 2-4684025087442026584: Processing: { Ans: , MgmtId: 90520732674657, via: 2, Ver: v1, Flags: 10, [{"com.cloud.agent.api.routing.GroupA nswer":{"results":["null - success: null","null - success: [INFO] update_config.py :: Processing incoming file => vm_dhcp_entry.json.4ea45061-2efb-4467-8eaa-db3d77fb0a7b\n[INFO] Processing JSON file vm_dhcp_entry.json.4ea4506 1-2efb-4467-8eaa-db3d77fb0a7b\n"],"result":true,"wait":0}}] } On the router: 2016-02-25 18:32:23,416 merge.py __moveFile:298 Processed file written to /var/cache/cloud/processed/vm_dhcp_entry.json.4ea45061-2efb-4467-8eaa-db3d77fb0a7b.gz Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
parent
d943eb916b
commit
551e11cf3e
|
|
@ -22,6 +22,8 @@ package com.cloud.agent.resource.virtualnetwork.facade;
|
|||
import java.util.Hashtable;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.agent.api.BumpUpPriorityCommand;
|
||||
import com.cloud.agent.api.SetupGuestNetworkCommand;
|
||||
|
|
@ -58,6 +60,8 @@ import com.google.gson.GsonBuilder;
|
|||
|
||||
public abstract class AbstractConfigItemFacade {
|
||||
|
||||
private static final Logger s_logger = Logger.getLogger(AbstractConfigItemFacade.class);
|
||||
|
||||
private final static Gson gson;
|
||||
|
||||
private static Hashtable<Class<? extends NetworkElementCommand>, AbstractConfigItemFacade> flyweight = new Hashtable<Class<? extends NetworkElementCommand>, AbstractConfigItemFacade>();
|
||||
|
|
@ -104,13 +108,26 @@ public abstract class AbstractConfigItemFacade {
|
|||
return instance;
|
||||
}
|
||||
|
||||
private static String appendUuidToJsonFiles(final String filename) {
|
||||
String remoteFileName = new String(filename);
|
||||
if (remoteFileName.endsWith("json")) {
|
||||
remoteFileName += "." + UUID.randomUUID().toString();
|
||||
}
|
||||
return remoteFileName;
|
||||
}
|
||||
|
||||
protected List<ConfigItem> generateConfigItems(final ConfigBase configuration) {
|
||||
final List<ConfigItem> cfg = new LinkedList<>();
|
||||
|
||||
final ConfigItem configFile = new FileConfigItem(VRScripts.CONFIG_PERSIST_LOCATION, destinationFile, gson.toJson(configuration));
|
||||
final String remoteFilename = appendUuidToJsonFiles(destinationFile);
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("Transformed filename: " + destinationFile + " to: " + remoteFilename);
|
||||
}
|
||||
|
||||
final ConfigItem configFile = new FileConfigItem(VRScripts.CONFIG_PERSIST_LOCATION, remoteFilename, gson.toJson(configuration));
|
||||
cfg.add(configFile);
|
||||
|
||||
final ConfigItem updateCommand = new ScriptConfigItem(VRScripts.UPDATE_CONFIG, destinationFile);
|
||||
final ConfigItem updateCommand = new ScriptConfigItem(VRScripts.UPDATE_CONFIG, remoteFilename);
|
||||
cfg.add(updateCommand);
|
||||
|
||||
return cfg;
|
||||
|
|
|
|||
|
|
@ -332,6 +332,9 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||
if (details == null) {
|
||||
details = parser.getLines();
|
||||
}
|
||||
|
||||
s_logger.debug("Executing script in VR: " + script);
|
||||
|
||||
return new ExecutionResult(command.getExitValue() == 0, details);
|
||||
}
|
||||
|
||||
|
|
@ -340,6 +343,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||
final File permKey = new File("/root/.ssh/id_rsa.cloud");
|
||||
String error = null;
|
||||
|
||||
s_logger.debug("Creating file in VR, with ip: " + routerIp + ", file: " + filename);
|
||||
|
||||
try {
|
||||
SshHelper.scpTo(routerIp, 3922, "root", permKey, null, path, content.getBytes(), filename, null);
|
||||
} catch (final Exception e) {
|
||||
|
|
|
|||
|
|
@ -67,12 +67,3 @@ then
|
|||
python /opt/cloud/bin/baremetal-vr.py &
|
||||
logger -t cloud "Started baremetal-vr service"
|
||||
fi
|
||||
|
||||
if [ "$TYPE" == "router" ] || [ "$TYPE" == "vpcrouter" ] || [ "$TYPE" == "dhcpsrvr" ]
|
||||
then
|
||||
if [ -x /opt/cloud/bin/update_config.py ]
|
||||
then
|
||||
/opt/cloud/bin/update_config.py cmd_line.json
|
||||
logger -t cloud "Updated config: cmd_line.json"
|
||||
fi
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -17,33 +17,29 @@
|
|||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import sys
|
||||
import os
|
||||
import base64
|
||||
|
||||
from merge import DataBag
|
||||
from pprint import pprint
|
||||
import subprocess
|
||||
from collections import OrderedDict
|
||||
|
||||
import logging
|
||||
import re
|
||||
import time
|
||||
import shutil
|
||||
|
||||
import os.path
|
||||
import os
|
||||
from fcntl import flock, LOCK_EX, LOCK_UN
|
||||
|
||||
from cs.CsDatabag import CsDataBag, CsCmdLine
|
||||
import cs.CsHelper
|
||||
from cs.CsDatabag import CsDataBag
|
||||
from cs.CsNetfilter import CsNetfilters
|
||||
from cs.CsDhcp import CsDhcp
|
||||
from cs.CsRedundant import *
|
||||
from cs.CsFile import CsFile
|
||||
from cs.CsApp import CsApache, CsDnsmasq
|
||||
from cs.CsMonitor import CsMonitor
|
||||
from cs.CsLoadBalancer import CsLoadBalancer
|
||||
from cs.CsConfig import CsConfig
|
||||
from cs.CsProcess import CsProcess
|
||||
from cs.CsStaticRoutes import CsStaticRoutes
|
||||
|
||||
OCCURRENCES = 1
|
||||
|
||||
class CsPassword(CsDataBag):
|
||||
|
||||
|
|
@ -668,16 +664,11 @@ class CsRemoteAccessVpn(CsDataBag):
|
|||
continue
|
||||
vpnconfig=self.dbag[public_ip]
|
||||
|
||||
#Enable remote access vpn
|
||||
# Enable remote access vpn
|
||||
if vpnconfig['create']:
|
||||
shutdownIpsec = False
|
||||
logging.debug("Enabling remote access vpn on "+ public_ip)
|
||||
|
||||
dev = CsHelper.get_device(public_ip)
|
||||
if dev == "":
|
||||
logging.error("Request for ipsec to %s not possible because ip is not configured", public_ip)
|
||||
continue
|
||||
|
||||
CsHelper.start_if_stopped("ipsec")
|
||||
self.configure_l2tpIpsec(public_ip, self.dbag[public_ip])
|
||||
logging.debug("Remote accessvpn data bag %s", self.dbag)
|
||||
|
|
@ -960,16 +951,49 @@ class CsForwardingRules(CsDataBag):
|
|||
self.fw.append(["nat", "front", "-A POSTROUTING -s %s -d %s -j SNAT -o eth0 --to-source %s" % (self.getNetworkByIp(rule['internal_ip']),rule["internal_ip"], self.getGuestIp())])
|
||||
|
||||
|
||||
class IpTablesExecutor:
|
||||
|
||||
config = None
|
||||
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
|
||||
def process(self):
|
||||
acls = CsAcl('networkacl', self.config)
|
||||
acls.process()
|
||||
|
||||
acls = CsAcl('firewallrules', self.config)
|
||||
acls.process()
|
||||
|
||||
fwd = CsForwardingRules("forwardingrules", self.config)
|
||||
fwd.process()
|
||||
|
||||
vpns = CsSite2SiteVpn("site2sitevpn", self.config)
|
||||
vpns.process()
|
||||
|
||||
rvpn = CsRemoteAccessVpn("remoteaccessvpn", self.config)
|
||||
rvpn.process()
|
||||
|
||||
lb = CsLoadBalancer("loadbalancer", self.config)
|
||||
lb.process()
|
||||
|
||||
logging.debug("Configuring iptables rules")
|
||||
nf = CsNetfilters()
|
||||
nf.compare(self.config.get_fw())
|
||||
|
||||
logging.debug("Configuring iptables rules done ...saving rules")
|
||||
|
||||
# Save iptables configuration - will be loaded on reboot by the iptables-restore that is configured on /etc/rc.local
|
||||
CsHelper.save_iptables("iptables-save", "/etc/iptables/router_rules.v4")
|
||||
CsHelper.save_iptables("ip6tables-save", "/etc/iptables/router_rules.v6")
|
||||
|
||||
def main(argv):
|
||||
# The file we are currently processing, if it is "cmd_line.json" everything will be processed.
|
||||
process_file = argv[1]
|
||||
|
||||
# process_file can be None, if so assume cmd_line.json
|
||||
if process_file is None:
|
||||
process_file = "cmd_line.json"
|
||||
|
||||
# Track if changes need to be committed to NetFilter
|
||||
iptables_change = False
|
||||
logging.debug("No file was received, do not go on processing the other actions. Just leave for now.")
|
||||
return
|
||||
|
||||
# The "GLOBAL" Configuration object
|
||||
config = CsConfig()
|
||||
|
|
@ -977,108 +1001,61 @@ def main(argv):
|
|||
logging.basicConfig(filename=config.get_logger(),
|
||||
level=config.get_level(),
|
||||
format=config.get_format())
|
||||
try:
|
||||
# Load stored ip addresses from disk to CsConfig()
|
||||
config.set_address()
|
||||
|
||||
logging.debug("Configuring ip addresses")
|
||||
config.address().compare()
|
||||
config.address().process()
|
||||
# Load stored ip addresses from disk to CsConfig()
|
||||
config.set_address()
|
||||
|
||||
if process_file in ["cmd_line.json", "guest_network.json"]:
|
||||
logging.debug("Configuring Guest Network")
|
||||
iptables_change = True
|
||||
logging.debug("Configuring ip addresses")
|
||||
config.address().compare()
|
||||
config.address().process()
|
||||
|
||||
if process_file in ["cmd_line.json", "vm_password.json"]:
|
||||
logging.debug("Configuring vmpassword")
|
||||
password = CsPassword("vmpassword", config)
|
||||
password.process()
|
||||
databag_map = OrderedDict([("guest_network.json", {"process_iptables" : True, "executor" : IpTablesExecutor(config)}),
|
||||
("vm_password.json", {"process_iptables" : False, "executor" : CsPassword("vmpassword", config)}),
|
||||
("vm_metadata.json", {"process_iptables" : False, "executor" : CsVmMetadata('vmdata', config)}),
|
||||
("network_acl.json", {"process_iptables" : True, "executor" : IpTablesExecutor(config)}),
|
||||
("firewall_rules.json", {"process_iptables" : True, "executor" : IpTablesExecutor(config)}),
|
||||
("forwarding_rules.json", {"process_iptables" : True, "executor" : IpTablesExecutor(config)}),
|
||||
("staticnat_rules.json", {"process_iptables" : True, "executor" : IpTablesExecutor(config)}),
|
||||
("site_2_site_vpn.json", {"process_iptables" : True, "executor" : IpTablesExecutor(config)}),
|
||||
("remote_access_vpn.json", {"process_iptables" : True, "executor" : IpTablesExecutor(config)}),
|
||||
("vpn_user_list.json", {"process_iptables" : False, "executor" : CsVpnUser("vpnuserlist", config)}),
|
||||
("vm_dhcp_entry.json", {"process_iptables" : False, "executor" : CsDhcp("dhcpentry", config)}),
|
||||
("dhcp.json", {"process_iptables" : False, "executor" : CsDhcp("dhcpentry", config)}),
|
||||
("load_balancer.json", {"process_iptables" : True, "executor" : IpTablesExecutor(config)}),
|
||||
("monitor_service.json", {"process_iptables" : False, "executor" : CsMonitor("monitorservice", config)}),
|
||||
("static_routes.json", {"process_iptables" : False, "executor" : CsStaticRoutes("staticroutes", config)})
|
||||
])
|
||||
|
||||
if process_file in ["cmd_line.json", "vm_metadata.json"]:
|
||||
logging.debug("Configuring vmdata")
|
||||
metadata = CsVmMetadata('vmdata', config)
|
||||
metadata.process()
|
||||
if process_file.count("cmd_line.json") == OCCURRENCES:
|
||||
logging.debug("cmd_line.json changed. All other files will be processed as well.")
|
||||
|
||||
if process_file in ["cmd_line.json", "network_acl.json"]:
|
||||
logging.debug("Configuring networkacl")
|
||||
iptables_change = True
|
||||
while databag_map:
|
||||
item = databag_map.popitem(last = False)
|
||||
item_name = item[0]
|
||||
item_dict = item[1]
|
||||
if not item_dict["process_iptables"]:
|
||||
executor = item_dict["executor"]
|
||||
executor.process()
|
||||
|
||||
if process_file in ["cmd_line.json", "firewall_rules.json"]:
|
||||
logging.debug("Configuring firewall rules")
|
||||
iptables_change = True
|
||||
iptables_executor = IpTablesExecutor(config)
|
||||
iptables_executor.process()
|
||||
else:
|
||||
while databag_map:
|
||||
item = databag_map.popitem(last = False)
|
||||
item_name = item[0]
|
||||
item_dict = item[1]
|
||||
if process_file.count(item_name) == OCCURRENCES:
|
||||
executor = item_dict["executor"]
|
||||
executor.process()
|
||||
|
||||
if process_file in ["cmd_line.json", "forwarding_rules.json", "staticnat_rules.json"]:
|
||||
logging.debug("Configuring PF rules")
|
||||
iptables_change = True
|
||||
if item_dict["process_iptables"]:
|
||||
iptables_executor = IpTablesExecutor(config)
|
||||
iptables_executor.process()
|
||||
|
||||
if process_file in ["cmd_line.json", "site_2_site_vpn.json"]:
|
||||
logging.debug("Configuring s2s vpn")
|
||||
iptables_change = True
|
||||
break
|
||||
|
||||
if process_file in ["cmd_line.json", "remote_access_vpn.json"]:
|
||||
logging.debug("Configuring remote access vpn")
|
||||
iptables_change = True
|
||||
|
||||
if process_file in ["cmd_line.json", "vpn_user_list.json"]:
|
||||
logging.debug("Configuring vpn users list")
|
||||
vpnuser = CsVpnUser("vpnuserlist", config)
|
||||
vpnuser.process()
|
||||
|
||||
if process_file in ["cmd_line.json", "vm_dhcp_entry.json", "dhcp.json"]:
|
||||
logging.debug("Configuring dhcp entry")
|
||||
dhcp = CsDhcp("dhcpentry", config)
|
||||
dhcp.process()
|
||||
|
||||
if process_file in ["cmd_line.json", "load_balancer.json"]:
|
||||
logging.debug("Configuring load balancer")
|
||||
iptables_change = True
|
||||
|
||||
if process_file in ["cmd_line.json", "monitor_service.json"]:
|
||||
logging.debug("Configuring monitor service")
|
||||
mon = CsMonitor("monitorservice", config)
|
||||
mon.process()
|
||||
|
||||
# If iptable rules have changed, apply them.
|
||||
if iptables_change:
|
||||
acls = CsAcl('networkacl', config)
|
||||
acls.process()
|
||||
|
||||
acls = CsAcl('firewallrules', config)
|
||||
acls.flushAllowAllEgressRules()
|
||||
acls.process()
|
||||
|
||||
fwd = CsForwardingRules("forwardingrules", config)
|
||||
fwd.process()
|
||||
|
||||
vpns = CsSite2SiteVpn("site2sitevpn", config)
|
||||
vpns.process()
|
||||
|
||||
rvpn = CsRemoteAccessVpn("remoteaccessvpn", config)
|
||||
rvpn.process()
|
||||
|
||||
lb = CsLoadBalancer("loadbalancer", config)
|
||||
lb.process()
|
||||
|
||||
logging.debug("Configuring iptables rules")
|
||||
nf = CsNetfilters()
|
||||
nf.compare(config.get_fw())
|
||||
|
||||
logging.debug("Configuring iptables rules done ...saving rules")
|
||||
|
||||
# Save iptables configuration - will be loaded on reboot by the iptables-restore that is configured on /etc/rc.local
|
||||
CsHelper.save_iptables("iptables-save", "/etc/iptables/router_rules.v4")
|
||||
CsHelper.save_iptables("ip6tables-save", "/etc/iptables/router_rules.v6")
|
||||
|
||||
red = CsRedundant(config)
|
||||
red.set()
|
||||
|
||||
if process_file in ["cmd_line.json", "static_routes.json"]:
|
||||
logging.debug("Configuring static routes")
|
||||
static_routes = CsStaticRoutes("staticroutes", config)
|
||||
static_routes.process()
|
||||
except Exception:
|
||||
logging.exception("Exception while configuring router")
|
||||
return 1
|
||||
red = CsRedundant(config)
|
||||
red.set()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv)
|
||||
|
|
|
|||
|
|
@ -18,8 +18,10 @@
|
|||
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
import uuid
|
||||
import logging
|
||||
import gzip
|
||||
import shutil
|
||||
import cs_ip
|
||||
import cs_guestnetwork
|
||||
import cs_cmdline
|
||||
|
|
@ -36,8 +38,6 @@ import cs_remoteaccessvpn
|
|||
import cs_vpnusers
|
||||
import cs_staticroutes
|
||||
|
||||
from pprint import pprint
|
||||
|
||||
|
||||
class DataBag:
|
||||
|
||||
|
|
@ -282,22 +282,26 @@ class QueueFile:
|
|||
if data is not None:
|
||||
self.data = data
|
||||
self.type = self.data["type"]
|
||||
proc = updateDataBag(self)
|
||||
updateDataBag(self)
|
||||
return
|
||||
fn = self.configCache + '/' + self.fileName
|
||||
filename = '{cache_location}/{json_file}'.format(cache_location = self.configCache, json_file = self.fileName)
|
||||
try:
|
||||
handle = open(fn)
|
||||
except IOError:
|
||||
logging.error("Could not open %s", fn)
|
||||
handle = open(filename)
|
||||
except IOError as exception:
|
||||
error_message = ("Exception occurred with the following exception error '{error}'. Could not open '{file}'. "
|
||||
"It seems that the file has already been moved.".format(error = exception, file = filename))
|
||||
logging.error(error_message)
|
||||
else:
|
||||
logging.info("Continuing with the processing of file '{file}'".format(file = filename))
|
||||
|
||||
self.data = json.load(handle)
|
||||
self.type = self.data["type"]
|
||||
handle.close()
|
||||
if self.keep:
|
||||
self.__moveFile(fn, self.configCache + "/processed")
|
||||
self.__moveFile(filename, self.configCache + "/processed")
|
||||
else:
|
||||
os.remove(fn)
|
||||
proc = updateDataBag(self)
|
||||
os.remove(filename)
|
||||
updateDataBag(self)
|
||||
|
||||
def setFile(self, name):
|
||||
self.fileName = name
|
||||
|
|
@ -314,8 +318,15 @@ class QueueFile:
|
|||
def __moveFile(self, origPath, path):
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
timestamp = str(int(round(time.time())))
|
||||
os.rename(origPath, path + "/" + self.fileName + "." + timestamp)
|
||||
originalName = os.path.basename(origPath)
|
||||
if originalName.count(".") == 1:
|
||||
originalName += "." + str(uuid.uuid4())
|
||||
zipped_file_name = path + "/" + originalName + ".gz"
|
||||
with open(origPath, 'rb') as f_in, gzip.open(zipped_file_name, 'wb') as f_out:
|
||||
shutil.copyfileobj(f_in, f_out)
|
||||
os.remove(origPath)
|
||||
|
||||
logging.debug("Processed file written to %s", zipped_file_name)
|
||||
|
||||
|
||||
class PrivateGatewayHack:
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ import os.path
|
|||
import configure
|
||||
import json
|
||||
|
||||
OCCURRENCES = 1
|
||||
|
||||
logging.basicConfig(filename='/var/log/cloud.log', level=logging.INFO, format='%(asctime)s %(filename)s %(funcName)s:%(lineno)d %(message)s')
|
||||
|
||||
# first commandline argument should be the file to process
|
||||
|
|
@ -39,6 +41,14 @@ jsonCmdConfigPath = jsonPath % sys.argv[1]
|
|||
currentGuestNetConfig = "/etc/cloudstack/guestnetwork.json"
|
||||
|
||||
|
||||
# If the command line json file is unprocessed process it
|
||||
# This is important or, the control interfaces will get deleted!
|
||||
if os.path.isfile(jsonPath % "cmd_line.json"):
|
||||
qf = QueueFile()
|
||||
qf.setFile("cmd_line.json")
|
||||
qf.load(None)
|
||||
|
||||
|
||||
def finish_config():
|
||||
# Converge
|
||||
returncode = configure.main(sys.argv)
|
||||
|
|
@ -111,19 +121,13 @@ def is_guestnet_configured(guestnet_dict, keys):
|
|||
|
||||
return exists
|
||||
|
||||
if not (os.path.isfile(jsonCmdConfigPath) and os.access(jsonCmdConfigPath, os.R_OK)):
|
||||
filename = jsonCmdConfigPath
|
||||
if not (os.path.isfile(filename) and os.access(filename, os.R_OK)):
|
||||
print "[ERROR] update_config.py :: You are telling me to process %s, but i can't access it" % jsonCmdConfigPath
|
||||
sys.exit(1)
|
||||
|
||||
# If the command line json file is unprocessed process it
|
||||
# This is important or, the control interfaces will get deleted!
|
||||
if os.path.isfile(jsonPath % "cmd_line.json"):
|
||||
qf = QueueFile()
|
||||
qf.setFile("cmd_line.json")
|
||||
qf.load(None)
|
||||
|
||||
# If the guest network is already configured and have the same IP, do not try to configure it again otherwise it will break
|
||||
if sys.argv[1] == "guest_network.json":
|
||||
if sys.argv[1] and sys.argv[1].count("guest_network.json") == OCCURRENCES:
|
||||
if os.path.isfile(currentGuestNetConfig):
|
||||
file = open(currentGuestNetConfig)
|
||||
guestnet_dict = json.load(file)
|
||||
|
|
|
|||
Loading…
Reference in New Issue