diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index 87eddca5a69..63b7cd0b32d 100755 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -313,4 +313,10 @@ public class EventTypes { public static final String EVENT_AUTOSCALEVMGROUP_UPDATE = "AUTOSCALEVMGROUP.UPDATE"; public static final String EVENT_AUTOSCALEVMGROUP_ENABLE = "AUTOSCALEVMGROUP.ENABLE"; public static final String EVENT_AUTOSCALEVMGROUP_DISABLE = "AUTOSCALEVMGROUP.DISABLE"; + + public static final String EVENT_BAREMETAL_DHCP_SERVER_ADD = "PHYSICAL.DHCP.ADD"; + public static final String EVENT_BAREMETAL_DHCP_SERVER_DELETE = "PHYSICAL.DHCP.DELETE"; + + public static final String EVENT_BAREMETAL_PXE_SERVER_ADD = "PHYSICAL.PXE.ADD"; + public static final String EVENT_BAREMETAL_PXE_SERVER_DELETE = "PHYSICAL.PXE.DELETE"; } diff --git a/api/src/com/cloud/host/Host.java b/api/src/com/cloud/host/Host.java index bd26f81c8c2..7236680fd7b 100755 --- a/api/src/com/cloud/host/Host.java +++ b/api/src/com/cloud/host/Host.java @@ -39,6 +39,8 @@ public interface Host extends StateObject, Identity, InternalIdentity { ExternalLoadBalancer(false), ExternalVirtualSwitchSupervisor(false), PxeServer(false), + BaremetalPxe(false), + BaremetalDhcp(false), TrafficMonitor(false), ExternalDhcp(false), diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java old mode 100644 new mode 100755 index 58a78318fae..d084271ca7c --- a/api/src/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/org/apache/cloudstack/api/ApiConstants.java @@ -427,6 +427,7 @@ public class ApiConstants { public static final String CONDITION_IDS = "conditionids"; public static final String COUNTERPARAM_LIST = "counterparam"; public static final String AUTOSCALE_USER_ID = "autoscaleuserid"; + public static final String BAREMETAL_DISCOVER_NAME = "baremetaldiscovername"; public enum HostDetails { all, capacity, events, stats, min; diff --git a/plugins/hypervisors/baremetal/pom.xml b/plugins/hypervisors/baremetal/pom.xml new file mode 100755 index 00000000000..600eedb1440 --- /dev/null +++ b/plugins/hypervisors/baremetal/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + org.apache.cloudstack + cloudstack-plugins + 4.1.0-SNAPSHOT + ../../pom.xml + + cloud-plugin-hypervisor-baremetal + Apache CloudStack Plugin - Hypervisor Baremetal + + + commons-lang + commons-lang + 2.6 + + + + diff --git a/plugins/hypervisors/baremetal/resources/security_group_agent/cs-sgagent b/plugins/hypervisors/baremetal/resources/security_group_agent/cs-sgagent new file mode 100755 index 00000000000..cda70175f62 --- /dev/null +++ b/plugins/hypervisors/baremetal/resources/security_group_agent/cs-sgagent @@ -0,0 +1,51 @@ +#!/bin/sh + +# the following is chkconfig init header +# +# cs-sgagent: cloudStack baremetal sercurity group agent +# +# chkconfig: 345 97 03 +# description: This is a daemon instructed by CloudStack management server \ +# to perform baremetal security group related operations\ +# +# processname: cs-sgagent +# pidfile: /var/run/cssgagent.pid +# + +check_status() { + pidfile='/var/run/cssgagent.pid' + if [ ! -f $pidfile ]; then + echo "cloudstack baremetal security group agent is stopped" + exit 1 + else + pid=`cat $pidfile` + ps -p $pid > /dev/null + if [ $? -eq 0 ]; then + echo "cloudstack baremetal security group agent is running, pid is $pid" + exit 0 + else + echo "cloudstack baremetal security group agent is stopped, but pidfile at $pidfile is not cleaned. It may be caused by the agent crashed at last time, manually cleaning it would be ok" + exit 1 + fi + fi +} + +if [ $# -eq 0 ]; then + echo "usage: $0 +[start|stop|restart|status]" + exit 1 +fi + +if [ "$@" = "status" ]; then + check_status +else + python -c "from security_group_agent import cs_sg_agent; cs_sg_agent.main()" $@ +fi + +if [ $? -eq 0 ]; then + echo "$@ cloudstack baremetal security group agent .... SUCCESS" + exit 0 +else + echo "$@ cloudstack baremetal security group agent .... FAILED" + exit 1 +fi diff --git a/plugins/hypervisors/baremetal/resources/security_group_agent/security_group_agent/__init__.py b/plugins/hypervisors/baremetal/resources/security_group_agent/security_group_agent/__init__.py new file mode 100644 index 00000000000..f7f5f60ff05 --- /dev/null +++ b/plugins/hypervisors/baremetal/resources/security_group_agent/security_group_agent/__init__.py @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Automatically generated by addcopyright.py at 01/29/2013 diff --git a/plugins/hypervisors/baremetal/resources/security_group_agent/security_group_agent/cs_sg_agent.py b/plugins/hypervisors/baremetal/resources/security_group_agent/security_group_agent/cs_sg_agent.py new file mode 100755 index 00000000000..a292c0bcfb8 --- /dev/null +++ b/plugins/hypervisors/baremetal/resources/security_group_agent/security_group_agent/cs_sg_agent.py @@ -0,0 +1,237 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Automatically generated by addcopyright.py at 01/29/2013 +''' +Created on Jan 2, 2013 + +@author: frank +''' +import cherrypy +import sglib +import xmlobject +import types +import uuid +import os.path +import sys +import os + +class SGRule(object): + def __init__(self): + self.protocol = None + self.start_port = None + self.end_port = None + self.allowed_ips = [] + +class IPSet(object): + IPSET_TYPE = 'hash:ip' + def __init__(self, setname, ips): + self.ips = ips + self.name = setname + + def create(self): + tmpname = str(uuid.uuid4()).replace('-', '')[0:30] + sglib.ShellCmd('ipset -N %s %s' % (tmpname, self.IPSET_TYPE))() + try: + for ip in self.ips: + sglib.ShellCmd('ipset -A %s %s' % (tmpname, ip))() + + try: + sglib.ShellCmd('ipset -N %s %s' % (self.name, self.IPSET_TYPE))() + cherrypy.log('created new ipset: %s' % self.name) + except Exception: + cherrypy.log('%s already exists, no need to create new' % self.name) + finally: + sglib.ShellCmd('ipset -W %s %s' % (tmpname, self.name))() + sglib.ShellCmd('ipset -F %s' % tmpname)() + sglib.ShellCmd('ipset -X %s' % tmpname)() + + @staticmethod + def destroy_sets(sets_to_keep): + sets = sglib.ShellCmd('ipset list')() + for s in sets.split('\n'): + if 'Name:' in s: + set_name = s.split(':', 1)[1].strip() + if not set_name in sets_to_keep: + sglib.ShellCmd('ipset destroy %s' % set_name)() + cherrypy.log('destroyed unused ipset: %s' % set_name) + +class SGAgent(object): + def __init__(self): + pass + + def _self_list(self, obj): + if isinstance(obj, types.ListType): + return obj + else: + return [obj] + + def set_rules(self, req): + body = req.body + doc = xmlobject.loads(body) + vm_name = doc.vmName.text_ + vm_id = doc.vmId.text_ + vm_ip = doc.vmIp.text_ + vm_mac = doc.vmMac.text_ + sig = doc.signature.text_ + seq = doc.sequenceNumber.text_ + + def parse_rules(rules, lst): + for i in self._self_list(rules): + r = SGRule() + r.protocol = i.protocol.text_ + r.start_port = i.startPort.text_ + r.end_port = i.endPort.text_ + if hasattr(i, 'ip'): + for ip in self._self_list(i.ip): + r.allowed_ips.append(ip.text_) + lst.append(r) + + i_rules = [] + if hasattr(doc, 'ingressRules'): + parse_rules(doc.ingressRules, i_rules) + + e_rules = [] + if hasattr(doc, 'egressRules'): + parse_rules(doc.egressRules, e_rules) + + def create_chain(name): + try: + sglib.ShellCmd('iptables -F %s' % name)() + except Exception: + sglib.ShellCmd('iptables -N %s' % name)() + + def apply_rules(rules, chainname, direction, action, current_set_names): + create_chain(chainname) + for r in i_rules: + allow_any = False + if '0.0.0.0/0' in r.allowed_ips: + allow_any = True + r.allowed_ips.remove('0.0.0.0/0') + + if r.allowed_ips: + setname = '_'.join([chainname, r.protocol, r.start_port, r.end_port]) + ipset = IPSet(setname, r.allowed_ips) + ipset.create() + current_set_names.append(setname) + + if r.protocol == 'all': + cmd = ['iptables -I', chainname, '-m state --state NEW -m set --set', setname, direction, '-j', action] + sglib.ShellCmd(' '.join(cmd))() + elif r.protocol != 'icmp': + port_range = ":".join([r.start_port, r.end_port]) + cmd = ['iptables', '-I', chainname, '-p', r.protocol, '-m', r.protocol, '--dport', port_range, '-m state --state NEW -m set --set', setname, direction, '-j', action] + sglib.ShellCmd(' '.join(cmd))() + else: + port_range = "/".join([r.start_port, r.end_port]) + if r.start_port == "-1": + port_range = "any" + cmd = ['iptables', '-I', i_chain_name, '-p', 'icmp', '--icmp-type', port_range, '-m set --set', setname, direction, '-j', action] + sglib.ShellCmd(' '.join(cmd))() + + + if allow_any and r.protocol != 'all': + if r.protocol != 'icmp': + port_range = ":".join([r.start_port, r.end_port]) + cmd = ['iptables', '-I', chainname, '-p', r.protocol, '-m', r.protocol, '--dport', port_range, '-m', 'state', '--state', 'NEW', '-j', action] + sglib.ShellCmd(' '.join(cmd))() + else: + port_range = "/".join([r.start_port, r.end_port]) + if r.start_port == "-1": + port_range = "any" + cmd = ['iptables', '-I', i_chain_name, '-p', 'icmp', '--icmp-type', port_range, '-j', action] + sglib.ShellCmd(' '.join(cmd))() + + current_sets = [] + i_chain_name = vm_name + '-in' + apply_rules(i_rules, i_chain_name, 'src', 'ACCEPT', current_sets) + e_chain_name = vm_name + '-eg' + apply_rules(e_rules, e_chain_name, 'dst', 'RETURN', current_sets) + + if e_rules: + sglib.ShellCmd('iptables -A %s -j RETURN' % e_chain_name) + else: + sglib.ShellCmd('iptables -A %s -j DROP' % e_chain_name) + + sglib.ShellCmd('iptables -A %s -j DROP' % i_chain_name) + IPSet.destroy_sets(current_sets) + + + def echo(self, req): + cherrypy.log("echo: I am alive") + + def index(self): + req = sglib.Request.from_cherrypy_request(cherrypy.request) + cmd_name = req.headers['command'] + + if not hasattr(self, cmd_name): + raise ValueError("SecurityGroupAgent doesn't have a method called '%s'" % cmd_name) + method = getattr(self, cmd_name) + + return method(req) + index.exposed = True + + @staticmethod + def start(): + cherrypy.log.access_file = '/var/log/cs-securitygroup.log' + cherrypy.log.error_file = '/var/log/cs-securitygroup.log' + cherrypy.server.socket_host = '0.0.0.0' + cherrypy.server.socket_port = 9988 + cherrypy.quickstart(SGAgent()) + + @staticmethod + def stop(): + cherrypy.engine.exit() + +PID_FILE = '/var/run/cssgagent.pid' +class SGAgentDaemon(sglib.Daemon): + def __init__(self): + super(SGAgentDaemon, self).__init__(PID_FILE) + self.is_stopped = False + self.agent = SGAgent() + sglib.Daemon.register_atexit_hook(self._do_stop) + + def _do_stop(self): + if self.is_stopped: + return + self.is_stopped = True + self.agent.stop() + + def run(self): + self.agent.start() + + def stop(self): + self.agent.stop() + super(SGAgentDaemon, self).stop() + +def main(): + usage = 'usage: python -c "from security_group_agent import cs_sg_agent; cs_sg_agent.main()" start|stop|restart' + if len(sys.argv) != 2 or not sys.argv[1] in ['start', 'stop', 'restart']: + print usage + sys.exit(1) + + cmd = sys.argv[1] + agentdaemon = SGAgentDaemon() + if cmd == 'start': + agentdaemon.start() + elif cmd == 'stop': + agentdaemon.stop() + else: + agentdaemon.restart() + + sys.exit(0) + \ No newline at end of file diff --git a/plugins/hypervisors/baremetal/resources/security_group_agent/security_group_agent/sglib.py b/plugins/hypervisors/baremetal/resources/security_group_agent/security_group_agent/sglib.py new file mode 100755 index 00000000000..bf64effa190 --- /dev/null +++ b/plugins/hypervisors/baremetal/resources/security_group_agent/security_group_agent/sglib.py @@ -0,0 +1,226 @@ +#!/usr/bin/env python +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Automatically generated by addcopyright.py at 01/29/2013 + +import sys, os, time, atexit +import traceback +import subprocess +from signal import SIGTERM +import cherrypy +import copy + +class Request(object): + def __init__(self): + self.headers = None + self.body = None + self.method = None + self.query_string = None + + @staticmethod + def from_cherrypy_request(creq): + req = Request() + req.headers = copy.copy(creq.headers) + req.body = creq.body.fp.read() if creq.body else None + req.method = copy.copy(creq.method) + req.query_string = copy.copy(creq.query_string) if creq.query_string else None + return req + +class ShellError(Exception): + '''shell error''' + +class ShellCmd(object): + ''' + classdocs + ''' + def __init__(self, cmd, workdir=None, pipe=True): + ''' + Constructor + ''' + self.cmd = cmd + if pipe: + self.process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE, executable='/bin/sh', cwd=workdir) + else: + self.process = subprocess.Popen(cmd, shell=True, executable='/bin/sh', cwd=workdir) + + self.stdout = None + self.stderr = None + self.return_code = None + + def __call__(self, is_exception=True): + (self.stdout, self.stderr) = self.process.communicate() + if is_exception and self.process.returncode != 0: + err = [] + err.append('failed to execute shell command: %s' % self.cmd) + err.append('return code: %s' % self.process.returncode) + err.append('stdout: %s' % self.stdout) + err.append('stderr: %s' % self.stderr) + raise ShellError('\n'.join(err)) + + self.return_code = self.process.returncode + return self.stdout + +class Daemon(object): + """ + A generic daemon class. + + Usage: subclass the Daemon class and override the run() method + """ + atexit_hooks = [] + + def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'): + self.stdin = stdin + self.stdout = stdout + self.stderr = stderr + self.pidfile = pidfile + + @staticmethod + def register_atexit_hook(hook): + Daemon.atexit_hooks.append(hook) + + @staticmethod + def _atexit(): + for hook in Daemon.atexit_hooks: + try: + hook() + except Exception: + content = traceback.format_exc() + err = 'Exception when calling atexit hook[%s]\n%s' % (hook.__name__, content) + #logger.error(err) + + def daemonize(self): + """ + do the UNIX double-fork magic, see Stevens' "Advanced + Programming in the UNIX Environment" for details (ISBN 0201563177) + http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16 + """ + try: + pid = os.fork() + if pid > 0: + # exit first parent + sys.exit(0) + except OSError, e: + sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror)) + sys.exit(1) + + # decouple from parent environment + os.chdir("/") + os.setsid() + os.umask(0) + + # do second fork + try: + pid = os.fork() + if pid > 0: + # exit from second parent + sys.exit(0) + except OSError, e: + sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror)) + sys.exit(1) + + # redirect standard file descriptors + sys.stdout.flush() + sys.stderr.flush() + si = file(self.stdin, 'r') + so = file(self.stdout, 'a+') + se = file(self.stderr, 'a+', 0) + os.dup2(si.fileno(), sys.stdin.fileno()) + os.dup2(so.fileno(), sys.stdout.fileno()) + os.dup2(se.fileno(), sys.stderr.fileno()) + + # write pidfile + Daemon.register_atexit_hook(self.delpid) + atexit.register(Daemon._atexit) + pid = str(os.getpid()) + file(self.pidfile,'w').write("%s\n" % pid) + + def delpid(self): + os.remove(self.pidfile) + + def start(self): + """ + Start the daemon + """ + # Check for a pidfile to see if the daemon already runs + try: + pf = file(self.pidfile,'r') + pid = int(pf.read().strip()) + pf.close() + except IOError: + pid = None + + if pid: + pscmd = ShellCmd('ps -p %s > /dev/null' % pid) + pscmd(is_exception=False) + if pscmd.return_code == 0: + message = "Daemon already running, pid is %s\n" + sys.stderr.write(message % pid) + sys.exit(0) + + # Start the daemon + self.daemonize() + try: + self.run() + except Exception: + content = traceback.format_exc() + #logger.error(content) + sys.exit(1) + + def stop(self): + """ + Stop the daemon + """ + # Get the pid from the pidfile + try: + pf = file(self.pidfile,'r') + pid = int(pf.read().strip()) + pf.close() + except IOError: + pid = None + + if not pid: + message = "pidfile %s does not exist. Daemon not running?\n" + sys.stderr.write(message % self.pidfile) + return # not an error in a restart + + # Try killing the daemon process + try: + while 1: + os.kill(pid, SIGTERM) + time.sleep(0.1) + except OSError, err: + err = str(err) + if err.find("No such process") > 0: + if os.path.exists(self.pidfile): + os.remove(self.pidfile) + else: + print str(err) + sys.exit(1) + + def restart(self): + """ + Restart the daemon + """ + self.stop() + self.start() + + def run(self): + """ + You should override this method when you subclass Daemon. It will be called after the process has been + daemonized by start() or restart(). + """ diff --git a/plugins/hypervisors/baremetal/resources/security_group_agent/security_group_agent/xmlobject.py b/plugins/hypervisors/baremetal/resources/security_group_agent/security_group_agent/xmlobject.py new file mode 100755 index 00000000000..cb66d265a5a --- /dev/null +++ b/plugins/hypervisors/baremetal/resources/security_group_agent/security_group_agent/xmlobject.py @@ -0,0 +1,97 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Automatically generated by addcopyright.py at 01/29/2013 +''' +Created on Dec 25, 2012 + +@author: Frank +''' +import xml.etree.ElementTree as etree +import re +import types + +class XmlObject(object): + def __init__(self, tag): + self.__tag_name__ = tag + + def put_attr(self, name, val): + val = val.strip().strip('\t') + setattr(self, name + '_', val) + + def put_text(self, val): + val = val.strip().strip('\n').strip('\t') + if val == "": + setattr(self, 'text_', None) + else: + setattr(self, 'text_', val) + + def put_node(self, name, val): + if not hasattr(self, name): + setattr(self, name, val) + return + + nodes = getattr(self, name) + if not isinstance(nodes, types.ListType): + nodes = [] + old = getattr(self, name) + nodes.append(old) + nodes.append(val) + setattr(self, name, nodes) + else: + nodes.append(val) + setattr(self, name, nodes) + + def get(self, name, default=None): + if hasattr(self, name): + val = getattr(self, name) + if name.endswith('_'): + return val + else: + return val.text_ + else: + return default + + def __getattr__(self, name): + if name.endswith('__'): + n = name[:-1] + if hasattr(self, n): + return getattr(self, n) + else: + return None + else: + e = AttributeError('%s has no attribute %s. missing attribute %s in element <%s>' % (self.__class__.__name__, name, name, self.__tag_name__)) + setattr(e, 'missing_attrib', name) + setattr(e, 'tag_name', self.__tag_name__) + raise e + + +def _loads(node): + xo = XmlObject(node.tag) + for key in node.attrib.keys(): + xo.put_attr(key, node.attrib.get(key)) + if node.text: + xo.put_text(node.text) + for n in list(node): + sub_xo = _loads(n) + xo.put_node(n.tag, sub_xo) + return xo + +def loads(xmlstr): + xmlstr = re.sub(r'xmlns=".*"', '', xmlstr) + root = etree.fromstring(xmlstr) + return _loads(root) \ No newline at end of file diff --git a/plugins/hypervisors/baremetal/resources/security_group_agent/setup.py b/plugins/hypervisors/baremetal/resources/security_group_agent/setup.py new file mode 100755 index 00000000000..2de41d265ba --- /dev/null +++ b/plugins/hypervisors/baremetal/resources/security_group_agent/setup.py @@ -0,0 +1,44 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Automatically generated by addcopyright.py at 01/29/2013 +from setuptools import setup, find_packages +import sys, os + +version = '1.0' + +setup(name='security_group_agent', + version=version, + description="security group agent for CloudStack Baremetal", + long_description="""\ +security group agent for CloudStack Baremetal""", + classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers + keywords='security group cloudstack', + author='Frank Zhang', + author_email='frank.zhang@citrix.com', + url='http://incubator.apache.org/cloudstack/', + license='Apache License 2', + packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), + include_package_data=True, + zip_safe=True, + install_requires=[ + 'CherryPy', + ], + entry_points=""" + # -*- Entry points: -*- + """, + ) diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalCmdbDao.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalCmdbDao.java new file mode 100755 index 00000000000..0f20c677f5d --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalCmdbDao.java @@ -0,0 +1,25 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.database; + +import com.cloud.utils.db.GenericDao; + +public interface BaremetalCmdbDao extends GenericDao { + +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalCmdbDaoImpl.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalCmdbDaoImpl.java new file mode 100755 index 00000000000..fcc95efba9c --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalCmdbDaoImpl.java @@ -0,0 +1,29 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.database; + +import javax.ejb.Local; + +import com.cloud.utils.db.DB; +import com.cloud.utils.db.GenericDaoBase; +@Local(value = {BaremetalCmdbDao.class}) +@DB(txn = false) +public class BaremetalCmdbDaoImpl extends GenericDaoBase implements BaremetalCmdbDao { + +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalCmdbVO.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalCmdbVO.java new file mode 100755 index 00000000000..ee3848a5e9d --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalCmdbVO.java @@ -0,0 +1,104 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.database; + +import java.util.UUID; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name="baremetal_cmdb") +public class BaremetalCmdbVO { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name="uuid") + private String uuid; + + @Column(name="zone_id") + private long zoneId; + + @Column(name="url") + private String url; + + @Column(name="password") + private String password; + + @Column(name="username") + private String username; + + public BaremetalCmdbVO() { + uuid = UUID.randomUUID().toString(); + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public long getZoneId() { + return zoneId; + } + + public void setZoneId(long zoneId) { + this.zoneId = zoneId; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalDhcpDao.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalDhcpDao.java new file mode 100644 index 00000000000..fe6d86c80d4 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalDhcpDao.java @@ -0,0 +1,25 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.database; + +import com.cloud.utils.db.GenericDao; + +public interface BaremetalDhcpDao extends GenericDao { + +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalDhcpDaoImpl.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalDhcpDaoImpl.java new file mode 100644 index 00000000000..3d9c6deaabc --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalDhcpDaoImpl.java @@ -0,0 +1,42 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.database; + +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import com.cloud.utils.db.DB; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.SearchCriteria2; + +@Local(value=BaremetalDhcpDao.class) +@DB(txn=false) +public class BaremetalDhcpDaoImpl extends GenericDaoBase implements BaremetalDhcpDao { + + public BaremetalDhcpDaoImpl() { + } + +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalDhcpVO.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalDhcpVO.java new file mode 100644 index 00000000000..0bfd541ffbc --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalDhcpVO.java @@ -0,0 +1,118 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.database; + +import java.util.UUID; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name="baremetal_dhcp_devices") +public class BaremetalDhcpVO { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name="uuid") + private String uuid; + + @Column(name = "host_id") + private long hostId; + + @Column(name = "pod_id") + private long podId; + + @Column(name = "physical_network_id") + private long physicalNetworkId; + + @Column(name = "nsp_id") + private long networkServiceProviderId ; + + @Column(name = "device_type") + private String deviceType; + + public BaremetalDhcpVO() { + super(); + this.uuid = UUID.randomUUID().toString(); + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public long getHostId() { + return hostId; + } + + public void setHostId(long hostId) { + this.hostId = hostId; + } + + public long getPhysicalNetworkId() { + return physicalNetworkId; + } + + public void setPhysicalNetworkId(long physicalNetworkId) { + this.physicalNetworkId = physicalNetworkId; + } + + public String getDeviceType() { + return deviceType; + } + + public void setDeviceType(String deviceType) { + this.deviceType = deviceType; + } + + public long getNetworkServiceProviderId() { + return networkServiceProviderId; + } + + public void setNetworkServiceProviderId(long networkServiceProviderId) { + this.networkServiceProviderId = networkServiceProviderId; + } + + public long getPodId() { + return podId; + } + + public void setPodId(long podId) { + this.podId = podId; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalPxeDao.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalPxeDao.java new file mode 100644 index 00000000000..c640638e9ba --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalPxeDao.java @@ -0,0 +1,26 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.database; + +import com.cloud.utils.db.GenericDao; +import com.cloud.utils.db.GenericDaoBase; + +public interface BaremetalPxeDao extends GenericDao { + +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalPxeDaoImpl.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalPxeDaoImpl.java new file mode 100644 index 00000000000..c47d6b2fa37 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalPxeDaoImpl.java @@ -0,0 +1,38 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.database; + +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import com.cloud.utils.db.DB; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.SearchCriteria2; + +@Local(value = {BaremetalPxeDao.class}) +@DB(txn = false) +public class BaremetalPxeDaoImpl extends GenericDaoBase implements BaremetalPxeDao { +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalPxeVO.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalPxeVO.java new file mode 100644 index 00000000000..e0daa46d819 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/database/BaremetalPxeVO.java @@ -0,0 +1,115 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.database; + +import java.util.UUID; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name="baremetal_pxe_devices") +public class BaremetalPxeVO { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name="uuid") + private String uuid; + + @Column(name = "host_id") + private long hostId; + + @Column(name = "pod_id") + private long podId; + + @Column(name = "physical_network_id") + private long physicalNetworkId; + + @Column(name = "nsp_id") + private long networkServiceProviderId ; + + @Column(name = "device_type") + private String deviceType; + + public long getId() { + return id; + } + + public BaremetalPxeVO() { + uuid = UUID.randomUUID().toString(); + } + + public void setId(long id) { + this.id = id; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public long getHostId() { + return hostId; + } + + public void setHostId(long hostId) { + this.hostId = hostId; + } + + public long getPhysicalNetworkId() { + return physicalNetworkId; + } + + public void setPhysicalNetworkId(long physicalNetworkId) { + this.physicalNetworkId = physicalNetworkId; + } + + public long getNetworkServiceProviderId() { + return networkServiceProviderId; + } + + public void setNetworkServiceProviderId(long networkServiceProviderId) { + this.networkServiceProviderId = networkServiceProviderId; + } + + public long getPodId() { + return podId; + } + + public void setPodId(long podId) { + this.podId = podId; + } + + public String getDeviceType() { + return deviceType; + } + + public void setDeviceType(String deviceType) { + this.deviceType = deviceType; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/AddBaremetalHostCmd.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/AddBaremetalHostCmd.java new file mode 100755 index 00000000000..5222d103699 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/AddBaremetalHostCmd.java @@ -0,0 +1,41 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.manager; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.command.admin.host.AddHostCmd; + +public class AddBaremetalHostCmd extends AddHostCmd { + + @Parameter(name=ApiConstants.IP_ADDRESS, type=CommandType.STRING, description="ip address intentionally allocated to this host after provisioning") + private String vmIpAddress; + + public AddBaremetalHostCmd() { + this.getFullUrlParams().put(ApiConstants.BAREMETAL_DISCOVER_NAME, BareMetalDiscoverer.class.getName()); + } + + public String getVmIpAddress() { + return vmIpAddress; + } + + public void setVmIpAddress(String vmIpAddress) { + this.vmIpAddress = vmIpAddress; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BareMetalDiscoverer.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BareMetalDiscoverer.java new file mode 100755 index 00000000000..64eeaea17c0 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BareMetalDiscoverer.java @@ -0,0 +1,280 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.manager; + +import java.net.InetAddress; +import java.net.URI; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.log4j.Logger; + +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupRoutingCommand; +import com.cloud.baremetal.networkservice.BareMetalResourceBase; +import com.cloud.configuration.Config; +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.dc.ClusterVO; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.ClusterDao; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.exception.DiscoveryException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.Network; +import com.cloud.resource.Discoverer; +import com.cloud.resource.DiscovererBase; +import com.cloud.resource.ResourceManager; +import com.cloud.resource.ResourceStateAdapter; +import com.cloud.resource.ServerResource; +import com.cloud.resource.UnableDeleteHostException; +import com.cloud.utils.component.Inject; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.Script; +import com.cloud.utils.script.Script2; +import com.cloud.utils.script.Script2.ParamType; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine.State; +import com.cloud.vm.dao.VMInstanceDao; + +@Local(value=Discoverer.class) +public class BareMetalDiscoverer extends DiscovererBase implements Discoverer, ResourceStateAdapter { + protected static final Logger s_logger = Logger.getLogger(BareMetalDiscoverer.class); + @Inject protected ClusterDao _clusterDao; + @Inject protected HostDao _hostDao; + @Inject protected DataCenterDao _dcDao; + @Inject protected VMInstanceDao _vmDao = null; + @Inject protected ResourceManager _resourceMgr; + @Inject protected ConfigurationDao _configDao; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _resourceMgr.registerResourceStateAdapter(this.getClass().getSimpleName(), this); + return super.configure(name, params); + } + + @Override + public boolean stop() { + _resourceMgr.unregisterResourceStateAdapter(this.getClass().getSimpleName()); + return super.stop(); + } + + @Override + public Map> find(long dcId, Long podId, Long clusterId, URI url, String username, String password, List hostTags) + throws DiscoveryException { + + String discoverName = _params.get(ApiConstants.BAREMETAL_DISCOVER_NAME); + if (!this.getClass().getName().equals(discoverName)) { + return null; + } + + Map> resources = new HashMap>(); + Map details = new HashMap(); + + if (!url.getScheme().equals("http")) { + String msg = "urlString is not http so we're not taking care of the discovery for this: " + url; + s_logger.debug(msg); + return null; + } + if (clusterId == null) { + String msg = "must specify cluster Id when add host"; + s_logger.debug(msg); + throw new RuntimeException(msg); + } + + if (podId == null) { + String msg = "must specify pod Id when add host"; + s_logger.debug(msg); + throw new RuntimeException(msg); + } + + ClusterVO cluster = _clusterDao.findById(clusterId); + if (cluster == null || (cluster.getHypervisorType() != HypervisorType.BareMetal)) { + if (s_logger.isInfoEnabled()) + s_logger.info("invalid cluster id or cluster is not for Bare Metal hosts"); + return null; + } + + DataCenterVO zone = _dcDao.findById(dcId); + if (zone == null) { + throw new RuntimeException("Cannot find zone " + dcId); + } + + try { + String hostname = url.getHost(); + InetAddress ia = InetAddress.getByName(hostname); + String ipmiIp = ia.getHostAddress(); + String guid = UUID.nameUUIDFromBytes(ipmiIp.getBytes()).toString(); + + String injectScript = "scripts/util/ipmi.py"; + String scriptPath = Script.findScript("", injectScript); + if (scriptPath == null) { + throw new CloudRuntimeException("Unable to find key ipmi script " + + injectScript); + } + + final Script2 command = new Script2(scriptPath, s_logger); + command.add("ping"); + command.add("hostname="+ipmiIp); + command.add("usrname="+username); + command.add("password="+password, ParamType.PASSWORD); + final String result = command.execute(); + if (result != null) { + s_logger.warn(String.format("Can not set up ipmi connection(ip=%1$s, username=%2$s, password=%3$s, args) because %4$s", ipmiIp, username, "******", result)); + return null; + } + + ClusterVO clu = _clusterDao.findById(clusterId); + if (clu.getGuid() == null) { + clu.setGuid(UUID.randomUUID().toString()); + _clusterDao.update(clusterId, clu); + } + + Map params = new HashMap(); + params.putAll(_params); + params.put("zone", Long.toString(dcId)); + params.put("pod", Long.toString(podId)); + params.put("cluster", Long.toString(clusterId)); + params.put("guid", guid); + params.put(ApiConstants.PRIVATE_IP, ipmiIp); + params.put(ApiConstants.USERNAME, username); + params.put(ApiConstants.PASSWORD, password); + + String resourceClassName = _configDao.getValue(Config.ExternalBaremetalResourceClassName.key()); + BareMetalResourceBase resource = null; + if (resourceClassName != null) { + Class clazz = Class.forName(resourceClassName); + resource = (BareMetalResourceBase) clazz.newInstance(); + String externalUrl = _configDao.getValue(Config.ExternalBaremetalSystemUrl.key()); + if (externalUrl == null) { + throw new IllegalArgumentException(String.format("You must specify ExternalBaremetalSystemUrl in global config page as ExternalBaremetalResourceClassName is not null")); + } + details.put(BaremetalManager.ExternalBaremetalSystemUrl, externalUrl); + } else { + resource = new BareMetalResourceBase(); + } + resource.configure("Bare Metal Agent", params); + + String memCapacity = (String)params.get(ApiConstants.MEMORY); + String cpuCapacity = (String)params.get(ApiConstants.CPU_SPEED); + String cpuNum = (String)params.get(ApiConstants.CPU_NUMBER); + String mac = (String)params.get(ApiConstants.HOST_MAC); + if (hostTags != null && hostTags.size() != 0) { + details.put("hostTag", hostTags.get(0)); + } + details.put(ApiConstants.MEMORY, memCapacity); + details.put(ApiConstants.CPU_SPEED, cpuCapacity); + details.put(ApiConstants.CPU_NUMBER, cpuNum); + details.put(ApiConstants.HOST_MAC, mac); + details.put(ApiConstants.USERNAME, username); + details.put(ApiConstants.PASSWORD, password); + details.put(ApiConstants.PRIVATE_IP, ipmiIp); + String vmIp = (String)params.get(ApiConstants.IP_ADDRESS); + if (vmIp != null) { + details.put(ApiConstants.IP_ADDRESS, vmIp); + } + String isEchoScAgent = _configDao.getValue(Config.EnableBaremetalSecurityGroupAgentEcho.key()); + details.put(BaremetalManager.EchoSecurityGroupAgent, isEchoScAgent); + + resources.put(resource, details); + resource.start(); + + zone.setGatewayProvider(Network.Provider.ExternalGateWay.getName()); + zone.setDnsProvider(Network.Provider.ExternalDhcpServer.getName()); + zone.setDhcpProvider(Network.Provider.ExternalDhcpServer.getName()); + _dcDao.update(zone.getId(), zone); + + s_logger.debug(String.format("Discover Bare Metal host successfully(ip=%1$s, username=%2$s, password=%3%s," + + "cpuNum=%4$s, cpuCapacity-%5$s, memCapacity=%6$s)", ipmiIp, username, "******", cpuNum, cpuCapacity, memCapacity)); + return resources; + } catch (Exception e) { + s_logger.warn("Can not set up bare metal agent", e); + } + + return null; + } + + @Override + public void postDiscovery(List hosts, long msId) + throws DiscoveryException { + } + + @Override + public boolean matchHypervisor(String hypervisor) { + return hypervisor.equalsIgnoreCase(Hypervisor.HypervisorType.BareMetal.toString()); + } + + @Override + public HypervisorType getHypervisorType() { + return Hypervisor.HypervisorType.BareMetal; + } + + @Override + public HostVO createHostVOForConnectedAgent(HostVO host, StartupCommand[] cmd) { + // TODO Auto-generated method stub + return null; + } + + @Override + public HostVO createHostVOForDirectConnectAgent(HostVO host, StartupCommand[] startup, ServerResource resource, Map details, + List hostTags) { + StartupCommand firstCmd = startup[0]; + if (!(firstCmd instanceof StartupRoutingCommand)) { + return null; + } + + StartupRoutingCommand ssCmd = ((StartupRoutingCommand) firstCmd); + if (ssCmd.getHypervisorType() != HypervisorType.BareMetal) { + return null; + } + + return _resourceMgr.fillRoutingHostVO(host, ssCmd, HypervisorType.BareMetal, details, hostTags); + } + + @Override + public DeleteHostAnswer deleteHost(HostVO host, boolean isForced, boolean isForceDeleteStorage) throws UnableDeleteHostException { + if (host.getType() != Host.Type.Routing || host.getHypervisorType() != HypervisorType.BareMetal) { + return null; + } + + List deadVms = _vmDao.listByLastHostId(host.getId()); + for (VMInstanceVO vm : deadVms) { + if (vm.getState() == State.Running || vm.getHostId() != null) { + throw new CloudRuntimeException("VM " + vm.getId() + "is still running on host " + host.getId()); + } + _vmDao.remove(vm.getId()); + } + + return new DeleteHostAnswer(true); + } + +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BareMetalGuru.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BareMetalGuru.java new file mode 100755 index 00000000000..b8a0af37f43 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BareMetalGuru.java @@ -0,0 +1,87 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.manager; + +import java.util.HashMap; +import java.util.Map; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.hypervisor.HypervisorGuru; +import com.cloud.hypervisor.HypervisorGuruBase; +import com.cloud.storage.GuestOSVO; +import com.cloud.storage.dao.GuestOSDao; +import com.cloud.utils.component.Inject; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.dao.VMInstanceDao; + +@Local(value=HypervisorGuru.class) +public class BareMetalGuru extends HypervisorGuruBase implements HypervisorGuru { + private static final Logger s_logger = Logger.getLogger(BareMetalGuru.class); + @Inject GuestOSDao _guestOsDao; + @Inject HostDao _hostDao; + @Inject VMInstanceDao _vmDao; + + protected BareMetalGuru() { + super(); + } + + @Override + public HypervisorType getHypervisorType() { + return HypervisorType.BareMetal; + } + + @Override + public VirtualMachineTO implement(VirtualMachineProfile vm) { + VirtualMachineTO to = toVirtualMachineTO(vm); + + VMInstanceVO vo = _vmDao.findById(vm.getId()); + if (vo.getLastHostId() == null) { + to.setBootArgs(BaremetalManager.DO_PXE); + } + + Map details = new HashMap(); + details.put("template", vm.getTemplate().getUrl()); + to.setDetails(details); + + // Determine the VM's OS description + GuestOSVO guestOS = _guestOsDao.findById(vm.getVirtualMachine().getGuestOSId()); + to.setOs(guestOS.getDisplayName()); + + return to; + } + + @Override + public boolean trackVmHostChange() { + return false; + } +} + + diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BareMetalTemplateAdapter.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BareMetalTemplateAdapter.java new file mode 100755 index 00000000000..15e63b9f90f --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BareMetalTemplateAdapter.java @@ -0,0 +1,217 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.manager; + +import java.util.Date; +import java.util.List; + +import javax.ejb.Local; + +import org.apache.cloudstack.api.command.user.iso.DeleteIsoCmd; +import org.apache.cloudstack.api.command.user.iso.RegisterIsoCmd; +import org.apache.cloudstack.api.command.user.template.RegisterTemplateCmd; +import org.apache.log4j.Logger; + +import com.cloud.configuration.Resource.ResourceType; +import com.cloud.dc.DataCenterVO; +import com.cloud.event.EventTypes; +import com.cloud.event.UsageEventVO; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.resource.ResourceManager; +import com.cloud.storage.VMTemplateHostVO; +import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; +import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.VMTemplateZoneVO; +import com.cloud.template.TemplateAdapter; +import com.cloud.template.TemplateAdapterBase; +import com.cloud.template.TemplateProfile; +import com.cloud.user.Account; +import com.cloud.utils.component.Inject; +import com.cloud.utils.db.DB; +import com.cloud.utils.exception.CloudRuntimeException; + +@Local(value=TemplateAdapter.class) +public class BareMetalTemplateAdapter extends TemplateAdapterBase implements TemplateAdapter { + private final static Logger s_logger = Logger.getLogger(BareMetalTemplateAdapter.class); + @Inject HostDao _hostDao; + @Inject ResourceManager _resourceMgr; + + @Override + public TemplateProfile prepare(RegisterTemplateCmd cmd) throws ResourceAllocationException { + TemplateProfile profile = super.prepare(cmd); + + if (profile.getZoneId() == null || profile.getZoneId() == -1) { + List dcs = _dcDao.listAllIncludingRemoved(); + for (DataCenterVO dc : dcs) { + List pxeServers = _resourceMgr.listAllHostsInOneZoneByType(Host.Type.BaremetalPxe, dc.getId()); + if (pxeServers.size() == 0) { + throw new CloudRuntimeException("Please add PXE server before adding baremetal template in zone " + dc.getName()); + } + } + } else { + List pxeServers = _resourceMgr.listAllHostsInOneZoneByType(Host.Type.BaremetalPxe, profile.getZoneId()); + if (pxeServers.size() == 0) { + throw new CloudRuntimeException("Please add PXE server before adding baremetal template in zone " + profile.getZoneId()); + } + } + + return profile; + } + + @Override + public TemplateProfile prepare(RegisterIsoCmd cmd) throws ResourceAllocationException { + throw new CloudRuntimeException("Baremetal doesn't support ISO template"); + } + + private void templateCreateUsage(VMTemplateVO template, HostVO host) { + if (template.getAccountId() != Account.ACCOUNT_ID_SYSTEM) { + UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_TEMPLATE_CREATE, template.getAccountId(), host.getDataCenterId(), + template.getId(), template.getName(), null, template.getSourceTemplateId(), 0L); + _usageEventDao.persist(usageEvent); + } + } + + @Override + public VMTemplateVO create(TemplateProfile profile) { + VMTemplateVO template = persistTemplate(profile); + Long zoneId = profile.getZoneId(); + + /* There is no secondary storage vm for baremetal, we use pxe server id. + * Tempalte is not bound to pxeserver right now, and we assume the pxeserver + * cannot be removed once it was added. so we use host id of first found pxe + * server as reference in template_host_ref. + * This maybe a FIXME in future. + */ + VMTemplateHostVO vmTemplateHost = null; + if (zoneId == null || zoneId == -1) { + List dcs = _dcDao.listAllIncludingRemoved(); + for (DataCenterVO dc : dcs) { + HostVO pxe = _resourceMgr.listAllHostsInOneZoneByType(Host.Type.BaremetalPxe, dc.getId()).get(0); + + vmTemplateHost = _tmpltHostDao.findByHostTemplate(dc.getId(), template.getId()); + if (vmTemplateHost == null) { + vmTemplateHost = new VMTemplateHostVO(pxe.getId(), template.getId(), new Date(), 100, + Status.DOWNLOADED, null, null, null, null, template.getUrl()); + _tmpltHostDao.persist(vmTemplateHost); + templateCreateUsage(template, pxe); + } + } + } else { + HostVO pxe = _resourceMgr.listAllHostsInOneZoneByType(Host.Type.BaremetalPxe, zoneId).get(0); + vmTemplateHost = new VMTemplateHostVO(pxe.getId(), template.getId(), new Date(), 100, + Status.DOWNLOADED, null, null, null, null, template.getUrl()); + _tmpltHostDao.persist(vmTemplateHost); + templateCreateUsage(template, pxe); + } + + _resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template); + return template; + } + + public TemplateProfile prepareDelete(DeleteIsoCmd cmd) { + throw new CloudRuntimeException("Baremetal doesn't support ISO, how the delete get here???"); + } + + @Override @DB + public boolean delete(TemplateProfile profile) { + VMTemplateVO template = profile.getTemplate(); + Long templateId = template.getId(); + boolean success = true; + String zoneName; + boolean isAllZone; + + if (!template.isCrossZones() && profile.getZoneId() != null) { + isAllZone = false; + zoneName = profile.getZoneId().toString(); + } else { + zoneName = "all zones"; + isAllZone = true; + } + + s_logger.debug("Attempting to mark template host refs for template: " + template.getName() + " as destroyed in zone: " + zoneName); + Account account = _accountDao.findByIdIncludingRemoved(template.getAccountId()); + String eventType = EventTypes.EVENT_TEMPLATE_DELETE; + List templateHostVOs = _tmpltHostDao.listByTemplateId(templateId); + + for (VMTemplateHostVO vo : templateHostVOs) { + VMTemplateHostVO lock = null; + try { + HostVO pxeServer = _hostDao.findById(vo.getHostId()); + if (!isAllZone && pxeServer.getDataCenterId() != profile.getZoneId()) { + continue; + } + + lock = _tmpltHostDao.acquireInLockTable(vo.getId()); + if (lock == null) { + s_logger.debug("Failed to acquire lock when deleting templateHostVO with ID: " + vo.getId()); + success = false; + break; + } + + vo.setDestroyed(true); + _tmpltHostDao.update(vo.getId(), vo); + VMTemplateZoneVO templateZone = _tmpltZoneDao.findByZoneTemplate(pxeServer.getDataCenterId(), templateId); + if (templateZone != null) { + _tmpltZoneDao.remove(templateZone.getId()); + } + + UsageEventVO usageEvent = new UsageEventVO(eventType, account.getId(), pxeServer.getDataCenterId(), templateId, null); + _usageEventDao.persist(usageEvent); + } finally { + if (lock != null) { + _tmpltHostDao.releaseFromLockTable(lock.getId()); + } + } + } + + s_logger.debug("Successfully marked template host refs for template: " + template.getName() + " as destroyed in zone: " + zoneName); + + // If there are no more non-destroyed template host entries for this template, delete it + if (success && (_tmpltHostDao.listByTemplateId(templateId).size() == 0)) { + long accountId = template.getAccountId(); + + VMTemplateVO lock = _tmpltDao.acquireInLockTable(templateId); + + try { + if (lock == null) { + s_logger.debug("Failed to acquire lock when deleting template with ID: " + templateId); + success = false; + } else if (_tmpltDao.remove(templateId)) { + // Decrement the number of templates + _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.template); + } + + } finally { + if (lock != null) { + _tmpltDao.releaseFromLockTable(lock.getId()); + } + } + s_logger.debug("Removed template: " + template.getName() + " because all of its template host refs were marked as destroyed."); + } + + return success; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalManager.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalManager.java new file mode 100755 index 00000000000..1599050453a --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalManager.java @@ -0,0 +1,28 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.manager; + +import com.cloud.network.Network.Provider; +import com.cloud.utils.component.Manager; + +public interface BaremetalManager extends Manager { + public static final String EchoSecurityGroupAgent = "EchoSecurityGroupAgent"; + public static final String ExternalBaremetalSystemUrl = "ExternalBaremetalSystemUrl"; + public static final String DO_PXE = "doPxe"; +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalManagerImpl.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalManagerImpl.java new file mode 100755 index 00000000000..53888645ade --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalManagerImpl.java @@ -0,0 +1,112 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.manager; + +import java.util.Map; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.api.StopAnswer; +import com.cloud.agent.manager.Commands; +import com.cloud.deploy.DeployDestination; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.utils.component.Inject; +import com.cloud.utils.fsm.StateListener; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineGuru; +import com.cloud.vm.VirtualMachineManager; +import com.cloud.vm.VirtualMachineName; +import com.cloud.vm.VirtualMachine.Event; +import com.cloud.vm.VirtualMachine.State; +import com.cloud.vm.dao.UserVmDao; +import com.cloud.vm.VirtualMachineProfile; + +@Local(value = {BaremetalManager.class}) +public class BaremetalManagerImpl implements BaremetalManager, StateListener { + private static final Logger s_logger = Logger.getLogger(BaremetalManagerImpl.class); + + @Inject + protected HostDao _hostDao; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + VirtualMachine.State.getStateMachine().registerListener(this); + return true; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public String getName() { + return "Baremetal Manager"; + } + + @Override + public boolean preStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, boolean status, Object opaque) { + return false; + } + + @Override + public boolean postStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, boolean status, Object opaque) { + if (newState != State.Starting && newState != State.Error && newState != State.Expunging) { + return true; + } + + if (vo.getHypervisorType() != HypervisorType.BareMetal) { + return true; + } + + HostVO host = _hostDao.findById(vo.getHostId()); + if (host == null) { + s_logger.debug("Skip oldState " + oldState + " to " + "newState " + newState + " transimtion"); + return true; + } + _hostDao.loadDetails(host); + + if (newState == State.Starting) { + host.setDetail("vmName", vo.getInstanceName()); + s_logger.debug("Add vmName " + host.getDetail("vmName") + " to host " + host.getId() + " details"); + } else { + if (host.getDetail("vmName") != null && host.getDetail("vmName").equalsIgnoreCase(vo.getInstanceName())) { + s_logger.debug("Remove vmName " + host.getDetail("vmName") + " from host " + host.getId() + " details"); + host.getDetails().remove("vmName"); + } + } + _hostDao.saveDetails(host); + + + return true; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/AddBaremetalDhcpCmd.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/AddBaremetalDhcpCmd.java new file mode 100755 index 00000000000..6a26fe27032 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/AddBaremetalDhcpCmd.java @@ -0,0 +1,149 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.BaseCmd.CommandType; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.PlugService; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.log4j.Logger; + +import com.cloud.baremetal.database.BaremetalDhcpVO; +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.UserContext; + +public class AddBaremetalDhcpCmd extends BaseAsyncCmd { + private static final String s_name = "addexternaldhcpresponse"; + public static final Logger s_logger = Logger.getLogger(AddBaremetalDhcpCmd.class); + + @PlugService BaremetalDhcpManager mgr; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name=ApiConstants.PHYSICAL_NETWORK_ID, type=CommandType.LONG, required=true, description="the Physical Network ID") + private Long physicalNetworkId; + + @Parameter(name=ApiConstants.POD_ID, type=CommandType.LONG, required = true, description="Pod Id") + private Long podId; + + @Parameter(name=ApiConstants.DHCP_SERVER_TYPE, type=CommandType.STRING, required = true, description="Type of dhcp device") + private String dhcpType; + + @Parameter(name=ApiConstants.URL, type=CommandType.STRING, required = true, description="URL of the external dhcp appliance.") + private String url; + + @Parameter(name=ApiConstants.USERNAME, type=CommandType.STRING, required = true, description="Credentials to reach external dhcp device") + private String username; + + @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, required = true, description="Credentials to reach external dhcp device") + private String password; + + @Override + public String getEventType() { + return EventTypes.EVENT_BAREMETAL_DHCP_SERVER_ADD; + } + + @Override + public String getEventDescription() { + return "Adding an external DHCP server"; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, + ResourceAllocationException, NetworkRuleConflictException { + try { + BaremetalDhcpVO vo = mgr.addDchpServer(this); + BaremetalDhcpResponse response = mgr.generateApiResponse(vo); + response.setObjectName(s_name); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } catch (Exception e) { + s_logger.warn("Unable to add external dhcp server with url: " + getUrl(), e); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return UserContext.current().getCaller().getId(); + } + + public Long getPodId() { + return podId; + } + + public void setPodId(Long podId) { + this.podId = podId; + } + + public String getDhcpType() { + return dhcpType; + } + + public void setDhcpType(String dhcpType) { + this.dhcpType = dhcpType; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public Long getPhysicalNetworkId() { + return physicalNetworkId; + } + + public void setPhysicalNetworkId(Long physicalNetworkId) { + this.physicalNetworkId = physicalNetworkId; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/AddBaremetalKickStartPxeCmd.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/AddBaremetalKickStartPxeCmd.java new file mode 100755 index 00000000000..4c3d0b22576 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/AddBaremetalKickStartPxeCmd.java @@ -0,0 +1,36 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd.CommandType; +import org.apache.cloudstack.api.Parameter; + +public class AddBaremetalKickStartPxeCmd extends AddBaremetalPxeCmd { + @Parameter(name=ApiConstants.TFTP_DIR, type=CommandType.STRING, required = true, description="Tftp root directory of PXE server") + private String tftpDir; + + public String getTftpDir() { + return tftpDir; + } + + public void setTftpDir(String tftpDir) { + this.tftpDir = tftpDir; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/AddBaremetalPxeCmd.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/AddBaremetalPxeCmd.java new file mode 100755 index 00000000000..a1d72a328c1 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/AddBaremetalPxeCmd.java @@ -0,0 +1,144 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.BaseCmd.CommandType; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.PlugService; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.log4j.Logger; + +import com.cloud.baremetal.database.BaremetalPxeVO; +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.UserContext; + +public class AddBaremetalPxeCmd extends BaseAsyncCmd { + private static final String s_name = "addexternalpxeresponse"; + public static final Logger s_logger = Logger.getLogger(AddBaremetalPxeCmd.class); + + @PlugService BaremetalPxeManager pxeMgr; + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name=ApiConstants.PHYSICAL_NETWORK_ID, type=CommandType.LONG, required=true, description="the Physical Network ID") + private Long physicalNetworkId; + + @Parameter(name=ApiConstants.POD_ID, type=CommandType.LONG, description="Pod Id") + private Long podId; + + @Parameter(name=ApiConstants.URL, type=CommandType.STRING, required = true, description="URL of the external pxe device") + private String url; + + @Parameter(name=ApiConstants.PXE_SERVER_TYPE, type=CommandType.STRING, required = true, description="type of pxe device") + private String deviceType; + + @Parameter(name=ApiConstants.USERNAME, type=CommandType.STRING, required = true, description="Credentials to reach external pxe device") + private String username; + + @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, required = true, description="Credentials to reach external pxe device") + private String password; + + @Override + public String getEventType() { + return EventTypes.EVENT_BAREMETAL_PXE_SERVER_ADD; + } + + @Override + public String getEventDescription() { + return "Adding an external pxe server"; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, + ResourceAllocationException, NetworkRuleConflictException { + try { + BaremetalPxeVO vo = pxeMgr.addPxeServer(this); + } catch (Exception e) { + s_logger.warn("Unable to add external pxe server with url: " + getUrl(), e); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return UserContext.current().getCaller().getId(); + } + + public Long getPhysicalNetworkId() { + return physicalNetworkId; + } + + public void setPhysicalNetworkId(Long physicalNetworkId) { + this.physicalNetworkId = physicalNetworkId; + } + + public Long getPodId() { + return podId; + } + + public void setPodId(Long podId) { + this.podId = podId; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getDeviceType() { + return deviceType; + } + + public void setDeviceType(String deviceType) { + this.deviceType = deviceType; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/AddBaremetalPxePingServerCmd.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/AddBaremetalPxePingServerCmd.java new file mode 100755 index 00000000000..70796f3499f --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/AddBaremetalPxePingServerCmd.java @@ -0,0 +1,80 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.Parameter; + +public class AddBaremetalPxePingServerCmd extends AddBaremetalPxeCmd { + + @Parameter(name=ApiConstants.PING_STORAGE_SERVER_IP, type=CommandType.STRING, required = true, description="PING storage server ip") + private String pingStorageServerIp; + + @Parameter(name=ApiConstants.PING_DIR, type=CommandType.STRING, required = true, description="Root directory on PING storage server") + private String pingDir; + + @Parameter(name=ApiConstants.TFTP_DIR, type=CommandType.STRING, required = true, description="Tftp root directory of PXE server") + private String tftpDir; + + @Parameter(name=ApiConstants.PING_CIFS_USERNAME, type=CommandType.STRING, description="Username of PING storage server") + private String pingStorageServerUserName; + + @Parameter(name=ApiConstants.PING_CIFS_PASSWORD, type=CommandType.STRING, description="Password of PING storage server") + private String pingStorageServerPassword; + + public String getPingStorageServerIp() { + return pingStorageServerIp; + } + + public void setPingStorageServerIp(String pingStorageServerIp) { + this.pingStorageServerIp = pingStorageServerIp; + } + + public String getPingDir() { + return pingDir; + } + + public void setPingDir(String pingDir) { + this.pingDir = pingDir; + } + + public String getTftpDir() { + return tftpDir; + } + + public void setTftpDir(String tftpDir) { + this.tftpDir = tftpDir; + } + + public String getPingStorageServerUserName() { + return pingStorageServerUserName; + } + + public void setPingStorageServerUserName(String pingStorageServerUserName) { + this.pingStorageServerUserName = pingStorageServerUserName; + } + + public String getPingStorageServerPassword() { + return pingStorageServerPassword; + } + + public void setPingStorageServerPassword(String pingStorageServerPassword) { + this.pingStorageServerPassword = pingStorageServerPassword; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalPingServiceImpl.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalPingServiceImpl.java new file mode 100755 index 00000000000..1f3defb9d30 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalPingServiceImpl.java @@ -0,0 +1,300 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.networkservice; + +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.baremetal.IpmISetBootDevCommand; +import com.cloud.agent.api.baremetal.IpmISetBootDevCommand.BootDev; +import com.cloud.agent.api.baremetal.PreparePxeServerAnswer; +import com.cloud.agent.api.baremetal.PreparePxeServerCommand; +import com.cloud.agent.api.baremetal.prepareCreateTemplateCommand; +import com.cloud.baremetal.database.BaremetalPxeDao; +import com.cloud.baremetal.database.BaremetalPxeVO; +import com.cloud.baremetal.networkservice.BaremetalPxeManager.BaremetalPxeType; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.HostPodVO; +import com.cloud.deploy.DeployDestination; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDetailsDao; +import com.cloud.network.PhysicalNetworkServiceProvider; +import com.cloud.network.PhysicalNetworkVO; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.dao.PhysicalNetworkServiceProviderDao; +import com.cloud.network.dao.PhysicalNetworkServiceProviderVO; +import com.cloud.resource.ResourceManager; +import com.cloud.resource.ServerResource; +import com.cloud.uservm.UserVm; +import com.cloud.utils.component.Inject; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.SearchCriteria.Op; +import com.cloud.utils.db.SearchCriteria2; +import com.cloud.utils.db.SearchCriteriaService; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.NicProfile; +import com.cloud.vm.NicVO; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachineProfile; + +@Local(value=BaremetalPxeService.class) +public class BareMetalPingServiceImpl extends BareMetalPxeServiceBase implements BaremetalPxeService { + private static final Logger s_logger = Logger.getLogger(BareMetalPingServiceImpl.class); + @Inject ResourceManager _resourceMgr; + @Inject PhysicalNetworkDao _physicalNetworkDao; + @Inject PhysicalNetworkServiceProviderDao _physicalNetworkServiceProviderDao; + @Inject HostDetailsDao _hostDetailsDao; + @Inject BaremetalPxeDao _pxeDao; + + + @Override + public boolean prepare(VirtualMachineProfile profile, NicProfile pxeNic, DeployDestination dest, ReservationContext context) { + SearchCriteriaService sc = SearchCriteria2.create(BaremetalPxeVO.class); + sc.addAnd(sc.getEntity().getDeviceType(), Op.EQ, BaremetalPxeType.PING.toString()); + sc.addAnd(sc.getEntity().getPodId(), Op.EQ, dest.getPod().getId()); + BaremetalPxeVO pxeVo = sc.find(); + if (pxeVo == null) { + throw new CloudRuntimeException("No PING PXE server found in pod: " + dest.getPod().getId() + ", you need to add it before starting VM"); + } + long pxeServerId = pxeVo.getHostId(); + + String mac = pxeNic.getMacAddress(); + String ip = pxeNic.getIp4Address(); + String gateway = pxeNic.getGateway(); + String mask = pxeNic.getNetmask(); + String dns = pxeNic.getDns1(); + if (dns == null) { + dns = pxeNic.getDns2(); + } + + try { + String tpl = profile.getTemplate().getUrl(); + assert tpl != null : "How can a null template get here!!!"; + PreparePxeServerCommand cmd = new PreparePxeServerCommand(ip, mac, mask, gateway, dns, tpl, + profile.getVirtualMachine().getInstanceName(), dest.getHost().getName()); + PreparePxeServerAnswer ans = (PreparePxeServerAnswer) _agentMgr.send(pxeServerId, cmd); + if (!ans.getResult()) { + s_logger.warn("Unable tot program PXE server: " + pxeVo.getId() + " because " + ans.getDetails()); + return false; + } + + IpmISetBootDevCommand bootCmd = new IpmISetBootDevCommand(BootDev.pxe); + Answer anw = _agentMgr.send(dest.getHost().getId(), bootCmd); + if (!anw.getResult()) { + s_logger.warn("Unable to set host: " + dest.getHost().getId() + " to PXE boot because " + anw.getDetails()); + } + + return anw.getResult(); + } catch (Exception e) { + s_logger.warn("Cannot prepare PXE server", e); + return false; + } + } + + + @Override + public boolean prepareCreateTemplate(Long pxeServerId, UserVm vm, String templateUrl) { + List nics = _nicDao.listByVmId(vm.getId()); + if (nics.size() != 1) { + throw new CloudRuntimeException("Wrong nic number " + nics.size() + " of vm " + vm.getId()); + } + + /* use last host id when VM stopped */ + Long hostId = (vm.getHostId() == null ? vm.getLastHostId() : vm.getHostId()); + HostVO host = _hostDao.findById(hostId); + DataCenterVO dc = _dcDao.findById(host.getDataCenterId()); + NicVO nic = nics.get(0); + String mask = nic.getNetmask(); + String mac = nic.getMacAddress(); + String ip = nic.getIp4Address(); + String gateway = nic.getGateway(); + String dns = dc.getDns1(); + if (dns == null) { + dns = dc.getDns2(); + } + + try { + prepareCreateTemplateCommand cmd = new prepareCreateTemplateCommand(ip, mac, mask, gateway, dns, templateUrl); + Answer ans = _agentMgr.send(pxeServerId, cmd); + return ans.getResult(); + } catch (Exception e) { + s_logger.debug("Prepare for creating baremetal template failed", e); + return false; + } + } + + + @Override + @DB + public BaremetalPxeVO addPxeServer(AddBaremetalPxeCmd cmd) { + AddBaremetalPxePingServerCmd pcmd = (AddBaremetalPxePingServerCmd)cmd; + + PhysicalNetworkVO pNetwork = null; + long zoneId; + + if (cmd.getPhysicalNetworkId() == null || cmd.getUrl() == null || cmd.getUsername() == null || cmd.getPassword() == null) { + throw new IllegalArgumentException("At least one of the required parameters(physical network id, url, username, password) is null"); + } + + pNetwork = _physicalNetworkDao.findById(cmd.getPhysicalNetworkId()); + if (pNetwork == null) { + throw new IllegalArgumentException("Could not find phyical network with ID: " + cmd.getPhysicalNetworkId()); + } + zoneId = pNetwork.getDataCenterId(); + + PhysicalNetworkServiceProviderVO ntwkSvcProvider = _physicalNetworkServiceProviderDao.findByServiceProvider(pNetwork.getId(), BaremetalPxeManager.BAREMETAL_PXE_SERVICE_PROVIDER.getName()); + if (ntwkSvcProvider == null) { + throw new CloudRuntimeException("Network Service Provider: " + BaremetalPxeManager.BAREMETAL_PXE_SERVICE_PROVIDER.getName() + + " is not enabled in the physical network: " + cmd.getPhysicalNetworkId() + "to add this device"); + } else if (ntwkSvcProvider.getState() == PhysicalNetworkServiceProvider.State.Shutdown) { + throw new CloudRuntimeException("Network Service Provider: " + ntwkSvcProvider.getProviderName() + + " is in shutdown state in the physical network: " + cmd.getPhysicalNetworkId() + "to add this device"); + } + + HostPodVO pod = _podDao.findById(cmd.getPodId()); + if (pod == null) { + throw new IllegalArgumentException("Could not find pod with ID: " + cmd.getPodId()); + } + + List pxes = _resourceMgr.listAllUpAndEnabledHosts(Host.Type.BaremetalPxe, null, cmd.getPodId(), zoneId); + if (pxes.size() != 0) { + throw new IllegalArgumentException("Already had a PXE server in Pod: " + cmd.getPodId() + " zone: " + zoneId); + } + + String storageServerIp = pcmd.getPingStorageServerIp(); + if (storageServerIp == null) { + throw new IllegalArgumentException("No IP for storage server specified"); + } + String pingDir = pcmd.getPingDir(); + if (pingDir == null) { + throw new IllegalArgumentException("No direcotry for storage server specified"); + } + String tftpDir = pcmd.getTftpDir(); + if (tftpDir == null) { + throw new IllegalArgumentException("No TFTP directory specified"); + } + + String cifsUsername = pcmd.getPingStorageServerUserName(); + if (cifsUsername == null || cifsUsername.equalsIgnoreCase("")) { + cifsUsername = "xxx"; + } + String cifsPassword = pcmd.getPingStorageServerPassword(); + if (cifsPassword == null || cifsPassword.equalsIgnoreCase("")) { + cifsPassword = "xxx"; + } + + + URI uri; + try { + uri = new URI(cmd.getUrl()); + } catch (Exception e) { + s_logger.debug(e); + throw new IllegalArgumentException(e.getMessage()); + } + String ipAddress = uri.getHost(); + + String guid = getPxeServerGuid(Long.toString(zoneId) + "-" + pod.getId(), BaremetalPxeType.PING.toString(), ipAddress); + + ServerResource resource = null; + Map params = new HashMap(); + params.put(BaremetalPxeService.PXE_PARAM_ZONE, Long.toString(zoneId)); + params.put(BaremetalPxeService.PXE_PARAM_POD, String.valueOf(pod.getId())); + params.put(BaremetalPxeService.PXE_PARAM_IP, ipAddress); + params.put(BaremetalPxeService.PXE_PARAM_USERNAME, cmd.getUsername()); + params.put(BaremetalPxeService.PXE_PARAM_PASSWORD, cmd.getPassword()); + params.put(BaremetalPxeService.PXE_PARAM_PING_STORAGE_SERVER_IP, storageServerIp); + params.put(BaremetalPxeService.PXE_PARAM_PING_ROOT_DIR, pingDir); + params.put(BaremetalPxeService.PXE_PARAM_TFTP_DIR, tftpDir); + params.put(BaremetalPxeService.PXE_PARAM_PING_STORAGE_SERVER_USERNAME, cifsUsername); + params.put(BaremetalPxeService.PXE_PARAM_PING_STORAGE_SERVER_PASSWORD, cifsPassword); + params.put(BaremetalPxeService.PXE_PARAM_GUID, guid); + + resource = new BaremetalPingPxeResource(); + try { + resource.configure("PING PXE resource", params); + } catch (Exception e) { + s_logger.debug(e); + throw new CloudRuntimeException(e.getMessage()); + } + + Host pxeServer = _resourceMgr.addHost(zoneId, resource, Host.Type.BaremetalPxe, params); + if (pxeServer == null) { + throw new CloudRuntimeException("Cannot add PXE server as a host"); + } + + BaremetalPxeVO vo = new BaremetalPxeVO(); + Transaction txn = Transaction.currentTxn(); + vo.setHostId(pxeServer.getId()); + vo.setNetworkServiceProviderId(ntwkSvcProvider.getId()); + vo.setPodId(pod.getId()); + vo.setPhysicalNetworkId(pcmd.getPhysicalNetworkId()); + vo.setDeviceType(BaremetalPxeType.PING.toString()); + txn.start(); + _pxeDao.persist(vo); + txn.commit(); + return vo; + } + + @Override + public BaremetalPxeResponse getApiResponse(BaremetalPxeVO vo) { + BaremetalPxePingResponse response = new BaremetalPxePingResponse(); + response.setId(String.valueOf(vo.getId())); + response.setPhysicalNetworkId(String.valueOf(vo.getPhysicalNetworkId())); + response.setPodId(String.valueOf(vo.getPodId())); + Map details = _hostDetailsDao.findDetails(vo.getHostId()); + response.setPingStorageServerIp(details.get(BaremetalPxeService.PXE_PARAM_PING_STORAGE_SERVER_IP)); + response.setPingDir(details.get(BaremetalPxeService.PXE_PARAM_PING_ROOT_DIR)); + response.setTftpDir(details.get(BaremetalPxeService.PXE_PARAM_TFTP_DIR)); + return response; + } + + + @Override + public List listPxeServers(ListBaremetalPxePingServersCmd cmd) { + SearchCriteriaService sc = SearchCriteria2.create(BaremetalPxeVO.class); + sc.addAnd(sc.getEntity().getDeviceType(), Op.EQ, BaremetalPxeType.PING.toString()); + if (cmd.getPodId() != null) { + sc.addAnd(sc.getEntity().getPodId(), Op.EQ, cmd.getPodId()); + if (cmd.getId() != null) { + sc.addAnd(sc.getEntity().getId(), Op.EQ, cmd.getId()); + } + } + List vos = sc.list(); + List responses = new ArrayList(vos.size()); + for (BaremetalPxeVO vo : vos) { + responses.add(getApiResponse(vo)); + } + return responses; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalPxeServiceBase.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalPxeServiceBase.java new file mode 100644 index 00000000000..85a9b3cff98 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalPxeServiceBase.java @@ -0,0 +1,68 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.networkservice; + +import java.util.Map; + +import javax.naming.ConfigurationException; + +import com.cloud.agent.AgentManager; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.HostPodDao; +import com.cloud.host.dao.HostDao; +import com.cloud.utils.component.Inject; +import com.cloud.vm.dao.NicDao; + +public abstract class BareMetalPxeServiceBase implements BaremetalPxeService { + protected String _name; + @Inject DataCenterDao _dcDao; + @Inject HostDao _hostDao; + @Inject AgentManager _agentMgr; + @Inject HostPodDao _podDao; + @Inject NicDao _nicDao; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _name = name; + return true; + } + + @Override + public String getName() { + return _name; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + protected String getPxeServerGuid(String zoneId, String name, String ip) { + return zoneId + "-" + name + "-" + ip; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalResourceBase.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalResourceBase.java new file mode 100755 index 00000000000..d615d1d4ac7 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalResourceBase.java @@ -0,0 +1,618 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.networkservice; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.log4j.Logger; + +import com.cloud.agent.IAgentControl; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.CheckNetworkAnswer; +import com.cloud.agent.api.CheckNetworkCommand; +import com.cloud.agent.api.CheckVirtualMachineAnswer; +import com.cloud.agent.api.CheckVirtualMachineCommand; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.MaintainAnswer; +import com.cloud.agent.api.MaintainCommand; +import com.cloud.agent.api.MigrateAnswer; +import com.cloud.agent.api.MigrateCommand; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.PingRoutingCommand; +import com.cloud.agent.api.PrepareForMigrationAnswer; +import com.cloud.agent.api.PrepareForMigrationCommand; +import com.cloud.agent.api.ReadyAnswer; +import com.cloud.agent.api.ReadyCommand; +import com.cloud.agent.api.RebootAnswer; +import com.cloud.agent.api.RebootCommand; +import com.cloud.agent.api.SecurityGroupRulesCmd; +import com.cloud.agent.api.StartAnswer; +import com.cloud.agent.api.StartCommand; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupRoutingCommand; +import com.cloud.agent.api.StopAnswer; +import com.cloud.agent.api.StopCommand; +import com.cloud.agent.api.baremetal.IpmISetBootDevCommand; +import com.cloud.agent.api.baremetal.IpmISetBootDevCommand.BootDev; +import com.cloud.agent.api.baremetal.IpmiBootorResetCommand; +import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.baremetal.manager.BaremetalManager; +import com.cloud.host.Host.Type; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.resource.ServerResource; +import com.cloud.server.ManagementServer; +import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.OutputInterpreter; +import com.cloud.utils.script.Script; +import com.cloud.utils.script.Script2; +import com.cloud.utils.script.Script2.ParamType; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachine.State; +import com.cloud.vm.dao.VMInstanceDao; + +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; + +@Local(value = ServerResource.class) +public class BareMetalResourceBase implements ServerResource { + private static final Logger s_logger = Logger.getLogger(BareMetalResourceBase.class); + protected HashMap _vms = new HashMap(2); + protected String _name; + protected String _uuid; + protected String _zone; + protected String _pod; + protected Long hostId; + protected String _cluster; + protected long _memCapacity; + protected long _cpuCapacity; + protected long _cpuNum; + protected String _mac; + protected String _username; + protected String _password; + protected String _ip; + protected boolean _isEchoScAgent; + protected IAgentControl _agentControl; + protected Script2 _pingCommand; + protected Script2 _setPxeBootCommand; + protected Script2 _setDiskBootCommand; + protected Script2 _rebootCommand; + protected Script2 _getStatusCommand; + protected Script2 _powerOnCommand; + protected Script2 _powerOffCommand; + protected Script2 _forcePowerOffCommand; + protected Script2 _bootOrRebootCommand; + protected String _vmName; + protected VMInstanceDao vmDao; + + private void changeVmState(String vmName, VirtualMachine.State state) { + synchronized (_vms) { + _vms.put(vmName, state); + } + } + + private State removeVmState(String vmName) { + synchronized (_vms) { + return _vms.remove(vmName); + } + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _name = name; + _uuid = (String) params.get("guid"); + try { + _memCapacity = Long.parseLong((String) params.get(ApiConstants.MEMORY)) * 1024L * 1024L; + _cpuCapacity = Long.parseLong((String) params.get(ApiConstants.CPU_SPEED)); + _cpuNum = Long.parseLong((String) params.get(ApiConstants.CPU_NUMBER)); + } catch (NumberFormatException e) { + throw new ConfigurationException(String.format("Unable to parse number of CPU or memory capacity " + + "or cpu capacity(cpu number = %1$s memCapacity=%2$s, cpuCapacity=%3$s", (String) params.get(ApiConstants.CPU_NUMBER), + (String) params.get(ApiConstants.MEMORY), (String) params.get(ApiConstants.CPU_SPEED))); + } + + _zone = (String) params.get("zone"); + _pod = (String) params.get("pod"); + _cluster = (String) params.get("cluster"); + hostId = (Long) params.get("hostId"); + _ip = (String) params.get(ApiConstants.PRIVATE_IP); + _mac = (String) params.get(ApiConstants.HOST_MAC); + _username = (String) params.get(ApiConstants.USERNAME); + _password = (String) params.get(ApiConstants.PASSWORD); + _vmName = (String) params.get("vmName"); + String echoScAgent = (String) params.get(BaremetalManager.EchoSecurityGroupAgent); + + if (_pod == null) { + throw new ConfigurationException("Unable to get the pod"); + } + + if (_cluster == null) { + throw new ConfigurationException("Unable to get the pod"); + } + + if (_ip == null) { + throw new ConfigurationException("Unable to get the host address"); + } + + if (_mac.equalsIgnoreCase("unknown")) { + throw new ConfigurationException("Unable to get the host mac address"); + } + + if (_mac.split(":").length != 6) { + throw new ConfigurationException("Wrong MAC format(" + _mac + + "). It must be in format of for example 00:11:ba:33:aa:dd which is not case sensitive"); + } + + if (_uuid == null) { + throw new ConfigurationException("Unable to get the uuid"); + } + + if (echoScAgent != null) { + _isEchoScAgent = Boolean.valueOf(echoScAgent); + } + + String injectScript = "scripts/util/ipmi.py"; + String scriptPath = Script.findScript("", injectScript); + if (scriptPath == null) { + throw new ConfigurationException("Cannot find ping script " + scriptPath); + } + _pingCommand = new Script2(scriptPath, s_logger); + _pingCommand.add("ping"); + _pingCommand.add("hostname=" + _ip); + _pingCommand.add("usrname=" + _username); + _pingCommand.add("password=" + _password, ParamType.PASSWORD); + + _setPxeBootCommand = new Script2(scriptPath, s_logger); + _setPxeBootCommand.add("boot_dev"); + _setPxeBootCommand.add("hostname=" + _ip); + _setPxeBootCommand.add("usrname=" + _username); + _setPxeBootCommand.add("password=" + _password, ParamType.PASSWORD); + _setPxeBootCommand.add("dev=pxe"); + + _setDiskBootCommand = new Script2(scriptPath, s_logger); + _setDiskBootCommand.add("boot_dev"); + _setDiskBootCommand.add("hostname=" + _ip); + _setDiskBootCommand.add("usrname=" + _username); + _setDiskBootCommand.add("password=" + _password, ParamType.PASSWORD); + _setDiskBootCommand.add("dev=disk"); + + _rebootCommand = new Script2(scriptPath, s_logger); + _rebootCommand.add("reboot"); + _rebootCommand.add("hostname=" + _ip); + _rebootCommand.add("usrname=" + _username); + _rebootCommand.add("password=" + _password, ParamType.PASSWORD); + + _getStatusCommand = new Script2(scriptPath, s_logger); + _getStatusCommand.add("ping"); + _getStatusCommand.add("hostname=" + _ip); + _getStatusCommand.add("usrname=" + _username); + _getStatusCommand.add("password=" + _password, ParamType.PASSWORD); + + _powerOnCommand = new Script2(scriptPath, s_logger); + _powerOnCommand.add("power"); + _powerOnCommand.add("hostname=" + _ip); + _powerOnCommand.add("usrname=" + _username); + _powerOnCommand.add("password=" + _password, ParamType.PASSWORD); + _powerOnCommand.add("action=on"); + + _powerOffCommand = new Script2(scriptPath, s_logger); + _powerOffCommand.add("power"); + _powerOffCommand.add("hostname=" + _ip); + _powerOffCommand.add("usrname=" + _username); + _powerOffCommand.add("password=" + _password, ParamType.PASSWORD); + _powerOffCommand.add("action=soft"); + + _forcePowerOffCommand = new Script2(scriptPath, s_logger); + _forcePowerOffCommand.add("power"); + _forcePowerOffCommand.add("hostname=" + _ip); + _forcePowerOffCommand.add("usrname=" + _username); + _forcePowerOffCommand.add("password=" + _password, ParamType.PASSWORD); + _forcePowerOffCommand.add("action=off"); + + _bootOrRebootCommand = new Script2(scriptPath, s_logger); + _bootOrRebootCommand.add("boot_or_reboot"); + _bootOrRebootCommand.add("hostname=" + _ip); + _bootOrRebootCommand.add("usrname=" + _username); + _bootOrRebootCommand.add("password=" + _password, ParamType.PASSWORD); + + return true; + } + + protected boolean doScript(Script cmd) { + return doScript(cmd, null); + } + + protected boolean doScript(Script cmd, OutputInterpreter interpreter) { + int retry = 5; + String res = null; + while (retry-- > 0) { + if (interpreter == null) { + res = cmd.execute(); + } else { + res = cmd.execute(interpreter); + } + if (res != null && res.startsWith("Error: Unable to establish LAN")) { + s_logger.warn("IPMI script timeout(" + cmd.toString() + "), will retry " + retry + " times"); + continue; + } else if (res == null) { + return true; + } else { + break; + } + } + + s_logger.warn("IPMI Scirpt failed due to " + res + "(" + cmd.toString() + ")"); + return false; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public String getName() { + return _name; + } + + @Override + public Type getType() { + return com.cloud.host.Host.Type.Routing; + } + + protected State getVmState() { + OutputInterpreter.AllLinesParser interpreter = new OutputInterpreter.AllLinesParser(); + if (!doScript(_getStatusCommand, interpreter)) { + s_logger.warn("Cannot get power status of " + _name + ", assume VM state was not changed"); + return null; + } + if (isPowerOn(interpreter.getLines())) { + return State.Running; + } else { + return State.Stopped; + } + } + + protected Map fullSync() { + Map states = new HashMap(); + if (hostId != null) { + ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name); + vmDao = locator.getDao(VMInstanceDao.class); + final List vms = vmDao.listByHostId(hostId); + for (VMInstanceVO vm : vms) { + states.put(vm.getInstanceName(), vm.getState()); + } + } + /* + * Map changes = new HashMap(); + * + * if (_vmName != null) { State state = getVmState(); if (state != null) + * { changes.put(_vmName, state); } } + */ + + return states; + } + + @Override + public StartupCommand[] initialize() { + StartupRoutingCommand cmd = new StartupRoutingCommand(0, 0, 0, 0, null, Hypervisor.HypervisorType.BareMetal, new HashMap(), null); + cmd.setDataCenter(_zone); + cmd.setPod(_pod); + cmd.setCluster(_cluster); + cmd.setGuid(_uuid); + cmd.setName(_ip); + cmd.setPrivateIpAddress(_ip); + cmd.setStorageIpAddress(_ip); + cmd.setVersion(BareMetalResourceBase.class.getPackage().getImplementationVersion()); + cmd.setCpus((int) _cpuNum); + cmd.setSpeed(_cpuCapacity); + cmd.setMemory(_memCapacity); + cmd.setPrivateMacAddress(_mac); + cmd.setPublicMacAddress(_mac); + cmd.setStateChanges(fullSync()); + return new StartupCommand[] { cmd }; + } + + private boolean ipmiPing() { + return doScript(_pingCommand); + } + + @Override + public PingCommand getCurrentStatus(long id) { + try { + if (!ipmiPing()) { + Thread.sleep(1000); + if (!ipmiPing()) { + s_logger.warn("Cannot ping ipmi nic " + _ip); + return null; + } + } + } catch (Exception e) { + s_logger.debug("Cannot ping ipmi nic " + _ip, e); + return null; + } + + return new PingRoutingCommand(getType(), id, deltaSync()); + } + + protected Answer execute(IpmISetBootDevCommand cmd) { + Script bootCmd = null; + if (cmd.getBootDev() == BootDev.disk) { + bootCmd = _setDiskBootCommand; + } else if (cmd.getBootDev() == BootDev.pxe) { + bootCmd = _setPxeBootCommand; + } else { + throw new CloudRuntimeException("Unkonwn boot dev " + cmd.getBootDev()); + } + + String bootDev = cmd.getBootDev().name(); + if (!doScript(bootCmd)) { + s_logger.warn("Set " + _ip + " boot dev to " + bootDev + "failed"); + return new Answer(cmd, false, "Set " + _ip + " boot dev to " + bootDev + "failed"); + } + + s_logger.warn("Set " + _ip + " boot dev to " + bootDev + "Success"); + return new Answer(cmd, true, "Set " + _ip + " boot dev to " + bootDev + "Success"); + } + + protected MaintainAnswer execute(MaintainCommand cmd) { + return new MaintainAnswer(cmd, false); + } + + protected PrepareForMigrationAnswer execute(PrepareForMigrationCommand cmd) { + return new PrepareForMigrationAnswer(cmd); + } + + protected MigrateAnswer execute(MigrateCommand cmd) { + if (!doScript(_powerOffCommand)) { + return new MigrateAnswer(cmd, false, "IPMI power off failed", null); + } + return new MigrateAnswer(cmd, true, "success", null); + } + + protected CheckVirtualMachineAnswer execute(final CheckVirtualMachineCommand cmd) { + return new CheckVirtualMachineAnswer(cmd, State.Stopped, null); + } + + protected Answer execute(IpmiBootorResetCommand cmd) { + if (!doScript(_bootOrRebootCommand)) { + return new Answer(cmd, false, "IPMI boot or reboot failed"); + } + return new Answer(cmd, true, "Success"); + + } + + protected CheckNetworkAnswer execute(CheckNetworkCommand cmd) { + return new CheckNetworkAnswer(cmd, true, "Success"); + } + + protected Answer execute(SecurityGroupRulesCmd cmd) { + SecurityGroupHttpClient hc = new SecurityGroupHttpClient(); + return hc.call(cmd.getGuestIp(), cmd); + } + + @Override + public Answer executeRequest(Command cmd) { + try { + if (cmd instanceof ReadyCommand) { + return execute((ReadyCommand) cmd); + } else if (cmd instanceof StartCommand) { + return execute((StartCommand) cmd); + } else if (cmd instanceof StopCommand) { + return execute((StopCommand) cmd); + } else if (cmd instanceof RebootCommand) { + return execute((RebootCommand) cmd); + } else if (cmd instanceof IpmISetBootDevCommand) { + return execute((IpmISetBootDevCommand) cmd); + } else if (cmd instanceof MaintainCommand) { + return execute((MaintainCommand) cmd); + } else if (cmd instanceof PrepareForMigrationCommand) { + return execute((PrepareForMigrationCommand) cmd); + } else if (cmd instanceof MigrateCommand) { + return execute((MigrateCommand) cmd); + } else if (cmd instanceof CheckVirtualMachineCommand) { + return execute((CheckVirtualMachineCommand) cmd); + } else if (cmd instanceof IpmiBootorResetCommand) { + return execute((IpmiBootorResetCommand) cmd); + } else if (cmd instanceof SecurityGroupRulesCmd) { + return execute((SecurityGroupRulesCmd) cmd); + } else if (cmd instanceof CheckNetworkCommand) { + return execute((CheckNetworkCommand) cmd); + } else { + return Answer.createUnsupportedCommandAnswer(cmd); + } + } catch (Throwable t) { + s_logger.debug(t.getMessage(), t); + return new Answer(cmd, false, t.getMessage()); + } + } + + protected boolean isPowerOn(String str) { + if (str.startsWith("Chassis Power is on")) { + return true; + } else if (str.startsWith("Chassis Power is off")) { + return false; + } else { + throw new CloudRuntimeException("Cannot parse IPMI power status " + str); + } + } + + protected RebootAnswer execute(final RebootCommand cmd) { + if (!doScript(_rebootCommand)) { + return new RebootAnswer(cmd, "IPMI reboot failed", false); + } + + return new RebootAnswer(cmd, "reboot succeeded", true); + } + + protected StopAnswer execute(final StopCommand cmd) { + boolean success = false; + int count = 0; + Script powerOff = _powerOffCommand; + + while (count < 10) { + if (!doScript(powerOff)) { + break; + } + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + break; + } + + OutputInterpreter.AllLinesParser interpreter = new OutputInterpreter.AllLinesParser(); + if (!doScript(_getStatusCommand, interpreter)) { + s_logger.warn("Cannot get power status of " + _name + ", assume VM state was not changed"); + break; + } + + if (!isPowerOn(interpreter.getLines())) { + success = true; + break; + } else { + powerOff = _forcePowerOffCommand; + } + + count++; + } + + return success ? new StopAnswer(cmd, "Success", 0, true) : new StopAnswer(cmd, "IPMI power off failed", false); + } + + protected StartAnswer execute(StartCommand cmd) { + VirtualMachineTO vm = cmd.getVirtualMachine(); + State state = State.Stopped; + + try { + changeVmState(vm.getName(), State.Starting); + + OutputInterpreter.AllLinesParser interpreter = new OutputInterpreter.AllLinesParser(); + if (!doScript(_getStatusCommand, interpreter)) { + return new StartAnswer(cmd, "Cannot get current power status of " + _name); + } + + if (isPowerOn(interpreter.getLines())) { + if (!doScript(_rebootCommand)) { + return new StartAnswer(cmd, "IPMI reboot failed"); + } + } else { + if (!doScript(_powerOnCommand)) { + return new StartAnswer(cmd, "IPMI power on failed"); + } + } + + if (_isEchoScAgent) { + SecurityGroupHttpClient hc = new SecurityGroupHttpClient(); + boolean echoRet = hc.echo(vm.getNics()[0].getIp(), TimeUnit.MINUTES.toMillis(30), TimeUnit.MINUTES.toMillis(1)); + if (!echoRet) { + return new StartAnswer(cmd, String.format("Call security group agent on vm[%s] timeout", vm.getNics()[0].getIp())); + } + } + + s_logger.debug("Start bare metal vm " + vm.getName() + "successfully"); + state = State.Running; + _vmName = vm.getName(); + return new StartAnswer(cmd); + } finally { + if (state != State.Stopped) { + changeVmState(vm.getName(), state); + } else { + removeVmState(vm.getName()); + } + } + } + + protected HashMap deltaSync() { + final HashMap changes = new HashMap(); + /* + * Disable sync until we find a way that only tracks status but not does + * action + * + * The scenario is: Baremetal will reboot host when creating template. + * Given most servers take a long time to boot up, there would be a + * period that mgmt server finds the host is stopped through fullsync. + * Then mgmt server updates database with marking the host as stopped, + * after that, the host comes up and full sync then indicates it's + * running. Because in database the host is already stopped, mgmt server + * sends out a stop command. As a result, creating image gets never + * happened. + * + * if (_vmName == null) { return null; } + * + * State newState = getVmState(); if (newState == null) { + * s_logger.warn("Cannot get power state of VM " + _vmName); return + * null; } + * + * final State oldState = removeVmState(_vmName); if (oldState == null) + * { changeVmState(_vmName, newState); changes.put(_vmName, newState); } + * else if (oldState == State.Starting) { if (newState == State.Running) + * { changeVmState(_vmName, newState); } else if (newState == + * State.Stopped) { s_logger.debug("Ignoring vm " + _vmName + + * " because of a lag in starting the vm."); } } else if (oldState == + * State.Migrating) { + * s_logger.warn("How can baremetal VM get into migrating state???"); } + * else if (oldState == State.Stopping) { if (newState == State.Stopped) + * { changeVmState(_vmName, newState); } else if (newState == + * State.Running) { s_logger.debug("Ignoring vm " + _vmName + + * " because of a lag in stopping the vm. "); } } else if (oldState != + * newState) { changeVmState(_vmName, newState); changes.put(_vmName, + * newState); } + */ + return changes; + + } + + protected ReadyAnswer execute(ReadyCommand cmd) { + // derived resource should check if the PXE server is ready + s_logger.debug("Bare metal resource " + _name + " is ready"); + return new ReadyAnswer(cmd); + } + + @Override + public void disconnected() { + + } + + @Override + public IAgentControl getAgentControl() { + return _agentControl; + } + + @Override + public void setAgentControl(IAgentControl agentControl) { + _agentControl = agentControl; + } + +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetaNetworkGuru.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetaNetworkGuru.java new file mode 100755 index 00000000000..5414106e275 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetaNetworkGuru.java @@ -0,0 +1,173 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import java.net.URI; + +import javax.ejb.Local; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.log4j.Logger; + +import com.cloud.dc.DataCenter; +import com.cloud.dc.Pod; +import com.cloud.dc.PodVlanMapVO; +import com.cloud.dc.Vlan; +import com.cloud.dc.Vlan.VlanType; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.PodVlanMapDao; +import com.cloud.dc.dao.VlanDao; +import com.cloud.deploy.DeployDestination; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientAddressCapacityException; +import com.cloud.exception.InsufficientVirtualNetworkCapcityException; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.IPAddressVO; +import com.cloud.network.Network; +import com.cloud.network.NetworkManager; +import com.cloud.network.Networks.AddressFormat; +import com.cloud.network.Networks.BroadcastDomainType; +import com.cloud.network.addr.PublicIp; +import com.cloud.network.dao.IPAddressDao; +import com.cloud.network.guru.DirectPodBasedNetworkGuru; +import com.cloud.network.guru.NetworkGuru; +import com.cloud.offerings.dao.NetworkOfferingDao; +import com.cloud.utils.component.Inject; +import com.cloud.utils.db.Transaction; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; + +@Local(value = { NetworkGuru.class }) +public class BaremetaNetworkGuru extends DirectPodBasedNetworkGuru { + private static final Logger s_logger = Logger.getLogger(BaremetaNetworkGuru.class); + @Inject + private HostDao _hostDao; + @Inject + DataCenterDao _dcDao; + @Inject + VlanDao _vlanDao; + @Inject + NetworkManager _networkMgr; + @Inject + IPAddressDao _ipAddressDao; + @Inject + NetworkOfferingDao _networkOfferingDao; + @Inject + PodVlanMapDao _podVlanDao; + + @Override + public void reserve(NicProfile nic, Network network, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) + throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException, ConcurrentOperationException { + if (dest.getHost().getHypervisorType() != HypervisorType.BareMetal) { + super.reserve(nic, network, vm, dest, context); + return; + } + + HostVO host = _hostDao.findById(dest.getHost().getId()); + _hostDao.loadDetails(host); + String intentIp = host.getDetail(ApiConstants.IP_ADDRESS); + if (intentIp == null) { + super.reserve(nic, network, vm, dest, context); + return; + } + + String oldIp = nic.getIp4Address(); + boolean getNewIp = false; + if (oldIp == null) { + getNewIp = true; + } else { + // we need to get a new ip address if we try to deploy a vm in a + // different pod + IPAddressVO ipVO = _ipAddressDao.findByIpAndSourceNetworkId(network.getId(), oldIp); + if (ipVO != null) { + PodVlanMapVO mapVO = _podVlanDao.listPodVlanMapsByVlan(ipVO.getVlanId()); + if (mapVO.getPodId() != dest.getPod().getId()) { + Transaction txn = Transaction.currentTxn(); + txn.start(); + + // release the old ip here + _networkMgr.markIpAsUnavailable(ipVO.getId()); + _ipAddressDao.unassignIpAddress(ipVO.getId()); + + txn.commit(); + + nic.setIp4Address(null); + getNewIp = true; + } + } + } + + if (getNewIp) { + // we don't set reservationStrategy to Create because we need this + // method to be called again for the case when vm fails to deploy in + // Pod1, and we try to redeploy it in Pod2 + getBaremetalIp(nic, dest.getPod(), vm, network, intentIp); + } + + DataCenter dc = _dcDao.findById(network.getDataCenterId()); + nic.setDns1(dc.getDns1()); + nic.setDns2(dc.getDns2()); + + /* + * Pod pod = dest.getPod(); Pair ip = + * _dcDao.allocatePrivateIpAddress(dest.getDataCenter().getId(), + * dest.getPod().getId(), nic.getId(), context.getReservationId(), + * intentIp); if (ip == null) { throw new + * InsufficientAddressCapacityException + * ("Unable to get a management ip address", Pod.class, pod.getId()); } + * + * nic.setIp4Address(ip.first()); + * nic.setMacAddress(NetUtils.long2Mac(NetUtils + * .createSequenceBasedMacAddress(ip.second()))); + * nic.setGateway(pod.getGateway()); nic.setFormat(AddressFormat.Ip4); + * String netmask = NetUtils.getCidrNetmask(pod.getCidrSize()); + * nic.setNetmask(netmask); + * nic.setBroadcastType(BroadcastDomainType.Native); + * nic.setBroadcastUri(null); nic.setIsolationUri(null); + */ + + s_logger.debug("Allocated a nic " + nic + " for " + vm); + } + + private void getBaremetalIp(NicProfile nic, Pod pod, VirtualMachineProfile vm, Network network, String requiredIp) + throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException, ConcurrentOperationException { + DataCenter dc = _dcDao.findById(pod.getDataCenterId()); + if (nic.getIp4Address() == null) { + s_logger.debug(String.format("Requiring ip address: %s", nic.getIp4Address())); + PublicIp ip = _networkMgr.assignPublicIpAddress(dc.getId(), pod.getId(), vm.getOwner(), VlanType.DirectAttached, network.getId(), requiredIp, false); + nic.setIp4Address(ip.getAddress().toString()); + nic.setFormat(AddressFormat.Ip4); + nic.setGateway(ip.getGateway()); + nic.setNetmask(ip.getNetmask()); + if (ip.getVlanTag() != null && ip.getVlanTag().equalsIgnoreCase(Vlan.UNTAGGED)) { + nic.setIsolationUri(URI.create("ec2://" + Vlan.UNTAGGED)); + nic.setBroadcastUri(URI.create("vlan://" + Vlan.UNTAGGED)); + nic.setBroadcastType(BroadcastDomainType.Native); + } + nic.setReservationId(String.valueOf(ip.getVlanTag())); + nic.setMacAddress(ip.getMacAddress()); + } + nic.setDns1(dc.getDns1()); + nic.setDns2(dc.getDns2()); + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpElement.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpElement.java new file mode 100755 index 00000000000..968aa6df3d8 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpElement.java @@ -0,0 +1,178 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.baremetal.database.BaremetalDhcpVO; +import com.cloud.baremetal.database.BaremetalPxeVO; +import com.cloud.dc.Pod; +import com.cloud.dc.DataCenter.NetworkType; +import com.cloud.deploy.DeployDestination; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.Host; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.Network; +import com.cloud.network.PhysicalNetworkServiceProvider; +import com.cloud.network.Network.Capability; +import com.cloud.network.Network.GuestType; +import com.cloud.network.Network.Provider; +import com.cloud.network.Network.Service; +import com.cloud.network.Networks.TrafficType; +import com.cloud.network.element.DhcpServiceProvider; +import com.cloud.network.element.IpDeployer; +import com.cloud.network.element.NetworkElement; +import com.cloud.offering.NetworkOffering; +import com.cloud.utils.component.AdapterBase; +import com.cloud.utils.component.Inject; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.SearchCriteria.Op; +import com.cloud.utils.db.SearchCriteria2; +import com.cloud.utils.db.SearchCriteriaService; +import com.cloud.utils.db.Transaction; +import com.cloud.vm.NicProfile; +import com.cloud.vm.NicVO; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachine.Type; +import com.cloud.vm.dao.NicDao; +import com.cloud.vm.VirtualMachineProfile; + +@Local(value = NetworkElement.class) +public class BaremetalDhcpElement extends AdapterBase implements DhcpServiceProvider { + private static final Logger s_logger = Logger.getLogger(BaremetalDhcpElement.class); + private static final Map> capabilities; + + @Inject NicDao _nicDao; + @Inject BaremetalDhcpManager _dhcpMgr; + + static { + Capability cap = new Capability(BaremetalDhcpManager.BAREMETAL_DHCP_SERVICE_CAPABITLITY); + Map baremetalCaps = new HashMap(); + baremetalCaps.put(cap, null); + capabilities = new HashMap>(); + capabilities.put(Service.Dhcp, baremetalCaps); + } + + @Override + public Map> getCapabilities() { + return capabilities; + } + + @Override + public Provider getProvider() { + return BaremetalDhcpManager.BAREMETAL_DHCP_SERVICE_PROVIDER; + } + + private boolean canHandle(DeployDestination dest, TrafficType trafficType, GuestType networkType) { + Pod pod = dest.getPod(); + if (pod != null && dest.getDataCenter().getNetworkType() == NetworkType.Basic && trafficType == TrafficType.Guest) { + SearchCriteriaService sc = SearchCriteria2.create(BaremetalDhcpVO.class); + sc.addAnd(sc.getEntity().getPodId(), Op.EQ, pod.getId()); + return sc.find() != null; + } + + return false; + } + + @Override + public boolean implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) + throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + if (offering.isSystemOnly() || !canHandle(dest, offering.getTrafficType(), network.getGuestType())) { + s_logger.debug("BaremetalDhcpElement can not handle networkoffering: " + offering.getName()); + return false; + } + return true; + } + + @Override + @DB + public boolean prepare(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, + ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + Host host = dest.getHost(); + if (vm.getType() != Type.User || vm.getHypervisorType() != HypervisorType.BareMetal) { + return false; + } + + Transaction txn = Transaction.currentTxn(); + txn.start(); + nic.setMacAddress(host.getPrivateMacAddress()); + NicVO vo = _nicDao.findById(nic.getId()); + assert vo != null : "Where ths nic " + nic.getId() + " going???"; + vo.setMacAddress(nic.getMacAddress()); + _nicDao.update(vo.getId(), vo); + txn.commit(); + return true; + } + + @Override + public boolean release(Network network, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) + throws ConcurrentOperationException, ResourceUnavailableException { + return true; + } + + @Override + public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException { + return true; + } + + @Override + public boolean destroy(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException { + return true; + } + + @Override + public boolean isReady(PhysicalNetworkServiceProvider provider) { + return true; + } + + @Override + public boolean shutdownProviderInstances(PhysicalNetworkServiceProvider provider, ReservationContext context) throws ConcurrentOperationException, + ResourceUnavailableException { + return true; + } + + @Override + public boolean canEnableIndividualServices() { + return false; + } + + @Override + public boolean verifyServicesCombination(Set services) { + return true; + } + + public boolean addDhcpEntry(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, + ReservationContext context) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { + if (vm.getHypervisorType() != HypervisorType.BareMetal || !canHandle(dest, network.getTrafficType(), network.getGuestType())) { + return false; + } + return _dhcpMgr.addVirtualMachineIntoNetwork(network, nic, vm, dest, context); + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpManager.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpManager.java new file mode 100644 index 00000000000..a9c63bfed2b --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpManager.java @@ -0,0 +1,58 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.networkservice; + +import java.util.List; + +import com.cloud.baremetal.database.BaremetalDhcpVO; +import com.cloud.deploy.DeployDestination; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.Host; +import com.cloud.network.Network; +import com.cloud.network.Network.Provider; +import com.cloud.uservm.UserVm; +import com.cloud.utils.component.Manager; +import com.cloud.utils.component.PluggableService; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; + +public interface BaremetalDhcpManager extends Manager, PluggableService { + public static enum BaremetalDhcpType { + DNSMASQ, + DHCPD, + } + + boolean addVirtualMachineIntoNetwork(Network network, NicProfile nic, VirtualMachineProfile profile, DeployDestination dest, ReservationContext context) throws ResourceUnavailableException; + + BaremetalDhcpVO addDchpServer(AddBaremetalDhcpCmd cmd); + + BaremetalDhcpResponse generateApiResponse(BaremetalDhcpVO vo); + + List listBaremetalDhcps(ListBaremetalDhcpCmd cmd); + + public static final String BAREMETAL_DHCP_SERVICE_CAPABITLITY = "BaremetalDhcp"; + public static final String BAREMETAL_DHCP_SERVICE_PROPERTIES = "baremetaldhcp_commands.properties"; + public static final Provider BAREMETAL_DHCP_SERVICE_PROVIDER = new Provider("BaremetalDhcpProvider", true); +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpManagerImpl.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpManagerImpl.java new file mode 100755 index 00000000000..ae8482ca961 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpManagerImpl.java @@ -0,0 +1,323 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.networkservice; + +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupExternalDhcpCommand; +import com.cloud.agent.api.routing.DhcpEntryCommand; +import com.cloud.baremetal.database.BaremetalDhcpDao; +import com.cloud.baremetal.database.BaremetalDhcpVO; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.HostPodVO; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.HostPodDao; +import com.cloud.deploy.DeployDestination; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.Host; +import com.cloud.host.Host.Type; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.network.Network; +import com.cloud.network.PhysicalNetworkServiceProvider; +import com.cloud.network.PhysicalNetworkVO; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.dao.PhysicalNetworkServiceProviderDao; +import com.cloud.network.dao.PhysicalNetworkServiceProviderVO; +import com.cloud.resource.ResourceManager; +import com.cloud.resource.ResourceStateAdapter; +import com.cloud.resource.ServerResource; +import com.cloud.resource.UnableDeleteHostException; +import com.cloud.utils.component.Inject; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.SearchCriteria.Op; +import com.cloud.utils.db.SearchCriteria2; +import com.cloud.utils.db.SearchCriteriaService; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.dao.NicDao; +import com.cloud.vm.dao.UserVmDao; + +@Local(value = { BaremetalDhcpManager.class }) +public class BaremetalDhcpManagerImpl implements BaremetalDhcpManager, ResourceStateAdapter { + private static final org.apache.log4j.Logger s_logger = Logger.getLogger(BaremetalDhcpManagerImpl.class); + protected String _name; + @Inject + DataCenterDao _dcDao; + @Inject + HostDao _hostDao; + @Inject + AgentManager _agentMgr; + @Inject + HostPodDao _podDao; + @Inject + UserVmDao _userVmDao; + @Inject + ResourceManager _resourceMgr; + @Inject + NicDao _nicDao; + @Inject + PhysicalNetworkDao _physicalNetworkDao; + @Inject + PhysicalNetworkServiceProviderDao _physicalNetworkServiceProviderDao; + @Inject + BaremetalDhcpDao _extDhcpDao; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _resourceMgr.registerResourceStateAdapter(this.getClass().getSimpleName(), this); + return true; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + _resourceMgr.unregisterResourceStateAdapter(this.getClass().getSimpleName()); + return true; + } + + @Override + public String getName() { + return _name; + } + + protected String getDhcpServerGuid(String zoneId, String name, String ip) { + return zoneId + "-" + name + "-" + ip; + } + + @Override + public boolean addVirtualMachineIntoNetwork(Network network, NicProfile nic, VirtualMachineProfile profile, + DeployDestination dest, ReservationContext context) throws ResourceUnavailableException { + Long zoneId = profile.getVirtualMachine().getDataCenterIdToDeployIn(); + Long podId = profile.getVirtualMachine().getPodIdToDeployIn(); + List hosts = _resourceMgr.listAllUpAndEnabledHosts(Type.BaremetalDhcp, null, podId, zoneId); + if (hosts.size() == 0) { + throw new CloudRuntimeException("No external Dhcp found in zone " + zoneId + " pod " + podId); + } + + if (hosts.size() > 1) { + throw new CloudRuntimeException("Something wrong, more than 1 external Dhcp found in zone " + zoneId + " pod " + podId); + } + + HostVO h = hosts.get(0); + String dns = nic.getDns1(); + if (dns == null) { + dns = nic.getDns2(); + } + DhcpEntryCommand dhcpCommand = new DhcpEntryCommand(nic.getMacAddress(), nic.getIp4Address(), profile.getVirtualMachine().getHostName(), dns, + nic.getGateway()); + String errMsg = String.format("Set dhcp entry on external DHCP %1$s failed(ip=%2$s, mac=%3$s, vmname=%4$s)", h.getPrivateIpAddress(), + nic.getIp4Address(), nic.getMacAddress(), profile.getVirtualMachine().getHostName()); + // prepareBareMetalDhcpEntry(nic, dhcpCommand); + try { + Answer ans = _agentMgr.send(h.getId(), dhcpCommand); + if (ans.getResult()) { + s_logger.debug(String.format("Set dhcp entry on external DHCP %1$s successfully(ip=%2$s, mac=%3$s, vmname=%4$s)", h.getPrivateIpAddress(), + nic.getIp4Address(), nic.getMacAddress(), profile.getVirtualMachine().getHostName())); + return true; + } else { + s_logger.debug(errMsg + " " + ans.getDetails()); + throw new ResourceUnavailableException(errMsg, DataCenter.class, zoneId); + } + } catch (Exception e) { + s_logger.debug(errMsg, e); + throw new ResourceUnavailableException(errMsg + e.getMessage(), DataCenter.class, zoneId); + } + } + + @Override + public HostVO createHostVOForConnectedAgent(HostVO host, StartupCommand[] cmd) { + // TODO Auto-generated method stub + return null; + } + + @Override + public HostVO createHostVOForDirectConnectAgent(HostVO host, StartupCommand[] startup, ServerResource resource, Map details, + List hostTags) { + if (!(startup[0] instanceof StartupExternalDhcpCommand)) { + return null; + } + + host.setType(Host.Type.BaremetalDhcp); + return host; + } + + @Override + public DeleteHostAnswer deleteHost(HostVO host, boolean isForced, boolean isForceDeleteStorage) throws UnableDeleteHostException { + // TODO Auto-generated method stub + return null; + } + + + @Override + @DB + public BaremetalDhcpVO addDchpServer(AddBaremetalDhcpCmd cmd) { + PhysicalNetworkVO pNetwork = null; + long zoneId; + + if (cmd.getPhysicalNetworkId() == null || cmd.getUrl() == null || cmd.getUsername() == null || cmd.getPassword() == null) { + throw new IllegalArgumentException("At least one of the required parameters(physical network id, url, username, password) is null"); + } + + pNetwork = _physicalNetworkDao.findById(cmd.getPhysicalNetworkId()); + if (pNetwork == null) { + throw new IllegalArgumentException("Could not find phyical network with ID: " + cmd.getPhysicalNetworkId()); + } + zoneId = pNetwork.getDataCenterId(); + DataCenterVO zone = _dcDao.findById(zoneId); + + PhysicalNetworkServiceProviderVO ntwkSvcProvider = _physicalNetworkServiceProviderDao.findByServiceProvider(pNetwork.getId(), + BaremetalDhcpManager.BAREMETAL_DHCP_SERVICE_PROVIDER.getName()); + if (ntwkSvcProvider == null) { + throw new CloudRuntimeException("Network Service Provider: " + BaremetalDhcpManager.BAREMETAL_DHCP_SERVICE_PROVIDER.getName() + " is not enabled in the physical network: " + + cmd.getPhysicalNetworkId() + "to add this device"); + } else if (ntwkSvcProvider.getState() == PhysicalNetworkServiceProvider.State.Shutdown) { + throw new CloudRuntimeException("Network Service Provider: " + ntwkSvcProvider.getProviderName() + + " is in shutdown state in the physical network: " + cmd.getPhysicalNetworkId() + "to add this device"); + } + + HostPodVO pod = _podDao.findById(cmd.getPodId()); + if (pod == null) { + throw new IllegalArgumentException("Could not find pod with ID: " + cmd.getPodId()); + } + + List dhcps = _resourceMgr.listAllUpAndEnabledHosts(Host.Type.BaremetalDhcp, null, cmd.getPodId(), zoneId); + if (dhcps.size() != 0) { + throw new IllegalArgumentException("Already had a DHCP server in Pod: " + cmd.getPodId() + " zone: " + zoneId); + } + + URI uri; + try { + uri = new URI(cmd.getUrl()); + } catch (Exception e) { + s_logger.debug(e); + throw new IllegalArgumentException(e.getMessage()); + } + + String ipAddress = uri.getHost(); + String guid = getDhcpServerGuid(Long.toString(zoneId) + "-" + Long.toString(cmd.getPodId()), "ExternalDhcp", ipAddress); + Map params = new HashMap(); + params.put("type", cmd.getDhcpType()); + params.put("zone", Long.toString(zoneId)); + params.put("pod", cmd.getPodId().toString()); + params.put("ip", ipAddress); + params.put("username", cmd.getUsername()); + params.put("password", cmd.getPassword()); + params.put("guid", guid); + params.put("gateway", pod.getGateway()); + String dns = zone.getDns1(); + if (dns == null) { + dns = zone.getDns2(); + } + params.put("dns", dns); + + ServerResource resource = null; + try { + if (cmd.getDhcpType().equalsIgnoreCase(BaremetalDhcpType.DNSMASQ.toString())) { + resource = new BaremetalDnsmasqResource(); + resource.configure("Dnsmasq resource", params); + } else if (cmd.getDhcpType().equalsIgnoreCase(BaremetalDhcpType.DHCPD.toString())) { + resource = new BaremetalDhcpdResource(); + resource.configure("Dhcpd resource", params); + } else { + throw new CloudRuntimeException("Unsupport DHCP server type: " + cmd.getDhcpType()); + } + } catch (Exception e) { + s_logger.debug(e); + throw new CloudRuntimeException(e.getMessage()); + } + + Host dhcpServer = _resourceMgr.addHost(zoneId, resource, Host.Type.BaremetalDhcp, params); + if (dhcpServer == null) { + throw new CloudRuntimeException("Cannot add external Dhcp server as a host"); + } + + BaremetalDhcpVO vo = new BaremetalDhcpVO(); + vo.setDeviceType(cmd.getDhcpType()); + vo.setHostId(dhcpServer.getId()); + vo.setNetworkServiceProviderId(ntwkSvcProvider.getId()); + vo.setPhysicalNetworkId(cmd.getPhysicalNetworkId()); + vo.setPodId(cmd.getPodId()); + Transaction txn = Transaction.currentTxn(); + txn.start(); + _extDhcpDao.persist(vo); + txn.commit(); + return vo; + } + + @Override + public BaremetalDhcpResponse generateApiResponse(BaremetalDhcpVO vo) { + BaremetalDhcpResponse response = new BaremetalDhcpResponse(); + response.setDeviceType(vo.getDeviceType()); + response.setId(String.valueOf(vo.getId())); + response.setPhysicalNetworkId(String.valueOf(vo.getPhysicalNetworkId())); + response.setProviderId(String.valueOf(vo.getNetworkServiceProviderId())); + return response; + } + + @Override + public List listBaremetalDhcps(ListBaremetalDhcpCmd cmd) { + SearchCriteriaService sc = SearchCriteria2.create(BaremetalDhcpVO.class); + if (cmd.getDeviceType() != null) { + sc.addAnd(sc.getEntity().getDeviceType(), Op.EQ, cmd.getDeviceType()); + } + if (cmd.getPodId() != null) { + sc.addAnd(sc.getEntity().getPodId(), Op.EQ, cmd.getPodId()); + if (cmd.getId() != null) { + sc.addAnd(sc.getEntity().getId(), Op.EQ, cmd.getId()); + } + } + List vos = sc.list(); + List responses = new ArrayList(vos.size()); + for (BaremetalDhcpVO vo : vos) { + responses.add(generateApiResponse(vo)); + } + return responses; + } + + @Override + public List> getCommands() { + return null; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpResourceBase.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpResourceBase.java new file mode 100644 index 00000000000..054141580e9 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpResourceBase.java @@ -0,0 +1,174 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.networkservice; + +import java.util.HashMap; +import java.util.Map; + +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.IAgentControl; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.PingRoutingCommand; +import com.cloud.agent.api.ReadyAnswer; +import com.cloud.agent.api.ReadyCommand; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupExternalDhcpCommand; +import com.cloud.agent.api.StartupPxeServerCommand; +import com.cloud.host.Host.Type; +import com.cloud.resource.ServerResource; +import com.cloud.utils.script.Script; +import com.cloud.utils.ssh.SSHCmdHelper; +import com.cloud.vm.VirtualMachine.State; +import com.trilead.ssh2.SCPClient; + +public class BaremetalDhcpResourceBase implements ServerResource { + private static final Logger s_logger = Logger.getLogger(BaremetalDhcpResourceBase.class); + String _name; + String _guid; + String _username; + String _password; + String _ip; + String _zoneId; + String _podId; + String _gateway; + String _dns; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _name = name; + _guid = (String)params.get("guid"); + _ip = (String)params.get("ip"); + _username = (String)params.get("username"); + _password = (String)params.get("password"); + _zoneId = (String)params.get("zone"); + _podId = (String)params.get("pod"); + _gateway = (String)params.get("gateway"); + _dns = (String)params.get("dns"); + + if (_guid == null) { + throw new ConfigurationException("No Guid specified"); + } + + if (_zoneId == null) { + throw new ConfigurationException("No Zone specified"); + } + + if (_podId == null) { + throw new ConfigurationException("No Pod specified"); + } + + if (_ip == null) { + throw new ConfigurationException("No IP specified"); + } + + if (_username == null) { + throw new ConfigurationException("No username specified"); + } + + if (_password == null) { + throw new ConfigurationException("No password specified"); + } + + if (_gateway == null) { + throw new ConfigurationException("No gateway specified"); + } + + if (_dns == null) { + throw new ConfigurationException("No dns specified"); + } + + return true; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public String getName() { + return _name; + } + + @Override + public Type getType() { + return Type.BaremetalDhcp; + } + + @Override + public StartupCommand[] initialize() { + StartupExternalDhcpCommand cmd = new StartupExternalDhcpCommand(); + cmd.setName(_name); + cmd.setDataCenter(_zoneId); + cmd.setPod(_podId); + cmd.setPrivateIpAddress(_ip); + cmd.setStorageIpAddress(""); + cmd.setVersion(""); + cmd.setGuid(_guid); + return new StartupCommand[]{cmd}; + } + + @Override + public PingCommand getCurrentStatus(long id) { + //TODO: check server + return new PingRoutingCommand(getType(), id, new HashMap()); + } + + protected ReadyAnswer execute(ReadyCommand cmd) { + s_logger.debug("External DHCP resource " + _name + " is ready"); + return new ReadyAnswer(cmd); + } + + @Override + public Answer executeRequest(Command cmd) { + if (cmd instanceof ReadyCommand) { + return execute((ReadyCommand) cmd); + } else { + return Answer.createUnsupportedCommandAnswer(cmd); + } + } + + @Override + public void disconnected() { + } + + @Override + public IAgentControl getAgentControl() { + return null; + } + + @Override + public void setAgentControl(IAgentControl agentControl) { + } + +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpResponse.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpResponse.java new file mode 100755 index 00000000000..952ac41a701 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpResponse.java @@ -0,0 +1,71 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class BaremetalDhcpResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) @Param(description="device id of ") + private String id; + + @SerializedName(ApiConstants.PHYSICAL_NETWORK_ID) @Param(description="the physical network to which this external dhcp device belongs to") + private String physicalNetworkId; + + @SerializedName(ApiConstants.PROVIDER) @Param(description="name of the provider") + private String providerId; + + @SerializedName(ApiConstants.DHCP_SERVER_TYPE) @Param(description="name of the provider") + private String deviceType; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getPhysicalNetworkId() { + return physicalNetworkId; + } + + public void setPhysicalNetworkId(String physicalNetworkId) { + this.physicalNetworkId = physicalNetworkId; + } + + public String getProviderId() { + return providerId; + } + + public void setProviderId(String providerId) { + this.providerId = providerId; + } + + public String getDeviceType() { + return deviceType; + } + + public void setDeviceType(String deviceType) { + this.deviceType = deviceType; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpdResource.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpdResource.java new file mode 100755 index 00000000000..a27a6f26896 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpdResource.java @@ -0,0 +1,139 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.networkservice; + +import java.util.HashMap; +import java.util.Map; + +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.PingRoutingCommand; +import com.cloud.agent.api.routing.DhcpEntryCommand; +import com.cloud.utils.script.Script; +import com.cloud.utils.ssh.SSHCmdHelper; +import com.cloud.vm.VirtualMachine.State; +import com.trilead.ssh2.SCPClient; + +public class BaremetalDhcpdResource extends BaremetalDhcpResourceBase { + private static final Logger s_logger = Logger.getLogger(BaremetalDhcpdResource.class); + + public boolean configure(String name, Map params) throws ConfigurationException { + com.trilead.ssh2.Connection sshConnection = null; + try { + super.configure(name, params); + s_logger.debug(String.format("Trying to connect to DHCP server(IP=%1$s, username=%2$s, password=%3$s)", _ip, _username, "******")); + sshConnection = SSHCmdHelper.acquireAuthorizedConnection(_ip, _username, _password); + if (sshConnection == null) { + throw new ConfigurationException( + String.format("Cannot connect to DHCP server(IP=%1$s, username=%2$s, password=%3$s", _ip, _username, "******")); + } + + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, "[ -f '/usr/sbin/dhcpd' ]")) { + throw new ConfigurationException("Cannot find dhcpd.conf /etc/dhcpd.conf at on " + _ip); + } + + SCPClient scp = new SCPClient(sshConnection); + + String editHosts = "scripts/network/exdhcp/dhcpd_edithosts.py"; + String editHostsPath = Script.findScript("", editHosts); + if (editHostsPath == null) { + throw new ConfigurationException("Can not find script dnsmasq_edithosts.sh at " + editHosts); + } + scp.put(editHostsPath, "/usr/bin/", "0755"); + + String prepareDhcpdScript = "scripts/network/exdhcp/prepare_dhcpd.sh"; + String prepareDhcpdScriptPath = Script.findScript("", prepareDhcpdScript); + if (prepareDhcpdScriptPath == null) { + throw new ConfigurationException("Can not find prepare_dhcpd.sh at " + prepareDhcpdScriptPath); + } + scp.put(prepareDhcpdScriptPath, "/usr/bin/", "0755"); + + //TODO: tooooooooooooooo ugly here!!! + String[] ips = _ip.split("\\."); + ips[3] = "0"; + StringBuffer buf = new StringBuffer(); + int i; + for (i=0;i()); + } + } + + Answer execute(DhcpEntryCommand cmd) { + com.trilead.ssh2.Connection sshConnection = null; + try { + sshConnection = SSHCmdHelper.acquireAuthorizedConnection(_ip, _username, _password); + if (sshConnection == null) { + return new Answer(cmd, false, "ssh authenticate failed"); + } + String addDhcp = String.format("python /usr/bin/dhcpd_edithosts.py %1$s %2$s %3$s %4$s %5$s %6$s", + cmd.getVmMac(), cmd.getVmIpAddress(), cmd.getVmName(), cmd.getDns(), cmd.getGateway(), cmd.getNextServer()); + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, addDhcp)) { + return new Answer(cmd, false, "add Dhcp entry failed"); + } else { + return new Answer(cmd); + } + } finally { + SSHCmdHelper.releaseSshConnection(sshConnection); + } + } + + @Override + public Answer executeRequest(Command cmd) { + if (cmd instanceof DhcpEntryCommand) { + return execute((DhcpEntryCommand)cmd); + } else { + return super.executeRequest(cmd); + } + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDnsmasqResource.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDnsmasqResource.java new file mode 100644 index 00000000000..6841c525107 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDnsmasqResource.java @@ -0,0 +1,129 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.networkservice; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.PingRoutingCommand; +import com.cloud.agent.api.routing.DhcpEntryCommand; +import com.cloud.utils.script.Script; +import com.cloud.utils.ssh.SSHCmdHelper; +import com.cloud.vm.VirtualMachine.State; +import com.trilead.ssh2.SCPClient; + +public class BaremetalDnsmasqResource extends BaremetalDhcpResourceBase { + private static final Logger s_logger = Logger.getLogger(BaremetalDnsmasqResource.class); + + public boolean configure(String name, Map params) throws ConfigurationException { + com.trilead.ssh2.Connection sshConnection = null; + try { + super.configure(name, params); + s_logger.debug(String.format("Trying to connect to DHCP server(IP=%1$s, username=%2$s, password=%3$s)", _ip, _username, _password)); + sshConnection = SSHCmdHelper.acquireAuthorizedConnection(_ip, _username, _password); + if (sshConnection == null) { + throw new ConfigurationException( + String.format("Cannot connect to DHCP server(IP=%1$s, username=%2$s, password=%3$s", _ip, _username, _password)); + } + + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, "[ -f '/usr/sbin/dnsmasq' ]")) { + throw new ConfigurationException("Cannot find dnsmasq at /usr/sbin/dnsmasq on " + _ip); + } + + SCPClient scp = new SCPClient(sshConnection); + + String editHosts = "scripts/network/exdhcp/dnsmasq_edithosts.sh"; + String editHostsPath = Script.findScript("", editHosts); + if (editHostsPath == null) { + throw new ConfigurationException("Can not find script dnsmasq_edithosts.sh at " + editHosts); + } + scp.put(editHostsPath, "/usr/bin/", "0755"); + + String prepareDnsmasq = "scripts/network/exdhcp/prepare_dnsmasq.sh"; + String prepareDnsmasqPath = Script.findScript("", prepareDnsmasq); + if (prepareDnsmasqPath == null) { + throw new ConfigurationException("Can not find script prepare_dnsmasq.sh at " + prepareDnsmasq); + } + scp.put(prepareDnsmasqPath, "/usr/bin/", "0755"); + + String prepareCmd = String.format("sh /usr/bin/prepare_dnsmasq.sh %1$s %2$s %3$s", _gateway, _dns, _ip); + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, prepareCmd)) { + throw new ConfigurationException("prepare dnsmasq at " + _ip + " failed"); + } + + s_logger.debug("Dnsmasq resource configure successfully"); + return true; + } catch (Exception e) { + s_logger.debug("Dnsmasq resorce configure failed", e); + throw new ConfigurationException(e.getMessage()); + } finally { + SSHCmdHelper.releaseSshConnection(sshConnection); + } + } + + @Override + public PingCommand getCurrentStatus(long id) { + com.trilead.ssh2.Connection sshConnection = SSHCmdHelper.acquireAuthorizedConnection(_ip, _username, _password); + if (sshConnection == null) { + return null; + } else { + SSHCmdHelper.releaseSshConnection(sshConnection); + return new PingRoutingCommand(getType(), id, new HashMap()); + } + } + + Answer execute(DhcpEntryCommand cmd) { + com.trilead.ssh2.Connection sshConnection = null; + try { + sshConnection = SSHCmdHelper.acquireAuthorizedConnection(_ip, _username, _password); + if (sshConnection == null) { + return new Answer(cmd, false, "ssh authenticate failed"); + } + String addDhcp = String.format("/usr/bin/dnsmasq_edithosts.sh %1$s %2$s %3$s", cmd.getVmMac(), cmd.getVmIpAddress(), cmd.getVmName()); + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, addDhcp)) { + return new Answer(cmd, false, "add Dhcp entry failed"); + } else { + return new Answer(cmd); + } + } finally { + SSHCmdHelper.releaseSshConnection(sshConnection); + } + } + + @Override + public Answer executeRequest(Command cmd) { + if (cmd instanceof DhcpEntryCommand) { + return execute((DhcpEntryCommand)cmd); + } else { + return super.executeRequest(cmd); + } + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartPxeResource.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartPxeResource.java new file mode 100755 index 00000000000..938b3ac1d46 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartPxeResource.java @@ -0,0 +1,201 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.naming.ConfigurationException; + +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.PingRoutingCommand; +import com.cloud.agent.api.routing.VmDataCommand; +import com.cloud.utils.script.Script; +import com.cloud.utils.ssh.SSHCmdHelper; +import com.cloud.vm.VirtualMachine.State; +import com.trilead.ssh2.SCPClient; + +public class BaremetalKickStartPxeResource extends BaremetalPxeResourceBase { + private static final Logger s_logger = Logger.getLogger(BaremetalKickStartPxeResource.class); + private static final String _name = "BaremetalKickStartPxeResource"; + String _tftpDir; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + super.configure(name, params); + _tftpDir = (String) params.get(BaremetalPxeService.PXE_PARAM_TFTP_DIR); + if (_tftpDir == null) { + throw new ConfigurationException("No tftp directory specified"); + } + + com.trilead.ssh2.Connection sshConnection = new com.trilead.ssh2.Connection(_ip, 22); + + s_logger.debug(String.format("Trying to connect to kickstart PXE server(IP=%1$s, username=%2$s, password=%3$s", _ip, _username, "******")); + try { + sshConnection.connect(null, 60000, 60000); + if (!sshConnection.authenticateWithPassword(_username, _password)) { + s_logger.debug("SSH Failed to authenticate"); + throw new ConfigurationException(String.format("Cannot connect to kickstart PXE server(IP=%1$s, username=%2$s, password=%3$s", _ip, _username, + "******")); + } + + String cmd = String.format("[ -f /%1$s/pxelinux.0 ]", _tftpDir); + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, cmd)) { + throw new ConfigurationException("Miss files in TFTP directory at " + _tftpDir + " check if pxelinux.0 are here"); + } + + SCPClient scp = new SCPClient(sshConnection); + String prepareScript = "scripts/network/ping/prepare_kickstart_bootfile.py"; + String prepareScriptPath = Script.findScript("", prepareScript); + if (prepareScriptPath == null) { + throw new ConfigurationException("Can not find prepare_kickstart_bootfile.py at " + prepareScriptPath); + } + scp.put(prepareScriptPath, "/usr/bin/", "0755"); + + String cpScript = "scripts/network/ping/prepare_kickstart_kernel_initrd.py"; + String cpScriptPath = Script.findScript("", cpScript); + if (cpScriptPath == null) { + throw new ConfigurationException("Can not find prepare_kickstart_kernel_initrd.py at " + cpScriptPath); + } + scp.put(cpScriptPath, "/usr/bin/", "0755"); + + String userDataScript = "scripts/network/ping/baremetal_user_data.py"; + String userDataScriptPath = Script.findScript("", userDataScript); + if (userDataScriptPath == null) { + throw new ConfigurationException("Can not find baremetal_user_data.py at " + userDataScriptPath); + } + scp.put(userDataScriptPath, "/usr/bin/", "0755"); + + return true; + } catch (Exception e) { + throw new ConfigurationException(e.getMessage()); + } finally { + if (sshConnection != null) { + sshConnection.close(); + } + } + } + + @Override + public PingCommand getCurrentStatus(long id) { + com.trilead.ssh2.Connection sshConnection = SSHCmdHelper.acquireAuthorizedConnection(_ip, _username, _password); + if (sshConnection == null) { + return null; + } else { + SSHCmdHelper.releaseSshConnection(sshConnection); + return new PingRoutingCommand(getType(), id, new HashMap()); + } + } + + private Answer execute(VmDataCommand cmd) { + com.trilead.ssh2.Connection sshConnection = new com.trilead.ssh2.Connection(_ip, 22); + try { + List vmData = cmd.getVmData(); + StringBuilder sb = new StringBuilder(); + for (String[] data : vmData) { + String folder = data[0]; + String file = data[1]; + String contents = (data[2] == null) ? "none" : data[2]; + sb.append(cmd.getVmIpAddress()); + sb.append(","); + sb.append(folder); + sb.append(","); + sb.append(file); + sb.append(","); + sb.append(contents); + sb.append(";"); + } + String arg = StringUtils.stripEnd(sb.toString(), ";"); + + sshConnection.connect(null, 60000, 60000); + if (!sshConnection.authenticateWithPassword(_username, _password)) { + s_logger.debug("SSH Failed to authenticate"); + throw new ConfigurationException(String.format("Cannot connect to PING PXE server(IP=%1$s, username=%2$s, password=%3$s", _ip, _username, + _password)); + } + + String script = String.format("python /usr/bin/baremetal_user_data.py '%s'", arg); + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, script)) { + return new Answer(cmd, false, "Failed to add user data, command:" + script); + } + + return new Answer(cmd, true, "Success"); + } catch (Exception e){ + s_logger.debug("Prepare for creating baremetal template failed", e); + return new Answer(cmd, false, e.getMessage()); + } finally { + if (sshConnection != null) { + sshConnection.close(); + } + } + } + + @Override + public Answer executeRequest(Command cmd) { + if (cmd instanceof PrepareKickstartPxeServerCommand) { + return execute((PrepareKickstartPxeServerCommand) cmd); + } else if (cmd instanceof VmDataCommand) { + return execute((VmDataCommand)cmd); + } else { + return super.executeRequest(cmd); + } + } + + private Answer execute(PrepareKickstartPxeServerCommand cmd) { + com.trilead.ssh2.Connection sshConnection = new com.trilead.ssh2.Connection(_ip, 22); + try { + sshConnection.connect(null, 60000, 60000); + if (!sshConnection.authenticateWithPassword(_username, _password)) { + s_logger.debug("SSH Failed to authenticate"); + throw new ConfigurationException(String.format("Cannot connect to PING PXE server(IP=%1$s, username=%2$s, password=%3$s", _ip, _username, + _password)); + } + + String copyTo = String.format("%s/%s", _tftpDir, cmd.getTemplateUuid()); + String script = String.format("python /usr/bin/prepare_kickstart_kernel_initrd.py %s %s", cmd.getRepo(), copyTo); + + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, script)) { + return new Answer(cmd, false, "prepare kickstart at pxe server " + _ip + " failed, command:" + script); + } + + String kernelPath = String.format("%s/vmlinuz", cmd.getTemplateUuid()); + String initrdPath = String.format("%s/initrd.img", cmd.getTemplateUuid()); + script = String.format("python /usr/bin/prepare_kickstart_bootfile.py %s %s %s %s %s %s", _tftpDir, cmd.getMac(), kernelPath, initrdPath, cmd.getKsFile(), cmd.getMac()); + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, script)) { + return new Answer(cmd, false, "prepare kickstart at pxe server " + _ip + " failed, command:" + script); + } + + s_logger.debug("Prepare kickstart PXE server successfully"); + return new Answer(cmd, true, "Success"); + } catch (Exception e){ + s_logger.debug("Prepare for kickstart server failed", e); + return new Answer(cmd, false, e.getMessage()); + } finally { + if (sshConnection != null) { + sshConnection.close(); + } + } + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartServiceImpl.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartServiceImpl.java new file mode 100755 index 00000000000..4a2369b1ea4 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartServiceImpl.java @@ -0,0 +1,238 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.baremetal.IpmISetBootDevCommand; +import com.cloud.agent.api.baremetal.IpmISetBootDevCommand.BootDev; +import com.cloud.baremetal.database.BaremetalPxeDao; +import com.cloud.baremetal.database.BaremetalPxeVO; +import com.cloud.baremetal.networkservice.BaremetalPxeManager.BaremetalPxeType; +import com.cloud.deploy.DeployDestination; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDetailsDao; +import com.cloud.network.NetworkVO; +import com.cloud.network.PhysicalNetworkServiceProvider; +import com.cloud.network.PhysicalNetworkVO; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.dao.PhysicalNetworkServiceProviderDao; +import com.cloud.network.dao.PhysicalNetworkServiceProviderVO; +import com.cloud.resource.ResourceManager; +import com.cloud.resource.ServerResource; +import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.dao.VMTemplateDao; +import com.cloud.uservm.UserVm; +import com.cloud.utils.component.Inject; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.SearchCriteria.Op; +import com.cloud.utils.db.SearchCriteria2; +import com.cloud.utils.db.SearchCriteriaService; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachineProfile; + +@Local(value = BaremetalPxeService.class) +public class BaremetalKickStartServiceImpl extends BareMetalPxeServiceBase implements BaremetalPxeService { + private static final Logger s_logger = Logger.getLogger(BaremetalKickStartServiceImpl.class); + @Inject + ResourceManager _resourceMgr; + @Inject + PhysicalNetworkDao _physicalNetworkDao; + @Inject + PhysicalNetworkServiceProviderDao _physicalNetworkServiceProviderDao; + @Inject + HostDetailsDao _hostDetailsDao; + @Inject + BaremetalPxeDao _pxeDao; + @Inject + NetworkDao _nwDao; + @Inject + VMTemplateDao _tmpDao; + + @Override + public boolean prepare(VirtualMachineProfile profile, NicProfile nic, DeployDestination dest, ReservationContext context) { + NetworkVO nwVO = _nwDao.findById(nic.getNetworkId()); + SearchCriteriaService sc = SearchCriteria2.create(BaremetalPxeVO.class); + sc.addAnd(sc.getEntity().getDeviceType(), Op.EQ, BaremetalPxeType.KICK_START.toString()); + sc.addAnd(sc.getEntity().getPhysicalNetworkId(), Op.EQ, nwVO.getPhysicalNetworkId()); + BaremetalPxeVO pxeVo = sc.find(); + if (pxeVo == null) { + throw new CloudRuntimeException("No kickstart PXE server found in pod: " + dest.getPod().getId() + ", you need to add it before starting VM"); + } + VMTemplateVO template = _tmpDao.findById(profile.getTemplateId()); + + try { + String tpl = profile.getTemplate().getUrl(); + assert tpl != null : "How can a null template get here!!!"; + String[] tpls = tpl.split(";"); + assert tpls.length == 2 : "Template is not correctly encoded. " + tpl; + PrepareKickstartPxeServerCommand cmd = new PrepareKickstartPxeServerCommand(); + cmd.setKsFile(tpls[0]); + cmd.setRepo(tpls[1]); + cmd.setMac(nic.getMacAddress()); + cmd.setTemplateUuid(template.getUuid()); + Answer aws = _agentMgr.send(pxeVo.getHostId(), cmd); + if (!aws.getResult()) { + s_logger.warn("Unable to set host: " + dest.getHost().getId() + " to PXE boot because " + aws.getDetails()); + return aws.getResult(); + } + + IpmISetBootDevCommand bootCmd = new IpmISetBootDevCommand(BootDev.pxe); + aws = _agentMgr.send(dest.getHost().getId(), bootCmd); + if (!aws.getResult()) { + s_logger.warn("Unable to set host: " + dest.getHost().getId() + " to PXE boot because " + aws.getDetails()); + } + + return aws.getResult(); + } catch (Exception e) { + s_logger.warn("Cannot prepare PXE server", e); + return false; + } + } + + @Override + public boolean prepareCreateTemplate(Long pxeServerId, UserVm vm, String templateUrl) { + // TODO Auto-generated method stub + return false; + } + + @Override + @DB + public BaremetalPxeVO addPxeServer(AddBaremetalPxeCmd cmd) { + AddBaremetalKickStartPxeCmd kcmd = (AddBaremetalKickStartPxeCmd)cmd; + PhysicalNetworkVO pNetwork = null; + long zoneId; + + if (cmd.getPhysicalNetworkId() == null || cmd.getUrl() == null || cmd.getUsername() == null || cmd.getPassword() == null) { + throw new IllegalArgumentException("At least one of the required parameters(physical network id, url, username, password) is null"); + } + + pNetwork = _physicalNetworkDao.findById(cmd.getPhysicalNetworkId()); + if (pNetwork == null) { + throw new IllegalArgumentException("Could not find phyical network with ID: " + cmd.getPhysicalNetworkId()); + } + zoneId = pNetwork.getDataCenterId(); + + PhysicalNetworkServiceProviderVO ntwkSvcProvider = _physicalNetworkServiceProviderDao.findByServiceProvider(pNetwork.getId(), BaremetalPxeManager.BAREMETAL_PXE_SERVICE_PROVIDER.getName()); + if (ntwkSvcProvider == null) { + throw new CloudRuntimeException("Network Service Provider: " + BaremetalPxeManager.BAREMETAL_PXE_SERVICE_PROVIDER.getName() + + " is not enabled in the physical network: " + cmd.getPhysicalNetworkId() + "to add this device"); + } else if (ntwkSvcProvider.getState() == PhysicalNetworkServiceProvider.State.Shutdown) { + throw new CloudRuntimeException("Network Service Provider: " + ntwkSvcProvider.getProviderName() + + " is in shutdown state in the physical network: " + cmd.getPhysicalNetworkId() + "to add this device"); + } + + List pxes = _resourceMgr.listAllHostsInOneZoneByType(Host.Type.BaremetalPxe, zoneId); + if (!pxes.isEmpty()) { + throw new IllegalArgumentException("Already had a PXE server zone: " + zoneId); + } + + String tftpDir = kcmd.getTftpDir(); + if (tftpDir == null) { + throw new IllegalArgumentException("No TFTP directory specified"); + } + + URI uri; + try { + uri = new URI(cmd.getUrl()); + } catch (Exception e) { + s_logger.debug(e); + throw new IllegalArgumentException(e.getMessage()); + } + String ipAddress = uri.getHost(); + + String guid = getPxeServerGuid(Long.toString(zoneId), BaremetalPxeType.KICK_START.toString(), ipAddress); + + ServerResource resource = null; + Map params = new HashMap(); + params.put(BaremetalPxeService.PXE_PARAM_ZONE, Long.toString(zoneId)); + params.put(BaremetalPxeService.PXE_PARAM_IP, ipAddress); + params.put(BaremetalPxeService.PXE_PARAM_USERNAME, cmd.getUsername()); + params.put(BaremetalPxeService.PXE_PARAM_PASSWORD, cmd.getPassword()); + params.put(BaremetalPxeService.PXE_PARAM_TFTP_DIR, tftpDir); + params.put(BaremetalPxeService.PXE_PARAM_GUID, guid); + resource = new BaremetalKickStartPxeResource(); + try { + resource.configure("KickStart PXE resource", params); + } catch (Exception e) { + throw new CloudRuntimeException(e.getMessage(), e); + } + + Host pxeServer = _resourceMgr.addHost(zoneId, resource, Host.Type.BaremetalPxe, params); + if (pxeServer == null) { + throw new CloudRuntimeException("Cannot add PXE server as a host"); + } + + BaremetalPxeVO vo = new BaremetalPxeVO(); + Transaction txn = Transaction.currentTxn(); + vo.setHostId(pxeServer.getId()); + vo.setNetworkServiceProviderId(ntwkSvcProvider.getId()); + vo.setPhysicalNetworkId(kcmd.getPhysicalNetworkId()); + vo.setDeviceType(BaremetalPxeType.KICK_START.toString()); + txn.start(); + _pxeDao.persist(vo); + txn.commit(); + return vo; + } + + @Override + public BaremetalPxeResponse getApiResponse(BaremetalPxeVO vo) { + BaremetalPxeKickStartResponse response = new BaremetalPxeKickStartResponse(); + response.setId(String.valueOf(vo.getId())); + response.setPhysicalNetworkId(String.valueOf(vo.getPhysicalNetworkId())); + response.setPodId(String.valueOf(vo.getPodId())); + Map details = _hostDetailsDao.findDetails(vo.getHostId()); + response.setTftpDir(details.get(BaremetalPxeService.PXE_PARAM_TFTP_DIR)); + return response; + } + + @Override + public List listPxeServers(ListBaremetalPxePingServersCmd cmd) { + SearchCriteriaService sc = SearchCriteria2.create(BaremetalPxeVO.class); + sc.addAnd(sc.getEntity().getDeviceType(), Op.EQ, BaremetalPxeType.KICK_START.toString()); + if (cmd.getPodId() != null) { + sc.addAnd(sc.getEntity().getPodId(), Op.EQ, cmd.getPodId()); + if (cmd.getId() != null) { + sc.addAnd(sc.getEntity().getId(), Op.EQ, cmd.getId()); + } + } + List vos = sc.list(); + List responses = new ArrayList(vos.size()); + for (BaremetalPxeVO vo : vos) { + responses.add(getApiResponse(vo)); + } + return responses; + } + +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPingPxeResource.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPingPxeResource.java new file mode 100755 index 00000000000..2fb54154489 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPingPxeResource.java @@ -0,0 +1,260 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.networkservice; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.PingRoutingCommand; +import com.cloud.agent.api.baremetal.PreparePxeServerAnswer; +import com.cloud.agent.api.baremetal.PreparePxeServerCommand; +import com.cloud.agent.api.baremetal.prepareCreateTemplateCommand; +import com.cloud.agent.api.routing.VmDataCommand; +import com.cloud.utils.script.Script; +import com.cloud.utils.ssh.SSHCmdHelper; +import com.cloud.vm.VirtualMachine.State; +import com.trilead.ssh2.SCPClient; + +public class BaremetalPingPxeResource extends BaremetalPxeResourceBase { + private static final Logger s_logger = Logger.getLogger(BaremetalPingPxeResource.class); + private static final String _name = "BaremetalPingPxeResource"; + String _storageServer; + String _pingDir; + String _share; + String _dir; + String _tftpDir; + String _cifsUserName; + String _cifsPassword; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + super.configure(name, params); + _storageServer = (String)params.get(BaremetalPxeService.PXE_PARAM_PING_STORAGE_SERVER_IP); + _pingDir = (String)params.get(BaremetalPxeService.PXE_PARAM_PING_ROOT_DIR); + _tftpDir = (String)params.get(BaremetalPxeService.PXE_PARAM_TFTP_DIR); + _cifsUserName = (String)params.get(BaremetalPxeService.PXE_PARAM_PING_STORAGE_SERVER_USERNAME); + _cifsPassword = (String)params.get(BaremetalPxeService.PXE_PARAM_PING_STORAGE_SERVER_PASSWORD); + + if (_podId == null) { + throw new ConfigurationException("No Pod specified"); + } + + if (_storageServer == null) { + throw new ConfigurationException("No stroage server specified"); + } + + if (_tftpDir == null) { + throw new ConfigurationException("No tftp directory specified"); + } + + if (_pingDir == null) { + throw new ConfigurationException("No PING directory specified"); + } + + if (_cifsUserName == null || _cifsUserName.equalsIgnoreCase("")) { + _cifsUserName = "xxx"; + } + + if (_cifsPassword == null || _cifsPassword.equalsIgnoreCase("")) { + _cifsPassword = "xxx"; + } + + String pingDirs[]= _pingDir.split("/"); + if (pingDirs.length != 2) { + throw new ConfigurationException("PING dir should have format like myshare/direcotry, eg: windows/64bit"); + } + _share = pingDirs[0]; + _dir = pingDirs[1]; + + com.trilead.ssh2.Connection sshConnection = new com.trilead.ssh2.Connection(_ip, 22); + + s_logger.debug(String.format("Trying to connect to PING PXE server(IP=%1$s, username=%2$s, password=%3$s", _ip, _username, "******")); + try { + sshConnection.connect(null, 60000, 60000); + if (!sshConnection.authenticateWithPassword(_username, _password)) { + s_logger.debug("SSH Failed to authenticate"); + throw new ConfigurationException(String.format("Cannot connect to PING PXE server(IP=%1$s, username=%2$s, password=%3$s", _ip, _username, + "******")); + } + + String cmd = String.format("[ -f /%1$s/pxelinux.0 ] && [ -f /%2$s/kernel ] && [ -f /%3$s/initrd.gz ] ", _tftpDir, _tftpDir, _tftpDir); + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, cmd)) { + throw new ConfigurationException("Miss files in TFTP directory at " + _tftpDir + " check if pxelinux.0, kernel initrd.gz are here"); + } + + SCPClient scp = new SCPClient(sshConnection); + String prepareScript = "scripts/network/ping/prepare_tftp_bootfile.py"; + String prepareScriptPath = Script.findScript("", prepareScript); + if (prepareScriptPath == null) { + throw new ConfigurationException("Can not find prepare_tftp_bootfile.py at " + prepareScriptPath); + } + scp.put(prepareScriptPath, "/usr/bin/", "0755"); + + String userDataScript = "scripts/network/ping/baremetal_user_data.py"; + String userDataScriptPath = Script.findScript("", userDataScript); + if (userDataScriptPath == null) { + throw new ConfigurationException("Can not find baremetal_user_data.py at " + userDataScriptPath); + } + scp.put(userDataScriptPath, "/usr/bin/", "0755"); + + return true; + } catch (Exception e) { + throw new ConfigurationException(e.getMessage()); + } finally { + if (sshConnection != null) { + sshConnection.close(); + } + } + } + + @Override + public PingCommand getCurrentStatus(long id) { + com.trilead.ssh2.Connection sshConnection = SSHCmdHelper.acquireAuthorizedConnection(_ip, _username, _password); + if (sshConnection == null) { + return null; + } else { + SSHCmdHelper.releaseSshConnection(sshConnection); + return new PingRoutingCommand(getType(), id, new HashMap()); + } + } + + protected PreparePxeServerAnswer execute(PreparePxeServerCommand cmd) { + com.trilead.ssh2.Connection sshConnection = new com.trilead.ssh2.Connection(_ip, 22); + try { + sshConnection.connect(null, 60000, 60000); + if (!sshConnection.authenticateWithPassword(_username, _password)) { + s_logger.debug("SSH Failed to authenticate"); + throw new ConfigurationException(String.format("Cannot connect to PING PXE server(IP=%1$s, username=%2$s, password=%3$s", _ip, _username, + _password)); + } + + String script = String.format("python /usr/bin/prepare_tftp_bootfile.py restore %1$s %2$s %3$s %4$s %5$s %6$s %7$s %8$s %9$s %10$s %11$s", + _tftpDir, cmd.getMac(), _storageServer, _share, _dir, cmd.getTemplate(), _cifsUserName, _cifsPassword, cmd.getIp(), cmd.getNetMask(), cmd.getGateWay()); + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, script)) { + return new PreparePxeServerAnswer(cmd, "prepare PING at " + _ip + " failed, command:" + script); + } + s_logger.debug("Prepare Ping PXE server successfully"); + + return new PreparePxeServerAnswer(cmd); + } catch (Exception e){ + s_logger.debug("Prepare PING pxe server failed", e); + return new PreparePxeServerAnswer(cmd, e.getMessage()); + } finally { + if (sshConnection != null) { + sshConnection.close(); + } + } + } + + protected Answer execute(prepareCreateTemplateCommand cmd) { + com.trilead.ssh2.Connection sshConnection = new com.trilead.ssh2.Connection(_ip, 22); + try { + sshConnection.connect(null, 60000, 60000); + if (!sshConnection.authenticateWithPassword(_username, _password)) { + s_logger.debug("SSH Failed to authenticate"); + throw new ConfigurationException(String.format("Cannot connect to PING PXE server(IP=%1$s, username=%2$s, password=%3$s", _ip, _username, + _password)); + } + + String script = String.format("python /usr/bin/prepare_tftp_bootfile.py backup %1$s %2$s %3$s %4$s %5$s %6$s %7$s %8$s %9$s %10$s %11$s", + _tftpDir, cmd.getMac(), _storageServer, _share, _dir, cmd.getTemplate(), _cifsUserName, _cifsPassword, cmd.getIp(), cmd.getNetMask(), cmd.getGateWay()); + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, script)) { + return new Answer(cmd, false, "prepare for creating template failed, command:" + script); + } + s_logger.debug("Prepare for creating template successfully"); + + return new Answer(cmd, true, "Success"); + } catch (Exception e){ + s_logger.debug("Prepare for creating baremetal template failed", e); + return new Answer(cmd, false, e.getMessage()); + } finally { + if (sshConnection != null) { + sshConnection.close(); + } + } + } + + @Override + public Answer executeRequest(Command cmd) { + if (cmd instanceof PreparePxeServerCommand) { + return execute((PreparePxeServerCommand) cmd); + } else if (cmd instanceof prepareCreateTemplateCommand) { + return execute((prepareCreateTemplateCommand)cmd); + } else if (cmd instanceof VmDataCommand) { + return execute((VmDataCommand)cmd); + } else { + return super.executeRequest(cmd); + } + } + + private Answer execute(VmDataCommand cmd) { + com.trilead.ssh2.Connection sshConnection = new com.trilead.ssh2.Connection(_ip, 22); + try { + List vmData = cmd.getVmData(); + StringBuilder sb = new StringBuilder(); + for (String[] data : vmData) { + String folder = data[0]; + String file = data[1]; + String contents = (data[2] == null) ? "none" : data[2]; + sb.append(cmd.getVmIpAddress()); + sb.append(","); + sb.append(folder); + sb.append(","); + sb.append(file); + sb.append(","); + sb.append(contents); + sb.append(";"); + } + String arg = org.apache.commons.lang.StringUtils.stripEnd(sb.toString(), ";"); + + sshConnection.connect(null, 60000, 60000); + if (!sshConnection.authenticateWithPassword(_username, _password)) { + s_logger.debug("SSH Failed to authenticate"); + throw new ConfigurationException(String.format("Cannot connect to PING PXE server(IP=%1$s, username=%2$s, password=%3$s", _ip, _username, + _password)); + } + + String script = String.format("python /usr/bin/baremetal_user_data.py '%s'", arg); + if (!SSHCmdHelper.sshExecuteCmd(sshConnection, script)) { + return new Answer(cmd, false, "Failed to add user data, command:" + script); + } + + return new Answer(cmd, true, "Success"); + } catch (Exception e){ + s_logger.debug("Prepare for creating baremetal template failed", e); + return new Answer(cmd, false, e.getMessage()); + } finally { + if (sshConnection != null) { + sshConnection.close(); + } + } + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeElement.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeElement.java new file mode 100755 index 00000000000..99b9c432072 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeElement.java @@ -0,0 +1,178 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.baremetal.database.BaremetalPxeVO; +import com.cloud.dc.DataCenter.NetworkType; +import com.cloud.dc.Pod; +import com.cloud.deploy.DeployDestination; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.Network; +import com.cloud.network.Network.Capability; +import com.cloud.network.Network.GuestType; +import com.cloud.network.Network.Provider; +import com.cloud.network.Network.Service; +import com.cloud.network.Networks.TrafficType; +import com.cloud.network.PhysicalNetworkServiceProvider; +import com.cloud.network.element.NetworkElement; +import com.cloud.offering.NetworkOffering; +import com.cloud.utils.component.AdapterBase; +import com.cloud.utils.component.Inject; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.SearchCriteria.Op; +import com.cloud.utils.db.SearchCriteria2; +import com.cloud.utils.db.SearchCriteriaService; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.NicProfile; +import com.cloud.vm.NicVO; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachine.Type; +import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.dao.NicDao; +import com.cloud.vm.dao.VMInstanceDao; + +@Local(value = NetworkElement.class) +public class BaremetalPxeElement extends AdapterBase implements NetworkElement { + private static final Logger s_logger = Logger.getLogger(BaremetalPxeElement.class); + private static final Map> capabilities; + + @Inject BaremetalPxeManager _pxeMgr;; + @Inject VMInstanceDao _vmDao; + @Inject NicDao _nicDao; + + static { + Capability cap = new Capability(BaremetalPxeManager.BAREMETAL_PXE_CAPABILITY); + Map baremetalCaps = new HashMap(); + baremetalCaps.put(cap, null); + capabilities = new HashMap>(); + capabilities.put(BaremetalPxeManager.BAREMETAL_PXE_SERVICE, baremetalCaps); + } + + @Override + public Map> getCapabilities() { + return capabilities; + } + + @Override + public Provider getProvider() { + return BaremetalPxeManager.BAREMETAL_PXE_SERVICE_PROVIDER; + } + + private boolean canHandle(DeployDestination dest, TrafficType trafficType, GuestType networkType) { + Pod pod = dest.getPod(); + if (pod != null && dest.getDataCenter().getNetworkType() == NetworkType.Basic && trafficType == TrafficType.Guest) { + SearchCriteriaService sc = SearchCriteria2.create(BaremetalPxeVO.class); + sc.addAnd(sc.getEntity().getPodId(), Op.EQ, pod.getId()); + return sc.find() != null; + } + + return false; + } + + @Override + public boolean implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) + throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + if (offering.isSystemOnly() || !canHandle(dest, offering.getTrafficType(), network.getGuestType())) { + s_logger.debug("BaremetalPxeElement can not handle network offering: " + offering.getName()); + return false; + } + return true; + } + + @Override + @DB + public boolean prepare(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, + ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + if (vm.getType() != Type.User || vm.getHypervisorType() != HypervisorType.BareMetal) { + return false; + } + + VMInstanceVO vo = _vmDao.findById(vm.getId()); + if (vo.getLastHostId() == null) { + Transaction txn = Transaction.currentTxn(); + txn.start(); + nic.setMacAddress(dest.getHost().getPrivateMacAddress()); + NicVO nicVo = _nicDao.findById(nic.getId()); + assert vo != null : "Where ths nic " + nic.getId() + " going???"; + nicVo.setMacAddress(nic.getMacAddress()); + _nicDao.update(nicVo.getId(), nicVo); + txn.commit(); + + /*This vm is just being created */ + if (!_pxeMgr.prepare(vm, nic, dest, context)) { + throw new CloudRuntimeException("Cannot prepare pxe server"); + } + } + + return false; + } + + @Override + public boolean release(Network network, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) + throws ConcurrentOperationException, ResourceUnavailableException { + return true; + } + + @Override + public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException { + return true; + } + + @Override + public boolean isReady(PhysicalNetworkServiceProvider provider) { + return true; + } + + @Override + public boolean shutdownProviderInstances(PhysicalNetworkServiceProvider provider, ReservationContext context) throws ConcurrentOperationException, + ResourceUnavailableException { + return true; + } + + @Override + public boolean canEnableIndividualServices() { + return false; + } + + @Override + public boolean destroy(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean verifyServicesCombination(Set services) { + // TODO Auto-generated method stub + return false; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeKickStartResponse.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeKickStartResponse.java new file mode 100755 index 00000000000..09c6cc6769d --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeKickStartResponse.java @@ -0,0 +1,37 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import org.apache.cloudstack.api.ApiConstants; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class BaremetalPxeKickStartResponse extends BaremetalPxeResponse { + @SerializedName(ApiConstants.TFTP_DIR) @Param(description="Tftp root directory of PXE server") + private String tftpDir; + + public String getTftpDir() { + return tftpDir; + } + + public void setTftpDir(String tftpDir) { + this.tftpDir = tftpDir; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeManager.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeManager.java new file mode 100755 index 00000000000..e0a51627fc4 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeManager.java @@ -0,0 +1,65 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.networkservice; + +import java.util.List; + +import com.cloud.baremetal.database.BaremetalPxeVO; +import com.cloud.deploy.DeployDestination; +import com.cloud.host.HostVO; +import com.cloud.network.Network; +import com.cloud.network.Network.Provider; +import com.cloud.uservm.UserVm; +import com.cloud.utils.component.Manager; +import com.cloud.utils.component.PluggableService; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachineProfile; + +public interface BaremetalPxeManager extends Manager, PluggableService { + public enum BaremetalPxeType { + PING, + KICK_START, + } + + boolean prepare(VirtualMachineProfile profile, NicProfile nic, DeployDestination dest, ReservationContext context); + + boolean prepareCreateTemplate(Long pxeServerId, UserVm vm, String templateUrl); + + BaremetalPxeType getPxeServerType(HostVO host); + + BaremetalPxeVO addPxeServer(AddBaremetalPxeCmd cmd); + + BaremetalPxeResponse getApiResponse(BaremetalPxeVO vo); + + List listPxeServers(ListBaremetalPxePingServersCmd cmd); + + boolean addUserData(NicProfile nic, VirtualMachineProfile vm); + + public static final Network.Service BAREMETAL_PXE_SERVICE = new Network.Service("BaremetalPxeService"); + public static final String BAREMETAL_PXE_CAPABILITY = "BaremetalPxe"; + public static final String BAREMETAL_PXE_SERVICE_PROPERTIES = "baremetalpxe_commands.properties"; + public static final Provider BAREMETAL_PXE_SERVICE_PROVIDER = new Provider("BaremetalPxeProvider", true);; + public static final Provider BAREMETAL_USERDATA_PROVIDER = new Provider("BaremetaUserdataProvider", true); +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeManagerImpl.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeManagerImpl.java new file mode 100755 index 00000000000..94010ece567 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeManagerImpl.java @@ -0,0 +1,242 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.networkservice; + + +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupPxeServerCommand; +import com.cloud.agent.api.routing.VmDataCommand; +import com.cloud.baremetal.database.BaremetalPxeVO; +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.deploy.DeployDestination; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.network.PhysicalNetworkVO; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.resource.ResourceManager; +import com.cloud.resource.ResourceStateAdapter; +import com.cloud.resource.ServerResource; +import com.cloud.resource.UnableDeleteHostException; +import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.uservm.UserVm; +import com.cloud.utils.StringUtils; +import com.cloud.utils.component.Adapters; +import com.cloud.utils.component.Inject; +import com.cloud.utils.db.SearchCriteria.Op; +import com.cloud.utils.db.SearchCriteria2; +import com.cloud.utils.db.SearchCriteriaService; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.NicProfile; +import com.cloud.vm.NicVO; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.dao.NicDao; +import com.cloud.vm.dao.UserVmDao; + +@Local(value = {BaremetalPxeManager.class}) +public class BaremetalPxeManagerImpl implements BaremetalPxeManager, ResourceStateAdapter { + private static final org.apache.log4j.Logger s_logger = Logger.getLogger(BaremetalPxeManagerImpl.class); + protected String _name; + @Inject DataCenterDao _dcDao; + @Inject HostDao _hostDao; + @Inject AgentManager _agentMgr; + @Inject ResourceManager _resourceMgr; + @Inject(adapter=BaremetalPxeService.class) + protected Adapters _services; + @Inject UserVmDao _vmDao; + @Inject ServiceOfferingDao _serviceOfferingDao; + @Inject NicDao _nicDao; + @Inject ConfigurationDao _configDao; + @Inject PhysicalNetworkDao _phynwDao; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _name = name; + _resourceMgr.registerResourceStateAdapter(this.getClass().getSimpleName(), this); + return true; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + _resourceMgr.unregisterResourceStateAdapter(this.getClass().getSimpleName()); + return true; + } + + @Override + public String getName() { + return _name; + } + + protected BaremetalPxeService getServiceByType(String type) { + BaremetalPxeService _service; + _service = _services.get(type); + if (_service == null) { + throw new CloudRuntimeException("Cannot find PXE service for " + type); + } + return _service; + } + + @Override + public boolean prepare(VirtualMachineProfile profile, NicProfile nic, DeployDestination dest, ReservationContext context) { + //TODO: select type from template + BaremetalPxeType type = BaremetalPxeType.KICK_START; + return getServiceByType(type.toString()).prepare(profile, nic, dest, context); + } + + @Override + public boolean prepareCreateTemplate(Long pxeServerId, UserVm vm, String templateUrl) { + //TODO: select type from template + BaremetalPxeType type = BaremetalPxeType.PING; + return getServiceByType(type.toString()).prepareCreateTemplate(pxeServerId, vm, templateUrl); + } + + @Override + public BaremetalPxeType getPxeServerType(HostVO host) { + if (host.getResource().equalsIgnoreCase(BaremetalPingPxeResource.class.getName())) { + return BaremetalPxeType.PING; + } else { + throw new CloudRuntimeException("Unkown PXE server resource " + host.getResource()); + } + } + + @Override + public HostVO createHostVOForConnectedAgent(HostVO host, StartupCommand[] cmd) { + // TODO Auto-generated method stub + return null; + } + + @Override + public HostVO createHostVOForDirectConnectAgent(HostVO host, StartupCommand[] startup, ServerResource resource, Map details, + List hostTags) { + if (!(startup[0] instanceof StartupPxeServerCommand)) { + return null; + } + + host.setType(Host.Type.BaremetalPxe); + return host; + } + + @Override + public DeleteHostAnswer deleteHost(HostVO host, boolean isForced, boolean isForceDeleteStorage) throws UnableDeleteHostException { + // TODO Auto-generated method stub + return null; + } + + @Override + public BaremetalPxeVO addPxeServer(AddBaremetalPxeCmd cmd) { + return getServiceByType(cmd.getDeviceType()).addPxeServer(cmd); + } + + @Override + public BaremetalPxeResponse getApiResponse(BaremetalPxeVO vo) { + return getServiceByType(vo.getDeviceType()).getApiResponse(vo); + } + + @Override + public List listPxeServers(ListBaremetalPxePingServersCmd cmd) { + return getServiceByType(BaremetalPxeManager.BaremetalPxeType.PING.toString()).listPxeServers(cmd); + } + + @Override + public boolean addUserData(NicProfile nic, VirtualMachineProfile profile) { + UserVmVO vm = (UserVmVO) profile.getVirtualMachine(); + _vmDao.loadDetails(vm); + + String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getServiceOfferingId()).getDisplayText(); + String zoneName = _dcDao.findById(vm.getDataCenterIdToDeployIn()).getName(); + NicVO nvo = _nicDao.findById(nic.getId()); + VmDataCommand cmd = new VmDataCommand(nvo.getIp4Address(), vm.getInstanceName()); + cmd.addVmData("userdata", "user-data", vm.getUserData()); + cmd.addVmData("metadata", "service-offering", StringUtils.unicodeEscape(serviceOffering)); + cmd.addVmData("metadata", "availability-zone", StringUtils.unicodeEscape(zoneName)); + cmd.addVmData("metadata", "local-ipv4", nic.getIp4Address()); + cmd.addVmData("metadata", "local-hostname", StringUtils.unicodeEscape(vm.getInstanceName())); + cmd.addVmData("metadata", "public-ipv4", nic.getIp4Address()); + cmd.addVmData("metadata", "public-hostname", StringUtils.unicodeEscape(vm.getInstanceName())); + cmd.addVmData("metadata", "instance-id", String.valueOf(vm.getId())); + cmd.addVmData("metadata", "vm-id", String.valueOf(vm.getInstanceName())); + cmd.addVmData("metadata", "public-keys", null); + String cloudIdentifier = _configDao.getValue("cloud.identifier"); + if (cloudIdentifier == null) { + cloudIdentifier = ""; + } else { + cloudIdentifier = "CloudStack-{" + cloudIdentifier + "}"; + } + cmd.addVmData("metadata", "cloud-identifier", cloudIdentifier); + + List phys = _phynwDao.listByZone(vm.getDataCenterIdToDeployIn()); + if (phys.isEmpty()) { + throw new CloudRuntimeException(String.format("Cannot find physical network in zone %s", vm.getDataCenterIdToDeployIn())); + } + if (phys.size() > 1) { + throw new CloudRuntimeException(String.format("Baremetal only supports one physical network in zone, but zone %s has %s physical networks", vm.getDataCenterIdToDeployIn(), phys.size())); + } + PhysicalNetworkVO phy = phys.get(0); + + SearchCriteriaService sc = SearchCriteria2.create(BaremetalPxeVO.class); + //TODO: handle both kickstart and PING + //sc.addAnd(sc.getEntity().getPodId(), Op.EQ, vm.getPodIdToDeployIn()); + sc.addAnd(sc.getEntity().getPhysicalNetworkId(), Op.EQ, phy.getId()); + BaremetalPxeVO pxeVo = sc.find(); + if (pxeVo == null) { + throw new CloudRuntimeException("No PXE server found in pod: " + vm.getPodIdToDeployIn() + ", you need to add it before starting VM"); + } + + try { + Answer ans = _agentMgr.send(pxeVo.getHostId(), cmd); + if (!ans.getResult()) { + s_logger.debug(String.format("Add userdata to vm:%s failed because %s", vm.getInstanceName(), ans.getDetails())); + return false; + } else { + return true; + } + } catch (Exception e) { + s_logger.debug(String.format("Add userdata to vm:%s failed", vm.getInstanceName()), e); + return false; + } + } + + @Override + public List> getCommands() { + // TODO Auto-generated method stub + return null; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxePingResponse.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxePingResponse.java new file mode 100755 index 00000000000..adbf0530e00 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxePingResponse.java @@ -0,0 +1,59 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import org.apache.cloudstack.api.ApiConstants; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class BaremetalPxePingResponse extends BaremetalPxeResponse { + @SerializedName(ApiConstants.PING_STORAGE_SERVER_IP) @Param(description="PING storage server ip") + private String pingStorageServerIp; + + @SerializedName(ApiConstants.PING_DIR) @Param(description="Root directory on PING storage server") + private String pingDir; + + @SerializedName(ApiConstants.TFTP_DIR) @Param(description="Tftp root directory of PXE server") + private String tftpDir; + + public String getPingStorageServerIp() { + return pingStorageServerIp; + } + + public void setPingStorageServerIp(String pingStorageServerIp) { + this.pingStorageServerIp = pingStorageServerIp; + } + + public String getPingDir() { + return pingDir; + } + + public void setPingDir(String pingDir) { + this.pingDir = pingDir; + } + + public String getTftpDir() { + return tftpDir; + } + + public void setTftpDir(String tftpDir) { + this.tftpDir = tftpDir; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeResourceBase.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeResourceBase.java new file mode 100755 index 00000000000..34175c8b326 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeResourceBase.java @@ -0,0 +1,157 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.networkservice; + +import java.util.Map; + +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.IAgentControl; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.ReadyAnswer; +import com.cloud.agent.api.ReadyCommand; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupPxeServerCommand; +import com.cloud.host.Host.Type; +import com.cloud.resource.ServerResource; + +public class BaremetalPxeResourceBase implements ServerResource { + private static final Logger s_logger = Logger.getLogger(BaremetalPxeResourceBase.class); + String _name; + String _guid; + String _username; + String _password; + String _ip; + String _zoneId; + String _podId; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _name = name; + _guid = (String)params.get(BaremetalPxeService.PXE_PARAM_GUID); + _ip = (String)params.get(BaremetalPxeService.PXE_PARAM_IP); + _username = (String)params.get(BaremetalPxeService.PXE_PARAM_USERNAME); + _password = (String)params.get(BaremetalPxeService.PXE_PARAM_PASSWORD); + _zoneId = (String)params.get(BaremetalPxeService.PXE_PARAM_ZONE); + _podId = (String)params.get(BaremetalPxeService.PXE_PARAM_POD); + + if (_guid == null) { + throw new ConfigurationException("No Guid specified"); + } + + if (_zoneId == null) { + throw new ConfigurationException("No Zone specified"); + } + + if (_ip == null) { + throw new ConfigurationException("No IP specified"); + } + + if (_username == null) { + throw new ConfigurationException("No username specified"); + } + + if (_password == null) { + throw new ConfigurationException("No password specified"); + } + + return true; + } + + protected ReadyAnswer execute(ReadyCommand cmd) { + s_logger.debug("Pxe resource " + _name + " is ready"); + return new ReadyAnswer(cmd); + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public String getName() { + // TODO Auto-generated method stub + return _name; + } + + @Override + public Type getType() { + return Type.BaremetalPxe; + } + + @Override + public StartupCommand[] initialize() { + StartupPxeServerCommand cmd = new StartupPxeServerCommand(); + cmd.setName(_name); + cmd.setDataCenter(_zoneId); + cmd.setPod(_podId); + cmd.setPrivateIpAddress(_ip); + cmd.setStorageIpAddress(""); + cmd.setVersion(""); + cmd.setGuid(_guid); + return new StartupCommand[]{cmd}; + } + + @Override + public PingCommand getCurrentStatus(long id) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void disconnected() { + // TODO Auto-generated method stub + + } + + @Override + public IAgentControl getAgentControl() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setAgentControl(IAgentControl agentControl) { + // TODO Auto-generated method stub + + } + + @Override + public Answer executeRequest(Command cmd) { + if (cmd instanceof ReadyCommand) { + return execute((ReadyCommand) cmd); + } else { + return Answer.createUnsupportedCommandAnswer(cmd); + } + } + +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeResponse.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeResponse.java new file mode 100755 index 00000000000..2103020cfef --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeResponse.java @@ -0,0 +1,71 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class BaremetalPxeResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) @Param(description="device id of ") + private String id; + + @SerializedName(ApiConstants.PHYSICAL_NETWORK_ID) @Param(description="the physical network to which this external dhcp device belongs to") + private String physicalNetworkId; + + @SerializedName(ApiConstants.PROVIDER) @Param(description="name of the provider") + private String providerId; + + @SerializedName(ApiConstants.POD_ID) @Param(description="pod id where the device is in") + private String podId; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getPhysicalNetworkId() { + return physicalNetworkId; + } + + public void setPhysicalNetworkId(String physicalNetworkId) { + this.physicalNetworkId = physicalNetworkId; + } + + public String getProviderId() { + return providerId; + } + + public void setProviderId(String providerId) { + this.providerId = providerId; + } + + public String getPodId() { + return podId; + } + + public void setPodId(String podId) { + this.podId = podId; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeService.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeService.java new file mode 100644 index 00000000000..8504f82a86f --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeService.java @@ -0,0 +1,61 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +// Apache License, Version 2.0 (the "License"); you may not use this +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.baremetal.networkservice; + +import java.util.List; + +import com.cloud.baremetal.database.BaremetalPxeVO; +import com.cloud.deploy.DeployDestination; +import com.cloud.host.Host; +import com.cloud.uservm.UserVm; +import com.cloud.utils.component.Adapter; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachineProfile; + +public interface BaremetalPxeService extends Adapter { + + public boolean prepare(VirtualMachineProfile profile, NicProfile nic, DeployDestination dest, ReservationContext context); + + public boolean prepareCreateTemplate(Long pxeServerId, UserVm vm, String templateUrl); + + BaremetalPxeVO addPxeServer(AddBaremetalPxeCmd cmd); + + BaremetalPxeResponse getApiResponse(BaremetalPxeVO vo); + + List listPxeServers(ListBaremetalPxePingServersCmd cmd); + + public static final String PXE_PARAM_TYPE = "type"; + public static final String PXE_PARAM_ZONE = "zone"; + public static final String PXE_PARAM_POD = "pod"; + public static final String PXE_PARAM_IP = "ip"; + public static final String PXE_PARAM_GUID = "guid"; + public static final String PXE_PARAM_TFTP_DIR = "tftpDir"; + public static final String PXE_PARAM_USERNAME = "username"; + public static final String PXE_PARAM_PASSWORD = "password"; + public static final String PXE_PARAM_PING_STORAGE_SERVER_IP = "pingStorageServerIp"; + public static final String PXE_PARAM_PING_ROOT_DIR = "pingDir"; + public static final String PXE_PARAM_PING_STORAGE_SERVER_USERNAME = "pingStorageServerUserName"; + public static final String PXE_PARAM_PING_STORAGE_SERVER_PASSWORD = "pingStorageServerPassword"; +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalUserdataElement.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalUserdataElement.java new file mode 100755 index 00000000000..d1f0e8bf453 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalUserdataElement.java @@ -0,0 +1,169 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import java.util.HashMap; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.ejb.Local; + +import com.cloud.baremetal.manager.BaremetalManager; +import com.cloud.dc.DataCenter.NetworkType; +import com.cloud.deploy.DeployDestination; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.Network; +import com.cloud.network.Network.Capability; +import com.cloud.network.Network.GuestType; +import com.cloud.network.Network.Provider; +import com.cloud.network.Network.Service; +import com.cloud.network.Networks.TrafficType; +import com.cloud.network.PhysicalNetworkServiceProvider; +import com.cloud.network.element.IpDeployer; +import com.cloud.network.element.NetworkElement; +import com.cloud.network.element.UserDataServiceProvider; +import com.cloud.offering.NetworkOffering; +import com.cloud.uservm.UserVm; +import com.cloud.utils.component.AdapterBase; +import com.cloud.utils.component.Inject; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; + +@Local(value = NetworkElement.class) +public class BaremetalUserdataElement extends AdapterBase implements NetworkElement, UserDataServiceProvider { + private static Map> capabilities; + + @Inject + private BaremetalPxeManager pxeMgr; + + static { + capabilities = new HashMap>(); + capabilities.put(Service.UserData, null); + } + + private boolean canHandle(DeployDestination dest) { + if (dest.getDataCenter().getNetworkType() == NetworkType.Basic && dest.getHost().getHypervisorType() == HypervisorType.BareMetal) { + return true; + } + return false; + } + + @Override + public boolean addPasswordAndUserdata(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, + ReservationContext context) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { + if (!canHandle(dest)) { + return false; + } + + if (vm.getType() != VirtualMachine.Type.User) { + return false; + } + + return pxeMgr.addUserData(nic, (VirtualMachineProfile) vm); + } + + @Override + public boolean savePassword(Network network, NicProfile nic, VirtualMachineProfile vm) throws ResourceUnavailableException { + // TODO Auto-generated method stub + return false; + } + + @Override + public Map> getCapabilities() { + return capabilities; + } + + @Override + public Provider getProvider() { + return BaremetalPxeManager.BAREMETAL_USERDATA_PROVIDER; + } + + @Override + public boolean implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) + throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean prepare(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, + ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean release(Network network, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) + throws ConcurrentOperationException, ResourceUnavailableException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isReady(PhysicalNetworkServiceProvider provider) { + return true; + } + + @Override + public boolean shutdownProviderInstances(PhysicalNetworkServiceProvider provider, ReservationContext context) throws ConcurrentOperationException, + ResourceUnavailableException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean canEnableIndividualServices() { + // TODO Auto-generated method stub + return false; + } + + + @Override + public boolean saveUserData(Network network, NicProfile nic, VirtualMachineProfile vm) + throws ResourceUnavailableException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean destroy(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean verifyServicesCombination(Set services) { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/ListBaremetalDhcpCmd.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/ListBaremetalDhcpCmd.java new file mode 100755 index 00000000000..ba5128b6db0 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/ListBaremetalDhcpCmd.java @@ -0,0 +1,102 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import java.util.List; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.BaseCmd.CommandType; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.PlugService; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.log4j.Logger; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; + +public class ListBaremetalDhcpCmd extends BaseListCmd { + private static final Logger s_logger = Logger.getLogger(ListBaremetalDhcpCmd.class); + private static final String s_name = "listexternaldhcpresponse"; + @PlugService BaremetalDhcpManager _dhcpMgr; + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + @Parameter(name = ApiConstants.ID, type = CommandType.LONG, description = "DHCP server device ID") + private Long id; + + @Parameter(name = ApiConstants.POD_ID, type = CommandType.LONG, description = "Pod ID where pxe server is in") + private Long podId; + + @Parameter(name = ApiConstants.DHCP_SERVER_TYPE, type = CommandType.STRING, description = "Type of DHCP device") + private String deviceType; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getPodId() { + return podId; + } + + public void setPodId(Long podId) { + this.podId = podId; + } + + public String getDeviceType() { + return deviceType; + } + + public void setDeviceType(String deviceType) { + this.deviceType = deviceType; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, + ResourceAllocationException, NetworkRuleConflictException { + try { + ListResponse response = new ListResponse(); + List dhcpResponses = _dhcpMgr.listBaremetalDhcps(this); + response.setResponses(dhcpResponses); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } catch (Exception e) { + s_logger.debug("Exception happend while executing ListBaremetalDhcpCmd"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + + } + + @Override + public String getCommandName() { + return s_name; + } + +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/ListBaremetalPxePingServersCmd.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/ListBaremetalPxePingServersCmd.java new file mode 100755 index 00000000000..dceb8bfa8f2 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/ListBaremetalPxePingServersCmd.java @@ -0,0 +1,92 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import java.util.List; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.BaseCmd.CommandType; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.PlugService; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.log4j.Logger; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; + +public class ListBaremetalPxePingServersCmd extends BaseListCmd { + private static final Logger s_logger = Logger.getLogger(ListBaremetalPxePingServersCmd.class); + private static final String s_name = "listpingpxeserverresponse"; + + @PlugService + BaremetalPxeManager _pxeMgr; + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.LONG, description = "Ping pxe server device ID") + private Long id; + + @Parameter(name = ApiConstants.POD_ID, type = CommandType.LONG, description = "Pod ID where pxe server is in") + private Long podId; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getPodId() { + return podId; + } + + public void setPodId(Long podId) { + this.podId = podId; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, + ResourceAllocationException, NetworkRuleConflictException { + try { + ListResponse response = new ListResponse(); + List pxeResponses = _pxeMgr.listPxeServers(this); + response.setResponses(pxeResponses); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } catch (Exception e) { + s_logger.debug("Exception happend while executing ListPingPxeServersCmd" ,e); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public String getCommandName() { + return s_name; + } + +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/PrepareKickstartPxeServerCommand.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/PrepareKickstartPxeServerCommand.java new file mode 100755 index 00000000000..89515475062 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/PrepareKickstartPxeServerCommand.java @@ -0,0 +1,74 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Automatically generated by addcopyright.py at 01/29/2013 +package com.cloud.baremetal.networkservice; + +import com.cloud.agent.api.Command; + +public class PrepareKickstartPxeServerCommand extends Command { + private String ksFile; + private String repo; + private String templateUuid; + private String mac; + private String ksDevice; + + @Override + public boolean executeInSequence() { + return true; + } + + public String getKsFile() { + return ksFile; + } + + public void setKsFile(String ksFile) { + this.ksFile = ksFile; + } + + public String getRepo() { + return repo; + } + + public void setRepo(String repo) { + this.repo = repo; + } + + public String getTemplateUuid() { + return templateUuid; + } + + public void setTemplateUuid(String templateUuid) { + this.templateUuid = templateUuid; + } + + public String getMac() { + return mac; + } + + public void setMac(String mac) { + this.mac = mac; + } + + public String getKsDevice() { + return ksDevice; + } + + public void setKsDevice(String ksDevice) { + this.ksDevice = ksDevice; + } +} diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/SecurityGroupHttpClient.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/SecurityGroupHttpClient.java new file mode 100755 index 00000000000..203fed3f115 --- /dev/null +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/SecurityGroupHttpClient.java @@ -0,0 +1,18 @@ +package com.cloud.baremetal.networkservice; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.SecurityGroupRulesCmd; + +public class SecurityGroupHttpClient { + + public Answer call(String guestIp, SecurityGroupRulesCmd cmd) { + // TODO Auto-generated method stub + return null; + } + + public boolean echo(String ip, long millis, long millis2) { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/plugins/pom.xml b/plugins/pom.xml index 7bb60a990fb..c5b6e58e1bb 100644 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -42,6 +42,7 @@ hypervisors/xen hypervisors/kvm hypervisors/simulator + hypervisors/baremetal network-elements/elastic-loadbalancer network-elements/ovs network-elements/nicira-nvp diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index 4ae144e6ce1..5e4996b144c 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -358,7 +358,13 @@ public enum Config { DetailBatchQuerySize("Advanced", ManagementServer.class, Integer.class, "detail.batch.query.size", "2000", "Default entity detail batch query size for listing", null), ConcurrentSnapshotsThresholdPerHost("Advanced", ManagementServer.class, Long.class, "concurrent.snapshots.threshold.perhost", - null, "Limits number of snapshots that can be handled by the host concurrently; default is NULL - unlimited", null); + null, "Limits number of snapshots that can be handled by the host concurrently; default is NULL - unlimited", null), + + ExternalBaremetalSystemUrl("Advanced", ManagementServer.class, String.class, "external.baremetal.system.url", null, "url of external baremetal system that CloudStack will talk to", null), + ExternalBaremetalResourceClassName("Advanced", ManagementServer.class, String.class, "external,baremetal.resource.classname", null, "class name for handling external baremetal resource", null), + EnableBaremetalSecurityGroupAgentEcho("Advanced", ManagementServer.class, Boolean.class, "enable.baremetal.securitygroup.agent.echo", "false", "After starting provision process, periodcially echo security agent installed in the template. Treat provisioning as success only if echo successfully", null), + IntervalToEchoBaremetalSecurityGroupAgent("Advanced", ManagementServer.class, Integer.class, "interval.baremetal.securitygroup.agent.echo", "10", "Interval to echo baremetal security group agent, in seconds", null), + TimeoutToEchoBaremetalSecurityGroupAgent("Advanced", ManagementServer.class, Integer.class, "timeout.baremetal.securitygroup.agent.echo", "3600", "Timeout to echo baremetal security group agent, in seconds, the provisioning process will be treated as a failure", null); private final String _category; private final Class _componentClass;